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

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


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new ca6e695  AMBARI-25392. Backport: Performance Tune Hosts and service 
Configs Pages to 2.7.5 (#3099)
ca6e695 is described below

commit ca6e6956e794df34c5f43ec5512f0286da10c57a
Author: Myroslav Papirkovskyi <mpapirkovs...@apache.org>
AuthorDate: Fri Oct 11 20:37:15 2019 +0300

    AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages to 
2.7.5 (#3099)
    
    * AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages 
to 2.7.5. (AMBARI-24876. BE: Performance Tune service Configs Pages.) 
(mpapirkovskyy)
    
    * AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages 
to 2.7.5. (AMBARI-24930. Recommendation configs request was failed with custom 
config group.) (mpapirkovskyy)
    
    * AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages 
to 2.7.5. (AMBARI-24842. BE: Performance Tune Hosts Pages) (mpapirkovskyy)
    
    * AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages 
to 2.7.5. License fix. (AMBARI-24876. BE: Performance Tune service Configs 
Pages.) (mpapirkovskyy)
    
    * AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages 
to 2.7.5. Test fix. (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    |  52 +++++
 .../StackAdvisorBlueprintProcessor.java            |   5 +-
 .../services/stackadvisor/StackAdvisorHelper.java  |  77 ++++++-
 .../services/stackadvisor/StackAdvisorRequest.java |  32 ++-
 .../ComponentLayoutRecommendationCommand.java      |   3 +-
 .../ConfigurationRecommendationCommand.java        |   7 +-
 .../stackadvisor/commands/StackAdvisorCommand.java |  88 ++++++--
 .../recommendations/RecommendationResponse.java    |  39 ++++
 .../controller/AmbariManagementControllerImpl.java |   6 +-
 .../ambari/server/controller/AmbariServer.java     |   2 +-
 .../controller/internal/HostResourceProvider.java  |   3 +-
 .../internal/RecommendationResourceProvider.java   |  75 ++++---
 .../internal/StackAdvisorResourceProvider.java     | 232 ++++++++++++++++++---
 .../events/listeners/hosts/HostUpdateListener.java |  12 +-
 .../org/apache/ambari/server/state/Cluster.java    |   7 +
 .../java/org/apache/ambari/server/state/Host.java  |  29 +++
 .../ambari/server/state/cluster/ClusterImpl.java   |  11 +-
 .../apache/ambari/server/state/host/HostImpl.java  |  65 ++++--
 .../topology/STOMPComponentsDeleteHandler.java     |   4 +-
 .../server/agent/stomp/TopologyClusterTest.java    | 186 +++++++++++++++++
 .../StackAdvisorBlueprintProcessorTest.java        |  13 +-
 .../stackadvisor/StackAdvisorHelperTest.java       | 149 ++++++++++++-
 .../ConfigurationRecommendationCommandTest.java    |   5 +-
 .../commands/StackAdvisorCommandTest.java          |  98 +++++++--
 .../server/controller/KerberosHelperTest.java      |  11 +-
 .../RecommendationResourceProviderTest.java        | 167 ++++++++++++++-
 .../internal/ValidationResourceProviderTest.java   |   4 +-
 .../app/controllers/main/service/info/configs.js   |   6 +
 .../app/mixins/common/configs/enhanced_configs.js  |  40 +++-
 .../mixins/common/configs/enhanced_configs_test.js |  63 ++++--
 35 files changed, 1376 insertions(+), 208 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 7224afc..7c58c1e 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;
@@ -62,6 +64,9 @@ public class TopologyHolder extends 
AgentClusterDataHolder<TopologyUpdateEvent>
   private Clusters clusters;
 
   @Inject
+  private StackAdvisorHelper stackAdvisorHelper;
+
+  @Inject
   public TopologyHolder(AmbariEventPublisher ambariEventPublisher) {
     ambariEventPublisher.register(this);
   }
@@ -101,6 +106,10 @@ 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();
             StackId stackId = cl.getDesiredStackVersion();
@@ -108,7 +117,7 @@ public class TopologyHolder extends 
AgentClusterDataHolder<TopologyUpdateEvent>
             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))
@@ -144,7 +153,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();
@@ -154,25 +163,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..3fbac25
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
@@ -0,0 +1,52 @@
+/**
+ * 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..6084711 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
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+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;
@@ -81,7 +82,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);
@@ -93,7 +94,7 @@ public class StackAdvisorBlueprintProcessor {
     Map<String, Set<String>> hgComponentsMap = 
gatherHostGroupComponents(clusterTopology);
     Map<String, Set<String>> hgHostsMap = 
gatherHostGroupBindings(clusterTopology);
     Map<String, Set<String>> componentHostsMap = 
gatherComponentsHostsMap(hgComponentsMap,
-            hgHostsMap);
+        hgHostsMap);
     return StackAdvisorRequest.StackAdvisorRequestBuilder
       .forStack(stack.getName(), stack.getVersion())
       .forServices(new 
ArrayList<>(clusterTopology.getBlueprint().getServices()))
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 f6770bfa..51e1899 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;
@@ -38,7 +44,9 @@ import 
org.apache.ambari.server.controller.internal.AmbariServerConfigurationHan
 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;
 
@@ -54,14 +62,19 @@ public class StackAdvisorHelper {
   public static String pythonStackAdvisorScript;
   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();
@@ -70,6 +83,7 @@ public class StackAdvisorHelper {
     this.saRunner = saRunner;
     this.metaInfo = metaInfo;
     this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+    this.gson = gson;
   }
 
   /**
@@ -122,7 +136,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.
@@ -132,7 +146,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("Arrived configuration stack advisor request with 
hash: %s, service: %s", hash, request.getServiceName()));
+      response = configsRecommendationResponse.computeIfAbsent(hash, h -> {
+        try {
+          LOG.info(String.format("Invoking configuration stack advisor request 
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 {
@@ -145,13 +192,13 @@ 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.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);
@@ -192,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 83a9367..6733a32 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
@@ -38,6 +38,8 @@ import com.google.common.base.Preconditions;
  */
 public class StackAdvisorRequest {
 
+  private Long clusterId;
+  private String serviceName;
   private String stackName;
   private String stackVersion;
   private StackAdvisorRequestType requestType;
@@ -52,6 +54,7 @@ public class StackAdvisorRequest {
   private Map<String, String> userContext = new HashMap<>();
   private Map<String, Object> ldapConfig = new HashMap<>();
   private Boolean gplLicenseAccepted;
+  private Boolean configsResponse = false;
 
   public String getStackName() {
     return stackName;
@@ -123,6 +126,14 @@ public class StackAdvisorRequest {
     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;
@@ -197,7 +212,7 @@ public class StackAdvisorRequest {
     }
 
     public StackAdvisorRequestBuilder withConfigGroups(
-      Set<RecommendationResponse.ConfigGroup> configGroups) {
+        Set<RecommendationResponse.ConfigGroup> configGroups) {
       this.instance.configGroups = configGroups;
       return this;
     }
@@ -219,6 +234,21 @@ public class StackAdvisorRequest {
       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/ConfigurationRecommendationCommand.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
index 56cf957..d140353 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
@@ -35,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
@@ -57,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;
   }
 
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 5b0097b..eb0025e 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
@@ -59,6 +59,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;
@@ -104,6 +105,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;
@@ -120,7 +123,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];
 
@@ -134,6 +138,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();
@@ -383,22 +393,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());
@@ -415,7 +474,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/api/services/stackadvisor/recommendations/RecommendationResponse.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
index 0743e6c..1374995 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
@@ -163,6 +163,24 @@ public class RecommendationResponse extends 
StackAdvisorResponse {
     public void setPropertyAttributes(Map<String, ValueAttributesInfo> 
propertyAttributes) {
       this.propertyAttributes = propertyAttributes;
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      BlueprintConfigurations that = (BlueprintConfigurations) o;
+
+      if (properties != null ? !properties.equals(that.properties) : 
that.properties != null) return false;
+      return propertyAttributes != null ? 
propertyAttributes.equals(that.propertyAttributes) : that.propertyAttributes == 
null;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = properties != null ? properties.hashCode() : 0;
+      result = 31 * result + (propertyAttributes != null ? 
propertyAttributes.hashCode() : 0);
+      return result;
+    }
   }
 
   public static class HostGroup {
@@ -268,6 +286,27 @@ public class RecommendationResponse extends 
StackAdvisorResponse {
     public void setDependentConfigurations(Map<String, 
BlueprintConfigurations> dependentConfigurations) {
       this.dependentConfigurations = dependentConfigurations;
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      ConfigGroup that = (ConfigGroup) o;
+
+      if (hosts != null ? !hosts.equals(that.hosts) : that.hosts != null) 
return false;
+      if (configurations != null ? !configurations.equals(that.configurations) 
: that.configurations != null)
+        return false;
+      return dependentConfigurations != null ? 
dependentConfigurations.equals(that.dependentConfigurations) : 
that.dependentConfigurations == null;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = hosts != null ? hosts.hashCode() : 0;
+      result = 31 * result + (configurations != null ? 
configurations.hashCode() : 0);
+      result = 31 * result + (dependentConfigurations != null ? 
dependentConfigurations.hashCode() : 0);
+      return result;
+    }
   }
 
 }
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 31cc39a..bdfdac5 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
@@ -116,6 +116,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;
@@ -790,8 +791,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()))
@@ -806,7 +806,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 b7f44ff..b8f440a 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
@@ -917,7 +917,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/internal/HostResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 9fd135d..059316f 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
@@ -827,7 +827,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());
 
@@ -835,7 +834,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 dcc6cb6..50aa092 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 cf716ea..0dd48e7 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,14 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+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 +47,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 +74,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";
 
@@ -84,14 +104,19 @@ public abstract class StackAdvisorResourceProvider extends 
ReadOnlyResourceProvi
   private static final String CONFIG_GROUPS_HOSTS_PROPERTY = "hosts";
 
   protected static StackAdvisorHelper saHelper;
-  protected static Configuration configuration;
+  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 +129,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;
+      Map<String, Set<String>> hgComponentsMap;
+      Map<String, Set<String>> hgHostsMap;
+      Map<String, Set<String>> componentHostsMap;
+      Map<String, Map<String, Map<String, String>>> configurations;
+      Set<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");
+          }
         }
-      }
-      List<String> services = (List<String>) servicesObject;
+        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);
+        hgComponentsMap = calculateHostGroupComponentsMap(request);
+        hgHostsMap = calculateHostGroupHostsMap(request);
+        componentHostsMap = calculateComponentHostsMap(hgComponentsMap, 
hgHostsMap);
+        configurations = calculateConfigurations(request);
+        configGroups = calculateConfigGroups(request);
+      }
       Map<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 +216,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)
@@ -194,6 +260,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 Map<String, Set<String>> calculateHostGroupComponentsMap(Cluster 
cluster) {
+    Map<String, Set<String>> map = new HashMap<>();
+    List<Host> hosts = new ArrayList<>(cluster.getHosts());
+    if (!hosts.isEmpty()) {
+      for (Host host : hosts) {
+        String hostGroupName = host.getHostName();
+
+        Set<String> components = new HashSet<>();
+        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.
    * 
@@ -224,6 +312,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 Map<String, Set<String>> calculateHostGroupHostsMap(Cluster cluster) 
{
+    Map<String, Set<String>> map = new HashMap<>();
+
+    List<Host> hosts = new ArrayList<>(cluster.getHosts());
+    if (!hosts.isEmpty()) {
+      for (Host host : hosts) {
+        map.put(host.getHostName(), Collections.singleton(host.getHostName()));
+      }
+    }
+
+    return map;
+  }
+
   protected List<ChangedConfigInfo> calculateChangedConfigurations(Request 
request) {
     List<ChangedConfigInfo> configs =
       new LinkedList<>();
@@ -270,6 +376,39 @@ public abstract class StackAdvisorResourceProvider extends 
ReadOnlyResourceProvi
     return configGroups;
   }
 
+  protected Set<RecommendationResponse.ConfigGroup> 
calculateConfigGroups(Cluster cluster, Request request) {
+
+    Set<RecommendationResponse.ConfigGroup> configGroups = new HashSet<>();
+
+    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" }
@@ -278,7 +417,7 @@ public abstract class StackAdvisorResourceProvider extends 
ReadOnlyResourceProvi
    * @return
    */
   protected Map<String, String> readUserContext(Request request) {
-    HashMap<String, String> userContext = new HashMap<>();
+    Map<String, String> userContext = new HashMap<>();
     if (null != getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY)) {
       userContext.put(OPERATION_PROPERTY,
                       (String) getRequestProperty(request, 
USER_CONTEXT_OPERATION_PROPERTY));
@@ -331,9 +470,33 @@ public abstract class StackAdvisorResourceProvider extends 
ReadOnlyResourceProvi
     return configurations;
   }
 
+  protected Map<String, Map<String, Map<String, String>>> 
calculateConfigurations(Cluster cluster, String serviceName)
+      throws AmbariException {
+    Map<String, Map<String, Map<String, String>>> configurations = new 
HashMap<>();
+    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(), 
Collections.singletonMap("properties", config.getProperties()));
+    }
+    return configurations;
+  }
+
   @SuppressWarnings("unchecked")
   private Map<String, Set<String>> calculateComponentHostsMap(Map<String, 
Set<String>> hostGroups,
-      Map<String, Set<String>> bindingHostGroups) {
+                                                              Map<String, 
Set<String>> bindingHostGroups) {
     /*
      * ClassCastException may occur in case of body inconsistency: property
      * missed, etc.
@@ -360,6 +523,23 @@ public abstract class StackAdvisorResourceProvider extends 
ReadOnlyResourceProvi
     return componentHostsMap;
   }
 
+  @SuppressWarnings("unchecked")
+  private Map<String, Set<String>> calculateComponentHostsMap(Cluster cluster) 
{
+    /*
+     * ClassCastException may occur in case of body inconsistency: property
+     * missed, etc.
+     */
+
+    Map<String, Set<String>> componentHostsMap = new HashMap<>();
+    List<ServiceComponentHost> schs = cluster.getServiceComponentHosts();
+    for (ServiceComponentHost sch : schs) {
+      componentHostsMap.putIfAbsent(sch.getServiceComponentName(), new 
HashSet<>());
+      
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 0955200..dc19e06 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
@@ -511,6 +511,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/Host.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
index e2b5911..cdf36f0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
@@ -28,6 +28,7 @@ import org.apache.ambari.server.agent.HostInfo;
 import org.apache.ambari.server.agent.RecoveryReport;
 import org.apache.ambari.server.controller.HostResponse;
 import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.HostStateEntity;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
@@ -176,6 +177,13 @@ public interface Host extends Comparable {
    */
   String getOsFamily();
 
+  /**
+   * Gets the os family from host attributes
+   * @param hostAttributes host attributes
+   * @return the os family for host
+   */
+  String getOsFamily(Map<String, String> hostAttributes);
+
   String getOSFamilyFromHostAttributes(Map<String, String> hostAttributes);
 
   /**
@@ -200,6 +208,13 @@ public interface Host extends Comparable {
   HostHealthStatus getHealthStatus();
 
   /**
+   * Gets the health status from host attributes
+   * @param hostStateEntity host attributes
+   * @return the health status
+   */
+  HostHealthStatus getHealthStatus(HostStateEntity hostStateEntity);
+
+  /**
    * Get detailed recovery report for the host
    * @return
    */
@@ -223,6 +238,13 @@ public interface Host extends Comparable {
   Map<String, String> getHostAttributes();
 
   /**
+   * Gets host attributes from host entity
+   * @param hostEntity host entity
+   * @return the host attributes
+   */
+  Map<String, String> getHostAttributes(HostEntity hostEntity);
+
+  /**
    * @param hostAttributes the hostAttributes to set
    */
   void setHostAttributes(Map<String, String> hostAttributes);
@@ -290,6 +312,13 @@ public interface Host extends Comparable {
   AgentVersion getAgentVersion();
 
   /**
+   * Gets version of the ambari agent running on the host.
+   * @param hostStateEntity host state entity
+   * @return the agentVersion
+   */
+  AgentVersion getAgentVersion(HostStateEntity hostStateEntity);
+
+  /**
    * @param agentVersion the agentVersion to set
    */
   void setAgentVersion(AgentVersion agentVersion);
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 6dbabc4..22b5a9f 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,8 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
+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 +533,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,7 +807,7 @@ 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<>();
+    Map<String, Set<String>> componentHostMap = new TreeMap<>();
 
     Collection<Host> hosts = getHosts();
 
@@ -820,7 +827,7 @@ public class ClusterImpl implements Cluster {
                 Set<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/state/host/HostImpl.java 
b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
index 0401a1c..3a867ee 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
@@ -765,6 +765,11 @@ public class HostImpl implements Host {
   }
 
   @Override
+  public String getOsFamily(Map<String, String> hostAttributes) {
+         return getOSFamilyFromHostAttributes(hostAttributes);
+  }
+
+  @Override
   public String getOSFamilyFromHostAttributes(Map<String, String> 
hostAttributes) {
     try {
       String majorVersion = 
hostAttributes.get(OS_RELEASE_VERSION).split("\\.")[0];
@@ -806,6 +811,15 @@ public class HostImpl implements Host {
   }
 
   @Override
+  public HostHealthStatus getHealthStatus(HostStateEntity hostStateEntity) {
+    if (hostStateEntity != null) {
+      return gson.fromJson(hostStateEntity.getHealthStatus(), 
HostHealthStatus.class);
+    }
+
+    return null;
+  }
+
+  @Override
   public void setHealthStatus(HostHealthStatus healthStatus) {
     HostStateEntity hostStateEntity = getHostStateEntity();
     if (hostStateEntity != null) {
@@ -837,6 +851,11 @@ public class HostImpl implements Host {
   }
 
   @Override
+  public Map<String, String> getHostAttributes(HostEntity hostEntity) {
+    return gson.fromJson(hostEntity.getHostAttributes(), hostAttributesType);
+  }
+
+  @Override
   public void setHostAttributes(Map<String, String> hostAttributes) {
     HostEntity hostEntity = getHostEntity();
     Map<String, String> hostAttrs = 
gson.fromJson(hostEntity.getHostAttributes(), hostAttributesType);
@@ -903,6 +922,15 @@ public class HostImpl implements Host {
   }
 
   @Override
+  public AgentVersion getAgentVersion(HostStateEntity hostStateEntity) {
+    if (hostStateEntity != null) {
+      return gson.fromJson(hostStateEntity.getAgentVersion(), 
AgentVersion.class);
+    }
+
+    return null;
+  }
+
+  @Override
   public void setAgentVersion(AgentVersion agentVersion) {
     HostStateEntity hostStateEntity = getHostStateEntity();
     if (hostStateEntity != null) {
@@ -963,26 +991,33 @@ public class HostImpl implements Host {
   public HostResponse convertToResponse() {
     HostResponse r = new HostResponse(getHostName());
 
-    r.setAgentVersion(getAgentVersion());
-    r.setPhCpuCount(getPhCpuCount());
-    r.setCpuCount(getCpuCount());
+    HostEntity hostEntity = getHostEntity();
+    HostStateEntity hostStateEntity = getHostStateEntity();
+
+    Map<String, String> hostAttributes = getHostAttributes(hostEntity);
+    r.setHostAttributes(hostAttributes);
+    r.setOsFamily(getOsFamily(hostAttributes));
+
+    r.setAgentVersion(getAgentVersion(hostStateEntity));
+    r.setHealthStatus(getHealthStatus(hostStateEntity));
+
+    r.setPhCpuCount(hostEntity.getPhCpuCount());
+    r.setCpuCount(hostEntity.getCpuCount());
+    r.setIpv4(hostEntity.getIpv4());
+    r.setOsArch(hostEntity.getOsArch());
+    r.setOsType(hostEntity.getOsType());
+    r.setTotalMemBytes(hostEntity.getTotalMem());
+    r.setLastRegistrationTime(hostEntity.getLastRegistrationTime());
+    r.setPublicHostName(hostEntity.getPublicHostName());
+    r.setRackInfo(hostEntity.getRackInfo());
+
     r.setDisksInfo(getDisksInfo());
-    r.setHealthStatus(getHealthStatus());
-    r.setHostAttributes(getHostAttributes());
-    r.setIpv4(getIPv4());
+    r.setStatus(getStatus());
     r.setLastHeartbeatTime(getLastHeartbeatTime());
     r.setLastAgentEnv(lastAgentEnv);
-    r.setLastRegistrationTime(getLastRegistrationTime());
-    r.setOsArch(getOsArch());
-    r.setOsType(getOsType());
-    r.setOsFamily(getOsFamily());
-    r.setRackInfo(getRackInfo());
-    r.setTotalMemBytes(getTotalMemBytes());
-    r.setPublicHostName(getPublicHostName());
-    r.setHostState(getState());
-    r.setStatus(getStatus());
     r.setRecoveryReport(getRecoveryReport());
     r.setRecoverySummary(getRecoveryReport().getSummary());
+    r.setHostState(getState());
     return r;
   }
 
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 2b3e6bb..3bbb9f7 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();
@@ -196,7 +215,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();
@@ -216,7 +235,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();
@@ -236,7 +255,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();
@@ -246,6 +265,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);
@@ -255,7 +392,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();
@@ -268,6 +405,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..26ea3a5 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
@@ -44,7 +44,10 @@ 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<>();
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 37b5a2c..c86c487 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
@@ -44,6 +44,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;
@@ -79,6 +80,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;
@@ -102,7 +106,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();
@@ -122,7 +126,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();
@@ -150,7 +154,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();
@@ -179,7 +183,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();
@@ -213,8 +218,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\"}}");
 
@@ -242,7 +247,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\"}}");
 
@@ -264,7 +269,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\"}}");
 
@@ -292,7 +297,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);
@@ -308,7 +313,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);
@@ -316,6 +321,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);
   }
@@ -343,8 +416,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 1567e38..c2e43e9 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,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.TimeUnit;
 
 import javax.persistence.EntityManager;
@@ -2379,11 +2380,11 @@ public class KerberosHelperTest extends EasyMockSupport 
{
     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"));
+    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/RecommendationResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
index 8d02821..4e46a83 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,177 @@
 
 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);
+
+    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);
+
+    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) 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(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS.equals(type)).
+      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/ValidationResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
index 34337ae..38cf6fe 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
@@ -30,6 +30,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.validations.ValidationResponse;
@@ -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-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);
       });
     });
   });

Reply via email to