You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mp...@apache.org on 2018/11/20 11:54:09 UTC

[ambari] branch trunk updated: AMBARI-24876. BE: Performance Tune service Configs Pages. (#2619)

This is an automated email from the ASF dual-hosted git repository.

mpapirkovskyy pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 4dde32c  AMBARI-24876. BE: Performance Tune service Configs Pages. (#2619)
4dde32c is described below

commit 4dde32c21251ad9485869f2976f0f4a95aa0e43d
Author: Myroslav Papirkovskyi <mp...@apache.org>
AuthorDate: Tue Nov 20 13:54:04 2018 +0200

    AMBARI-24876. BE: Performance Tune service Configs Pages. (#2619)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
    
    * AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
---
 .../ambari/server/NullHostNameException.java}      |  14 +-
 .../ambari/server/agent/stomp/TopologyHolder.java  |  26 +-
 .../server/agent/stomp/dto/TopologyCluster.java    |  40 ++-
 .../server/agent/stomp/dto/TopologyComponent.java  |  10 +-
 .../server/agent/stomp/dto/TopologyHost.java       |   3 -
 .../stomp/dto/TopologyUpdateHandlingReport.java    |  51 ++++
 .../StackAdvisorBlueprintProcessor.java            |  51 ++--
 .../services/stackadvisor/StackAdvisorHelper.java  |  83 +++++-
 .../services/stackadvisor/StackAdvisorRequest.java |  82 ++++--
 .../ComponentLayoutRecommendationCommand.java      |   3 +-
 ...igurationDependenciesRecommendationCommand.java |   5 +-
 .../ConfigurationRecommendationCommand.java        |  12 +-
 .../stackadvisor/commands/StackAdvisorCommand.java |  96 +++++--
 .../controller/AmbariManagementControllerImpl.java |   6 +-
 .../ambari/server/controller/AmbariServer.java     |   2 +-
 .../server/controller/KerberosHelperImpl.java      |  21 +-
 ...erverStackAdvisorAwareConfigurationHandler.java |  12 +-
 .../controller/internal/HostResourceProvider.java  |   3 +-
 .../internal/RecommendationResourceProvider.java   |  75 +++---
 .../internal/StackAdvisorResourceProvider.java     | 280 +++++++++++++++++----
 .../events/listeners/hosts/HostUpdateListener.java |  12 +-
 .../org/apache/ambari/server/state/Cluster.java    |  11 +-
 .../ambari/server/state/cluster/ClusterImpl.java   |  17 +-
 .../topology/STOMPComponentsDeleteHandler.java     |   4 +-
 .../server/agent/stomp/TopologyClusterTest.java    | 186 ++++++++++++++
 .../StackAdvisorBlueprintProcessorTest.java        |  13 +-
 .../stackadvisor/StackAdvisorHelperTest.java       | 149 ++++++++++-
 .../ConfigurationRecommendationCommandTest.java    |  15 +-
 .../commands/StackAdvisorCommandTest.java          |  98 +++++++-
 .../server/controller/KerberosHelperTest.java      |  18 +-
 .../AmbariServerSSOConfigurationHandlerTest.java   |   8 +-
 .../RecommendationResourceProviderTest.java        | 170 ++++++++++++-
 .../internal/StackAdvisorResourceProviderTest.java |   9 +-
 .../internal/ValidationResourceProviderTest.java   |   4 +-
 .../metrics/RestMetricsPropertyProviderTest.java   |  39 +--
 .../ambari/server/state/cluster/ClusterTest.java   |  10 +-
 .../app/controllers/main/service/info/configs.js   |   6 +
 .../app/mixins/common/configs/enhanced_configs.js  |  40 ++-
 .../mixins/common/configs/enhanced_configs_test.js |  63 ++++-
 39 files changed, 1428 insertions(+), 319 deletions(-)

diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java b/ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
similarity index 77%
copy from ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
copy to ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
index 8d02821..346000d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
@@ -16,14 +16,14 @@
  * limitations under the License.
  */
 
-package org.apache.ambari.server.controller.internal;
+package org.apache.ambari.server;
 
-import org.junit.Test;
-
-public class RecommendationResourceProviderTest {
-
-  @Test
-  public void testCreateResources() throws Exception {
+/**
+ * Thrown when an attempt is made to use null as host name
+ */
+public class NullHostNameException extends AmbariException {
 
+  public NullHostNameException(String message) {
+    super(message);
   }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
index 8caa57a..4eb877c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
@@ -29,6 +29,8 @@ import org.apache.ambari.server.ClusterNotFoundException;
 import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
 import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
 import org.apache.ambari.server.agent.stomp.dto.TopologyHost;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
 import org.apache.ambari.server.controller.AmbariManagementControllerImpl;
 import org.apache.ambari.server.events.ClusterComponentsRepoChangedEvent;
 import org.apache.ambari.server.events.TopologyAgentUpdateEvent;
@@ -61,6 +63,9 @@ public class TopologyHolder extends AgentClusterDataHolder<TopologyUpdateEvent>
   private Clusters clusters;
 
   @Inject
+  private StackAdvisorHelper stackAdvisorHelper;
+
+  @Inject
   public TopologyHolder(AmbariEventPublisher ambariEventPublisher) {
     ambariEventPublisher.register(this);
   }
@@ -100,13 +105,17 @@ public class TopologyHolder extends AgentClusterDataHolder<TopologyUpdateEvent>
               .filter(h -> hostNames.contains(h.getHostName()))
               .map(Host::getHostId)
               .collect(Collectors.toSet());
+            Set<String> hostOrderNames = clusterHosts.stream()
+              .filter(h -> hostNames.contains(h.getHostName()))
+              .map(Host::getHostName)
+              .collect(Collectors.toSet());
             String serviceName = sch.getServiceName();
             String componentName = sch.getServiceComponentName();
 
             TopologyComponent topologyComponent = TopologyComponent.newBuilder()
                 .setComponentName(sch.getServiceComponentName())
                 .setServiceName(sch.getServiceName())
-                .setHostIds(hostOrderIds)
+                .setHostIdentifiers(hostOrderIds, hostOrderNames)
                 .setComponentLevelParams(ambariManagementController.getTopologyComponentLevelParams(cl.getClusterId(), serviceName,
                     componentName, cl.getSecurityType()))
                 .setCommandParams(ambariManagementController.getTopologyCommandParams(cl.getClusterId(), serviceName, componentName, sch))
@@ -142,7 +151,7 @@ public class TopologyHolder extends AgentClusterDataHolder<TopologyUpdateEvent>
 
   @Override
   protected boolean handleUpdate(TopologyUpdateEvent update) throws AmbariException {
-    boolean changed = false;
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
     UpdateEventType eventType = update.getEventType();
     for (Map.Entry<String, TopologyCluster> updatedCluster : update.getClusters().entrySet()) {
       String clusterId = updatedCluster.getKey();
@@ -152,25 +161,24 @@ public class TopologyHolder extends AgentClusterDataHolder<TopologyUpdateEvent>
             CollectionUtils.isEmpty(cluster.getTopologyComponents()) &&
             CollectionUtils.isEmpty(cluster.getTopologyHosts())) {
           getData().getClusters().remove(clusterId);
-          changed = true;
+          report.mappingWasChanged();
         } else {
-          if (getData().getClusters().get(clusterId).update(
+          getData().getClusters().get(clusterId).update(
               update.getClusters().get(clusterId).getTopologyComponents(),
               update.getClusters().get(clusterId).getTopologyHosts(),
-              eventType)) {
-            changed = true;
-          }
+              eventType, report);
         }
       } else {
         if (eventType.equals(UpdateEventType.UPDATE)) {
           getData().getClusters().put(clusterId, cluster);
-          changed = true;
+          report.mappingWasChanged();
         } else {
           throw new ClusterNotFoundException(Long.parseLong(clusterId));
         }
       }
     }
-    return changed;
+    stackAdvisorHelper.clearCaches(report.getUpdatedHostNames());
+    return report.wasChanged();
   }
 
   private void prepareAgentTopology(TopologyUpdateEvent topologyUpdateEvent) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
index 2a49f43..272f1e1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
@@ -21,6 +21,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 
+import org.apache.ambari.server.NullHostNameException;
 import org.apache.ambari.server.events.UpdateEventType;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.SetUtils;
@@ -44,9 +45,8 @@ public class TopologyCluster {
     this.topologyHosts = topologyHosts;
   }
 
-  public boolean update(Set<TopologyComponent> componentsToUpdate, Set<TopologyHost> hostsToUpdate,
-                     UpdateEventType eventType) {
-    boolean changed = false;
+  public void update(Set<TopologyComponent> componentsToUpdate, Set<TopologyHost> hostsToUpdate,
+                     UpdateEventType eventType, TopologyUpdateHandlingReport report) throws NullHostNameException {
     for (TopologyComponent componentToUpdate : componentsToUpdate) {
       boolean isPresent = false;
       for (Iterator<TopologyComponent> iter = getTopologyComponents().iterator(); iter.hasNext() && !isPresent; ) {
@@ -55,19 +55,32 @@ public class TopologyCluster {
           if (eventType.equals(UpdateEventType.DELETE)) {
             if (SetUtils.isEqualSet(existsComponent.getHostIds(), componentToUpdate.getHostIds())) {
               iter.remove();
-              changed = true;
+              report.mappingWasChanged();
+              report.addHostsNames(componentToUpdate.getHostNames());
             } else {
-              changed |= existsComponent.removeComponent(componentToUpdate);
+              if (existsComponent.removeComponent(componentToUpdate)) {
+                report.mappingWasChanged();
+                report.addHostsNames(componentToUpdate.getHostNames());
+              }
             }
           } else {
-            changed |= existsComponent.updateComponent(componentToUpdate);
+            Set<String> preExistNames = new HashSet<>(existsComponent.getHostNames());
+            if (existsComponent.updateComponent(componentToUpdate)) {
+              report.mappingWasChanged();
+
+              // calc changed hosts
+              Set<String> namesToUpdate = new HashSet<>(componentToUpdate.getHostNames());
+              namesToUpdate.removeAll(preExistNames);
+              report.addHostsNames(namesToUpdate);
+            }
           }
           isPresent = true;
         }
       }
       if (!isPresent && eventType.equals(UpdateEventType.UPDATE)) {
         getTopologyComponents().add(componentToUpdate);
-        changed = true;
+        report.mappingWasChanged();
+        report.addHostsNames(componentToUpdate.getHostNames());
       }
     }
     for (TopologyHost hostToUpdate : hostsToUpdate) {
@@ -77,19 +90,24 @@ public class TopologyCluster {
         if (existsHost.equals(hostToUpdate)) {
           if (eventType.equals(UpdateEventType.DELETE)) {
             iter.remove();
-            changed = true;
+            report.mappingWasChanged();
+            report.addHostName(existsHost.getHostName());
           } else {
-            changed |= existsHost.updateHost(hostToUpdate);
+            if (existsHost.updateHost(hostToUpdate)) {
+              report.mappingWasChanged();
+              report.addHostName(existsHost.getHostName());
+              report.addHostName(hostToUpdate.getHostName());
+            }
           }
           isPresent = true;
         }
       }
       if (!isPresent && eventType.equals(UpdateEventType.UPDATE)) {
         getTopologyHosts().add(hostToUpdate);
-        changed = true;
+        report.mappingWasChanged();
+        report.addHostName(hostToUpdate.getHostName());
       }
     }
-    return changed;
   }
 
   public Set<TopologyComponent> getTopologyComponents() {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
index ef0871f..1351e79 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
@@ -75,12 +75,8 @@ public class TopologyComponent {
       return this;
     }
 
-    public Builder setHostIds(Set<Long> hostIds) {
+    public Builder setHostIdentifiers(Set<Long> hostIds, Set<String> hostNames) {
       TopologyComponent.this.setHostIds(hostIds);
-      return this;
-    }
-
-    public Builder setHostNames(Set<String> hostNames) {
       TopologyComponent.this.setHostNames(hostNames);
       return this;
     }
@@ -188,8 +184,8 @@ public class TopologyComponent {
         .setDisplayName(getDisplayName())
         .setServiceName(getServiceName())
         .setComponentLevelParams(getComponentLevelParams() == null ? null : new TreeMap<>(getComponentLevelParams()))
-        .setHostIds(getHostIds() == null ? null : new HashSet<>(getHostIds()))
-        .setHostNames(getHostNames() == null ? null : new HashSet<>(getHostNames()))
+        .setHostIdentifiers(getHostIds() == null ? null : new HashSet<>(getHostIds()),
+            getHostNames() == null ? null : new HashSet<>(getHostNames()))
         .setPublicHostNames(getPublicHostNames() == null ? null : new HashSet<>(getPublicHostNames()))
         .setCommandParams(getCommandParams() == null ? null : new TreeMap<>(getCommandParams()))
         .build();
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
index 8b1d43d..45ac78f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
@@ -28,9 +28,6 @@ public class TopologyHost {
   private String rackName;
   private String ipv4;
 
-  public TopologyHost() {
-  }
-
   public TopologyHost(Long hostId, String hostName) {
     this.hostId = hostId;
     this.hostName = hostName;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
new file mode 100644
index 0000000..6a616d8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.agent.stomp.dto;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ambari.server.NullHostNameException;
+
+public class TopologyUpdateHandlingReport {
+  private Set<String> updatedHostNames = new HashSet<>();
+  private boolean mappingChanged = false;
+
+  public boolean wasChanged(){
+    return mappingChanged || !updatedHostNames.isEmpty();
+  }
+
+  public Set<String> getUpdatedHostNames() {
+    return updatedHostNames;
+  }
+
+  public void addHostName(String updatedHostName) throws NullHostNameException {
+    if (updatedHostName == null) {
+      throw new NullHostNameException("Host name could not be a null");
+    }
+    this.updatedHostNames.add(updatedHostName);
+  }
+
+  public void addHostsNames(Set<String> updatedHostNames) {
+    this.updatedHostNames.addAll(updatedHostNames);
+  }
+
+  public void mappingWasChanged() {
+    this.mappingChanged = true;
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
index 273c0ff..54b7e1f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
@@ -19,12 +19,15 @@
 package org.apache.ambari.server.api.services.stackadvisor;
 
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.BlueprintConfigurations;
@@ -44,7 +47,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import com.google.inject.Singleton;
 
 /**
@@ -64,10 +66,10 @@ public class StackAdvisorBlueprintProcessor {
     stackAdvisorHelper = instance;
   }
 
-  private static final Map<String, String> userContext;
+  private static final SortedMap<String, String> userContext;
   static
   {
-    userContext = new HashMap<>();
+    userContext = new TreeMap<>();
     userContext.put("operation", "ClusterCreate");
   }
 
@@ -81,7 +83,7 @@ public class StackAdvisorBlueprintProcessor {
     try {
       RecommendationResponse response = stackAdvisorHelper.recommend(request);
       addAdvisedConfigurationsToTopology(response, clusterTopology, userProvidedConfigurations);
-    } catch (StackAdvisorException e) {
+    } catch (StackAdvisorException | AmbariException e) {
       throw new ConfigurationTopologyException(RECOMMENDATION_FAILED, e);
     } catch (IllegalArgumentException e) {
       throw new ConfigurationTopologyException(INVALID_RESPONSE, e);
@@ -90,9 +92,9 @@ public class StackAdvisorBlueprintProcessor {
 
   private StackAdvisorRequest createStackAdvisorRequest(ClusterTopology clusterTopology, StackAdvisorRequestType requestType) {
     Stack stack = clusterTopology.getBlueprint().getStack();
-    Map<String, Set<String>> hgComponentsMap = gatherHostGroupComponents(clusterTopology);
-    Map<String, Set<String>> hgHostsMap = gatherHostGroupBindings(clusterTopology);
-    Map<String, Set<String>> componentHostsMap = gatherComponentsHostsMap(hgComponentsMap,
+    SortedMap<String, SortedSet<String>> hgComponentsMap = gatherHostGroupComponents(clusterTopology);
+    SortedMap<String, SortedSet<String>> hgHostsMap = gatherHostGroupBindings(clusterTopology);
+    SortedMap<String, SortedSet<String>> componentHostsMap = gatherComponentsHostsMap(hgComponentsMap,
             hgHostsMap);
     return StackAdvisorRequest.StackAdvisorRequestBuilder
       .forStack(stack.getName(), stack.getVersion())
@@ -107,46 +109,47 @@ public class StackAdvisorBlueprintProcessor {
       .build();
   }
 
-  private Map<String, Set<String>> gatherHostGroupBindings(ClusterTopology clusterTopology) {
-    Map<String, Set<String>> hgBindngs = Maps.newHashMap();
+  private SortedMap<String, SortedSet<String>> gatherHostGroupBindings(ClusterTopology clusterTopology) {
+    SortedMap<String, SortedSet<String>> hgBindngs = Maps.newTreeMap();
     for (Map.Entry<String, HostGroupInfo> hgEnrty: clusterTopology.getHostGroupInfo().entrySet()) {
-      hgBindngs.put(hgEnrty.getKey(), Sets.newCopyOnWriteArraySet(hgEnrty.getValue().getHostNames()));
+      hgBindngs.put(hgEnrty.getKey(), new TreeSet<>(hgEnrty.getValue().getHostNames()));
     }
     return hgBindngs;
   }
 
-  private Map<String, Set<String>> gatherHostGroupComponents(ClusterTopology clusterTopology) {
-    Map<String, Set<String>> hgComponentsMap = Maps.newHashMap();
+  private SortedMap<String, SortedSet<String>> gatherHostGroupComponents(ClusterTopology clusterTopology) {
+    SortedMap<String, SortedSet<String>> hgComponentsMap = Maps.newTreeMap();
     for (Map.Entry<String, HostGroup> hgEnrty: clusterTopology.getBlueprint().getHostGroups().entrySet()) {
-      hgComponentsMap.put(hgEnrty.getKey(), Sets.newCopyOnWriteArraySet(hgEnrty.getValue().getComponentNames()));
+      hgComponentsMap.put(hgEnrty.getKey(), new TreeSet<>(hgEnrty.getValue().getComponentNames()));
     }
     return hgComponentsMap;
   }
 
-  private Map<String, Map<String, Map<String, String>>> calculateConfigs(ClusterTopology clusterTopology) {
-    Map<String, Map<String, Map<String, String>>> result = Maps.newHashMap();
+  private SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigs(ClusterTopology clusterTopology) {
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> result = Maps.newTreeMap();
     Map<String, Map<String, String>> fullProperties = clusterTopology.getConfiguration().getFullProperties();
     for (Map.Entry<String, Map<String, String>> siteEntry : fullProperties.entrySet()) {
-      Map<String, Map<String, String>> propsMap = Maps.newHashMap();
-      propsMap.put("properties", siteEntry.getValue());
+      SortedMap<String, SortedMap<String, String>> propsMap = Maps.newTreeMap();
+      propsMap.put("properties", new TreeMap<>(siteEntry.getValue()));
       result.put(siteEntry.getKey(), propsMap);
     }
     return result;
   }
 
-  private Map<String, Set<String>> gatherComponentsHostsMap(Map<String, Set<String>> hostGroups, Map<String, Set<String>> bindingHostGroups) {
-    Map<String, Set<String>> componentHostsMap = new HashMap<>();
+  private SortedMap<String, SortedSet<String>> gatherComponentsHostsMap(SortedMap<String, SortedSet<String>> hostGroups,
+                                                                  SortedMap<String, SortedSet<String>> bindingHostGroups) {
+    SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
     if (null != bindingHostGroups && null != hostGroups) {
-      for (Map.Entry<String, Set<String>> hgComponents : hostGroups.entrySet()) {
+      for (Map.Entry<String, SortedSet<String>> hgComponents : hostGroups.entrySet()) {
         String hgName = hgComponents.getKey();
         Set<String> components = hgComponents.getValue();
 
         Set<String> hosts = bindingHostGroups.get(hgName);
         if (hosts != null) {
           for (String component : components) {
-            Set<String> componentHosts = componentHostsMap.get(component);
+            SortedSet<String> componentHosts = componentHostsMap.get(component);
             if (componentHosts == null) { // if was not initialized
-              componentHosts = new HashSet<>();
+              componentHosts = new TreeSet<>();
               componentHostsMap.put(component, componentHosts);
             }
             componentHosts.addAll(hosts);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
index d3f2072..e92bef8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
@@ -20,6 +20,12 @@ package org.apache.ambari.server.api.services.stackadvisor;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -36,7 +42,11 @@ import org.apache.ambari.server.api.services.stackadvisor.validations.Validation
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
 import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.JsonNode;
 
+import com.google.gson.Gson;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -44,25 +54,33 @@ import com.google.inject.Singleton;
 @Singleton
 public class StackAdvisorHelper {
 
+  protected static Log LOG = LogFactory.getLog(StackAdvisorHelper.class);
+
   private File recommendationsDir;
   private String recommendationsArtifactsLifetime;
   private int recommendationsArtifactsRolloverMax;
   private final AmbariMetaInfo metaInfo;
   private final AmbariServerConfigurationHandler ambariServerConfigurationHandler;
+  private final Gson gson;
 
   /* Monotonically increasing requestid */
   private int requestId = 0;
   private StackAdvisorRunner saRunner;
 
+  private Map<String, JsonNode> hostInfoCache = new ConcurrentHashMap<>();
+  private Map<String, RecommendationResponse> configsRecommendationResponse = new ConcurrentHashMap<>();
+
+
   @Inject
-  public StackAdvisorHelper(Configuration conf, StackAdvisorRunner saRunner,
-                            AmbariMetaInfo metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) throws IOException {
+  public StackAdvisorHelper(Configuration conf, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo,
+                            AmbariServerConfigurationHandler ambariServerConfigurationHandler, Gson gson) throws IOException {
     this.recommendationsDir = conf.getRecommendationsDir();
     this.recommendationsArtifactsLifetime = conf.getRecommendationsArtifactsLifetime();
     this.recommendationsArtifactsRolloverMax = conf.getRecommendationsArtifactsRolloverMax();
     this.saRunner = saRunner;
     this.metaInfo = metaInfo;
     this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+    this.gson = gson;
   }
 
   /**
@@ -115,7 +133,7 @@ public class StackAdvisorHelper {
    * @throws StackAdvisorException in case of stack advisor script errors
    */
   public synchronized RecommendationResponse recommend(StackAdvisorRequest request)
-      throws StackAdvisorException {
+      throws StackAdvisorException, AmbariException {
       requestId = generateRequestId();
 
     // TODO, need to pass the service Name that was modified.
@@ -125,7 +143,40 @@ public class StackAdvisorHelper {
     ServiceInfo.ServiceAdvisorType serviceAdvisorType = getServiceAdvisorType(request.getStackName(), request.getStackVersion(), serviceName);
     StackAdvisorCommand<RecommendationResponse> command = createRecommendationCommand(serviceName, request);
 
-    return command.invoke(request, serviceAdvisorType);
+    StackAdvisorRequestType requestType = request.getRequestType();
+    RecommendationResponse response = null;
+    if (requestType == StackAdvisorRequestType.CONFIGURATIONS) {
+      String hash = getHash(request);
+      LOG.info(String.format("Calling stack advisor with hash: %s, service: %s", hash, request.getServiceName()));
+      response = configsRecommendationResponse.computeIfAbsent(hash, h -> {
+        try {
+          LOG.info(String.format("Invoking configuration stack advisor command with hash: %s, service: %s", hash, request.getServiceName()));
+          return command.invoke(request, serviceAdvisorType);
+        } catch (StackAdvisorException e) {
+          return null;
+        }
+      });
+    }
+
+    return response == null ? command.invoke(request, serviceAdvisorType) : response;
+  }
+
+  protected String getHash(StackAdvisorRequest request) {
+    String json = gson.toJson(request);
+    String generatedPassword = null;
+    try {
+      MessageDigest md = MessageDigest.getInstance("SHA-512");
+      md.update("".getBytes("UTF-8"));
+      byte[] bytes = md.digest(json.getBytes("UTF-8"));
+      StringBuilder sb = new StringBuilder();
+      for (byte b : bytes) {
+        sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
+      }
+      generatedPassword = sb.toString();
+    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+      e.printStackTrace();
+    }
+    return generatedPassword;
   }
 
   StackAdvisorCommand<RecommendationResponse> createRecommendationCommand(String serviceName, StackAdvisorRequest request) throws StackAdvisorException {
@@ -138,16 +189,16 @@ public class StackAdvisorHelper {
           requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
     } else if (requestType == StackAdvisorRequestType.CONFIGURATIONS) {
       command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
-          requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+          requestId, saRunner, metaInfo, ambariServerConfigurationHandler, hostInfoCache);
     } else if (requestType == StackAdvisorRequestType.SSO_CONFIGURATIONS) {
       command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_SSO, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
-          requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+          requestId, saRunner, metaInfo, ambariServerConfigurationHandler, null);
     } else if (requestType == StackAdvisorRequestType.LDAP_CONFIGURATIONS) {
       command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_LDAP, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
-          requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+          requestId, saRunner, metaInfo, ambariServerConfigurationHandler, null);
     } else if (requestType == StackAdvisorRequestType.KERBEROS_CONFIGURATIONS) {
       command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_KERBEROS, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
-          requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+          requestId, saRunner, metaInfo, ambariServerConfigurationHandler, null);
     } else if (requestType == StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES) {
       command = new ConfigurationDependenciesRecommendationCommand(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
           requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
@@ -188,4 +239,20 @@ public class StackAdvisorHelper {
 
   }
 
+  public void clearCaches(String hostName) {
+    configsRecommendationResponse.clear();
+    hostInfoCache.remove(hostName);
+    LOG.info("Clear stack advisor caches, host: " + hostName);
+  }
+
+  public void clearCaches(Set<String> hostNames) {
+    if (hostNames != null && !hostNames.isEmpty()) {
+      configsRecommendationResponse.clear();
+      for (String hostName : hostNames) {
+        hostInfoCache.remove(hostName);
+      }
+    }
+    LOG.info("Clear stack advisor caches, hosts: " + hostNames.toString());
+  }
+
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
index ea4a4e0..de6448b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
@@ -21,11 +21,11 @@ package org.apache.ambari.server.api.services.stackadvisor;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
 
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.state.ChangedConfigInfo;
@@ -38,20 +38,23 @@ import com.google.common.base.Preconditions;
  */
 public class StackAdvisorRequest {
 
+  private Long clusterId;
+  private String serviceName;
   private String stackName;
   private String stackVersion;
   private StackAdvisorRequestType requestType;
   private List<String> hosts = new ArrayList<>();
   private Collection<String> services = new ArrayList<>();
-  private Map<String, Set<String>> componentHostsMap = new HashMap<>();
-  private Map<String, Set<String>> hostComponents = new HashMap<>();
-  private Map<String, Set<String>> hostGroupBindings = new HashMap<>();
-  private Map<String, Map<String, Map<String, String>>> configurations = new HashMap<>();
+  private SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
+  private SortedMap<String, SortedSet<String>> hostComponents = new TreeMap<>();
+  private SortedMap<String, SortedSet<String>> hostGroupBindings = new TreeMap<>();
+  private SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
   private List<ChangedConfigInfo> changedConfigurations = new LinkedList<>();
-  private Set<RecommendationResponse.ConfigGroup> configGroups;
-  private Map<String, String> userContext = new HashMap<>();
-  private Map<String, Object> ldapConfig = new HashMap<>();
+  private SortedSet<RecommendationResponse.ConfigGroup> configGroups;
+  private SortedMap<String, String> userContext = new TreeMap<>();
+  private SortedMap<String, Object> ldapConfig = new TreeMap<>();
   private Boolean gplLicenseAccepted;
+  private Boolean configsResponse = false;
 
   public String getStackName() {
     return stackName;
@@ -73,7 +76,7 @@ public class StackAdvisorRequest {
     return services;
   }
 
-  public Map<String, Set<String>> getComponentHostsMap() {
+  public SortedMap<String, SortedSet<String>> getComponentHostsMap() {
     return componentHostsMap;
   }
 
@@ -85,19 +88,19 @@ public class StackAdvisorRequest {
     return StringUtils.join(services, ",");
   }
 
-  public Map<String, Set<String>> getHostComponents() {
+  public SortedMap<String, SortedSet<String>> getHostComponents() {
     return hostComponents;
   }
 
-  public Map<String, Set<String>> getHostGroupBindings() {
+  public SortedMap<String, SortedSet<String>> getHostGroupBindings() {
     return hostGroupBindings;
   }
 
-  public Map<String, Map<String, Map<String, String>>> getConfigurations() {
+  public SortedMap<String, SortedMap<String, SortedMap<String, String>>> getConfigurations() {
     return configurations;
   }
 
-  public Map<String, Object> getLdapConfig() { return ldapConfig; }
+  public SortedMap<String, Object> getLdapConfig() { return ldapConfig; }
 
   public List<ChangedConfigInfo> getChangedConfigurations() {
     return changedConfigurations;
@@ -107,22 +110,30 @@ public class StackAdvisorRequest {
     this.changedConfigurations = changedConfigurations;
   }
 
-  public Map<String, String> getUserContext() {
+  public SortedMap<String, String> getUserContext() {
     return this.userContext;
   }
 
-  public void setUserContext(Map<String, String> userContext) {
+  public void setUserContext(SortedMap<String, String> userContext) {
     this.userContext = userContext;
   }
 
-  public Set<RecommendationResponse.ConfigGroup> getConfigGroups() {
+  public SortedSet<RecommendationResponse.ConfigGroup> getConfigGroups() {
     return configGroups;
   }
 
-  public void setConfigGroups(Set<RecommendationResponse.ConfigGroup> configGroups) {
+  public void setConfigGroups(SortedSet<RecommendationResponse.ConfigGroup> configGroups) {
     this.configGroups = configGroups;
   }
 
+  public Long getClusterId() {
+    return clusterId;
+  }
+
+  public String getServiceName() {
+    return serviceName;
+  }
+
   /**
    * @return true if GPL license is accepted, false otherwise
    */
@@ -130,6 +141,10 @@ public class StackAdvisorRequest {
     return gplLicenseAccepted;
   }
 
+  public Boolean getConfigsResponse() {
+    return configsResponse;
+  }
+
   private StackAdvisorRequest(String stackName, String stackVersion) {
     this.stackName = stackName;
     this.stackVersion = stackVersion;
@@ -162,24 +177,24 @@ public class StackAdvisorRequest {
     }
 
     public StackAdvisorRequestBuilder withComponentHostsMap(
-        Map<String, Set<String>> componentHostsMap) {
+        SortedMap<String, SortedSet<String>> componentHostsMap) {
       this.instance.componentHostsMap = componentHostsMap;
       return this;
     }
 
-    public StackAdvisorRequestBuilder forHostComponents(Map<String, Set<String>> hostComponents) {
+    public StackAdvisorRequestBuilder forHostComponents(SortedMap<String, SortedSet<String>> hostComponents) {
       this.instance.hostComponents = hostComponents;
       return this;
     }
 
     public StackAdvisorRequestBuilder forHostsGroupBindings(
-        Map<String, Set<String>> hostGroupBindings) {
+        SortedMap<String, SortedSet<String>> hostGroupBindings) {
       this.instance.hostGroupBindings = hostGroupBindings;
       return this;
     }
 
     public StackAdvisorRequestBuilder withConfigurations(
-        Map<String, Map<String, Map<String, String>>> configurations) {
+        SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations) {
       this.instance.configurations = configurations;
       return this;
     }
@@ -191,13 +206,13 @@ public class StackAdvisorRequest {
     }
 
     public StackAdvisorRequestBuilder withUserContext(
-        Map<String, String> userContext) {
+        SortedMap<String, String> userContext) {
       this.instance.userContext = userContext;
       return this;
     }
 
     public StackAdvisorRequestBuilder withConfigGroups(
-      Set<RecommendationResponse.ConfigGroup> configGroups) {
+        SortedSet<RecommendationResponse.ConfigGroup> configGroups) {
       this.instance.configGroups = configGroups;
       return this;
     }
@@ -213,12 +228,27 @@ public class StackAdvisorRequest {
       return this;
     }
 
-    public StackAdvisorRequestBuilder withLdapConfig(Map<String, Object> ldapConfig) {
+    public StackAdvisorRequestBuilder withLdapConfig(SortedMap<String, Object> ldapConfig) {
       Preconditions.checkNotNull(ldapConfig);
       this.instance.ldapConfig = ldapConfig;
       return this;
     }
 
+    public StackAdvisorRequestBuilder withClusterId(Long clusterId) {
+      this.instance.clusterId = clusterId;
+      return this;
+    }
+
+    public StackAdvisorRequestBuilder withServiceName(String serviceName) {
+      this.instance.serviceName = serviceName;
+      return this;
+    }
+
+    public StackAdvisorRequestBuilder withConfigsResponse(
+        Boolean configsResponse) {
+      this.instance.configsResponse = configsResponse;
+      return this;
+    }
 
     public StackAdvisorRequest build() {
       return this.instance;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
index e38a48d..371727a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
@@ -42,7 +42,8 @@ public class ComponentLayoutRecommendationCommand extends
                                               StackAdvisorRunner saRunner,
                                               AmbariMetaInfo metaInfo,
                                               AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
-    super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+    super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo,
+        ambariServerConfigurationHandler);
   }
 
   @Override
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
index 209cab9..4d43c80 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -77,7 +78,7 @@ public class ConfigurationDependenciesRecommendationCommand extends
 
   protected Set<HostGroup> processHostGroups(StackAdvisorRequest request) {
     Set<HostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> componentHost : request.getHostComponents().entrySet()) {
+    for (Map.Entry<String, SortedSet<String>> componentHost : request.getHostComponents().entrySet()) {
       String hostGroupName = componentHost.getKey();
       Set<String> components = componentHost.getValue();
       if (hostGroupName != null && components != null) {
@@ -98,7 +99,7 @@ public class ConfigurationDependenciesRecommendationCommand extends
 
   private Set<BindingHostGroup> processHostGroupBindings(StackAdvisorRequest request) {
     Set<BindingHostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
+    for (Map.Entry<String, SortedSet<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
       String hostGroupName = hostBinding.getKey();
       Set<String> hosts = hostBinding.getValue();
       if (hostGroupName != null && hosts != null) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
index 15a7240..76f5fa7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -34,6 +35,7 @@ import org.apache.ambari.server.api.services.stackadvisor.recommendations.Recomm
 import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.commons.collections.CollectionUtils;
+import org.codehaus.jackson.JsonNode;
 
 /**
  * {@link org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand} implementation for
@@ -56,8 +58,10 @@ public class ConfigurationRecommendationCommand extends StackAdvisorCommand<Reco
                                             int requestId,
                                             StackAdvisorRunner saRunner,
                                             AmbariMetaInfo metaInfo,
-                                            AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
-    super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+                                            AmbariServerConfigurationHandler ambariServerConfigurationHandler,
+                                            Map<String, JsonNode> hostInfoCache) {
+    super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo,
+        ambariServerConfigurationHandler, hostInfoCache);
     this.commandType = commandType;
   }
 
@@ -82,7 +86,7 @@ public class ConfigurationRecommendationCommand extends StackAdvisorCommand<Reco
 
   protected Set<HostGroup> processHostGroups(StackAdvisorRequest request) {
     Set<HostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> componentHost : request.getHostComponents().entrySet()) {
+    for (Map.Entry<String, SortedSet<String>> componentHost : request.getHostComponents().entrySet()) {
       String hostGroupName = componentHost.getKey();
       Set<String> components = componentHost.getValue();
       if (hostGroupName != null && components != null) {
@@ -103,7 +107,7 @@ public class ConfigurationRecommendationCommand extends StackAdvisorCommand<Reco
 
   private Set<BindingHostGroup> processHostGroupBindings(StackAdvisorRequest request) {
     Set<BindingHostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
+    for (Map.Entry<String, SortedSet<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
       String hostGroupName = hostBinding.getKey();
       Set<String> hosts = hostBinding.getValue();
       if (hostGroupName != null && hosts != null) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
index 56ddcef..a7d45fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
@@ -31,6 +31,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
 
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Response;
@@ -58,6 +60,7 @@ import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig;
 import org.codehaus.jackson.node.ArrayNode;
+import org.codehaus.jackson.node.JsonNodeFactory;
 import org.codehaus.jackson.node.ObjectNode;
 import org.codehaus.jackson.node.TextNode;
 import org.slf4j.Logger;
@@ -103,6 +106,8 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
   private static final String AMBARI_SERVER_PROPERTIES_PROPERTY = "ambari-server-properties";
   private static final String AMBARI_SERVER_CONFIGURATIONS_PROPERTY = "ambari-server-configuration";
 
+  private final Map<String, JsonNode> hostInfoCache;
+
   private File recommendationsDir;
   private String recommendationsArtifactsLifetime;
   private ServiceInfo.ServiceAdvisorType serviceAdvisorType;
@@ -119,7 +124,8 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
 
   @SuppressWarnings("unchecked")
   public StackAdvisorCommand(File recommendationsDir, String recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType serviceAdvisorType, int requestId,
-                             StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
+                             StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler,
+                             Map<String, JsonNode> hostInfoCache) {
     this.type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
         .getActualTypeArguments()[0];
 
@@ -133,6 +139,12 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
     this.saRunner = saRunner;
     this.metaInfo = metaInfo;
     this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+    this.hostInfoCache = hostInfoCache;
+  }
+  public StackAdvisorCommand(File recommendationsDir, String recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType serviceAdvisorType, int requestId,
+                             StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
+    this(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo,
+        ambariServerConfigurationHandler, null);
   }
 
   protected abstract StackAdvisorCommandType getCommandType();
@@ -202,13 +214,13 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
 
   private void populateConfigurations(ObjectNode root,
                                       StackAdvisorRequest request) {
-    Map<String, Map<String, Map<String, String>>> configurations =
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations =
         request.getConfigurations();
     ObjectNode configurationsNode = root.putObject(CONFIGURATIONS_PROPERTY);
     for (String siteName : configurations.keySet()) {
       ObjectNode siteNode = configurationsNode.putObject(siteName);
 
-      Map<String, Map<String, String>> siteMap = configurations.get(siteName);
+      SortedMap<String, SortedMap<String, String>> siteMap = configurations.get(siteName);
       for (String properties : siteMap.keySet()) {
         ObjectNode propertiesNode = siteNode.putObject(properties);
 
@@ -249,7 +261,7 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
     }
   }
 
-  private void populateComponentHostsMap(ObjectNode root, Map<String, Set<String>> componentHostsMap) {
+  private void populateComponentHostsMap(ObjectNode root, SortedMap<String, SortedSet<String>> componentHostsMap) {
     ArrayNode services = (ArrayNode) root.get(SERVICES_PROPERTY);
     Iterator<JsonNode> servicesIter = services.getElements();
 
@@ -378,22 +390,71 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
   }
 
   String getHostsInformation(StackAdvisorRequest request) throws StackAdvisorException {
-    String hostsURI = String.format(GET_HOSTS_INFO_URI, request.getHostsCommaSeparated());
-
-    Response response = handleRequest(null, null, new LocalUriInfo(hostsURI), Request.Type.GET,
-        createHostResource());
+    List<String> hostNames = new ArrayList<>(request.getHosts());
+
+    // retrieve cached info
+    List<JsonNode> resultInfos = new ArrayList<>();
+    if (hostInfoCache != null && !hostInfoCache.isEmpty()) {
+      Iterator<String> hostNamesIterator = hostNames.iterator();
+      while(hostNamesIterator.hasNext()) {
+        String hostName = hostNamesIterator.next();
+        JsonNode node = hostInfoCache.get(hostName);
+        if (node != null) {
+          resultInfos.add(node);
+          hostNamesIterator.remove();
+        }
+      }
+    }
+    String hostsJSON = null;
+
+    // get hosts info for not cached hosts only
+    if (!hostNames.isEmpty()) {
+      LOG.info(String.format("Fire host info request for hosts: " + hostNames.toString()));
+      String hostsURI = String.format(GET_HOSTS_INFO_URI, String.join(",", hostNames));
+
+      Response response = handleRequest(null, null, new LocalUriInfo(hostsURI), Request.Type.GET,
+          createHostResource());
+
+      if (response.getStatus() != Status.OK.getStatusCode()) {
+        String message = String.format(
+            "Error occured during hosts information retrieving, status=%s, response=%s",
+            response.getStatus(), (String) response.getEntity());
+        LOG.warn(message);
+        throw new StackAdvisorException(message);
+      }
 
-    if (response.getStatus() != Status.OK.getStatusCode()) {
-      String message = String.format(
-          "Error occured during hosts information retrieving, status=%s, response=%s",
-          response.getStatus(), (String) response.getEntity());
-      LOG.warn(message);
-      throw new StackAdvisorException(message);
+      hostsJSON = (String) response.getEntity();
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Hosts information: {}", hostsJSON);
+      }
     }
 
-    String hostsJSON = (String) response.getEntity();
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Hosts information: {}", hostsJSON);
+    // when cache is used we should merge cached info with just got
+    if (hostInfoCache != null) {
+      if (hostsJSON != null && !hostsJSON.isEmpty()) {
+        try {
+          JsonNode root = mapper.readTree(hostsJSON);
+          Iterator<JsonNode> iterator = root.get("items").getElements();
+          while (iterator.hasNext()) {
+            JsonNode next = iterator.next();
+            String hostName = next.get("Hosts").get("host_name").getTextValue();
+            hostInfoCache.put(hostName, next);
+            resultInfos.add(next);
+          }
+        } catch (IOException e) {
+          throw new StackAdvisorException("Error occured during parsing result host infos", e);
+        }
+      }
+
+      String fullHostsURI = String.format(GET_HOSTS_INFO_URI, request.getHostsCommaSeparated());
+      JsonNodeFactory f = JsonNodeFactory.instance;
+      ObjectNode resultRoot = f.objectNode();
+      resultRoot.put("href", fullHostsURI);
+      ArrayNode resultArray = resultRoot.putArray("items");
+      resultArray.addAll(resultInfos);
+
+      hostsJSON = resultRoot.toString();
+
     }
 
     Collection<String> unregistered = getUnregisteredHosts(hostsJSON, request.getHosts());
@@ -410,7 +471,6 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
   @SuppressWarnings("unchecked")
   private Collection<String> getUnregisteredHosts(String hostsJSON, List<String> hosts)
       throws StackAdvisorException {
-    ObjectMapper mapper = new ObjectMapper();
     List<String> registeredHosts = new ArrayList<>();
 
     try {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index cffed9e..9b5f74b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -115,6 +115,7 @@ import org.apache.ambari.server.agent.stomp.dto.MetadataCluster;
 import org.apache.ambari.server.agent.stomp.dto.MetadataServiceInfo;
 import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
 import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.LoggingService;
 import org.apache.ambari.server.configuration.Configuration;
@@ -839,8 +840,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           .setComponentName(sch.getServiceComponentName())
           .setServiceName(sch.getServiceName())
           .setDisplayName(sc.getDisplayName())
-          .setHostIds(hostIds)
-          .setHostNames(hostNames)
+          .setHostIdentifiers(hostIds, hostNames)
           .setPublicHostNames(publicHostNames)
           .setComponentLevelParams(getTopologyComponentLevelParams(cluster.getClusterId(), serviceName, componentName,
               cluster.getSecurityType()))
@@ -855,7 +855,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         Set<TopologyComponent> newComponents = new HashSet<>();
         newComponents.add(newComponent);
         topologyUpdates.get(clusterId).update(newComponents, Collections.emptySet(),
-            UpdateEventType.UPDATE);
+            UpdateEventType.UPDATE, new TopologyUpdateHandlingReport());
       } else {
         topologyUpdates.get(clusterId).addTopologyComponent(newComponent);
       }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index 6f5f4e6..40fe567 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -914,7 +914,7 @@ public class AmbariServer {
     KeyService.init(injector.getInstance(PersistKeyValueImpl.class));
     BootStrapResource.init(injector.getInstance(BootStrapImpl.class));
     StackAdvisorResourceProvider.init(injector.getInstance(StackAdvisorHelper.class),
-        injector.getInstance(Configuration.class));
+        injector.getInstance(Configuration.class), injector.getInstance(Clusters.class), injector.getInstance(AmbariMetaInfo.class));
     StageUtils.setGson(injector.getInstance(Gson.class));
     StageUtils.setTopologyManager(injector.getInstance(TopologyManager.class));
     StageUtils.setConfiguration(injector.getInstance(Configuration.class));
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index 23a0d96..008ee47 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -35,6 +35,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.regex.Matcher;
@@ -684,19 +685,19 @@ public class KerberosHelperImpl implements KerberosHelper {
     // This could happen when enabling Kerberos while installing a cluster via Blueprints due to the
     // way hosts are discovered during the install process.
     if (!hostNames.isEmpty()) {
-      Map<String, Map<String, Map<String, String>>> requestConfigurations = new HashMap<>();
+      SortedMap<String, SortedMap<String, SortedMap<String, String>>> requestConfigurations = new TreeMap<>();
       if (existingConfigurations != null) {
         for (Map.Entry<String, Map<String, String>> configuration : existingConfigurations.entrySet()) {
-          Map<String, Map<String, String>> properties = new HashMap<>();
+          SortedMap<String, SortedMap<String, String>> properties = new TreeMap<>();
           String configType = configuration.getKey();
-          Map<String, String> configurationProperties = configuration.getValue();
+          SortedMap<String, String> configurationProperties = new TreeMap<>(configuration.getValue());
 
           if (configurationProperties == null) {
-            configurationProperties = Collections.emptyMap();
+            configurationProperties = Collections.emptySortedMap();
           }
 
           if ("cluster-env".equals(configType)) {
-            configurationProperties = new HashMap<>(configurationProperties);
+            configurationProperties = new TreeMap<>(configurationProperties);
             configurationProperties.put("security_enabled", (kerberosEnabled) ? "true" : "false");
           }
 
@@ -711,18 +712,18 @@ public class KerberosHelperImpl implements KerberosHelper {
         Map<String, String> configurationProperties = configuration.getValue();
 
         if ((configurationProperties != null) && !configurationProperties.isEmpty()) {
-          Map<String, Map<String, String>> requestConfiguration = requestConfigurations.get(configType);
+          SortedMap<String, SortedMap<String, String>> requestConfiguration = requestConfigurations.get(configType);
 
           if (requestConfiguration == null) {
-            requestConfiguration = new HashMap<>();
+            requestConfiguration = new TreeMap<>();
             requestConfigurations.put(configType, requestConfiguration);
           }
 
-          Map<String, String> requestConfigurationProperties = requestConfiguration.get("properties");
+          SortedMap<String, String> requestConfigurationProperties = requestConfiguration.get("properties");
           if (requestConfigurationProperties == null) {
-            requestConfigurationProperties = new HashMap<>();
+            requestConfigurationProperties = new TreeMap<>();
           } else {
-            requestConfigurationProperties = new HashMap<>(requestConfigurationProperties);
+            requestConfigurationProperties = new TreeMap<>(requestConfigurationProperties);
           }
 
           requestConfigurationProperties.putAll(configurationProperties);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
index e872de9..e9763ca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
@@ -25,6 +25,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
@@ -185,19 +187,19 @@ class AmbariServerStackAdvisorAwareConfigurationHandler extends AmbariServerConf
    * @return a map of services and their configurations
    * @throws AmbariException
    */
-  private Map<String, Map<String, Map<String, String>>> calculateExistingConfigurations(Cluster cluster) throws AmbariException {
+  private SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateExistingConfigurations(Cluster cluster) throws AmbariException {
     Map<String, Map<String, String>> configurationTags = configHelper.getEffectiveDesiredTags(cluster, null);
     Map<String, Map<String, String>> effectiveConfigs = configHelper.getEffectiveConfigProperties(cluster, configurationTags);
 
-    Map<String, Map<String, Map<String, String>>> requestConfigurations = new HashMap<>();
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> requestConfigurations = new TreeMap<>();
     if (effectiveConfigs != null) {
       for (Map.Entry<String, Map<String, String>> configuration : effectiveConfigs.entrySet()) {
-        Map<String, Map<String, String>> properties = new HashMap<>();
+        SortedMap<String, SortedMap<String, String>> properties = new TreeMap<>();
         String configType = configuration.getKey();
-        Map<String, String> configurationProperties = configuration.getValue();
+        SortedMap<String, String> configurationProperties = new TreeMap<>(configuration.getValue());
 
         if (configurationProperties == null) {
-          configurationProperties = Collections.emptyMap();
+          configurationProperties = Collections.emptySortedMap();
         }
 
         properties.put("properties", configurationProperties);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index a6ed0e5..861a648 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -815,7 +815,6 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
       if (LOG.isDebugEnabled()) {
         LOG.debug("Received an updateHost request, hostname={}, request={}", request.getHostname(), request);
       }
-      TopologyHost topologyHost = new TopologyHost();
 
       Host host = clusters.getHost(request.getHostname());
 
@@ -823,7 +822,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
       Cluster cluster = clusters.getCluster(clusterName);
       Long clusterId = cluster.getClusterId();
       Long resourceId = cluster.getResourceId();
-      topologyHost.setHostId(host.getHostId());
+      TopologyHost topologyHost = new TopologyHost(host.getHostId(), host.getHostName());
 
       try {
         // The below method call throws an exception when trying to create a duplicate mapping in the clusterhostmapping
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
index e1a58ef..d3d672e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
@@ -56,6 +56,10 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
   protected static final String RECOMMENDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId(
       "Recommendation", "id");
 
+  protected static final String CLUSTER_ID_PROPERTY_ID = "clusterId";
+  protected static final String SERVICE_NAME_PROPERTY_ID = "serviceName";
+  protected static final String AUTO_COMPLETE_PROPERTY_ID = "autoComplete";
+  protected static final String CONFIGS_RESPONSE_PROPERTY_ID = "configsResponse";
   protected static final String HOSTS_PROPERTY_ID = "hosts";
   protected static final String SERVICES_PROPERTY_ID = "services";
   protected static final String RECOMMEND_PROPERTY_ID = "recommend";
@@ -105,6 +109,10 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
       STACK_NAME_PROPERTY_ID,
       STACK_VERSION_PROPERTY_ID,
       RECOMMEND_PROPERTY_ID,
+      CLUSTER_ID_PROPERTY_ID,
+      SERVICE_NAME_PROPERTY_ID,
+      AUTO_COMPLETE_PROPERTY_ID,
+      CONFIGS_RESPONSE_PROPERTY_ID,
       HOSTS_PROPERTY_ID,
       SERVICES_PROPERTY_ID,
       CONFIG_GROUPS_PROPERTY_ID,
@@ -146,7 +154,7 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
     } catch (StackAdvisorRequestException e) {
       LOG.warn("Error occured during recommendation", e);
       throw new IllegalArgumentException(e.getMessage(), e);
-    } catch (StackAdvisorException e) {
+    } catch (StackAdvisorException | AmbariException e) {
       LOG.warn("Error occured during recommendation", e);
       throw new SystemException(e.getMessage(), e);
     }
@@ -156,43 +164,46 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
       public Resource invoke() throws AmbariException {
 
         Resource resource = new ResourceImpl(Resource.Type.Recommendation);
-        setResourceProperty(resource, RECOMMENDATION_ID_PROPERTY_ID, response.getId(), getPropertyIds());
-        setResourceProperty(resource, STACK_NAME_PROPERTY_ID, response.getVersion().getStackName(),
-            getPropertyIds());
-        setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, response.getVersion()
-            .getStackVersion(), getPropertyIds());
-        setResourceProperty(resource, HOSTS_PROPERTY_ID, response.getHosts(), getPropertyIds());
-        setResourceProperty(resource, SERVICES_PROPERTY_ID, response.getServices(),
-            getPropertyIds());
+        if (!recommendationRequest.getConfigsResponse()) {
+          setResourceProperty(resource, RECOMMENDATION_ID_PROPERTY_ID, response.getId(), getPropertyIds());
+          setResourceProperty(resource, STACK_NAME_PROPERTY_ID, response.getVersion().getStackName(),
+              getPropertyIds());
+          setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, response.getVersion()
+              .getStackVersion(), getPropertyIds());
+          setResourceProperty(resource, HOSTS_PROPERTY_ID, response.getHosts(), getPropertyIds());
+          setResourceProperty(resource, SERVICES_PROPERTY_ID, response.getServices(),
+              getPropertyIds());
+        }
         setResourceProperty(resource, CONFIG_GROUPS_PROPERTY_ID,
           response.getRecommendations().getConfigGroups(), getPropertyIds());
         setResourceProperty(resource, BLUEPRINT_CONFIGURATIONS_PROPERTY_ID, response
             .getRecommendations().getBlueprint().getConfigurations(), getPropertyIds());
 
-        Set<HostGroup> hostGroups = response.getRecommendations().getBlueprint().getHostGroups();
-        List<Map<String, Object>> listGroupProps = new ArrayList<>();
-        for (HostGroup hostGroup : hostGroups) {
-          Map<String, Object> mapGroupProps = new HashMap<>();
-          mapGroupProps.put(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY_ID, hostGroup.getName());
-          mapGroupProps
-              .put(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY_ID, hostGroup.getComponents());
-          listGroupProps.add(mapGroupProps);
-        }
-        setResourceProperty(resource, BLUEPRINT_HOST_GROUPS_PROPERTY_ID, listGroupProps,
-            getPropertyIds());
-
-        Set<BindingHostGroup> bindingHostGroups = response.getRecommendations()
-            .getBlueprintClusterBinding().getHostGroups();
-        List<Map<String, Object>> listBindingGroupProps = new ArrayList<>();
-        for (BindingHostGroup hostGroup : bindingHostGroups) {
-          Map<String, Object> mapGroupProps = new HashMap<>();
-          mapGroupProps.put(BINDING_HOST_GROUPS_NAME_PROPERTY_ID, hostGroup.getName());
-          mapGroupProps.put(BINDING_HOST_GROUPS_HOSTS_PROPERTY_ID, hostGroup.getHosts());
-          listBindingGroupProps.add(mapGroupProps);
+        if (!recommendationRequest.getConfigsResponse()) {
+          Set<HostGroup> hostGroups = response.getRecommendations().getBlueprint().getHostGroups();
+          List<Map<String, Object>> listGroupProps = new ArrayList<>();
+          for (HostGroup hostGroup : hostGroups) {
+            Map<String, Object> mapGroupProps = new HashMap<>();
+            mapGroupProps.put(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY_ID, hostGroup.getName());
+            mapGroupProps
+                .put(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY_ID, hostGroup.getComponents());
+            listGroupProps.add(mapGroupProps);
+          }
+          setResourceProperty(resource, BLUEPRINT_HOST_GROUPS_PROPERTY_ID, listGroupProps,
+              getPropertyIds());
+
+          Set<BindingHostGroup> bindingHostGroups = response.getRecommendations()
+              .getBlueprintClusterBinding().getHostGroups();
+          List<Map<String, Object>> listBindingGroupProps = new ArrayList<>();
+          for (BindingHostGroup hostGroup : bindingHostGroups) {
+            Map<String, Object> mapGroupProps = new HashMap<>();
+            mapGroupProps.put(BINDING_HOST_GROUPS_NAME_PROPERTY_ID, hostGroup.getName());
+            mapGroupProps.put(BINDING_HOST_GROUPS_HOSTS_PROPERTY_ID, hostGroup.getHosts());
+            listBindingGroupProps.add(mapGroupProps);
+          }
+          setResourceProperty(resource, BINDING_HOST_GROUPS_PROPERTY_ID, listBindingGroupProps,
+              getPropertyIds());
         }
-        setResourceProperty(resource, BINDING_HOST_GROUPS_PROPERTY_ID, listBindingGroupProps,
-            getPropertyIds());
-
         return resource;
       }
     });
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
index 37215d0..8a8a2eb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,11 +27,18 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
@@ -43,6 +51,16 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.Resource.Type;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.state.ChangedConfigInfo;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.DesiredConfig;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,6 +78,12 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
   protected static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId(
       "Versions", "stack_version");
 
+
+  private static final String CLUSTER_ID_PROPERTY = "clusterId";
+  private static final String SERVICE_NAME_PROPERTY = "serviceName";
+  private static final String AUTO_COMPLETE_PROPERTY = "autoComplete";
+  private static final String CONFIGS_RESPONSE_PROPERTY = "configsResponse";
+  private static final String CONFIG_GROUPS_GROUP_ID_PROPERTY = "group_id";
   private static final String HOST_PROPERTY = "hosts";
   private static final String SERVICES_PROPERTY = "services";
 
@@ -85,13 +109,18 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
 
   protected static StackAdvisorHelper saHelper;
   private static Configuration configuration;
+  private static Clusters clusters;
+  private static AmbariMetaInfo ambariMetaInfo;
   protected static final String USER_CONTEXT_OPERATION_PROPERTY = "user_context/operation";
   protected static final String USER_CONTEXT_OPERATION_DETAILS_PROPERTY = "user_context/operation_details";
 
   @Inject
-  public static void init(StackAdvisorHelper instance, Configuration serverConfig) {
+  public static void init(StackAdvisorHelper instance, Configuration serverConfig, Clusters clusters,
+                          AmbariMetaInfo ambariMetaInfo) {
     saHelper = instance;
     configuration = serverConfig;
+    StackAdvisorResourceProvider.clusters = clusters;
+    StackAdvisorResourceProvider.ambariMetaInfo = ambariMetaInfo;
   }
 
   protected StackAdvisorResourceProvider(Resource.Type type, Set<String> propertyIds, Map<Type, String> keyPropertyIds,
@@ -104,46 +133,84 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
   @SuppressWarnings("unchecked")
   protected StackAdvisorRequest prepareStackAdvisorRequest(Request request) {
     try {
+      String clusterIdProperty = (String) getRequestProperty(request, CLUSTER_ID_PROPERTY);
+      Long clusterId = clusterIdProperty == null ? null : Long.valueOf(clusterIdProperty);
+
+      String serviceName = (String) getRequestProperty(request, SERVICE_NAME_PROPERTY);
+
+      String autoCompleteProperty = (String) getRequestProperty(request, AUTO_COMPLETE_PROPERTY);
+      Boolean autoComplete = autoCompleteProperty == null ? false : Boolean.valueOf(autoCompleteProperty);
+
       String stackName = (String) getRequestProperty(request, STACK_NAME_PROPERTY_ID);
       String stackVersion = (String) getRequestProperty(request, STACK_VERSION_PROPERTY_ID);
       StackAdvisorRequestType requestType = StackAdvisorRequestType
           .fromString((String) getRequestProperty(request, getRequestTypePropertyId()));
 
-      /*
+      List<String> hosts;
+      List<String> services;
+      SortedMap<String, SortedSet<String>> hgComponentsMap;
+      SortedMap<String, SortedSet<String>> hgHostsMap;
+      SortedMap<String, SortedSet<String>> componentHostsMap;
+      SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations;
+      SortedSet<RecommendationResponse.ConfigGroup> configGroups;
+
+      // In auto complete case all required fields will be filled will cluster current info
+      if (autoComplete) {
+        if (clusterId == null || serviceName == null) {
+          throw new Exception(
+              String.format("Incomplete request, clusterId and/or serviceName are not valid, clusterId=%s, serviceName=%s",
+                  clusterId, serviceName));
+        }
+        Cluster cluster = clusters.getCluster(clusterId);
+        List<Host> hostObjects = new ArrayList<>(cluster.getHosts());
+        Map<String, Service> serviceObjects = cluster.getServices();
+
+        hosts = hostObjects.stream().map(h -> h.getHostName()).collect(Collectors.toList());
+        services = new ArrayList<>(serviceObjects.keySet());
+        hgComponentsMap = calculateHostGroupComponentsMap(cluster);
+        hgHostsMap = calculateHostGroupHostsMap(cluster);
+        componentHostsMap = calculateComponentHostsMap(cluster);
+        configurations = calculateConfigurations(cluster, serviceName);
+
+        configGroups = calculateConfigGroups(cluster, request);
+      } else {
+        /*
        * ClassCastException will occur if hosts or services are empty in the
        * request.
-       * 
+       *
        * @see JsonRequestBodyParser for arrays parsing
        */
-      Object hostsObject = getRequestProperty(request, HOST_PROPERTY);
-      if (hostsObject instanceof LinkedHashSet) {
-        if (((LinkedHashSet)hostsObject).isEmpty()) {
-          throw new Exception("Empty host list passed to recommendation service");
+        Object hostsObject = getRequestProperty(request, HOST_PROPERTY);
+        if (hostsObject instanceof LinkedHashSet) {
+          if (((LinkedHashSet)hostsObject).isEmpty()) {
+            throw new Exception("Empty host list passed to recommendation service");
+          }
         }
-      }
-      List<String> hosts = (List<String>) hostsObject;
+        hosts = (List<String>) hostsObject;
 
-      Object servicesObject = getRequestProperty(request, SERVICES_PROPERTY);
-      if (servicesObject instanceof LinkedHashSet) {
-        if (((LinkedHashSet)servicesObject).isEmpty()) {
-          throw new Exception("Empty service list passed to recommendation service");
+        Object servicesObject = getRequestProperty(request, SERVICES_PROPERTY);
+        if (servicesObject instanceof LinkedHashSet) {
+          if (((LinkedHashSet)servicesObject).isEmpty()) {
+            throw new Exception("Empty service list passed to recommendation service");
+          }
         }
+        services = (List<String>) servicesObject;
+
+        hgComponentsMap = calculateHostGroupComponentsMap(request);
+        hgHostsMap = calculateHostGroupHostsMap(request);
+        componentHostsMap = calculateComponentHostsMap(hgComponentsMap, hgHostsMap);
+        configurations = calculateConfigurations(request);
+        configGroups = calculateConfigGroups(request);
       }
-      List<String> services = (List<String>) servicesObject;
-
-      Map<String, Set<String>> hgComponentsMap = calculateHostGroupComponentsMap(request);
-      Map<String, Set<String>> hgHostsMap = calculateHostGroupHostsMap(request);
-      Map<String, Set<String>> componentHostsMap = calculateComponentHostsMap(hgComponentsMap,
-          hgHostsMap);
-      Map<String, Map<String, Map<String, String>>> configurations = calculateConfigurations(request);
-      Map<String, String> userContext = readUserContext(request);
+      SortedMap<String, String> userContext = readUserContext(request);
       Boolean gplLicenseAccepted = configuration.getGplLicenseAccepted();
-
       List<ChangedConfigInfo> changedConfigurations =
         requestType == StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES ?
           calculateChangedConfigurations(request) : Collections.emptyList();
 
-      Set<RecommendationResponse.ConfigGroup> configGroups = calculateConfigGroups(request);
+      String configsResponseProperty = (String) getRequestProperty(request, CONFIGS_RESPONSE_PROPERTY);
+      Boolean configsResponse = configsResponseProperty == null ? false : Boolean.valueOf(configsResponseProperty);
+
       return StackAdvisorRequestBuilder.
         forStack(stackName, stackVersion).ofType(requestType).forHosts(hosts).
         forServices(services).forHostComponents(hgComponentsMap).
@@ -153,7 +220,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
         withConfigGroups(configGroups).
         withChangedConfigurations(changedConfigurations).
         withUserContext(userContext).
-        withGPLLicenseAccepted(gplLicenseAccepted).build();
+        withGPLLicenseAccepted(gplLicenseAccepted).
+        withClusterId(clusterId).
+        withServiceName(serviceName).
+        withConfigsResponse(configsResponse).build();
     } catch (Exception e) {
       LOG.warn("Error occurred during preparation of stack advisor request", e);
       Response response = Response.status(Status.BAD_REQUEST)
@@ -170,10 +240,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
    * @return host-group to components map
    */
   @SuppressWarnings("unchecked")
-  private Map<String, Set<String>> calculateHostGroupComponentsMap(Request request) {
+  private SortedMap<String, SortedSet<String>> calculateHostGroupComponentsMap(Request request) {
     Set<Map<String, Object>> hostGroups = (Set<Map<String, Object>>) getRequestProperty(request,
         BLUEPRINT_HOST_GROUPS_PROPERTY);
-    Map<String, Set<String>> map = new HashMap<>();
+    SortedMap<String, SortedSet<String>> map = new TreeMap<>();
     if (hostGroups != null) {
       for (Map<String, Object> hostGroup : hostGroups) {
         String hostGroupName = (String) hostGroup.get(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY);
@@ -181,7 +251,7 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
         Set<Map<String, Object>> componentsSet = (Set<Map<String, Object>>) hostGroup
             .get(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY);
 
-        Set<String> components = new HashSet<>();
+        SortedSet<String> components = new TreeSet<>();
         for (Map<String, Object> component : componentsSet) {
           components.add((String) component.get(BLUEPRINT_HOST_GROUPS_COMPONENTS_NAME_PROPERTY));
         }
@@ -194,6 +264,28 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
   }
 
   /**
+   * Retrieves component names mapped by host groups, host name is used as host group identifier
+   * @param cluster cluster for calculating components mapping by host groups
+   * @return map "host group name" -> ["component name1", "component name 2", ...]
+   */
+  private SortedMap<String, SortedSet<String>> calculateHostGroupComponentsMap(Cluster cluster) {
+    SortedMap<String, SortedSet<String>> map = new TreeMap<>();
+    List<Host> hosts = new ArrayList<>(cluster.getHosts());
+    if (!hosts.isEmpty()) {
+      for (Host host : hosts) {
+        String hostGroupName = host.getHostName();
+
+        SortedSet<String> components = new TreeSet<>();
+        for (ServiceComponentHost sch : cluster.getServiceComponentHosts(host.getHostName())) {
+          components.add(sch.getServiceComponentName());
+        }
+        map.put(hostGroupName, components);
+      }
+    }
+    return map;
+  }
+
+  /**
    * Will prepare host-group names to hosts names map from the recommendation
    * binding host groups.
    * 
@@ -201,10 +293,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
    * @return host-group to hosts map
    */
   @SuppressWarnings("unchecked")
-  private Map<String, Set<String>> calculateHostGroupHostsMap(Request request) {
+  private SortedMap<String, SortedSet<String>> calculateHostGroupHostsMap(Request request) {
     Set<Map<String, Object>> bindingHostGroups = (Set<Map<String, Object>>) getRequestProperty(
         request, BINDING_HOST_GROUPS_PROPERTY);
-    Map<String, Set<String>> map = new HashMap<>();
+    SortedMap<String, SortedSet<String>> map = new TreeMap<>();
     if (bindingHostGroups != null) {
       for (Map<String, Object> hostGroup : bindingHostGroups) {
         String hostGroupName = (String) hostGroup.get(BINDING_HOST_GROUPS_NAME_PROPERTY);
@@ -212,7 +304,7 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
         Set<Map<String, Object>> hostsSet = (Set<Map<String, Object>>) hostGroup
             .get(BINDING_HOST_GROUPS_HOSTS_PROPERTY);
 
-        Set<String> hosts = new HashSet<>();
+        SortedSet<String> hosts = new TreeSet<>();
         for (Map<String, Object> host : hostsSet) {
           hosts.add((String) host.get(BINDING_HOST_GROUPS_HOSTS_NAME_PROPERTY));
         }
@@ -224,6 +316,24 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
     return map;
   }
 
+  /**
+   * Retrieves hosts names mapped by host groups, host name is used as host group identifier
+   * @param cluster cluster for calculating hosts mapping by host groups
+   * @return map "host group name" -> ["host name 1"]
+   */
+  private SortedMap<String, SortedSet<String>> calculateHostGroupHostsMap(Cluster cluster) {
+    SortedMap<String, SortedSet<String>> map = new TreeMap<>();
+
+    List<Host> hosts = new ArrayList<>(cluster.getHosts());
+    if (!hosts.isEmpty()) {
+      for (Host host : hosts) {
+        map.put(host.getHostName(), new TreeSet<String>(){{add(host.getHostName());}});
+      }
+    }
+
+    return map;
+  }
+
   protected List<ChangedConfigInfo> calculateChangedConfigurations(Request request) {
     List<ChangedConfigInfo> configs =
       new LinkedList<>();
@@ -236,10 +346,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
     return configs;
   }
 
-  protected Set<RecommendationResponse.ConfigGroup> calculateConfigGroups(Request request) {
+  protected SortedSet<RecommendationResponse.ConfigGroup> calculateConfigGroups(Request request) {
 
-    Set<RecommendationResponse.ConfigGroup> configGroups =
-      new HashSet<>();
+    SortedSet<RecommendationResponse.ConfigGroup> configGroups =
+      new TreeSet<>();
 
     Set<HashMap<String, Object>> configGroupsProperties =
       (HashSet<HashMap<String, Object>>) getRequestProperty(request, CONFIG_GROUPS_PROPERTY);
@@ -270,6 +380,40 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
     return configGroups;
   }
 
+  protected SortedSet<RecommendationResponse.ConfigGroup> calculateConfigGroups(Cluster cluster, Request request) {
+
+    SortedSet<RecommendationResponse.ConfigGroup> configGroups =
+      new TreeSet<>();
+
+    Set<HashMap<String, Object>> configGroupsProperties =
+      (HashSet<HashMap<String, Object>>) getRequestProperty(request, CONFIG_GROUPS_PROPERTY);
+    if (configGroupsProperties != null) {
+      for (HashMap<String, Object> props : configGroupsProperties) {
+        RecommendationResponse.ConfigGroup configGroup = new RecommendationResponse.ConfigGroup();
+        Object groupIdObject = props.get(CONFIG_GROUPS_GROUP_ID_PROPERTY);
+        if (groupIdObject != null) {
+          Long groupId = Long.valueOf((String) groupIdObject);
+          ConfigGroup clusterConfigGroup = cluster.getConfigGroupsById(groupId);
+
+          // convert configs
+          Map<String, RecommendationResponse.BlueprintConfigurations> typedConfiguration = new HashMap<>();
+          for (Map.Entry<String, Config> config : clusterConfigGroup.getConfigurations().entrySet()) {
+            RecommendationResponse.BlueprintConfigurations blueprintConfiguration = new RecommendationResponse.BlueprintConfigurations();
+            blueprintConfiguration.setProperties(config.getValue().getProperties());
+            typedConfiguration.put(config.getKey(), blueprintConfiguration);
+          }
+
+          configGroup.setConfigurations(typedConfiguration);
+
+          configGroup.setHosts(clusterConfigGroup.getHosts().values().stream().map(h -> h.getHostName()).collect(Collectors.toList()));
+          configGroups.add(configGroup);
+        }
+      }
+    }
+
+    return configGroups;
+  }
+
   /**
    * Parse the user contex for the call. Typical structure
    * { "operation" : "createCluster" }
@@ -277,8 +421,8 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
    * @param request
    * @return
    */
-  protected Map<String, String> readUserContext(Request request) {
-    HashMap<String, String> userContext = new HashMap<>();
+  protected SortedMap<String, String> readUserContext(Request request) {
+    SortedMap<String, String> userContext = new TreeMap<>();
     if (null != getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY)) {
       userContext.put(OPERATION_PROPERTY,
                       (String) getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY));
@@ -292,8 +436,8 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
 
   protected static final String CONFIGURATIONS_PROPERTY_ID = "recommendations/blueprint/configurations/";
 
-  protected Map<String, Map<String, Map<String, String>>> calculateConfigurations(Request request) {
-    Map<String, Map<String, Map<String, String>>> configurations = new HashMap<>();
+  protected SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigurations(Request request) {
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
     Map<String, Object> properties = request.getProperties().iterator().next();
     for (String property : properties.keySet()) {
       if (property.startsWith(CONFIGURATIONS_PROPERTY_ID)) {
@@ -304,15 +448,15 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
           String propertiesProperty = propertyPath[1];
           String propertyName = propertyPath[2];
 
-          Map<String, Map<String, String>> siteMap = configurations.get(siteName);
+          SortedMap<String, SortedMap<String, String>> siteMap = configurations.get(siteName);
           if (siteMap == null) {
-            siteMap = new HashMap<>();
+            siteMap = new TreeMap<>();
             configurations.put(siteName, siteMap);
           }
 
-          Map<String, String> propertiesMap = siteMap.get(propertiesProperty);
+          SortedMap<String, String> propertiesMap = siteMap.get(propertiesProperty);
           if (propertiesMap == null) {
-            propertiesMap = new HashMap<>();
+            propertiesMap = new TreeMap<>();
             siteMap.put(propertiesProperty, propertiesMap);
           }
 
@@ -331,25 +475,50 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
     return configurations;
   }
 
+  protected SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigurations(Cluster cluster, String serviceName)
+      throws AmbariException {
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
+    Service service = cluster.getService(serviceName);
+
+    StackId stackId = service.getDesiredStackId();
+    ServiceInfo serviceInfo = ambariMetaInfo.getService(stackId.getStackName(),
+        stackId.getStackVersion(), serviceName);
+
+    List<String> requiredConfigTypes = serviceInfo.getConfigDependenciesWithComponents();
+    Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
+    Map<String, DesiredConfig> requiredDesiredConfigs = new HashMap<>();
+    for (String requiredConfigType : requiredConfigTypes) {
+      if (desiredConfigs.containsKey(requiredConfigType)) {
+        requiredDesiredConfigs.put(requiredConfigType, desiredConfigs.get(requiredConfigType));
+      }
+    }
+    for (Map.Entry<String, DesiredConfig> requiredDesiredConfigEntry : requiredDesiredConfigs.entrySet()) {
+      Config config = cluster.getConfig(requiredDesiredConfigEntry.getKey(), requiredDesiredConfigEntry.getValue().getTag());
+      configurations.put(requiredDesiredConfigEntry.getKey(),
+          new TreeMap<String, SortedMap<String, String>>(){{put("properties", new TreeMap<>(config.getProperties()));}});
+    }
+    return configurations;
+  }
+
   @SuppressWarnings("unchecked")
-  private Map<String, Set<String>> calculateComponentHostsMap(Map<String, Set<String>> hostGroups,
-      Map<String, Set<String>> bindingHostGroups) {
+  private SortedMap<String, SortedSet<String>> calculateComponentHostsMap(SortedMap<String, SortedSet<String>> hostGroups,
+                                                                    SortedMap<String, SortedSet<String>> bindingHostGroups) {
     /*
      * ClassCastException may occur in case of body inconsistency: property
      * missed, etc.
      */
 
-    Map<String, Set<String>> componentHostsMap = new HashMap<>();
+    SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
     if (null != bindingHostGroups && null != hostGroups) {
-      for (Map.Entry<String, Set<String>> hgComponents : hostGroups.entrySet()) {
+      for (Map.Entry<String, SortedSet<String>> hgComponents : hostGroups.entrySet()) {
         String hgName = hgComponents.getKey();
         Set<String> components = hgComponents.getValue();
 
         Set<String> hosts = bindingHostGroups.get(hgName);
         for (String component : components) {
-          Set<String> componentHosts = componentHostsMap.get(component);
+          SortedSet<String> componentHosts = componentHostsMap.get(component);
           if (componentHosts == null) { // if was not initialized
-            componentHosts = new HashSet<>();
+            componentHosts = new TreeSet<>();
             componentHostsMap.put(component, componentHosts);
           }
           componentHosts.addAll(hosts);
@@ -360,6 +529,23 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
     return componentHostsMap;
   }
 
+  @SuppressWarnings("unchecked")
+  private SortedMap<String, SortedSet<String>> calculateComponentHostsMap(Cluster cluster) {
+    /*
+     * ClassCastException may occur in case of body inconsistency: property
+     * missed, etc.
+     */
+
+    SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
+    List<ServiceComponentHost> schs = cluster.getServiceComponentHosts();
+    for (ServiceComponentHost sch : schs) {
+      componentHostsMap.putIfAbsent(sch.getServiceComponentName(), new TreeSet<>());
+      componentHostsMap.get(sch.getServiceComponentName()).add(sch.getHostName());
+    }
+
+    return componentHostsMap;
+  }
+
   protected Object getRequestProperty(Request request, String propertyName) {
     for (Map<String, Object> propertyMap : request.getProperties()) {
       if (propertyMap.containsKey(propertyName)) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
index 4840bc0..cf43b6c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
 import org.apache.ambari.server.events.AlertEvent;
 import org.apache.ambari.server.events.AlertStateChangeEvent;
 import org.apache.ambari.server.events.HostStateUpdateEvent;
@@ -34,7 +35,6 @@ import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.STOMPUpdatePublisher;
 import org.apache.ambari.server.orm.dao.AlertSummaryDTO;
 import org.apache.ambari.server.orm.dao.AlertsDAO;
-import org.apache.ambari.server.orm.dao.ServiceDesiredStateDAO;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Host;
@@ -56,15 +56,15 @@ public class HostUpdateListener {
   private STOMPUpdatePublisher STOMPUpdatePublisher;
 
   @Inject
-  private ServiceDesiredStateDAO serviceDesiredStateDAO;
-
-  @Inject
   private AlertsDAO alertsDAO;
 
   @Inject
   private Provider<Clusters> m_clusters;
 
   @Inject
+  private Provider<StackAdvisorHelper> stackAdvisorHelperProvider;
+
+  @Inject
   public HostUpdateListener(AmbariEventPublisher ambariEventPublisher, AlertEventPublisher m_alertEventPublisher) {
     ambariEventPublisher.register(this);
     m_alertEventPublisher.register(this);
@@ -92,6 +92,7 @@ public class HostUpdateListener {
           hostUpdateEvent.getHostName(),
           hostUpdateEvent.getHostStatus(),
           hostUpdateEvent.getLastHeartbeatTime()));
+      stackAdvisorHelperProvider.get().clearCaches(hostName);
     }
   }
 
@@ -118,6 +119,7 @@ public class HostUpdateListener {
           hostUpdateEvent.getHostState(),
           hostUpdateEvent.getLastHeartbeatTime()));
     }
+    stackAdvisorHelperProvider.get().clearCaches(hostName);
   }
 
   @Subscribe
@@ -175,6 +177,7 @@ public class HostUpdateListener {
         STOMPUpdatePublisher.publish(HostUpdateEvent.createHostAlertsUpdate(hostUpdateEvent.getClusterName(),
             hostName, summary));
       }
+      stackAdvisorHelperProvider.get().clearCaches(hostName);
     } else if (event.getService()!= null) {
       String serviceName = event.getService().getName();
       for (String hostName : m_clusters.get().getCluster(clusterId).getService(serviceName).getServiceHosts()) {
@@ -189,6 +192,7 @@ public class HostUpdateListener {
 
         STOMPUpdatePublisher.publish(HostUpdateEvent.createHostAlertsUpdate(hostUpdateEvent.getClusterName(),
             hostName, summary));
+        stackAdvisorHelperProvider.get().clearCaches(hostName);
       }
     }
   }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index a150796..817563d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -24,6 +24,8 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ClusterResponse;
@@ -109,7 +111,7 @@ public interface Cluster {
    * @param serviceNames
    * @return a map of (filtered) components to hosts
    */
-  Map<String, Set<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames);
+  SortedMap<String, SortedSet<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames);
 
   /**
    * Get all ServiceComponentHosts for a given service and optional component
@@ -514,6 +516,13 @@ public interface Cluster {
       throws AmbariException;
 
   /**
+   * Find config group by config group id
+   * @param configId id of config group to return
+   * @return config group
+   */
+  ConfigGroup getConfigGroupsById(Long configId);
+
+  /**
    * Add a @RequestExecution to the cluster
    * @param requestExecution
    * @throws AmbariException
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 4ca2caf..06a6067 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -33,6 +33,10 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentSkipListMap;
@@ -531,6 +535,11 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
+  public ConfigGroup getConfigGroupsById(Long configId) {
+    return clusterConfigGroups.get(configId);
+  }
+
+  @Override
   public void addRequestExecution(RequestExecution requestExecution) throws AmbariException {
     LOG.info("Adding a new request schedule" + ", clusterName = " + getClusterName() + ", id = "
         + requestExecution.getId() + ", description = " + requestExecution.getDescription());
@@ -800,8 +809,8 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
-  public Map<String, Set<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames) {
-    Map<String, Set<String>> componentHostMap = new HashMap<>();
+  public SortedMap<String, SortedSet<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames) {
+    SortedMap<String, SortedSet<String>> componentHostMap = new TreeMap<>();
 
     Collection<Host> hosts = getHosts();
 
@@ -818,10 +827,10 @@ public class ClusterImpl implements Cluster {
               // If the service for this ServiceComponentHost is not filtered out, continue processing
               if ((serviceNames == null) || serviceNames.contains(sch.getServiceName())) {
                 String component = sch.getServiceComponentName();
-                Set<String> componentHosts = componentHostMap.get(component);
+                SortedSet<String> componentHosts = componentHostMap.get(component);
 
                 if (componentHosts == null) {
-                  componentHosts = new HashSet<>();
+                  componentHosts = new TreeSet<>();
                   componentHostMap.put(component, componentHosts);
                 }
 
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
index c48d4cf..79dd948 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
@@ -120,8 +120,8 @@ public class STOMPComponentsDeleteHandler {
           .setComponentName(hostComponent.getComponentName())
           .setServiceName(hostComponent.getServiceName())
           .setVersion(hostComponent.getVersion())
-          .setHostIds(new HashSet<>(Arrays.asList(hostComponent.getHostId())))
-          .setHostNames(new HashSet<>(Arrays.asList(hostComponent.getHostName())))
+          .setHostIdentifiers(new HashSet<>(Arrays.asList(hostComponent.getHostId())),
+              new HashSet<>(Arrays.asList(hostComponent.getHostName())))
           .setLastComponentState(hostComponent.getLastComponentState())
           .build();
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java
new file mode 100644
index 0000000..680523a
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.agent.stomp;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.apache.ambari.server.NullHostNameException;
+import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
+import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
+import org.apache.ambari.server.agent.stomp.dto.TopologyHost;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
+import org.apache.ambari.server.events.UpdateEventType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TopologyClusterTest {
+
+  @Test
+  public void testHandlingReportHostAdd() throws NullHostNameException {
+    TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+    TopologyHost hostToAddition = new TopologyHost(2L, "hostName2");
+
+    TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new HashSet(){{add(dummyHost);}});
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.emptySet(), Collections.singleton(hostToAddition), UpdateEventType.UPDATE, report);
+
+    Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+    Assert.assertEquals("hostName2", report.getUpdatedHostNames().iterator().next());
+
+    Assert.assertEquals(2L, topologyCluster.getTopologyHosts().size());
+  }
+
+  @Test
+  public void testHandlingReportHostDelete() throws NullHostNameException {
+    TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+    TopologyHost hostToDelete = new TopologyHost(2L, "hostName2");
+    TopologyHost update = new TopologyHost(2L, "hostName2");
+
+    TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new HashSet(){{add(dummyHost); add(hostToDelete);}});
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.emptySet(), Collections.singleton(update), UpdateEventType.DELETE, report);
+
+    Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+    Assert.assertEquals("hostName2", report.getUpdatedHostNames().iterator().next());
+
+    Assert.assertEquals(1L, topologyCluster.getTopologyHosts().size());
+    Assert.assertEquals("hostName1", topologyCluster.getTopologyHosts().iterator().next().getHostName());
+  }
+
+  @Test
+  public void testHandlingReportHostUpdate() throws NullHostNameException {
+    TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+    TopologyHost hostToUpdate = new TopologyHost(2L, "hostName2");
+    TopologyHost update = new TopologyHost(2L, "hostName2", "rack","ipv4");
+
+    TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new HashSet(){{add(dummyHost); add(hostToUpdate);}});
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.emptySet(), Collections.singleton(update), UpdateEventType.UPDATE, report);
+
+    Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+    Assert.assertEquals("hostName2", report.getUpdatedHostNames().iterator().next());
+
+    Assert.assertEquals(2L, topologyCluster.getTopologyHosts().size());
+  }
+
+  @Test
+  public void testHandlingReportComponentAdd() throws NullHostNameException {
+    TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+        new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+    TopologyComponent componentToAddition = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+    TopologyCluster topologyCluster = new TopologyCluster(new HashSet(){{add(dummyComponent);}}, new HashSet<>());
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.singleton(componentToAddition), Collections.emptySet(), UpdateEventType.UPDATE, report);
+
+    Assert.assertEquals(2L, report.getUpdatedHostNames().size());
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName3"));
+
+    Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+  }
+
+  @Test
+  public void testHandlingReportComponentDeletePartially() throws NullHostNameException {
+    TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+        new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+    TopologyComponent componentToDelete = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+    TopologyComponent update = createDummyTopologyComponent("comp2",
+        new Long[]{1L}, new String[]{"hostName1"});
+
+    TopologyCluster topologyCluster = new TopologyCluster(
+        new HashSet(){{add(dummyComponent); add(componentToDelete);}}, new HashSet<>());
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.singleton(update), Collections.emptySet(), UpdateEventType.DELETE, report);
+
+    Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+
+    Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+  }
+
+  @Test
+  public void testHandlingReportComponentDeleteFully() throws NullHostNameException {
+    TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+        new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+    TopologyComponent componentToDelete = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+    TopologyComponent update = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+    TopologyCluster topologyCluster = new TopologyCluster(
+        new HashSet(){{add(dummyComponent); add(componentToDelete);}}, new HashSet<>());
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.singleton(update), Collections.emptySet(), UpdateEventType.DELETE, report);
+
+    Assert.assertEquals(2L, report.getUpdatedHostNames().size());
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName3"));
+
+    Assert.assertEquals(1L, topologyCluster.getTopologyComponents().size());
+  }
+
+  @Test
+  public void testHandlingReportComponentUpdate() throws NullHostNameException {
+    TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+        new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+    TopologyComponent componentToUpdate = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+    TopologyComponent update = createDummyTopologyComponent("comp2",
+        new Long[]{1L, 4L}, new String[]{"hostName1", "hostName4"});
+
+    TopologyCluster topologyCluster = new TopologyCluster(
+        new HashSet(){{add(dummyComponent); add(componentToUpdate);}}, new HashSet<>());
+
+    TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+    topologyCluster.update(Collections.singleton(update), Collections.emptySet(), UpdateEventType.UPDATE, report);
+
+    Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+    Assert.assertTrue(report.getUpdatedHostNames().contains("hostName4"));
+
+    Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+  }
+
+  private TopologyComponent createDummyTopologyComponent(String componentName, Long[] hostIds, String[] hostNames) {
+    return TopologyComponent.newBuilder()
+        .setComponentName(componentName)
+        .setServiceName("serviceName")
+        .setHostIdentifiers(new HashSet<Long>(Arrays.asList(hostIds)), new HashSet<String>(Arrays.asList(hostNames)))
+        .build();
+  }
+}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
index bc82999..abc4d22 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
@@ -32,6 +32,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.controller.internal.ConfigurationTopologyException;
 import org.apache.ambari.server.controller.internal.Stack;
@@ -71,7 +72,7 @@ public class StackAdvisorBlueprintProcessorTest {
   }
 
   @Test
-  public void testAdviseConfiguration() throws StackAdvisorException, ConfigurationTopologyException {
+  public void testAdviseConfiguration() throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -106,7 +107,7 @@ public class StackAdvisorBlueprintProcessorTest {
   }
 
   @Test
-  public void testAdviseConfigurationWithOnlyStackDefaultsApply() throws StackAdvisorException, ConfigurationTopologyException {
+  public void testAdviseConfigurationWithOnlyStackDefaultsApply() throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -141,7 +142,7 @@ public class StackAdvisorBlueprintProcessorTest {
   }
 
   @Test
-  public void testAdviseConfigurationWithOnlyStackDefaultsApplyWhenNoUserInputForDefault() throws StackAdvisorException, ConfigurationTopologyException {
+  public void testAdviseConfigurationWithOnlyStackDefaultsApplyWhenNoUserInputForDefault() throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     props.get("core-site").put("dummyKey3", "stackDefaultValue");
@@ -176,7 +177,7 @@ public class StackAdvisorBlueprintProcessorTest {
 
   @Test
   public void testAdviseConfigurationWith_ALWAYS_APPLY_DONT_OVERRIDE_CUSTOM_VALUES() throws StackAdvisorException,
-    ConfigurationTopologyException {
+      ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -209,7 +210,7 @@ public class StackAdvisorBlueprintProcessorTest {
   }
 
   @Test
-  public void testAdviseConfigurationWhenConfigurationRecommendFails() throws StackAdvisorException, ConfigurationTopologyException {
+  public void testAdviseConfigurationWhenConfigurationRecommendFails() throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -238,7 +239,7 @@ public class StackAdvisorBlueprintProcessorTest {
   }
 
   @Test
-  public void testAdviseConfigurationWhenConfigurationRecommendHasInvalidResponse() throws StackAdvisorException, ConfigurationTopologyException {
+  public void testAdviseConfigurationWhenConfigurationRecommendHasInvalidResponse() throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
     // GIVEN
     Map<String, Map<String, String>> props = createProps();
     Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
index 8f85216..d7c0ae9 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
@@ -18,6 +18,15 @@
 
 package org.apache.ambari.server.api.services.stackadvisor;
 
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.partialMockBuilder;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -27,6 +36,11 @@ import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
@@ -41,10 +55,15 @@ import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorC
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
 import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
 import org.apache.ambari.server.state.ServiceInfo;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.JsonNodeFactory;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import com.google.gson.Gson;
+
 /**
  * StackAdvisorHelper unit tests.
  */
@@ -160,7 +179,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null);
     StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -201,7 +220,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null);
     StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -221,7 +240,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null);
     StackAdvisorRequestType requestType = StackAdvisorRequestType.CONFIGURATIONS;
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -241,7 +260,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null);
     StackAdvisorRequestType requestType = StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES;
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -251,6 +270,124 @@ public class StackAdvisorHelperTest {
     assertEquals(ConfigurationDependenciesRecommendationCommand.class, command.getClass());
   }
 
+  @Test
+  public void testClearCacheAndHost() throws IOException, NoSuchFieldException, IllegalAccessException {
+    Field hostInfoCacheField = StackAdvisorHelper.class.getDeclaredField("hostInfoCache");
+    Field configsRecommendationResponseField = StackAdvisorHelper.class.getDeclaredField("configsRecommendationResponse");
+    StackAdvisorHelper helper = testClearCachesSetup(hostInfoCacheField, configsRecommendationResponseField);
+
+    helper.clearCaches("hostName1");
+
+    Map<String, JsonNode> hostInfoCache = (Map<String, JsonNode>) hostInfoCacheField.get(helper);
+    Map<String, RecommendationResponse> configsRecommendationResponse =
+        (Map<String, RecommendationResponse>) configsRecommendationResponseField.get(helper);
+
+    assertEquals(2, hostInfoCache.size());
+    assertTrue(hostInfoCache.containsKey("hostName2"));
+    assertTrue(hostInfoCache.containsKey("hostName3"));
+    assertTrue(configsRecommendationResponse.isEmpty());
+  }
+
+  @Test
+  public void testClearCacheAndHosts() throws IOException, NoSuchFieldException, IllegalAccessException {
+
+    Field hostInfoCacheField = StackAdvisorHelper.class.getDeclaredField("hostInfoCache");
+    Field configsRecommendationResponseField = StackAdvisorHelper.class.getDeclaredField("configsRecommendationResponse");
+    StackAdvisorHelper helper = testClearCachesSetup(hostInfoCacheField, configsRecommendationResponseField);
+
+    helper.clearCaches(new HashSet<>(Arrays.asList(new String[]{"hostName1", "hostName2"})));
+
+    Map<String, JsonNode> hostInfoCache = (Map<String, JsonNode>) hostInfoCacheField.get(helper);
+    Map<String, RecommendationResponse> configsRecommendationResponse =
+        (Map<String, RecommendationResponse>) configsRecommendationResponseField.get(helper);
+
+    assertEquals(1, hostInfoCache.size());
+    assertTrue(hostInfoCache.containsKey("hostName3"));
+    assertTrue(configsRecommendationResponse.isEmpty());
+  }
+
+  @Test
+  public void testCacheRecommendations() throws IOException, StackAdvisorException {
+    Configuration configuration = createNiceMock(Configuration.class);
+    StackAdvisorRunner stackAdvisorRunner = createNiceMock(StackAdvisorRunner.class);
+    AmbariMetaInfo ambariMetaInfo = createNiceMock(AmbariMetaInfo.class);
+    AmbariServerConfigurationHandler ambariServerConfigurationHandler = createNiceMock(AmbariServerConfigurationHandler.class);
+
+    expect(configuration.getRecommendationsArtifactsRolloverMax()).andReturn(1);
+
+    replay(configuration, stackAdvisorRunner, ambariMetaInfo, ambariServerConfigurationHandler);
+
+    StackAdvisorHelper helper = partialMockBuilder(StackAdvisorHelper.class).withConstructor(Configuration.class,
+        StackAdvisorRunner.class, AmbariMetaInfo.class, AmbariServerConfigurationHandler.class, Gson.class).
+        withArgs(configuration, stackAdvisorRunner, ambariMetaInfo, ambariServerConfigurationHandler, new Gson()).
+        addMockedMethod("createRecommendationCommand").
+        createMock();
+
+    verify(configuration, stackAdvisorRunner, ambariMetaInfo, ambariServerConfigurationHandler);
+    reset(ambariMetaInfo);
+
+    ServiceInfo serviceInfo = new ServiceInfo();
+    serviceInfo.setServiceAdvisorType(ServiceInfo.ServiceAdvisorType.PYTHON);
+    expect(ambariMetaInfo.getService(anyString(), anyString(), anyString())).andReturn(serviceInfo).atLeastOnce();
+
+    ConfigurationRecommendationCommand command = createMock(ConfigurationRecommendationCommand.class);
+
+    StackAdvisorRequest request = StackAdvisorRequestBuilder.
+        forStack(null, null).ofType(StackAdvisorRequestType.CONFIGURATIONS).
+        build();
+
+    expect(helper.createRecommendationCommand(eq("ZOOKEEPER"), eq(request))).andReturn(command).times(2);
+
+    // populate response with dummy info to check equivalence
+    RecommendationResponse response = new RecommendationResponse();
+    response.setServices(new HashSet<String>(){{add("service1"); add("service2"); add("service3");}});
+
+    // invoke() should be fired for first recommend() execution only
+    expect(command.invoke(eq(request), eq(ServiceInfo.ServiceAdvisorType.PYTHON))).andReturn(response).once();
+
+    replay(ambariMetaInfo, helper, command);
+
+    RecommendationResponse calculatedResponse = helper.recommend(request);
+    RecommendationResponse cachedResponse = helper.recommend(request);
+
+    verify(ambariMetaInfo, helper, command);
+
+    assertEquals(response.getServices(), calculatedResponse.getServices());
+    assertEquals(response.getServices(), cachedResponse.getServices());
+  }
+
+  private StackAdvisorHelper testClearCachesSetup(Field hostInfoCacheField,
+                                                  Field configsRecommendationResponseField) throws IOException,
+      NoSuchFieldException, IllegalAccessException {
+    Configuration configuration = createNiceMock(Configuration.class);
+    StackAdvisorRunner stackAdvisorRunner = createNiceMock(StackAdvisorRunner.class);
+    AmbariMetaInfo ambariMetaInfo = createNiceMock(AmbariMetaInfo.class);
+    AmbariServerConfigurationHandler ambariServerConfigurationHandler = createNiceMock(AmbariServerConfigurationHandler.class);
+
+    replay(configuration, stackAdvisorRunner, ambariMetaInfo, ambariServerConfigurationHandler);
+
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, stackAdvisorRunner,
+        ambariMetaInfo, ambariServerConfigurationHandler, null);
+
+    verify(configuration, stackAdvisorRunner, ambariMetaInfo, ambariServerConfigurationHandler);
+
+    hostInfoCacheField.setAccessible(true);
+    configsRecommendationResponseField.setAccessible(true);
+
+    Map<String, JsonNode> hostInfoCache = new ConcurrentHashMap<>();
+    JsonNodeFactory jnf = JsonNodeFactory.instance;
+    hostInfoCache.put("hostName1", jnf.nullNode());
+    hostInfoCache.put("hostName2", jnf.nullNode());
+    hostInfoCache.put("hostName3", jnf.nullNode());
+    Map<String, RecommendationResponse> configsRecommendationResponse = new ConcurrentHashMap<>();
+    configsRecommendationResponse.put("111", new RecommendationResponse());
+    configsRecommendationResponse.put("222", new RecommendationResponse());
+
+    hostInfoCacheField.set(helper, hostInfoCache);
+    configsRecommendationResponseField.set(helper, configsRecommendationResponse);
+    return helper;
+  }
+
   private void testCreateConfigurationRecommendationCommand(StackAdvisorRequestType requestType, StackAdvisorCommandType expectedCommandType)
       throws IOException, StackAdvisorException {
     Configuration configuration = mock(Configuration.class);
@@ -260,7 +397,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null);
 
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -273,6 +410,6 @@ public class StackAdvisorHelperTest {
   }
 
   private static StackAdvisorHelper stackAdvisorHelperSpy(Configuration configuration, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo) throws IOException {
-    return spy(new StackAdvisorHelper(configuration, saRunner, metaInfo, null));
+    return spy(new StackAdvisorHelper(configuration, saRunner, metaInfo, null, null));
   }
 }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
index c983af2..7d943d6 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
@@ -29,6 +29,10 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
@@ -44,16 +48,19 @@ public class ConfigurationRecommendationCommandTest {
     StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
     File file = mock(File.class);
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
-    ConfigurationRecommendationCommand command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS, file, "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo, null);
+    ConfigurationRecommendationCommand command =
+        new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS, file,
+            "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo,
+            null, null);
 
     StackAdvisorRequest request = mock(StackAdvisorRequest.class);
-    Map<String, Set<String>> componentHostGroupMap = new HashMap<>();
-    Set<String> components1 = new HashSet<>();
+    SortedMap<String, SortedSet<String>> componentHostGroupMap = new TreeMap<>();
+    SortedSet<String> components1 = new TreeSet<>();
     components1.add("component1");
     components1.add("component4");
     components1.add("component5");
     componentHostGroupMap.put("group1", components1);
-    Set<String> components2 = new HashSet<>();
+    SortedSet<String> components2 = new TreeSet<>();
     components2.add("component2");
     components2.add("component3");
     componentHostGroupMap.put("group2", components2);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
index 5d7339a..d110023 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
@@ -43,6 +43,7 @@ import java.util.Map;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import org.apache.ambari.server.api.resources.ResourceInstance;
@@ -80,6 +81,9 @@ import com.google.common.collect.Lists;
  */
 @RunWith(MockitoJUnitRunner.class)
 public class StackAdvisorCommandTest {
+  private static final String SINGLE_HOST_RESPONSE = "{\"href\":\"/api/v1/hosts?fields=Hosts/*&Hosts/host_name.in(%1$s)\",\"items\":[{\"href\":\"/api/v1/hosts/%1$s\",\"Hosts\":{\"host_name\":\"%1$s\"}}]}";
+  private static final String TWO_HOST_RESPONSE = "{\"href\":\"/api/v1/hosts?fields=Hosts/*&Hosts/host_name.in(%1$s,%2$s)\",\"items\":[{\"href\":\"/api/v1/hosts/%1$s\",\"Hosts\":{\"host_name\":\"%1$s\"}},{\"href\":\"/api/v1/hosts/%2$s\",\"Hosts\":{\"host_name\":\"%2$s\"}}]}";
+
   private TemporaryFolder temp = new TemporaryFolder();
   @Mock
   AmbariServerConfigurationHandler ambariServerConfigurationHandler;
@@ -103,7 +107,7 @@ public class StackAdvisorCommandTest {
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
     doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
     StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
-        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo, null));
 
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .build();
@@ -123,7 +127,7 @@ public class StackAdvisorCommandTest {
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
     doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
     StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
-        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo, null));
 
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .build();
@@ -151,7 +155,7 @@ public class StackAdvisorCommandTest {
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
     doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
     StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
-        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+        ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo, null));
 
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .build();
@@ -180,7 +184,8 @@ public class StackAdvisorCommandTest {
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
     doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(), anyString());
     final StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(
-        recommendationsDir, recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+        recommendationsDir, recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, requestId,
+        saRunner, metaInfo, null));
 
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .build();
@@ -215,8 +220,8 @@ public class StackAdvisorCommandTest {
     String recommendationsArtifactsLifetime = "1w";
     StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
     AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
-    StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
-        stackAdvisorRunner, ambariMetaInfo);
+    StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime,
+        ServiceInfo.ServiceAdvisorType.PYTHON, 1, stackAdvisorRunner, ambariMetaInfo, null);
     ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
         "{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
 
@@ -244,7 +249,7 @@ public class StackAdvisorCommandTest {
     StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
     AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
     StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
-      stackAdvisorRunner, ambariMetaInfo);
+      stackAdvisorRunner, ambariMetaInfo, null);
     ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
       "{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
 
@@ -266,7 +271,7 @@ public class StackAdvisorCommandTest {
     StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
     AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
     StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
-        stackAdvisorRunner, ambariMetaInfo);
+        stackAdvisorRunner, ambariMetaInfo, null);
     ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": " +
         "{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
 
@@ -294,7 +299,7 @@ public class StackAdvisorCommandTest {
       ServiceInfo.ServiceAdvisorType.PYTHON,
       0,
       mock(StackAdvisorRunner.class),
-      mock(AmbariMetaInfo.class));
+      mock(AmbariMetaInfo.class), null);
     when(ambariServerConfigurationHandler.getConfigurations()).thenReturn(storedConfig);
     JsonNode servicesRootNode = json("{}");
     command.populateAmbariConfiguration((ObjectNode)servicesRootNode);
@@ -310,7 +315,7 @@ public class StackAdvisorCommandTest {
       ServiceInfo.ServiceAdvisorType.PYTHON,
       0,
       mock(StackAdvisorRunner.class),
-      mock(AmbariMetaInfo.class));
+      mock(AmbariMetaInfo.class), null);
     when(ambariServerConfigurationHandler.getConfigurations()).thenReturn(emptyMap());
     JsonNode servicesRootNode = json("{}");
     command.populateAmbariConfiguration((ObjectNode)servicesRootNode);
@@ -318,6 +323,74 @@ public class StackAdvisorCommandTest {
     assertEquals(expectedLdapConfig, servicesRootNode);
   }
 
+  /**
+   * Try to retrieve host info twice. The inner cache should be populated with first usage (with handleRequest method calling).
+   * And for next info retrieving for the same host the saved value should be used.
+   */
+  @Test
+  public void testHostInfoCachingSingleHost() throws StackAdvisorException {
+    File file = mock(File.class);
+    String recommendationsArtifactsLifetime = "1w";
+    StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
+    AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
+    Map<String, JsonNode> hostInfoCache = new HashMap<>();
+    TestStackAdvisorCommand command = spy(new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime,
+        ServiceInfo.ServiceAdvisorType.PYTHON, 1,
+        stackAdvisorRunner, ambariMetaInfo, hostInfoCache));
+
+    // in second handling case NPE will be fired during result processing
+    doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE, "hostName1")).build())
+        .doReturn(null)
+        .when(command).handleRequest(any(HttpHeaders.class), any(String.class), any(UriInfo.class), any(Request.Type.class),
+        any(MediaType.class), any(ResourceInstance.class));
+
+    StackAdvisorRequest request = StackAdvisorRequestBuilder.
+        forStack(null, null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+        forHosts(Arrays.asList(new String[]{"hostName1"})).
+        build();
+    String firstResponse = command.getHostsInformation(request);
+    assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"), firstResponse);
+
+    String secondResponse = command.getHostsInformation(request);
+    assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"), secondResponse);
+  }
+
+  /**
+   * Try to retrieve multiple hosts info twice. The inner cache should be populated with first usage for first host (hostName1).
+   * For the next usage with the both hosts handleRequest should be used for second host only.
+   */
+  @Test
+  public void testHostInfoCachingTwoHost() throws StackAdvisorException {
+    File file = mock(File.class);
+    String recommendationsArtifactsLifetime = "1w";
+    StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
+    AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
+    Map<String, JsonNode> hostInfoCache = new HashMap<>();
+    TestStackAdvisorCommand command = spy(new TestStackAdvisorCommand(file, recommendationsArtifactsLifetime,
+        ServiceInfo.ServiceAdvisorType.PYTHON, 1,
+        stackAdvisorRunner, ambariMetaInfo, hostInfoCache));
+
+    doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE, "hostName1")).build())
+        .doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE, "hostName2")).build())
+        .doReturn(null)
+        .when(command).handleRequest(any(HttpHeaders.class), any(String.class), any(UriInfo.class), any(Request.Type.class),
+        any(MediaType.class), any(ResourceInstance.class));
+
+    StackAdvisorRequest request = StackAdvisorRequestBuilder.
+        forStack(null, null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+        forHosts(Arrays.asList(new String[]{"hostName1"})).
+        build();
+    String firstResponse = command.getHostsInformation(request);
+    assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"), firstResponse);
+
+    request = StackAdvisorRequestBuilder.
+        forStack(null, null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+        forHosts(Arrays.asList(new String[]{"hostName1", "hostName2"})).
+        build();
+    String secondResponse = command.getHostsInformation(request);
+    assertEquals(String.format(TWO_HOST_RESPONSE, "hostName1", "hostName2"), secondResponse);
+  }
+
   private static String jsonString(Object obj) throws IOException {
     return new ObjectMapper().writeValueAsString(obj);
   }
@@ -345,8 +418,9 @@ public class StackAdvisorCommandTest {
 
   class TestStackAdvisorCommand extends StackAdvisorCommand<TestResource> {
     public TestStackAdvisorCommand(File recommendationsDir, String recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType serviceAdvisorType,
-                                   int requestId, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo) {
-      super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+                                   int requestId, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo, Map<String, JsonNode> hostInfoCache) {
+      super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo,
+          ambariServerConfigurationHandler, hostInfoCache);
     }
 
     @Override
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index 9721e8b..a056c1b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -52,6 +52,10 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.TimeUnit;
 
 import javax.persistence.EntityManager;
@@ -1994,7 +1998,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     serviceNames.add("EXISTING_SERVICE");
     serviceNames.add("PRECONFIGURE_SERVICE");
 
-    Map<String, Set<String>> hostMap = new HashMap<>();
+    SortedMap<String, SortedSet<String>> hostMap = new TreeMap<>();
 
     Map<String, Service> services = new HashMap<>();
 
@@ -2377,12 +2381,12 @@ public class KerberosHelperTest extends EasyMockSupport {
     services.put("SERVICE2", service2);
     services.put("SERVICE3", service3);
 
-    Map<String, Set<String>> serviceComponentHostMap = new HashMap<>();
-    serviceComponentHostMap.put("COMPONENT1A", Collections.singleton("hostA"));
-    serviceComponentHostMap.put("COMPONENT1B", new HashSet<>(Arrays.asList("hostB", "hostC")));
-    serviceComponentHostMap.put("COMPONENT2A", Collections.singleton("hostA"));
-    serviceComponentHostMap.put("COMPONENT2B", new HashSet<>(Arrays.asList("hostB", "hostC")));
-    serviceComponentHostMap.put("COMPONEN3A", Collections.singleton("hostA"));
+    SortedMap<String, SortedSet<String>> serviceComponentHostMap = new TreeMap<>();
+    serviceComponentHostMap.put("COMPONENT1A", new TreeSet<>(Arrays.asList("hostA")));
+    serviceComponentHostMap.put("COMPONENT1B", new TreeSet<>(Arrays.asList("hostB", "hostC")));
+    serviceComponentHostMap.put("COMPONENT2A", new TreeSet<>(Arrays.asList("hostA")));
+    serviceComponentHostMap.put("COMPONENT2B", new TreeSet<>(Arrays.asList("hostB", "hostC")));
+    serviceComponentHostMap.put("COMPONEN3A", new TreeSet<>(Arrays.asList("hostA")));
 
     final Cluster cluster = createMockCluster("c1", hosts, SecurityType.KERBEROS, krb5ConfConfig, kerberosEnvConfig);
     expect(cluster.getServices()).andReturn(services).anyTimes();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
index 9c93cd3..861c176 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
@@ -30,7 +30,10 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -102,7 +105,8 @@ public class AmbariServerSSOConfigurationHandlerTest extends EasyMockSupport {
 
     StackId stackId = new StackId("HDP-3.0");
 
-    Map<String, Set<String>> serviceComponentHostMap = Collections.singletonMap("ATLAS_COMPONENT", Collections.singleton("host1"));
+    SortedMap<String, SortedSet<String>> serviceComponentHostMap =
+        new TreeMap<String, SortedSet<String>>(){{put("ATLAS_COMPONENT", new TreeSet<String>(){{add("host1");}});}};
 
     Host host = createMock(Host.class);
     expect(host.getHostName()).andReturn("host1").once();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
index 8d02821..844b2ae 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
@@ -18,12 +18,180 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.partialMockBuilder;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorResponse;
+import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.state.Clusters;
 import org.junit.Test;
 
 public class RecommendationResourceProviderTest {
 
   @Test
-  public void testCreateResources() throws Exception {
+  public void testCreateConfigurationResources() throws Exception {
+    Set<String> hosts = new HashSet<>(Arrays.asList(new String[]{"hostName1", "hostName2", "hostName3"}));
+    Set<String> services = new HashSet<>(Arrays.asList(new String[]{"serviceName1", "serviceName2", "serviceName3"}));
+    RequestStatus requestStatus = testCreateResources(hosts, services,
+        StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS, true);
+
+    assertFalse(requestStatus == null);
+    assertEquals(1, requestStatus.getAssociatedResources().size());
+    assertEquals(Resource.Type.Recommendation, requestStatus.getAssociatedResources().iterator().next().getType());
+
+    Map<String, Map<String, Object>> propertiesMap = requestStatus.getAssociatedResources().iterator().next().getPropertiesMap();
+    assertEquals(2, propertiesMap.size());
+    assertTrue(propertiesMap.containsKey("recommendations"));
+    assertTrue(propertiesMap.containsKey("recommendations/blueprint/configurations"));
+
+    assertEquals(1, propertiesMap.get("recommendations").size());
+    assertTrue(propertiesMap.get("recommendations").containsKey("config-groups"));
+    assertNotNull(propertiesMap.get("recommendations").get("config-groups"));
+
+    assertEquals(0, propertiesMap.get("recommendations/blueprint/configurations").size());
+  }
+
+  @Test
+  public void testCreateNotConfigurationResources() throws Exception {
+    Set<String> hosts = new HashSet<>(Arrays.asList(new String[]{"hostName1", "hostName2", "hostName3"}));
+    Set<String> services = new HashSet<>(Arrays.asList(new String[]{"serviceName1", "serviceName2", "serviceName3"}));
+    RequestStatus requestStatus = testCreateResources(hosts, services,
+        StackAdvisorRequest.StackAdvisorRequestType.HOST_GROUPS, false);
+
+    assertFalse(requestStatus == null);
+    assertEquals(1, requestStatus.getAssociatedResources().size());
+    assertEquals(Resource.Type.Recommendation, requestStatus.getAssociatedResources().iterator().next().getType());
+
+    Map<String, Map<String, Object>> propertiesMap = requestStatus.getAssociatedResources().iterator().next().getPropertiesMap();
+    assertEquals(7, propertiesMap.size());
+    assertTrue(propertiesMap.containsKey(""));
+    assertTrue(propertiesMap.containsKey("Recommendation"));
+    assertTrue(propertiesMap.containsKey("Versions"));
+    assertTrue(propertiesMap.containsKey("recommendations"));
+    assertTrue(propertiesMap.containsKey("recommendations/blueprint"));
+    assertTrue(propertiesMap.containsKey("recommendations/blueprint/configurations"));
+    assertTrue(propertiesMap.containsKey("recommendations/blueprint_cluster_binding"));
+
+    assertEquals(2, propertiesMap.get("").size());
+    assertTrue(propertiesMap.get("").containsKey("hosts"));
+    assertTrue(propertiesMap.get("").containsKey("services"));
+    assertEquals(hosts, propertiesMap.get("").get("hosts"));
+    assertEquals(services, propertiesMap.get("").get("services"));
+
+    assertEquals(1, propertiesMap.get("Recommendation").size());
+    assertTrue(propertiesMap.get("Recommendation").containsKey("id"));
+    assertEquals(1, propertiesMap.get("Recommendation").get("id"));
+
+    assertEquals(2, propertiesMap.get("Versions").size());
+    assertTrue(propertiesMap.get("Versions").containsKey("stack_name"));
+    assertTrue(propertiesMap.get("Versions").containsKey("stack_version"));
+    assertEquals("stackName", propertiesMap.get("Versions").get("stack_name"));
+    assertEquals("stackVersion", propertiesMap.get("Versions").get("stack_version"));
+
+    assertEquals(1, propertiesMap.get("recommendations").size());
+    assertTrue(propertiesMap.get("recommendations").containsKey("config-groups"));
+    assertNotNull(propertiesMap.get("recommendations").get("config-groups"));
+
+    assertEquals(1, propertiesMap.get("recommendations/blueprint").size());
+    assertTrue(propertiesMap.get("recommendations/blueprint").containsKey("host_groups"));
+    assertNotNull(propertiesMap.get("recommendations/blueprint").get("host_groups"));
+
+    assertEquals(0, propertiesMap.get("recommendations/blueprint/configurations").size());
+
+    assertEquals(1, propertiesMap.get("recommendations/blueprint_cluster_binding").size());
+    assertTrue(propertiesMap.get("recommendations/blueprint_cluster_binding").containsKey("host_groups"));
+    assertNotNull(propertiesMap.get("recommendations/blueprint_cluster_binding").get("host_groups"));
+
+  }
+
+  private RequestStatus testCreateResources(Set<String> hosts, Set<String> services,
+                                            StackAdvisorRequest.StackAdvisorRequestType type,
+                                            Boolean configsOnlyResponse) throws
+      NoSuchParentResourceException, ResourceAlreadyExistsException,
+      UnsupportedPropertyException, SystemException, StackAdvisorException, AmbariException {
+    StackAdvisorHelper stackAdvisorHelper = createMock(StackAdvisorHelper.class);
+    Configuration configuration = createMock(Configuration.class);
+    Clusters clusters = createMock(Clusters.class);
+    AmbariMetaInfo ambariMetaInfo = createMock(AmbariMetaInfo.class);
+
+    RecommendationResourceProvider provider = partialMockBuilder(RecommendationResourceProvider.class)
+        .withConstructor(AmbariManagementController.class)
+        .withArgs(createMock(AmbariManagementController.class))
+        .addMockedMethod("prepareStackAdvisorRequest", Request.class)
+        .createMock();
+    RecommendationResourceProvider.init(stackAdvisorHelper, configuration, clusters, ambariMetaInfo);
+
+    StackAdvisorRequest stackAdvisorRequest = StackAdvisorRequest.StackAdvisorRequestBuilder.
+        forStack(null, null).ofType(type).
+        withConfigsResponse(configsOnlyResponse).
+        build();
+
+    Request request = createMock(Request.class);
+    expect(provider.prepareStackAdvisorRequest(eq(request))).andReturn(stackAdvisorRequest);
+
+    RecommendationResponse response = new RecommendationResponse();
+    RecommendationResponse.Recommendation recommendation = new RecommendationResponse.Recommendation();
+
+    recommendation.setConfigGroups(new HashSet<>());
+
+    RecommendationResponse.Blueprint blueprint = new RecommendationResponse.Blueprint();
+    blueprint.setConfigurations(new HashMap<>());
+    blueprint.setHostGroups(new HashSet<>());
+    recommendation.setBlueprint(blueprint);
+
+    RecommendationResponse.BlueprintClusterBinding blueprintClusterBinding = new RecommendationResponse.BlueprintClusterBinding();
+    blueprintClusterBinding.setHostGroups(new HashSet<>());
+    recommendation.setBlueprintClusterBinding(blueprintClusterBinding);
+
+    response.setRecommendations(recommendation);
+
+    response.setId(1);
+
+    StackAdvisorResponse.Version version = new StackAdvisorResponse.Version();
+    version.setStackName("stackName");
+    version.setStackVersion("stackVersion");
+    response.setVersion(version);
+
+    response.setHosts(hosts);
+    response.setServices(services);
+
+    expect(stackAdvisorHelper.recommend(anyObject(StackAdvisorRequest.class))).andReturn(response).anyTimes();
+
+    replay(provider, request, stackAdvisorHelper);
+
+    RequestStatus requestStatus = provider.createResources(request);
+
+    verify(provider, request, stackAdvisorHelper);
 
+    return requestStatus;
   }
 }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
index a6c7d42..61d99c7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
 
 import javax.annotation.Nonnull;
 
@@ -55,11 +56,11 @@ public class StackAdvisorResourceProviderTest {
         CONFIGURATIONS_PROPERTY_ID + "site/properties/string_prop", "string",
         CONFIGURATIONS_PROPERTY_ID + "site/properties/array_prop", Lists.newArrayList("array1", "array2"));
 
-    Map<String, Map<String, Map<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
 
     assertNotNull(calculatedConfigurations);
     assertEquals(1, calculatedConfigurations.size());
-    Map<String, Map<String, String>> site = calculatedConfigurations.get("site");
+    SortedMap<String, SortedMap<String, String>> site = calculatedConfigurations.get("site");
     assertNotNull(site);
     assertEquals(1, site.size());
     Map<String, String> properties = site.get("properties");
@@ -112,11 +113,11 @@ public class StackAdvisorResourceProviderTest {
         CONFIGURATIONS_PROPERTY_ID + "site/properties/string_prop", null,
         CONFIGURATIONS_PROPERTY_ID + "site/properties/array_prop", Lists.newArrayList("array1", "array2"));
 
-    Map<String, Map<String, Map<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
+    SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
 
     assertNotNull(calculatedConfigurations);
     assertEquals(1, calculatedConfigurations.size());
-    Map<String, Map<String, String>> site = calculatedConfigurations.get("site");
+    SortedMap<String, SortedMap<String, String>> site = calculatedConfigurations.get("site");
     assertNotNull(site);
     assertEquals(1, site.size());
     Map<String, String> properties = site.get("properties");
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
index 1eba8fa..5c0eb4f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorResponse.Version;
@@ -38,6 +39,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.state.Clusters;
 import org.junit.Test;
 
 public class ValidationResourceProviderTest {
@@ -60,7 +62,7 @@ public class ValidationResourceProviderTest {
     doReturn(3).when(response).getId();
     doReturn(version).when(response).getVersion();
     doReturn(response).when(saHelper).validate(any(StackAdvisorRequest.class));
-    ValidationResourceProvider.init(saHelper, configuration);
+    ValidationResourceProvider.init(saHelper, configuration, mock(Clusters.class), mock(AmbariMetaInfo.class));
 
     RequestStatus status = provider.createResources(request);
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
index 6bcdc08..8370772 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
@@ -490,30 +490,35 @@ public class RestMetricsPropertyProviderTest {
     RestMetricsPropertyProvider restMetricsPropertyProvider = createRestMetricsPropertyProvider(metricDefinition, componentMetrics, streamProvider,
         metricsHostProvider);
 
-    // set the provider timeout to 50 millis
-    restMetricsPropertyProvider.setPopulateTimeout(50L);
-
-    Resource resource = new ResourceImpl(Resource.Type.HostComponent);
+    // set the provider timeout to -1 millis to guarantee timeout
+    restMetricsPropertyProvider.setPopulateTimeout(-1L);
+    try {
+      Resource resource = new ResourceImpl(Resource.Type.HostComponent);
 
-    resource.setProperty("HostRoles/cluster_name", "c1");
-    resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
-    resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "STORM_REST_API");
+      resource.setProperty("HostRoles/cluster_name", "c1");
+      resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+      resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "STORM_REST_API");
 
-    resources.add(resource);
+      resources.add(resource);
 
-    // request with an empty set should get all supported properties
-    Request request = PropertyHelper.getReadRequest(Collections.emptySet());
+      // request with an empty set should get all supported properties
+      Request request = PropertyHelper.getReadRequest(Collections.emptySet());
 
-    Set<Resource> resourceSet = restMetricsPropertyProvider.populateResources(resources, request, null);
+      Set<Resource> resourceSet = restMetricsPropertyProvider.populateResources(resources, request, null);
 
-    // make sure that the thread running the stream provider has completed
-    Thread.sleep(150L);
+      // make sure that the thread running the stream provider has completed
+      Thread.sleep(150L);
 
-    Assert.assertEquals(0, resourceSet.size());
+      Assert.assertEquals(0, resourceSet.size());
 
-    // assert that properties never get set on the resource
-    Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/tasks.total"));
-    Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/supervisors"));
+      // assert that properties never get set on the resource
+      Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/tasks.total"));
+      Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/supervisors"));
+    } finally {
+      // reset default value
+      restMetricsPropertyProvider.setPopulateTimeout(injector.getInstance(Configuration.class)
+          .getPropertyProvidersCompletionServiceTimeout());
+    }
   }
 
   public static class TestMetricsHostProvider implements MetricHostProvider {
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index da75ed2..655b703 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -42,6 +42,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
 
 import javax.persistence.EntityManager;
 
@@ -747,7 +749,7 @@ public class ClusterTest {
     ServiceComponentHost scDNH2 = serviceComponentHostFactory.createNew(scDN, "h2");
     scDN.addServiceComponentHost(scDNH2);
 
-    Map<String, Set<String>> componentHostMap;
+    SortedMap<String, SortedSet<String>> componentHostMap;
 
     componentHostMap = c1.getServiceComponentHostMap(null, null);
     Assert.assertEquals(2, componentHostMap.size());
@@ -789,7 +791,7 @@ public class ClusterTest {
     ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
     scJT.addServiceComponentHost(schJTH1);
 
-    Map<String, Set<String>> componentHostMap;
+    SortedMap<String, SortedSet<String>> componentHostMap;
 
     componentHostMap = c1.getServiceComponentHostMap(null, Collections.singleton("HDFS"));
     Assert.assertEquals(2, componentHostMap.size());
@@ -847,7 +849,7 @@ public class ClusterTest {
     ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
     scJT.addServiceComponentHost(schJTH1);
 
-    Map<String, Set<String>> componentHostMap;
+    SortedMap<String, SortedSet<String>> componentHostMap;
 
     componentHostMap = c1.getServiceComponentHostMap(Collections.singleton("h1"), null);
     Assert.assertEquals(3, componentHostMap.size());
@@ -906,7 +908,7 @@ public class ClusterTest {
     ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
     scJT.addServiceComponentHost(schJTH1);
 
-    Map<String, Set<String>> componentHostMap;
+    SortedMap<String, SortedSet<String>> componentHostMap;
 
     componentHostMap = c1.getServiceComponentHostMap(Collections.singleton("h1"), Collections.singleton("HDFS"));
     Assert.assertEquals(2, componentHostMap.size());
diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js
index 54fd484..7b93f9a 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -24,6 +24,12 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.AddSecurityConfi
   App.ConfigsComparator, App.ComponentActionsByConfigs, {
 
   name: 'mainServiceInfoConfigsController',
+  
+  /**
+   * Recommendations data will be completed on server side,
+   * UI doesn't have to send all cluster data as hosts, configurations, config-groups, etc.
+   */
+  isRecommendationsAutoComplete: true,
 
   isHostsConfigsPage: false,
 
diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js
index bf091fb..9bd1ba2 100644
--- a/ambari-web/app/mixins/common/configs/enhanced_configs.js
+++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js
@@ -221,11 +221,12 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
     var updateDependencies = Em.isArray(changedConfigs) && changedConfigs.length > 0;
     var stepConfigs = this.get('stepConfigs');
     var requiredTags = [];
+    const isAutoComplete = Boolean(this.get('isRecommendationsAutoComplete'));
 
     if (updateDependencies || Em.isNone(this.get('recommendationsConfigs'))) {
-      var recommendations = this.get('hostGroups');
-      var dataToSend = this.getConfigRecommendationsParams(updateDependencies, changedConfigs);
-      this.modifyRecommendationConfigGroups(recommendations);
+      var recommendations = isAutoComplete ? {} : this.get('hostGroups');
+      var dataToSend = this.getConfigRecommendationsParams(updateDependencies, changedConfigs, isAutoComplete);
+      this.modifyRecommendationConfigGroups(recommendations, isAutoComplete);
 
       if (!stepConfigs.someProperty('serviceName', 'MISC')) {
         requiredTags.push({site: 'cluster-env', serviceName: 'MISC'});
@@ -265,8 +266,16 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
    * @param stepConfigs
    */
   addRecommendationRequestParams: function(recommendations, dataToSend, stepConfigs) {
-    recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(stepConfigs);
+    const isAutoComplete = Boolean(this.get('isRecommendationsAutoComplete'));
+    if (!isAutoComplete) {
+        recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(stepConfigs);
+    }
     dataToSend.recommendations = recommendations;
+    dataToSend.serviceName = this.get('content.serviceName');
+    dataToSend.clusterId = App.get('clusterId');
+    dataToSend.autoComplete = String(isAutoComplete);
+    // configsResponse - tells server to return only configurations in recommendations call
+    dataToSend.configsResponse = String(isAutoComplete);
   },
 
   /**
@@ -289,12 +298,13 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
   /**
    *
    * @param {object} recommendations
+   * @param {boolean} isAutoComplete
    */
-  modifyRecommendationConfigGroups: function(recommendations) {
+  modifyRecommendationConfigGroups: function(recommendations, isAutoComplete) {
     var configGroup = this.get('selectedConfigGroup');
 
     if (configGroup && !configGroup.get('isDefault') && configGroup.get('hosts.length') > 0) {
-      recommendations.config_groups = [this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup)];
+      recommendations.config_groups = [this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup, isAutoComplete)];
     } else {
       delete recommendations.config_groups;
     }
@@ -304,13 +314,14 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
    *
    * @param {boolean} updateDependencies
    * @param {Array} changedConfigs
+   * @param {boolean} isAutoComplete
    * @returns {{recommend: string, hosts: *, services: *, changed_configurations: *}}
    */
-  getConfigRecommendationsParams: function(updateDependencies, changedConfigs) {
+  getConfigRecommendationsParams: function(updateDependencies, changedConfigs, isAutoComplete) {
     return {
       recommend: updateDependencies ? 'configuration-dependencies' : 'configurations',
-      hosts: this.get('hostNames'),
-      services: this.get('serviceNames'),
+      hosts: isAutoComplete ? undefined : this.get('hostNames'),
+      services: isAutoComplete ? undefined : this.get('serviceNames'),
       changed_configurations: updateDependencies ? changedConfigs : undefined
     };
   },
@@ -378,10 +389,16 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
    * generates JSON with config group info to send it for recommendations
    * @param configs
    * @param configGroup
+   * @param {boolean} isAutoComplete
    * @returns {{configurations: Object[], hosts: string[]}}
    */
-  buildConfigGroupJSON: function(configs, configGroup) {
+  buildConfigGroupJSON: function(configs, configGroup, isAutoComplete) {
     Em.assert('configGroup can\'t be null', configGroup);
+    if (isAutoComplete) {
+      return {
+        group_id: Number(configGroup.get('id'))
+      }
+    }
     var hosts = configGroup.get('hosts');
     var configurations = {};
     var overrides = configs.forEach(function(cp) {
@@ -396,7 +413,8 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
     });
     return {
       configurations: [configurations],
-      hosts: hosts
+      hosts: hosts,
+      group_id: Number(configGroup.get('id'))
     }
   },
 
diff --git a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
index 1cf7620..1d6f6d2 100644
--- a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
+++ b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
@@ -25,7 +25,12 @@ describe('App.EnhancedConfigsMixin', function () {
   var mixinClass = Em.Controller.extend(App.EnhancedConfigsMixin);
 
   beforeEach(function () {
-    mixin = Em.Object.create(App.EnhancedConfigsMixin);
+    mixin = Em.Object.create(App.EnhancedConfigsMixin, {
+      isRecommendationsAutoComplete: false,
+      content: Em.Object.create({
+        serviceName: 'S1'
+      })
+    });
   });
 
   describe('#removeCurrentFromDependentList()', function () {
@@ -54,6 +59,7 @@ describe('App.EnhancedConfigsMixin', function () {
     it('generates JSON based on config group info', function () {
       var configGroup = Em.Object.create({
         name: 'group1',
+        id: '1',
         isDefault: false,
         hosts: ['host1', 'host2']
       });
@@ -94,7 +100,8 @@ describe('App.EnhancedConfigsMixin', function () {
             }
           }
         ],
-        "hosts": ['host1', 'host2']
+        "hosts": ['host1', 'host2'],
+        "group_id": 1
       })
     });
 
@@ -575,10 +582,12 @@ describe('App.EnhancedConfigsMixin', function () {
 
     beforeEach(function() {
       sinon.stub(blueprintUtils, 'buildConfigsJSON').returns([{}]);
+      sinon.stub(App, 'get').returns(1);
     });
 
     afterEach(function() {
       blueprintUtils.buildConfigsJSON.restore();
+      App.get.restore();
     });
 
     it("recommendations should be set", function () {
@@ -591,7 +600,11 @@ describe('App.EnhancedConfigsMixin', function () {
               {}
             ]
           }
-        }
+        },
+        "autoComplete": "false",
+        "configsResponse": "false",
+        "serviceName": "S1",
+        "clusterId": 1
       });
     });
   });
@@ -775,11 +788,25 @@ describe('App.EnhancedConfigsMixin', function () {
       {
         configs: [],
         configGroup: Em.Object.create({
-          hosts: []
+          hosts: [],
+          id: '1'
         }),
+        autoComplete: false,
         expected: {
           configurations: [{}],
-          hosts: []
+          hosts: [],
+          group_id: 1
+        }
+      },
+      {
+        configs: [],
+        configGroup: Em.Object.create({
+          hosts: [],
+          id: '1'
+        }),
+        autoComplete: true,
+        expected: {
+          group_id: 1
         }
       },
       {
@@ -787,11 +814,14 @@ describe('App.EnhancedConfigsMixin', function () {
           overrides: null
         })],
         configGroup: Em.Object.create({
-          hosts: []
+          hosts: [],
+          id: '2'
         }),
+        autoComplete: false,
         expected: {
           configurations: [{}],
-          hosts: []
+          hosts: [],
+          group_id: 2
         }
       },
       {
@@ -804,11 +834,14 @@ describe('App.EnhancedConfigsMixin', function () {
         })],
         configGroup: Em.Object.create({
           name: 'g1',
+          id: '3',
           hosts: []
         }),
+        autoComplete: false,
         expected: {
           configurations: [{}],
-          hosts: []
+          hosts: [],
+          group_id: 3
         }
       },
       {
@@ -823,8 +856,10 @@ describe('App.EnhancedConfigsMixin', function () {
         })],
         configGroup: Em.Object.create({
           name: 'g1',
-          hosts: []
+          hosts: [],
+          id: '4'
         }),
+        autoComplete: false,
         expected: {
           configurations: [{
             "tag1": {
@@ -833,15 +868,17 @@ describe('App.EnhancedConfigsMixin', function () {
               }
             }
           }],
-          hosts: []
+          hosts: [],
+          group_id: 4
         }
       }
     ];
 
     testCases.forEach(function(test) {
-      it("configs = " + test.configs +
-         "configGroup = " + JSON.stringify(test.configGroup), function() {
-        expect(mixin.buildConfigGroupJSON(test.configs, test.configGroup)).to.be.eql(test.expected);
+      it("configs = " + JSON.stringify(test.configs) +
+         "configGroup = " + JSON.stringify(test.configGroup) +
+         "autoComplete = " + test.autoComplete, function() {
+        expect(mixin.buildConfigGroupJSON(test.configs, test.configGroup, test.autoComplete)).to.be.eql(test.expected);
       });
     });
   });