You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2014/11/26 23:57:01 UTC

[1/2] ambari git commit: AMBARI-8458. Add support for "add hosts" specifying host name, blueprint name and host group name AMBARI-8437. Fix regression that prvented cluster creation via blueprints

Repository: ambari
Updated Branches:
  refs/heads/trunk 501785fa5 -> 058dc168e


http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
----------------------------------------------------------------------
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 c53eb78..d868320 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
@@ -38,6 +38,7 @@ import org.apache.ambari.server.controller.ConfigurationRequest;
 import org.apache.ambari.server.controller.HostRequest;
 import org.apache.ambari.server.controller.HostResponse;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
+import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -48,6 +49,7 @@ 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.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.entities.BlueprintEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -55,19 +57,19 @@ import org.apache.ambari.server.state.DesiredConfig;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
-import com.google.inject.persist.Transactional;
 
 
 /**
  * Resource provider for host resources.
  */
-public class HostResourceProvider extends AbstractControllerResourceProvider {
+public class HostResourceProvider extends BaseBlueprintProcessor {
 
   // ----- Property ID constants ---------------------------------------------
 
@@ -114,6 +116,13 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   protected static final String HOST_DESIRED_CONFIGS_PROPERTY_ID = 
       PropertyHelper.getPropertyId("Hosts", "desired_configs");
 
+  protected static final String BLUEPRINT_PROPERTY_ID =
+      PropertyHelper.getPropertyId(null, "blueprint");
+  protected static final String HOSTGROUP_PROPERTY_ID =
+      PropertyHelper.getPropertyId(null, "host_group");
+  protected static final String HOST_NAME_NO_CATEGORY_PROPERTY_ID =
+      PropertyHelper.getPropertyId(null, "host_name");
+
   private static Set<String> pkPropertyIds =
       new HashSet<String>(Arrays.asList(new String[]{
           HOST_NAME_PROPERTY_ID}));
@@ -140,31 +149,31 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   // ----- ResourceProvider ------------------------------------------------
 
   @Override
-  public RequestStatus createResources(Request request)
+  public RequestStatus createResources(final Request request)
       throws SystemException,
           UnsupportedPropertyException,
           ResourceAlreadyExistsException,
           NoSuchParentResourceException {
 
-    final Set<HostRequest> requests = new HashSet<HostRequest>();
-    for (Map<String, Object> propertyMap : request.getProperties()) {
-      requests.add(getRequest(propertyMap));
+    RequestStatusResponse createResponse = null;
+    if (isHostGroupRequest(request)) {
+      createResponse = addHostsUsingHostgroup(request);
+    } else {
+      createResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          createHosts(request);
+          return null;
+        }
+      });
     }
-    createResources(new Command<Void>() {
-      @Override
-      public Void invoke() throws AmbariException {
-        createHosts(requests);
-        return null;
-      }
-    });
 
     notifyCreate(Resource.Type.Host, request);
 
-    return getRequestStatus(null);
+    return getRequestStatus(createResponse);
   }
 
   @Override
-  @Transactional
   public Set<Resource> getResources(Request request, Predicate predicate)
       throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
 
@@ -256,7 +265,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     modifyResources(new Command<Void>() {
       @Override
       public Void invoke() throws AmbariException {
-        updateHosts(requests, request.getRequestInfoProperties());
+        updateHosts(requests);
         return null;
       }
     });
@@ -292,6 +301,10 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   public Set<String> checkPropertyIds(Set<String> propertyIds) {
     Set<String> baseUnsupported = super.checkPropertyIds(propertyIds);
 
+    baseUnsupported.remove(BLUEPRINT_PROPERTY_ID);
+    baseUnsupported.remove(HOSTGROUP_PROPERTY_ID);
+    baseUnsupported.remove(HOST_NAME_NO_CATEGORY_PROPERTY_ID);
+
     return checkConfigPropertyIds(baseUnsupported, "Hosts");
   }
 
@@ -307,6 +320,25 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   // ----- utility methods ---------------------------------------------------
 
   /**
+   * Determine if a request is a high level "add hosts" call or a simple lower level request
+   * to add a host resources.
+   *
+   * @param request  current request
+   * @return true if this is a high level "add hosts" request;
+   *         false if it is a simple create host resources request
+   */
+  private boolean isHostGroupRequest(Request request) {
+    boolean isHostGroupRequest = false;
+    Set<Map<String, Object>> properties = request.getProperties();
+    if (properties != null && ! properties.isEmpty()) {
+      //todo: for now, either all or none of the hosts need to specify a hg.  Unable to mix.
+      String hgName = (String) properties.iterator().next().get(HOSTGROUP_PROPERTY_ID);
+      isHostGroupRequest = hgName != null && ! hgName.isEmpty();
+    }
+    return isHostGroupRequest;
+  }
+
+  /**
    * Get a host request object from a map of property values.
    *
    * @param properties  the predicate
@@ -320,11 +352,13 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     }
 
     HostRequest hostRequest = new HostRequest(
-        (String) properties.get(HOST_NAME_PROPERTY_ID),
+        getHostNameFromProperties(properties),
         (String) properties.get(HOST_CLUSTER_NAME_PROPERTY_ID),
         null);
     hostRequest.setPublicHostName((String) properties.get(HOST_PUBLIC_NAME_PROPERTY_ID));
     hostRequest.setRackInfo((String) properties.get(HOST_RACK_INFO_PROPERTY_ID));
+    hostRequest.setBlueprintName((String) properties.get(BLUEPRINT_PROPERTY_ID));
+    hostRequest.setHostGroupName((String) properties.get(HOSTGROUP_PROPERTY_ID));
     
     Object o = properties.get(HOST_MAINTENANCE_STATE_PROPERTY_ID);
     if (null != o) {
@@ -342,14 +376,15 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   /**
    * Accepts a request with registered hosts and if the request contains a cluster name then will map all of the
    * hosts onto that cluster.
-   * @param requests Request that must contain registered hosts, and optionally a cluster.
+   * @param request Request that must contain registered hosts, and optionally a cluster.
    * @throws AmbariException
    */
-  protected synchronized void createHosts(Set<HostRequest> requests)
+  protected synchronized void createHosts(Request request)
       throws AmbariException {
 
-    if (requests.isEmpty()) {
-      LOG.warn("Received an empty requests set");
+    Set<Map<String, Object>> propertySet = request.getProperties();
+    if (propertySet == null || propertySet.isEmpty()) {
+      LOG.warn("Received a create host request with no associated property sets");
       return;
     }
 
@@ -359,43 +394,14 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     Set<String> duplicates = new HashSet<String>();
     Set<String> unknowns = new HashSet<String>();
     Set<String> allHosts = new HashSet<String>();
-    for (HostRequest request : requests) {
-      if (request.getHostname() == null
-          || request.getHostname().isEmpty()) {
-        throw new IllegalArgumentException("Invalid arguments, hostname"
-            + " cannot be null");
-      }
-
-      if (LOG.isDebugEnabled()) {
-        LOG.debug("Received a createHost request"
-            + ", hostname=" + request.getHostname()
-            + ", request=" + request);
-      }
 
-      if (allHosts.contains(request.getHostname())) {
-        // throw dup error later
-        duplicates.add(request.getHostname());
-        continue;
-      }
-      allHosts.add(request.getHostname());
 
-      try {
-        // ensure host is registered
-        clusters.getHost(request.getHostname());
-      }
-      catch (HostNotFoundException e) {
-        unknowns.add(request.getHostname());
-        continue;
-      }
-
-      if (request.getClusterName() != null) {
-        try {
-          // validate that cluster_name is valid
-          clusters.getCluster(request.getClusterName());
-        } catch (ClusterNotFoundException e) {
-          throw new ParentObjectNotFoundException("Attempted to add a host to a cluster which doesn't exist: "
-              + " clusterName=" + request.getClusterName());
-        }
+    Set<HostRequest> hostRequests = new HashSet<HostRequest>();
+    for (Map<String, Object> propertyMap : propertySet) {
+      HostRequest hostRequest = getRequest(propertyMap);
+      hostRequests.add(hostRequest);
+      if (! propertyMap.containsKey(HOSTGROUP_PROPERTY_ID)) {
+        createHostResource(clusters, duplicates, unknowns, allHosts, hostRequest);
       }
     }
 
@@ -431,22 +437,183 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
 
     Map<String, Set<String>> hostClustersMap = new HashMap<String, Set<String>>();
     Map<String, Map<String, String>> hostAttributes = new HashMap<String, Map<String, String>>();
-    for (HostRequest request : requests) {
-      if (request.getHostname() != null && !request.getHostname().isEmpty() && request.getClusterName() != null && !request.getClusterName().isEmpty()) {
+
+    for (HostRequest hostRequest : hostRequests) {
+      if (hostRequest.getHostname() != null &&
+          !hostRequest.getHostname().isEmpty() &&
+          hostRequest.getClusterName() != null &&
+          !hostRequest.getClusterName().isEmpty()){
+
         Set<String> clusterSet = new HashSet<String>();
-        clusterSet.add(request.getClusterName());
-        hostClustersMap.put(request.getHostname(), clusterSet);
-        if (request.getHostAttributes() != null) {
-          hostAttributes.put(request.getHostname(), request.getHostAttributes());
+        clusterSet.add(hostRequest.getClusterName());
+        hostClustersMap.put(hostRequest.getHostname(), clusterSet);
+        if (hostRequest.getHostAttributes() != null) {
+          hostAttributes.put(hostRequest.getHostname(), hostRequest.getHostAttributes());
         }
       }
     }
     clusters.updateHostWithClusterAndAttributes(hostClustersMap, hostAttributes);
   }
 
-
-  protected Set<HostResponse> getHosts(Set<HostRequest> requests)
+  private void createHostResource(Clusters clusters, Set<String> duplicates,
+                                  Set<String> unknowns, Set<String> allHosts,
+                                  HostRequest request)
       throws AmbariException {
+
+
+    if (request.getHostname() == null
+        || request.getHostname().isEmpty()) {
+      throw new IllegalArgumentException("Invalid arguments, hostname"
+          + " cannot be null");
+    }
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Received a createHost request"
+          + ", hostname=" + request.getHostname()
+          + ", request=" + request);
+    }
+
+    if (allHosts.contains(request.getHostname())) {
+      // throw dup error later
+      duplicates.add(request.getHostname());
+      return;
+    }
+    allHosts.add(request.getHostname());
+
+    try {
+      // ensure host is registered
+      clusters.getHost(request.getHostname());
+    }
+    catch (HostNotFoundException e) {
+      unknowns.add(request.getHostname());
+      return;
+    }
+
+    if (request.getClusterName() != null) {
+      try {
+        // validate that cluster_name is valid
+        clusters.getCluster(request.getClusterName());
+      } catch (ClusterNotFoundException e) {
+        throw new ParentObjectNotFoundException("Attempted to add a host to a cluster which doesn't exist: "
+            + " clusterName=" + request.getClusterName());
+      }
+    }
+  }
+
+
+  /**
+   * Add hosts based on a blueprint and hostgroup.  This will create the necessary resources and install/start all
+   * if the components on the hosts.
+   *
+   * @param request  add hosts request
+   * @return async request response
+   *
+   * @throws ResourceAlreadyExistsException  if an added host already exists in the cluster
+   * @throws SystemException                 in an unknown exception occurs
+   * @throws NoSuchParentResourceException   a parent resource doesnt exist
+   * @throws UnsupportedPropertyException    an unsupported property was specified for the request
+   */
+  private RequestStatusResponse addHostsUsingHostgroup(final Request request)
+      throws ResourceAlreadyExistsException,
+      SystemException,
+      NoSuchParentResourceException,
+      UnsupportedPropertyException {
+
+    //todo: idempotency of request.  Need to define failure models ...
+    Set<Map<String, Object>> propertySet = request.getProperties();
+    if (propertySet == null || propertySet.isEmpty()) {
+      LOG.warn("Received a create host request with no associated property sets");
+      return null;
+    }
+
+    Set<String> addedHosts = new HashSet<String>();
+    // all hosts will have same cluster
+    String clusterName = null;
+    for (Map<String, Object> properties : propertySet) {
+      clusterName = (String) properties.get(HOST_CLUSTER_NAME_PROPERTY_ID);
+      String bpName = (String) properties.get(BLUEPRINT_PROPERTY_ID);
+      String hgName = (String) properties.get(HOSTGROUP_PROPERTY_ID);
+      String hostname = getHostNameFromProperties(properties);
+
+      addedHosts.add(hostname);
+
+      String configGroupName = getConfigurationGroupName(bpName, hgName);
+      BlueprintEntity blueprint = getExistingBlueprint(bpName);
+      Stack stack = parseStack(blueprint);
+      Map<String, HostGroupImpl> blueprintHostGroups = parseBlueprintHostGroups(blueprint, stack);
+      addHostToHostgroup(hgName, hostname, blueprintHostGroups);
+      createHostAndComponentResources(blueprintHostGroups, clusterName, this);
+      //todo: optimize: update once per hostgroup with added hosts
+      addHostToExistingConfigGroups(configGroupName, clusterName, hostname);
+    }
+    return ((HostComponentResourceProvider) getResourceProvider(Resource.Type.HostComponent)).
+        installAndStart(clusterName, addedHosts);
+  }
+
+  /**
+   * Add the new host to an existing config group.
+   *
+   * @param configGroupName  name of the config group
+   * @param clusterName      cluster name
+   * @param hostName         host name
+   *
+   * @throws SystemException                an unknown exception occurred
+   * @throws UnsupportedPropertyException   an unsupported property was specified in the request
+   * @throws NoSuchParentResourceException  a parent resource doesn't exist
+   */
+  private void addHostToExistingConfigGroups(String configGroupName, String clusterName, String hostName)
+      throws SystemException,
+      UnsupportedPropertyException,
+      NoSuchParentResourceException {
+
+    Clusters clusters;
+    Cluster cluster;
+    try {
+      clusters = getManagementController().getClusters();
+      cluster = clusters.getCluster(clusterName);
+    } catch (AmbariException e) {
+      throw new IllegalArgumentException(
+          String.format("Attempt to add hosts to a non-existent cluster: '%s'", clusterName));
+    }
+    Map<Long, ConfigGroup> configGroups = cluster.getConfigGroups();
+    for (ConfigGroup group : configGroups.values()) {
+      if (group.getName().equals(configGroupName)) {
+        try {
+          group.addHost(clusters.getHost(hostName));
+          group.persist();
+        } catch (AmbariException e) {
+          // shouldn't occur, this host was just added to the cluster
+          throw new SystemException(String.format(
+              "Unable to obtain newly created host '%s' from cluster '%s'", hostName, clusterName));
+        }
+      }
+    }
+  }
+
+  /**
+   * Associate a host with a host group.
+   *
+   * @param hostGroupName        name of host group
+   * @param hostname             host name
+   * @param blueprintHostGroups  map of host group name to host group
+   *
+   * @throws IllegalArgumentException if the specified host group doesn't exist
+   */
+  private void addHostToHostgroup(String hostGroupName, String hostname, Map<String, HostGroupImpl> blueprintHostGroups)
+      throws IllegalArgumentException {
+
+    HostGroupImpl hostGroup = blueprintHostGroups.get(hostGroupName);
+    if (hostGroup == null) {
+      // this case should have been caught sooner
+      throw new IllegalArgumentException(String.format("Invalid host_group specified '%s'. " +
+          "All request host groups must have a corresponding host group in the specified blueprint", hostGroupName));
+    }
+
+    hostGroup.addHostInfo(hostname);
+  }
+
+
+  protected Set<HostResponse> getHosts(Set<HostRequest> requests) throws AmbariException {
     Set<HostResponse> response = new HashSet<HostResponse>();
 
     AmbariManagementController controller = getManagementController();
@@ -529,9 +696,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     return response;
   }
 
-  protected synchronized void updateHosts(Set<HostRequest> requests,
-      Map<String, String> requestProperties)
-      throws AmbariException {
+  protected synchronized void updateHosts(Set<HostRequest> requests) throws AmbariException {
 
     if (requests.isEmpty()) {
       LOG.warn("Received an empty requests set");
@@ -541,20 +706,15 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     AmbariManagementController controller = getManagementController();
     Clusters                   clusters   = controller.getClusters();
 
-    // We don't expect batch requests for different clusters, that's why
-    // nothing bad should happen if value is overwritten few times
-    String maintenanceCluster = null;
-
     for (HostRequest request : requests) {
       if (request.getHostname() == null || request.getHostname().isEmpty()) {
         throw new IllegalArgumentException("Invalid arguments, hostname should be provided");
       }
     }
 
-
     for (HostRequest request : requests) {
       if (LOG.isDebugEnabled()) {
-        LOG.debug("Received a updateHost request"
+        LOG.debug("Received an updateHost request"
             + ", hostname=" + request.getHostname()
             + ", request=" + request);
       }
@@ -592,7 +752,6 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
               "maintenance state to one of " + EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON));
           } else {
             h.setMaintenanceState(c.getClusterId(), newState);
-            maintenanceCluster = c.getClusterName();
           }
         }
       }
@@ -710,4 +869,21 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
       }
     }
   }
+
+  /**
+   * Obtain the hostname from the request properties.  The hostname property name may differ
+   * depending on the request type.  For the low level host resource creation calls, it is always
+   * "Hosts/host_name".  For multi host "add host from hostgroup", the hostname property is a top level
+   * property "host_name".
+   *
+   * @param properties  request properties
+   *
+   * @return the host name for the host request
+   */
+  private String getHostNameFromProperties(Map<String, Object> properties) {
+    String hostname = (String) properties.get(HOST_NAME_PROPERTY_ID);
+
+    return hostname != null ? hostname :
+        (String) properties.get(HOST_NAME_NO_CATEGORY_PROPERTY_ID);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
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 945a4db..125b71b 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
@@ -341,11 +341,10 @@ public interface Cluster {
   public void addConfigGroup(ConfigGroup configGroup) throws AmbariException;
 
   /**
-   * Get all config groups associated with this cluster
-   * @return
-   * @throws AmbariException
+   * Get config groups associated with this cluster
+   * @return unmodifiable map of config group id to config group.  Will not return null.
    */
-  public Map<Long, ConfigGroup> getConfigGroups() throws AmbariException;
+  public Map<Long, ConfigGroup> getConfigGroups();
 
   /**
    * Delete this config group identified by the config group id

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
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 e3f6d4b..5a9731a 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
@@ -483,7 +483,7 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
-  public Map<Long, ConfigGroup> getConfigGroups() throws AmbariException {
+  public Map<Long, ConfigGroup> getConfigGroups() {
     loadConfigGroups();
     clusterGlobalLock.readLock().lock();
     try {

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
index 1f57397..75e0868 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
@@ -77,6 +77,7 @@ import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.ComponentResourceProviderTest;
+import org.apache.ambari.server.controller.internal.HostComponentResourceProviderTest;
 import org.apache.ambari.server.controller.internal.HostResourceProviderTest;
 import org.apache.ambari.server.controller.internal.RequestOperationLevel;
 import org.apache.ambari.server.controller.internal.RequestResourceFilter;
@@ -365,7 +366,7 @@ public class AmbariManagementControllerTest {
   }
 
   private long stopServiceComponentHosts(String clusterName,
-      String serviceName) throws AmbariException {
+      String serviceName) throws Exception {
     Cluster c = clusters.getCluster(clusterName);
     Service s = c.getService(serviceName);
     Set<ServiceComponentHostRequest> requests = new
@@ -380,8 +381,8 @@ public class AmbariManagementControllerTest {
     }
     Map<String, String> mapRequestProps = new HashMap<String, String>();
     mapRequestProps.put("context", "Called from a test");
-    RequestStatusResponse resp = controller.updateHostComponents(requests,
-      mapRequestProps, false);
+    RequestStatusResponse resp = HostComponentResourceProviderTest.updateHostComponents(controller, injector, requests,
+        mapRequestProps, false);
 
     // manually change live state to started as no running action manager
     for (ServiceComponent sc :
@@ -1505,16 +1506,11 @@ public class AmbariManagementControllerTest {
     clusters.getHost("h2").persist();
     clusters.getHost("h3").persist();
 
-    Map<String, String> hostAttrs =
-        new HashMap<String, String>();
-    hostAttrs.put("attr1", "val1");
-    hostAttrs.put("attr2", "val2");
-
-    String clusterName = "c1";
+   String clusterName = "c1";
 
     HostRequest r1 = new HostRequest("h1", clusterName, null);
-    HostRequest r2 = new HostRequest("h2", clusterName, hostAttrs);
-    HostRequest r3 = new HostRequest("h3", null, hostAttrs);
+    HostRequest r2 = new HostRequest("h2", clusterName, null);
+    HostRequest r3 = new HostRequest("h3", null, null);
 
     Set<HostRequest> set1 = new HashSet<HostRequest>();
     set1.add(r1);
@@ -1525,13 +1521,6 @@ public class AmbariManagementControllerTest {
     Assert.assertEquals(1, clusters.getClustersForHost("h1").size());
     Assert.assertEquals(1, clusters.getClustersForHost("h2").size());
     Assert.assertEquals(0, clusters.getClustersForHost("h3").size());
-
-    Assert.assertEquals(4, clusters.getHost("h2").getHostAttributes().size());
-    Assert.assertEquals(2, clusters.getHost("h3").getHostAttributes().size());
-    Assert.assertEquals("val1",
-        clusters.getHost("h2").getHostAttributes().get("attr1"));
-    Assert.assertEquals("val2",
-        clusters.getHost("h2").getHostAttributes().get("attr2"));
   }
 
   @Test
@@ -2334,7 +2323,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testServiceComponentHostsWithDecommissioned() throws AmbariException {
+  public void testServiceComponentHostsWithDecommissioned() throws Exception {
 
     final String host1 = "h1";
     final String host2 = "h2";
@@ -2408,7 +2397,7 @@ public class AmbariManagementControllerTest {
     r = new ServiceComponentHostRequest(clusterName, "HDFS", "DATANODE", host2, null);
     r.setAdminState("DECOMMISSIONED");
     try {
-      controller.updateHostComponents(Collections.singleton(r), new HashMap<String, String>(), false);
+      updateHostComponents(Collections.singleton(r), new HashMap<String, String>(), false);
       Assert.fail("Must throw exception when decommission attribute is updated.");
     } catch (IllegalArgumentException ex) {
       Assert.assertTrue(ex.getMessage().contains("Property adminState cannot be modified through update"));
@@ -3363,7 +3352,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testServiceComponentHostUpdateRecursive() throws AmbariException {
+  public void testServiceComponentHostUpdateRecursive() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     String serviceName1 = "HDFS";
@@ -3444,7 +3433,7 @@ public class AmbariManagementControllerTest {
           componentName1, host1,
           State.STARTED.toString());
       reqs.add(req1);
-      controller.updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
+      updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
       fail("Expected failure for invalid transition");
     } catch (Exception e) {
       // Expected
@@ -3472,7 +3461,7 @@ public class AmbariManagementControllerTest {
       reqs.add(req3);
       reqs.add(req4);
       reqs.add(req5);
-      controller.updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
+      updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
       fail("Expected failure for invalid states");
     } catch (Exception e) {
       // Expected
@@ -3494,7 +3483,7 @@ public class AmbariManagementControllerTest {
     reqs.add(req3);
     reqs.add(req4);
     reqs.add(req5);
-    RequestStatusResponse trackAction = controller.updateHostComponents(reqs,
+    RequestStatusResponse trackAction = updateHostComponents(reqs,
         Collections.<String, String>emptyMap(), true);
     Assert.assertNotNull(trackAction);
 
@@ -3527,14 +3516,14 @@ public class AmbariManagementControllerTest {
         State.INSTALLED.toString());
     reqs.add(req1);
     reqs.add(req2);
-    trackAction = controller.updateHostComponents(reqs, Collections.<String,
+    trackAction = updateHostComponents(reqs, Collections.<String,
         String>emptyMap(), true);
     Assert.assertNull(trackAction);
   }
 
   @Ignore
   @Test
-  public void testServiceComponentHostUpdateStackId() throws AmbariException {
+  public void testServiceComponentHostUpdateStackId() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     String serviceName1 = "HDFS";
@@ -3614,7 +3603,7 @@ public class AmbariManagementControllerTest {
     Map<String,String> mapRequestProps = new HashMap<String, String>();
     mapRequestProps.put("context", "testServiceComponentHostUpdateStackId");
 
-    RequestStatusResponse resp = controller.updateHostComponents(reqs, mapRequestProps, true);
+    RequestStatusResponse resp = updateHostComponents(reqs, mapRequestProps, true);
     List<Stage> stages = actionDB.getAllStages(resp.getRequestId());
     Assert.assertEquals(1, stages.size());
     Assert.assertEquals(2, stages.get(0).getOrderedHostRoleCommands().size());
@@ -3659,7 +3648,7 @@ public class AmbariManagementControllerTest {
     req3.setDesiredStackId("HDP-0.2");
     reqs.add(req3);
 
-    resp = controller.updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
+    resp = updateHostComponents(reqs, Collections.<String, String>emptyMap(), true);
     stages = actionDB.getAllStages(resp.getRequestId());
     Assert.assertEquals(2, stages.size());
     Assert.assertEquals(2, stages.get(0).getOrderedHostRoleCommands().size());
@@ -3687,7 +3676,7 @@ public class AmbariManagementControllerTest {
 
   @Ignore
   @Test
-  public void testServiceComponentHostUpdateStackIdError() throws AmbariException {
+  public void testServiceComponentHostUpdateStackIdError() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     String serviceName1 = "HDFS";
@@ -3818,7 +3807,7 @@ public class AmbariManagementControllerTest {
     req1.setDesiredStackId("HDP-0.2");
     reqs.add(req1);
 
-    RequestStatusResponse resp = controller.updateHostComponents(reqs,
+    RequestStatusResponse resp = updateHostComponents(reqs,
         Collections.<String,String>emptyMap(), true);
     Assert.assertNull(resp);
 
@@ -3831,14 +3820,14 @@ public class AmbariManagementControllerTest {
         State.INSTALLED.toString());
     req1.setDesiredStackId("HDP-0.2");
     reqs.add(req1);
-    resp = controller.updateHostComponents(reqs, Collections.<String,String>emptyMap(), true);
+    resp = updateHostComponents(reqs, Collections.<String,String>emptyMap(), true);
     Assert.assertNull(resp);
   }
 
   private void updateHostAndCompareExpectedFailure(Set<ServiceComponentHostRequest> reqs,
                                                    String expectedMessage) {
     try {
-      controller.updateHostComponents(reqs, Collections.<String,String>emptyMap(), true);
+      updateHostComponents(reqs, Collections.<String,String>emptyMap(), true);
       fail("Expected failure: " + expectedMessage);
     } catch (Exception e) {
       LOG.info("Actual exception message: " + e.getMessage());
@@ -4758,7 +4747,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
             componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     configVersions.clear();
     configVersions.put("typeC", "v1");
@@ -4785,7 +4774,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
             componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     // update configs at SC level
     configVersions.clear();
@@ -4799,7 +4788,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testConfigUpdates() throws AmbariException {
+  public void testConfigUpdates() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -4906,7 +4895,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
         componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     configVersions.clear();
     configVersions.put("typeC", "v1");
@@ -4933,7 +4922,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
         componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     // update configs at SC level
     configVersions.clear();
@@ -5042,7 +5031,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
       componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     // Reconfigure SCH level
     configVersions.clear();
@@ -5050,7 +5039,7 @@ public class AmbariManagementControllerTest {
     schReqs.clear();
     schReqs.add(new ServiceComponentHostRequest(clusterName, serviceName,
       componentName1, host1, null));
-    Assert.assertNull(controller.updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
+    Assert.assertNull(updateHostComponents(schReqs, Collections.<String, String>emptyMap(), true));
 
     // Clear Entity Manager
     entityManager.clear();
@@ -5284,8 +5273,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testReconfigureClientWithServiceStarted() throws
-    AmbariException {
+  public void testReconfigureClientWithServiceStarted() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -5774,7 +5762,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testReInstallClientComponent() throws AmbariException {
+  public void testReInstallClientComponent() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -5818,7 +5806,7 @@ public class AmbariManagementControllerTest {
     Set<ServiceComponentHostRequest> setReqs = new
       HashSet<ServiceComponentHostRequest>();
     setReqs.add(schr);
-    RequestStatusResponse resp = controller.updateHostComponents(setReqs,
+    RequestStatusResponse resp = updateHostComponents(setReqs,
       Collections.<String, String>emptyMap(), false);
 
     Assert.assertNotNull(resp);
@@ -5960,7 +5948,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testDecommissonDatanodeAction() throws AmbariException {
+  public void testDecommissonDatanodeAction() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -6087,13 +6075,13 @@ public class AmbariManagementControllerTest {
         componentName1, host2, State.INSTALLED.toString());
     Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
     requests.add(r);
-    controller.updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
+    updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
     s.getServiceComponent(componentName1).getServiceComponentHost(host2).setState(State.INSTALLED);
     r = new ServiceComponentHostRequest(clusterName, serviceName,
         componentName1, host2, State.STARTED.toString());
     requests.clear();
     requests.add(r);
-    controller.updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
+    updateHostComponents(requests, Collections.<String, String>emptyMap(), true);
     s.getServiceComponent(componentName1).getServiceComponentHost(host2).setState(State.STARTED);
 
     params = new HashMap<String, String>(){{
@@ -7616,7 +7604,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testServiceStopWhileStopping() throws AmbariException {
+  public void testServiceStopWhileStopping() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -7733,7 +7721,7 @@ public class AmbariManagementControllerTest {
           Set<ServiceComponentHostRequest> reqs1 = new
             HashSet<ServiceComponentHostRequest>();
           reqs1.add(r1);
-          controller.updateHostComponents(reqs1, Collections.<String, String>emptyMap(), true);
+          updateHostComponents(reqs1, Collections.<String, String>emptyMap(), true);
           Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
         }
       }
@@ -7890,7 +7878,7 @@ public class AmbariManagementControllerTest {
   }
 
   @Test
-  public void testUpdateHostComponentsBadState() throws AmbariException {
+  public void testUpdateHostComponentsBadState() throws Exception {
     String clusterName = "foo1";
     createCluster(clusterName);
     clusters.getCluster(clusterName)
@@ -7971,7 +7959,7 @@ public class AmbariManagementControllerTest {
     Map<String, String> requestProps = new HashMap<String, String>();
     requestProps.put("datanode", "dn_value");
     requestProps.put("namenode", "nn_value");
-    RequestStatusResponse rsr = controller.updateHostComponents(Collections.singleton(schr), requestProps, false);
+    RequestStatusResponse rsr = updateHostComponents(Collections.singleton(schr), requestProps, false);
 
     List<Stage> stages = actionDB.getAllStages(rsr.getRequestId());
     Assert.assertEquals(1, stages.size());
@@ -7988,7 +7976,6 @@ public class AmbariManagementControllerTest {
     for (ServiceComponentHost sch : clusters.getCluster(clusterName).getServiceComponentHosts(host2)) {
       Assert.assertEquals(State.UNKNOWN, sch.getState());
     }
-
   }
 
   @Test
@@ -8452,7 +8439,7 @@ public class AmbariManagementControllerTest {
     // disable HC for non-clients
     schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, "DISABLED"));
     schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName2, host1, "DISABLED"));
-    controller.updateHostComponents(schRequests, new HashMap<String,String>(), false);
+    updateHostComponents(schRequests, new HashMap<String,String>(), false);
 
     // delete HC
     schRequests.clear();
@@ -8635,7 +8622,7 @@ public class AmbariManagementControllerTest {
     schRequests.clear();
     // disable HC, DN was already stopped
     schRequests.add(new ServiceComponentHostRequest(clusterName, serviceName, componentName1, host1, "DISABLED"));
-    controller.updateHostComponents(schRequests, new HashMap<String,String>(), false);
+    updateHostComponents(schRequests, new HashMap<String,String>(), false);
 
     // delete HC
     schRequests.clear();
@@ -9003,21 +8990,21 @@ public class AmbariManagementControllerTest {
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "DISABLED"));
 
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, true);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
 
       Assert.assertEquals(State.DISABLED, componentHost.getState());
 
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "INSTALLED"));
 
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, true);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
 
       Assert.assertEquals(State.INSTALLED, componentHost.getState());
 
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "DISABLED"));
 
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, true);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
 
       Assert.assertEquals(State.DISABLED, componentHost.getState());
 
@@ -9029,7 +9016,7 @@ public class AmbariManagementControllerTest {
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host2", "INSTALLED"));
 
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, true);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
 
       namenodes = cluster.getService("HDFS").getServiceComponent("NAMENODE").getServiceComponentHosts();
       Assert.assertEquals(2, namenodes.size());
@@ -9071,7 +9058,7 @@ public class AmbariManagementControllerTest {
       componentHost.handleEvent(new ServiceComponentHostOpSucceededEvent(componentHost.getServiceComponentName(), componentHost.getHostName(), System.currentTimeMillis()));
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "NAMENODE", "host1", "INSTALLED"));
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, true);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, true);
       assertEquals(State.INSTALLED, namenodes.get("host1").getState());
 
       // make unknown
@@ -9087,7 +9074,7 @@ public class AmbariManagementControllerTest {
       // make disabled
       componentHostRequests.clear();
       componentHostRequests.add(new ServiceComponentHostRequest("c1", null, "DATANODE", "host2", "DISABLED"));
-      amc.updateHostComponents(componentHostRequests, mapRequestProps, false);
+      updateHostComponents(amc, componentHostRequests, mapRequestProps, false);
       org.junit.Assert.assertEquals(State.DISABLED, sch.getState());
 
       // ServiceComponentHost remains in disabled after service stop
@@ -9770,8 +9757,8 @@ public class AmbariManagementControllerTest {
     // passivate a host
     HostRequest hr = new HostRequest(host1, clusterName, requestProperties);
     hr.setMaintenanceState(MaintenanceState.ON.name());
-    HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr),
-        new HashMap<String, String>());
+    HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr)
+    );
 
     Host host = hosts.get(host1);
     Assert.assertEquals(MaintenanceState.ON,
@@ -9793,8 +9780,8 @@ public class AmbariManagementControllerTest {
 
     // reset
     hr.setMaintenanceState(MaintenanceState.OFF.name());
-    HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr),
-        new HashMap<String, String>());
+    HostResourceProviderTest.updateHosts(controller, Collections.singleton(hr)
+    );
 
     host = hosts.get(host1);
     Assert.assertEquals(MaintenanceState.OFF,
@@ -9817,8 +9804,8 @@ public class AmbariManagementControllerTest {
     Set<HostRequest> set = new HashSet<HostRequest>();
     set.add(hr1);
     set.add(hr2);
-    HostResourceProviderTest.updateHosts(controller, set,
-        new HashMap<String, String>());
+    HostResourceProviderTest.updateHosts(controller, set
+    );
 
     host = hosts.get(host1);
     Assert.assertEquals(MaintenanceState.ON,
@@ -9836,8 +9823,8 @@ public class AmbariManagementControllerTest {
     set.add(hr1);
     set.add(hr2);
 
-    HostResourceProviderTest.updateHosts(controller, set,
-        new HashMap<String, String>());
+    HostResourceProviderTest.updateHosts(controller, set
+    );
     host = hosts.get(host1);
     Assert.assertEquals(MaintenanceState.OFF,
         host.getMaintenanceState(cluster.getClusterId()));
@@ -10334,7 +10321,7 @@ public class AmbariManagementControllerTest {
 
     Map<String, String> requestProperties = new HashMap<String, String>();
     requestProperties.put("namenode", "p1");
-    RequestStatusResponse resp = controller.updateHostComponents(Collections.singleton(req), requestProperties, false);
+    RequestStatusResponse resp = updateHostComponents(Collections.singleton(req), requestProperties, false);
 
     // succeed in creating a task
     assertNotNull(resp);
@@ -10346,15 +10333,33 @@ public class AmbariManagementControllerTest {
     }
 
     // no new commands since no targeted info
-    resp = controller.updateHostComponents(Collections.singleton(req), new HashMap<String, String>(), false);
+    resp = updateHostComponents(Collections.singleton(req), new HashMap<String, String>(), false);
     assertNull(resp);
 
     // role commands added for targeted command
-    resp = controller.updateHostComponents(Collections.singleton(req), requestProperties, false);
+    resp = updateHostComponents(Collections.singleton(req), requestProperties, false);
     assertNotNull(resp);
 
   }
 
+  // this is a temporary measure as a result of moving updateHostComponents from AmbariManagementController
+  // to HostComponentResourceProvider.  Eventually the tests should be moved out of this class.
+  private RequestStatusResponse updateHostComponents(Set<ServiceComponentHostRequest> requests,
+                                                     Map<String, String> requestProperties,
+                                                     boolean runSmokeTest) throws Exception {
+
+    return updateHostComponents(controller, requests, requestProperties, runSmokeTest);
+  }
+
+  private RequestStatusResponse updateHostComponents(AmbariManagementController controller,
+                                                     Set<ServiceComponentHostRequest> requests,
+                                                     Map<String, String> requestProperties,
+                                                     boolean runSmokeTest) throws Exception {
+
+    return HostComponentResourceProviderTest.updateHostComponents(
+        controller, injector, requests, requestProperties, runSmokeTest);
+  }
+
 
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
index f7149a8..f382588 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
@@ -420,6 +420,7 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getHostGroups()).andReturn(Collections.singleton(hostGroup)).anyTimes();
     expect(hostGroup.getName()).andReturn("group1").anyTimes();
     expect(hostGroup.getComponents()).andReturn(hostGroupComponents).anyTimes();
+    expect(hostGroup.getBlueprintName()).andReturn(blueprintName).anyTimes();
     expect(hostGroupComponent1.getName()).andReturn("component1").anyTimes();
     expect(hostGroupComponent2.getName()).andReturn("component2").anyTimes();
     expect(hostGroupComponent3.getName()).andReturn("component3").anyTimes();
@@ -634,7 +635,7 @@ public class ClusterResourceProviderTest {
     assertEquals(1, configGroupRequests.size());
     ConfigGroupRequest configGroupRequest = configGroupRequests.iterator().next();
     assertEquals(clusterName, configGroupRequest.getClusterName());
-    assertEquals("group1", configGroupRequest.getGroupName());
+    assertEquals(blueprintName + ":group1", configGroupRequest.getGroupName());
     assertEquals("service1", configGroupRequest.getTag());
     assertEquals("Host Group Configuration", configGroupRequest.getDescription());
     Set<String> hosts = configGroupRequest.getHosts();
@@ -1688,6 +1689,7 @@ public class ClusterResourceProviderTest {
 
     expect(blueprint.getHostGroups()).andReturn(Collections.singleton(hostGroup)).anyTimes();
     expect(hostGroup.getName()).andReturn("group1").anyTimes();
+    expect(hostGroup.getBlueprintName()).andReturn(blueprintName).anyTimes();
     expect(hostGroup.getComponents()).andReturn(hostGroupComponents).anyTimes();
     expect(hostGroupComponent1.getName()).andReturn("component1").anyTimes();
     expect(hostGroupComponent2.getName()).andReturn("component2").anyTimes();
@@ -1886,7 +1888,7 @@ public class ClusterResourceProviderTest {
     assertEquals(1, configGroupRequests.size());
     ConfigGroupRequest configGroupRequest = configGroupRequests.iterator().next();
     assertEquals(clusterName, configGroupRequest.getClusterName());
-    assertEquals("group1", configGroupRequest.getGroupName());
+    assertEquals(blueprintName + ":group1", configGroupRequest.getGroupName());
     assertEquals("service1", configGroupRequest.getTag());
     assertEquals("Host Group Configuration", configGroupRequest.getDescription());
     Set<String> hosts = configGroupRequest.getHosts();
@@ -2392,6 +2394,7 @@ public class ClusterResourceProviderTest {
 
     expect(blueprint.getHostGroups()).andReturn(Collections.singleton(hostGroup)).anyTimes();
     expect(hostGroup.getName()).andReturn("group1").anyTimes();
+    expect(hostGroup.getBlueprintName()).andReturn(blueprintName).anyTimes();
     expect(hostGroup.getComponents()).andReturn(hostGroupComponents).anyTimes();
     expect(hostGroupComponent1.getName()).andReturn("component1").anyTimes();
     expect(hostGroupComponent2.getName()).andReturn("component2").anyTimes();
@@ -2616,7 +2619,7 @@ public class ClusterResourceProviderTest {
     assertEquals(1, configGroupRequests.size());
     ConfigGroupRequest configGroupRequest = configGroupRequests.iterator().next();
     assertEquals(clusterName, configGroupRequest.getClusterName());
-    assertEquals("group1", configGroupRequest.getGroupName());
+    assertEquals(blueprintName + ":group1", configGroupRequest.getGroupName());
     assertEquals("service1", configGroupRequest.getTag());
     assertEquals("Host Group Configuration", configGroupRequest.getDescription());
     Set<String> hosts = configGroupRequest.getHosts();

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostComponentResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostComponentResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostComponentResourceProviderTest.java
index abe30cc..0ffc6e1 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostComponentResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostComponentResourceProviderTest.java
@@ -19,30 +19,40 @@
 package org.apache.ambari.server.controller.internal;
 
 import com.google.inject.Injector;
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.ResourceProviderFactory;
 import org.apache.ambari.server.controller.ServiceComponentHostRequest;
 import org.apache.ambari.server.controller.ServiceComponentHostResponse;
 import org.apache.ambari.server.controller.spi.Predicate;
 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.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
-import org.apache.ambari.server.state.cluster.ClustersImpl;
 import org.easymock.EasyMock;
 import org.junit.Assert;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -53,6 +63,9 @@ import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 
 /**
  * HostComponentResourceProvider tests.
@@ -241,6 +254,14 @@ public class HostComponentResourceProviderTest {
     RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
     ResourceProviderFactory resourceProviderFactory = createNiceMock(ResourceProviderFactory.class);
     Injector injector = createNiceMock(Injector.class);
+    Clusters clusters = createNiceMock(Clusters.class);
+    Cluster cluster = createNiceMock(Cluster.class);
+    Service service = createNiceMock(Service.class);
+    ServiceComponent component = createNiceMock(ServiceComponent.class);
+    ServiceComponentHost componentHost = createNiceMock(ServiceComponentHost.class);
+    RequestStageContainer stageContainer = createNiceMock(RequestStageContainer.class);
+    MaintenanceStateHelper maintenanceStateHelper = createNiceMock(MaintenanceStateHelper.class);
+
 
     Map<String, String> mapRequestProps = new HashMap<String, String>();
     mapRequestProps.put("context", "Called from a test");
@@ -248,27 +269,49 @@ public class HostComponentResourceProviderTest {
     Set<ServiceComponentHostResponse> nameResponse = new HashSet<ServiceComponentHostResponse>();
     nameResponse.add(new ServiceComponentHostResponse(
         "Cluster102", "Service100", "Component100", "Host100", "STARTED", "", "", "", null));
-    
-    HostComponentResourceProvider provider = 
-        new HostComponentResourceProvider(PropertyHelper.getPropertyIds(type),
-        PropertyHelper.getKeyPropertyIds(type),
-        managementController, injector);
 
     // set expectations
+    expect(managementController.getClusters()).andReturn(clusters).anyTimes();
+    expect(managementController.findServiceName(cluster, "Component100")).andReturn("Service100").anyTimes();
+    expect(clusters.getCluster("Cluster102")).andReturn(cluster).anyTimes();
+    expect(cluster.getService("Service100")).andReturn(service).anyTimes();
+    expect(service.getServiceComponent("Component100")).andReturn(component).anyTimes();
+    expect(component.getServiceComponentHost("Host100")).andReturn(componentHost).anyTimes();
+    expect(component.getName()).andReturn("Component100").anyTimes();
+    expect(componentHost.getState()).andReturn(State.INSTALLED).anyTimes();
+    expect(response.getMessage()).andReturn("response msg").anyTimes();
+    expect(response.getRequestId()).andReturn(1000L);
+
+    //Cluster is default type.  Maintenance mode is not being tested here so the default is returned.
+    expect(maintenanceStateHelper.isOperationAllowed(Resource.Type.Cluster, componentHost)).andReturn(true).anyTimes();
+
     expect(managementController.getHostComponents(
         EasyMock.<Set<ServiceComponentHostRequest>>anyObject())).andReturn(nameResponse).once();
-    expect(managementController.updateHostComponents(
-        AbstractResourceProviderTest.Matcher.getHostComponentRequestSet(
-            "Cluster102", "Service100", "Component100", "Host100", null, "STARTED"),
-            eq(mapRequestProps), eq(false))).andReturn(response).once();
-    
+
+    Map<String, Map<State, List<ServiceComponentHost>>> changedHosts = new HashMap<String, Map<State, List<ServiceComponentHost>>>();
+    List<ServiceComponentHost> changedComponentHosts = new ArrayList<ServiceComponentHost>();
+    changedComponentHosts.add(componentHost);
+    changedHosts.put("Component100", Collections.singletonMap(State.STARTED, changedComponentHosts));
+
+    expect(managementController.addStages(null, cluster, mapRequestProps, null, null, null, changedHosts,
+        Collections.<ServiceComponentHost>emptyList(), false, false)).andReturn(stageContainer).once();
+
+    stageContainer.persist();
+    expect(stageContainer.getRequestStatusResponse()).andReturn(response).once();
+
+    HostComponentResourceProvider provider =
+        new TestHostComponentResourceProvider(PropertyHelper.getPropertyIds(type),
+            PropertyHelper.getKeyPropertyIds(type),
+            managementController, injector, maintenanceStateHelper);
+
     expect(resourceProviderFactory.getHostComponentResourceProvider(anyObject(Set.class),
         anyObject(Map.class),
         eq(managementController))).
         andReturn(provider).anyTimes();
 
     // replay
-    replay(managementController, response, resourceProviderFactory);
+    replay(managementController, response, resourceProviderFactory, clusters, cluster, service,
+        component, componentHost, stageContainer, maintenanceStateHelper);
 
     Map<String, Object> properties = new LinkedHashMap<String, Object>();
 
@@ -279,11 +322,116 @@ public class HostComponentResourceProviderTest {
 
     // update the cluster named Cluster102
     Predicate predicate = new PredicateBuilder().property(
-        HostComponentResourceProvider.HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID).equals("Cluster102").toPredicate();
-    provider.updateResources(request, predicate);
+        HostComponentResourceProvider.HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID).equals("Cluster102").and().
+        property(HostComponentResourceProvider.HOST_COMPONENT_HOST_NAME_PROPERTY_ID).equals("Host100").and().
+        property(HostComponentResourceProvider.HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID).equals("Component100").toPredicate();
+    RequestStatus requestStatus = provider.updateResources(request, predicate);
+    Resource responseResource = requestStatus.getRequestResource();
+    assertEquals("response msg", responseResource.getPropertyValue(PropertyHelper.getPropertyId("Requests", "message")));
+    assertEquals(1000L, responseResource.getPropertyValue(PropertyHelper.getPropertyId("Requests", "id")));
+    assertEquals("InProgress", responseResource.getPropertyValue(PropertyHelper.getPropertyId("Requests", "status")));
+    assertTrue(requestStatus.getAssociatedResources().isEmpty());
 
     // verify
-    verify(managementController, response, resourceProviderFactory);
+    verify(managementController, response, resourceProviderFactory, stageContainer);
+  }
+
+  @Test
+  public void testInstallAndStart() throws Exception {
+    Resource.Type type = Resource.Type.HostComponent;
+
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
+    ResourceProviderFactory resourceProviderFactory = createNiceMock(ResourceProviderFactory.class);
+    Injector injector = createNiceMock(Injector.class);
+    Clusters clusters = createNiceMock(Clusters.class);
+    Cluster cluster = createNiceMock(Cluster.class);
+    Service service = createNiceMock(Service.class);
+    ServiceComponent component = createNiceMock(ServiceComponent.class);
+    ServiceComponentHost componentHost = createNiceMock(ServiceComponentHost.class);
+    RequestStageContainer stageContainer = createNiceMock(RequestStageContainer.class);
+    MaintenanceStateHelper maintenanceStateHelper = createNiceMock(MaintenanceStateHelper.class);
+
+    Collection<String> hosts = new HashSet<String>();
+    hosts.add("Host100");
+
+    Map<String, String> mapRequestProps = new HashMap<String, String>();
+    mapRequestProps.put("context", "Install and start components on added hosts");
+
+    Set<ServiceComponentHostResponse> nameResponse = new HashSet<ServiceComponentHostResponse>();
+    nameResponse.add(new ServiceComponentHostResponse(
+        "Cluster102", "Service100", "Component100", "Host100", "INIT", "", "INIT", "", null));
+    Set<ServiceComponentHostResponse> nameResponse2 = new HashSet<ServiceComponentHostResponse>();
+    nameResponse2.add(new ServiceComponentHostResponse(
+        "Cluster102", "Service100", "Component100", "Host100", "INIT", "", "INSTALLED", "", null));
+
+
+    // set expectations
+    expect(managementController.getClusters()).andReturn(clusters).anyTimes();
+    expect(managementController.findServiceName(cluster, "Component100")).andReturn("Service100").anyTimes();
+    expect(clusters.getCluster("Cluster102")).andReturn(cluster).anyTimes();
+    expect(cluster.getService("Service100")).andReturn(service).anyTimes();
+    expect(service.getServiceComponent("Component100")).andReturn(component).anyTimes();
+    expect(component.getServiceComponentHost("Host100")).andReturn(componentHost).anyTimes();
+    expect(component.getName()).andReturn("Component100").anyTimes();
+    // actual state is always INIT until stages actually execute
+    expect(componentHost.getState()).andReturn(State.INIT).anyTimes();
+    expect(componentHost.getHostName()).andReturn("Host100").anyTimes();
+    expect(componentHost.getServiceComponentName()).andReturn("Component100").anyTimes();
+    expect(response.getMessage()).andReturn("response msg").anyTimes();
+    //expect(response.getRequestId()).andReturn(1000L);
+
+    //Cluster is default type.  Maintenance mode is not being tested here so the default is returned.
+    expect(maintenanceStateHelper.isOperationAllowed(Resource.Type.Cluster, componentHost)).andReturn(true).anyTimes();
+
+    //todo: can we change to prevent having to call twice?
+    expect(managementController.getHostComponents(
+        EasyMock.<Set<ServiceComponentHostRequest>>anyObject())).andReturn(nameResponse);
+    expect(managementController.getHostComponents(
+        EasyMock.<Set<ServiceComponentHostRequest>>anyObject())).andReturn(nameResponse2);
+
+    Map<String, Map<State, List<ServiceComponentHost>>> changedHosts =
+        new HashMap<String, Map<State, List<ServiceComponentHost>>>();
+    List<ServiceComponentHost> changedComponentHosts = Collections.singletonList(componentHost);
+    changedHosts.put("Component100", Collections.singletonMap(State.INSTALLED, changedComponentHosts));
+
+    Map<String, Map<State, List<ServiceComponentHost>>> changedHosts2 =
+        new HashMap<String, Map<State, List<ServiceComponentHost>>>();
+    List<ServiceComponentHost> changedComponentHosts2 = Collections.singletonList(componentHost);
+    changedHosts2.put("Component100", Collections.singletonMap(State.STARTED, changedComponentHosts2));
+
+    expect(managementController.addStages(null, cluster, mapRequestProps, null, null, null, changedHosts,
+        Collections.<ServiceComponentHost>emptyList(), false, false)).andReturn(stageContainer).once();
+
+    expect(managementController.addStages(stageContainer, cluster, mapRequestProps, null, null, null, changedHosts2,
+        Collections.<ServiceComponentHost>emptyList(), false, false)).andReturn(stageContainer).once();
+
+    stageContainer.persist();
+    expect(stageContainer.getProjectedState("Host100", "Component100")).andReturn(State.INSTALLED).once();
+    expect(stageContainer.getRequestStatusResponse()).andReturn(response).once();
+
+    HostComponentResourceProvider provider =
+        new TestHostComponentResourceProvider(PropertyHelper.getPropertyIds(type),
+            PropertyHelper.getKeyPropertyIds(type),
+            managementController, injector, maintenanceStateHelper);
+
+    expect(resourceProviderFactory.getHostComponentResourceProvider(anyObject(Set.class),
+        anyObject(Map.class),
+        eq(managementController))).
+        andReturn(provider).anyTimes();
+
+    // replay
+    replay(managementController, response, resourceProviderFactory, clusters, cluster, service,
+        component, componentHost, stageContainer, maintenanceStateHelper);
+
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+    properties.put(HostComponentResourceProvider.HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+
+    RequestStatusResponse requestResponse = provider.installAndStart("Cluster102", hosts);
+
+    assertSame(response, requestResponse);
+    // verify
+    verify(managementController, response, resourceProviderFactory, stageContainer);
   }
 
   @Test
@@ -309,7 +457,7 @@ public class HostComponentResourceProviderTest {
 
     AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
 
-    ((ObservableResourceProvider)provider).addObserver(observer);
+    provider.addObserver(observer);
 
     Predicate predicate = new PredicateBuilder().
         property(HostComponentResourceProvider.HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID).equals("Component100").and().
@@ -372,4 +520,44 @@ public class HostComponentResourceProviderTest {
     unsupported = provider.checkPropertyIds(Collections.singleton("config/unknown_property"));
     Assert.assertTrue(unsupported.isEmpty());
   }
+
+  // Used to directly call updateHostComponents on the resource provider.
+  // This exists as a temporary solution as a result of moving updateHostComponents from
+  // AmbariManagentControllerImpl to HostComponentResourceProvider.
+  public static RequestStatusResponse updateHostComponents(AmbariManagementController controller,
+                                                           Injector injector,
+                                                           Set<ServiceComponentHostRequest> requests,
+                                                           Map<String, String> requestProperties,
+                                                           boolean runSmokeTest) throws Exception {
+    Resource.Type type = Resource.Type.HostComponent;
+    HostComponentResourceProvider provider =
+        new TestHostComponentResourceProvider(PropertyHelper.getPropertyIds(type),
+            PropertyHelper.getKeyPropertyIds(type),
+            controller, injector, injector.getInstance(MaintenanceStateHelper.class));
+    RequestStageContainer requestStages = provider.updateHostComponents(null, requests, requestProperties, runSmokeTest);
+    requestStages.persist();
+    return requestStages.getRequestStatusResponse();
+  }
+
+  private static class TestHostComponentResourceProvider extends HostComponentResourceProvider {
+
+    /**
+     * Create a  new resource provider for the given management controller.
+     *
+     * @param propertyIds          the property ids
+     * @param keyPropertyIds       the key property ids
+     * @param managementController the management controller
+     */
+    public TestHostComponentResourceProvider(Set<String> propertyIds, Map<Resource.Type, String> keyPropertyIds,
+                                             AmbariManagementController managementController, Injector injector,
+                                             MaintenanceStateHelper maintenanceStateHelper) throws Exception {
+
+      super(propertyIds, keyPropertyIds, managementController, injector);
+
+      Class<?> c = getClass().getSuperclass();
+      Field f = c.getDeclaredField("maintenanceStateHelper");
+      f.setAccessible(true);
+      f.set(this, maintenanceStateHelper);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
index cab75ee..2e80e24 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
@@ -51,11 +51,13 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
+
 import static org.powermock.api.easymock.PowerMock.replayAll;
 import java.net.InetAddress;
 import static org.powermock.api.easymock.PowerMock.*;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -1153,7 +1155,15 @@ public class HostResourceProviderTest {
 
   public static void createHosts(AmbariManagementController controller, Set<HostRequest> requests) throws AmbariException {
     HostResourceProvider provider = getHostProvider(controller);
-    provider.createHosts(requests);
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+
+    for (HostRequest request : requests) {
+      Map<String, Object> requestProperties = new HashMap<String, Object>();
+      requestProperties.put(HostResourceProvider.HOST_NAME_PROPERTY_ID, request.getHostname());
+      requestProperties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, request.getClusterName());
+      properties.add(requestProperties);
+    }
+    provider.createHosts(PropertyHelper.getCreateRequest(properties, Collections.<String, String>emptyMap()));
   }
 
   public static Set<HostResponse> getHosts(AmbariManagementController controller,
@@ -1168,11 +1178,9 @@ public class HostResourceProviderTest {
     provider.deleteHosts(requests);
   }
   
-  public static void updateHosts(AmbariManagementController controller, Set<HostRequest> requests,
-      Map<String, String> requestProperties)
+  public static void updateHosts(AmbariManagementController controller, Set<HostRequest> requests)
       throws AmbariException {
     HostResourceProvider provider = getHostProvider(controller);
-    provider.updateHosts(requests, requestProperties);
+    provider.updateHosts(requests);
   }
-  
 }


[2/2] ambari git commit: AMBARI-8458. Add support for "add hosts" specifying host name, blueprint name and host group name AMBARI-8437. Fix regression that prvented cluster creation via blueprints

Posted by js...@apache.org.
AMBARI-8458. Add support for "add hosts" specifying host name, blueprint name and host group name
AMBARI-8437. Fix regression that prvented cluster creation via blueprints


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/058dc168
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/058dc168
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/058dc168

Branch: refs/heads/trunk
Commit: 058dc168e6173f4206837a1db87d066b372b07b1
Parents: 501785f
Author: John Speidel <js...@hortonworks.com>
Authored: Wed Nov 26 13:35:45 2014 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Wed Nov 26 17:56:02 2014 -0500

----------------------------------------------------------------------
 .../controller/AmbariManagementController.java  |  18 -
 .../AmbariManagementControllerImpl.java         | 336 +-----------
 .../ambari/server/controller/HostRequest.java   |  31 +-
 .../internal/AbstractResourceProvider.java      |   2 +-
 .../internal/BaseBlueprintProcessor.java        |  81 +++
 .../internal/ClusterControllerImpl.java         |  13 +-
 .../internal/ClusterResourceProvider.java       |  46 +-
 .../internal/ConfigGroupResourceProvider.java   |  31 +-
 .../internal/HostComponentResourceProvider.java | 544 ++++++++++++++++++-
 .../internal/HostResourceProvider.java          | 324 ++++++++---
 .../org/apache/ambari/server/state/Cluster.java |   7 +-
 .../server/state/cluster/ClusterImpl.java       |   2 +-
 .../AmbariManagementControllerTest.java         | 145 ++---
 .../internal/ClusterResourceProviderTest.java   |   9 +-
 .../HostComponentResourceProviderTest.java      | 220 +++++++-
 .../internal/HostResourceProviderTest.java      |  18 +-
 16 files changed, 1239 insertions(+), 588 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
index 5af88a8..a1ece2c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
@@ -235,24 +235,6 @@ public interface AmbariManagementController {
       throws AmbariException;
 
   /**
-   * Update the host component identified by the given request object with the
-   * values carried by the given request object.
-   *
-   *
-   *
-   * @param requests           the request object which defines which host component to
-   *                           update and the values to set
-   * @param requestProperties  the request properties
-   * @param runSmokeTest       indicates whether or not to run a smoke test
-   *
-   * @return a track action response
-   *
-   * @throws AmbariException thrown if the resource cannot be updated
-   */
-  public RequestStatusResponse updateHostComponents(
-      Set<ServiceComponentHostRequest> requests, Map<String, String> requestProperties, boolean runSmokeTest) throws AmbariException;
-
-  /**
    * Updates the users specified.
    *
    * @param requests the users to modify

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
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 7d08a7b..0e65a1d 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
@@ -44,7 +44,6 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
-import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -120,11 +119,8 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
-import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostDisableEvent;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostInstallEvent;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostRestoreEvent;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStartEvent;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostStopEvent;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostUpgradeEvent;
@@ -975,9 +971,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           try {
             if (serviceComponentHostMap == null
                 || !serviceComponentHostMap.containsKey(request.getHostname())) {
-              ServiceComponentHostNotFoundException e = new ServiceComponentHostNotFoundException(cluster.getClusterName(),
+              throw new ServiceComponentHostNotFoundException(cluster.getClusterName(),
                 s.getName(), sc.getName(), request.getHostname());
-              throw e;
             }
 
             ServiceComponentHost sch = serviceComponentHostMap.get(request.getHostname());
@@ -1309,8 +1304,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
   /**
    * Save cluster update results to retrieve later
-   * @param clusterRequest
-   * @param clusterResponse
+   * @param clusterRequest   cluster request info
+   * @param clusterResponse  cluster response info
    */
   public void saveClusterUpdate(ClusterRequest clusterRequest, ClusterResponse clusterResponse) {
     clusterUpdateCache.put(clusterRequest, clusterResponse);
@@ -1705,7 +1700,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   private List<ServiceOsSpecific> getOSSpecificsByFamily(Map<String, ServiceOsSpecific> osSpecifics, String osFamily) {
     List<ServiceOsSpecific> foundedOSSpecifics = new ArrayList<ServiceOsSpecific>();
     for (Entry<String, ServiceOsSpecific> osSpecific : osSpecifics.entrySet()) {
-      if (osSpecific.getKey().indexOf(osFamily) != -1) {
+      if (osSpecific.getKey().contains(osFamily)) {
         foundedOSSpecifics.add(osSpecific.getValue());
       }
     }
@@ -1833,8 +1828,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
                       nowTimestamp,
                       scHost.getDesiredStackVersion().getStackId());
                 } else if (oldSchState == State.STARTED
-// TODO: oldSchState == State.INSTALLED is always false, looks like a bug
-//                    || oldSchState == State.INSTALLED
+                      // TODO: oldSchState == State.INSTALLED is always false, looks like a bug
+                      //|| oldSchState == State.INSTALLED
                     || oldSchState == State.STOPPING) {
                   roleCommand = RoleCommand.STOP;
                   event = new ServiceComponentHostStopEvent(
@@ -2105,272 +2100,9 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     return requestStages;
   }
 
-  @Override
-  public synchronized RequestStatusResponse updateHostComponents(Set<ServiceComponentHostRequest> requests,
-                                                                 Map<String, String> requestProperties, boolean runSmokeTest)
-                                                                 throws AmbariException {
-
-    if (requests.isEmpty()) {
-      LOG.warn("Received an empty requests set");
-      return null;
-    }
-
-    Map<String, Map<State, List<ServiceComponentHost>>> changedScHosts =
-        new HashMap<String, Map<State, List<ServiceComponentHost>>>();
-    Collection<ServiceComponentHost> ignoredScHosts =
-        new ArrayList<ServiceComponentHost>();
-
-    Set<String> clusterNames = new HashSet<String>();
-    Map<String, Map<String, Map<String, Set<String>>>> hostComponentNames =
-        new HashMap<String, Map<String, Map<String, Set<String>>>>();
-    Set<State> seenNewStates = new HashSet<State>();
-    Map<ServiceComponentHost, State> directTransitionScHosts = new HashMap<ServiceComponentHost, State>();
-
-    // We don't expect batch requests for different clusters, that's why
-    // nothing bad should happen if value is overwritten few times
-    String maintenanceCluster = null;
-
-    // Determine operation level
-    Resource.Type reqOpLvl;
-    if (requestProperties.containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) {
-      RequestOperationLevel operationLevel = new RequestOperationLevel(requestProperties);
-      reqOpLvl = operationLevel.getLevel();
-    } else {
-      String message = "Can not determine request operation level. " +
-              "Operation level property should " +
-              "be specified for this request.";
-      LOG.warn(message);
-      reqOpLvl = Resource.Type.Cluster;
-    }
-
-    for (ServiceComponentHostRequest request : requests) {
-      validateServiceComponentHostRequest(request);
-
-      Cluster cluster = clusters.getCluster(request.getClusterName());
-
-      if (StringUtils.isEmpty(request.getServiceName())) {
-        request.setServiceName(findServiceName(cluster, request.getComponentName()));
-      }
-
-      LOG.info("Received a updateHostComponent request"
-          + ", clusterName=" + request.getClusterName()
-          + ", serviceName=" + request.getServiceName()
-          + ", componentName=" + request.getComponentName()
-          + ", hostname=" + request.getHostname()
-          + ", request=" + request);
-
-      clusterNames.add(request.getClusterName());
-
-      if (clusterNames.size() > 1) {
-        throw new IllegalArgumentException("Updates to multiple clusters is not"
-            + " supported");
-      }
-
-      if (!hostComponentNames.containsKey(request.getClusterName())) {
-        hostComponentNames.put(request.getClusterName(),
-            new HashMap<String, Map<String, Set<String>>>());
-      }
-      if (!hostComponentNames.get(request.getClusterName())
-          .containsKey(request.getServiceName())) {
-        hostComponentNames.get(request.getClusterName()).put(
-            request.getServiceName(), new HashMap<String, Set<String>>());
-      }
-      if (!hostComponentNames.get(request.getClusterName())
-          .get(request.getServiceName())
-          .containsKey(request.getComponentName())) {
-        hostComponentNames.get(request.getClusterName())
-            .get(request.getServiceName()).put(request.getComponentName(),
-            new HashSet<String>());
-      }
-      if (hostComponentNames.get(request.getClusterName())
-          .get(request.getServiceName()).get(request.getComponentName())
-          .contains(request.getHostname())) {
-        throw new IllegalArgumentException("Invalid request contains duplicate"
-            + " hostcomponents");
-      }
-      hostComponentNames.get(request.getClusterName())
-          .get(request.getServiceName()).get(request.getComponentName())
-          .add(request.getHostname());
-
-      Service s = cluster.getService(request.getServiceName());
-      ServiceComponent sc = s.getServiceComponent(
-          request.getComponentName());
-      ServiceComponentHost sch = sc.getServiceComponentHost(
-          request.getHostname());
-      State oldState = sch.getState();
-      State newState = null;
-      if (request.getDesiredState() != null) {
-        newState = State.valueOf(request.getDesiredState());
-        if (!newState.isValidDesiredState()) {
-          throw new IllegalArgumentException("Invalid arguments, invalid"
-              + " desired state, desiredState=" + newState.toString());
-        }
-      }
-
-      // Setting Maintenance state for host component
-      if (null != request.getMaintenanceState()) {
-        MaintenanceStateHelper psh = injector.getInstance(MaintenanceStateHelper.class);
-
-        MaintenanceState newMaint = MaintenanceState.valueOf(request.getMaintenanceState());
-        MaintenanceState oldMaint = psh.getEffectiveState(sch);
-
-        if (newMaint != oldMaint) {
-          if (sc.isClientComponent()) {
-            throw new IllegalArgumentException("Invalid arguments, cannot set " +
-              "maintenance state on a client component");
-          } else if (newMaint.equals(MaintenanceState.IMPLIED_FROM_HOST)
-              || newMaint.equals(MaintenanceState.IMPLIED_FROM_SERVICE)) {
-            throw new IllegalArgumentException("Invalid arguments, can only set " +
-              "maintenance state to one of " + EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON));
-          } else {
-            sch.setMaintenanceState(newMaint);
-            maintenanceCluster = sch.getClusterName();
-          }
-        }
-      }
-
-      if (newState == null) {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Nothing to do for new updateServiceComponentHost request"
-              + ", clusterName=" + request.getClusterName()
-              + ", serviceName=" + request.getServiceName()
-              + ", componentName=" + request.getComponentName()
-              + ", hostname=" + request.getHostname()
-              + ", oldState=" + oldState
-              + ", newDesiredState=null");
-        }
-        continue;
-      }
-
-      if (sc.isClientComponent() &&
-          !newState.isValidClientComponentState()) {
-        throw new IllegalArgumentException("Invalid desired state for a client"
-            + " component");
-      }
-
-      seenNewStates.add(newState);
-
-      State oldSchState = sch.getState();
-      // Client component reinstall allowed
-      if (newState == oldSchState &&
-          !sc.isClientComponent() &&
-          !requestProperties.containsKey(sch.getServiceComponentName().toLowerCase())) {
-
-        ignoredScHosts.add(sch);
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Ignoring ServiceComponentHost"
-              + ", clusterName=" + request.getClusterName()
-              + ", serviceName=" + s.getName()
-              + ", componentName=" + sc.getName()
-              + ", hostname=" + sch.getHostName()
-              + ", currentState=" + oldSchState
-              + ", newDesiredState=" + newState);
-        }
-        continue;
-      }
-
-      if (! maintenanceStateHelper.isOperationAllowed(reqOpLvl, sch)) {
-        ignoredScHosts.add(sch);
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Ignoring ServiceComponentHost"
-                  + ", clusterName=" + request.getClusterName()
-                  + ", serviceName=" + s.getName()
-                  + ", componentName=" + sc.getName()
-                  + ", hostname=" + sch.getHostName());
-        }
-        continue;
-      }
-
-      if (!State.isValidStateTransition(oldSchState, newState)) {
-        throw new AmbariException("Invalid transition for"
-            + " servicecomponenthost"
-            + ", clusterName=" + cluster.getClusterName()
-            + ", clusterId=" + cluster.getClusterId()
-            + ", serviceName=" + sch.getServiceName()
-            + ", componentName=" + sch.getServiceComponentName()
-            + ", hostname=" + sch.getHostName()
-            + ", currentState=" + oldSchState
-            + ", newDesiredState=" + newState);
-      }
-
-      if (isDirectTransition(oldSchState, newState)) {
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Handling direct transition update to ServiceComponentHost"
-              + ", clusterName=" + request.getClusterName()
-              + ", serviceName=" + s.getName()
-              + ", componentName=" + sc.getName()
-              + ", hostname=" + sch.getHostName()
-              + ", currentState=" + oldSchState
-              + ", newDesiredState=" + newState);
-        }
-        directTransitionScHosts.put(sch, newState);
-      } else {
-        if (!changedScHosts.containsKey(sc.getName())) {
-          changedScHosts.put(sc.getName(),
-              new EnumMap<State, List<ServiceComponentHost>>(State.class));
-        }
-        if (!changedScHosts.get(sc.getName()).containsKey(newState)) {
-          changedScHosts.get(sc.getName()).put(newState,
-              new ArrayList<ServiceComponentHost>());
-        }
-        if (LOG.isDebugEnabled()) {
-          LOG.debug("Handling update to ServiceComponentHost"
-              + ", clusterName=" + request.getClusterName()
-              + ", serviceName=" + s.getName()
-              + ", componentName=" + sc.getName()
-              + ", hostname=" + sch.getHostName()
-              + ", currentState=" + oldSchState
-              + ", newDesiredState=" + newState);
-        }
-        changedScHosts.get(sc.getName()).get(newState).add(sch);
-      }
-    }
-
-    if (seenNewStates.size() > 1) {
-      // FIXME should we handle this scenario
-      throw new IllegalArgumentException("Cannot handle different desired"
-          + " state changes for a set of service components at the same time");
-    }
-
-    // Perform direct transitions (without task generation)
-    for (Entry<ServiceComponentHost, State> entry : directTransitionScHosts.entrySet()) {
-      ServiceComponentHost componentHost = entry.getKey();
-      State newState = entry.getValue();
-      long timestamp = System.currentTimeMillis();
-      ServiceComponentHostEvent event;
-      componentHost.setDesiredState(newState);
-      switch (newState) {
-        case DISABLED:
-          event = new ServiceComponentHostDisableEvent(
-              componentHost.getServiceComponentName(),
-              componentHost.getHostName(),
-              timestamp);
-          break;
-        case INSTALLED:
-          event = new ServiceComponentHostRestoreEvent(
-              componentHost.getServiceComponentName(),
-              componentHost.getHostName(),
-              timestamp);
-          break;
-        default:
-          throw new AmbariException("Direct transition from " + componentHost.getState() + " to " + newState + " not supported");
-      }
-      try {
-        componentHost.handleEvent(event);
-      } catch (InvalidStateTransitionException e) {
-        //Should not occur, must be covered by previous checks
-        throw new AmbariException("Internal error - not supported transition", e);
-      }
-    }
-
-    Cluster cluster = clusters.getCluster(clusterNames.iterator().next());
-
-    return createAndPersistStages(cluster, requestProperties, null, null, null,
-      changedScHosts, ignoredScHosts, runSmokeTest, false);
-  }
-
-
-  private void validateServiceComponentHostRequest(ServiceComponentHostRequest request) {
+  //todo: for now made this public since is is still used by createHostComponents
+  //todo: delete after all host component logic is in HostComponentResourceProvider
+  public void validateServiceComponentHostRequest(ServiceComponentHostRequest request) {
     if (request.getClusterName() == null
         || request.getClusterName().isEmpty()
         || request.getComponentName() == null
@@ -2435,31 +2167,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     return serviceName;
   }
 
-
-  /**
-   * Checks if assigning new state does not require performing
-   * any additional actions
-   */
-  private boolean isDirectTransition(State oldState, State newState) {
-    switch (newState) {
-      case INSTALLED:
-        if (oldState == State.DISABLED) {
-          return true;
-        }
-        break;
-      case DISABLED:
-        if (oldState == State.INSTALLED ||
-          oldState == State.INSTALL_FAILED ||
-          oldState == State.UNKNOWN) {
-          return true;
-        }
-        break;
-      default:
-        break;
-    }
-    return false;
-  }
-
   @Override
   public synchronized void updateUsers(Set<UserRequest> requests) throws AmbariException {
     for (UserRequest request : requests) {
@@ -2935,13 +2642,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
   @Override
   public void updateGroups(Set<GroupRequest> requests) throws AmbariException {
-    for (GroupRequest request: requests) {
-      final Group group = users.getGroup(request.getGroupName());
-      if (group == null) {
-        continue;
-      }
-      // currently no group updates are supported
-    }
+    // currently no group updates are supported
   }
 
   protected String getClientHostForRunningAction(Cluster cluster, Service service, ServiceComponent serviceComponent)
@@ -3239,16 +2940,16 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           String errorMessage = null;
 
           String[] suffixes = configs.getRepoValidationSuffixes(rr.getOsType());
-          for (int i = 0; i < suffixes.length; i++) {
-            String suffix = String.format(suffixes[i], repoName);
+          for (String suffix : suffixes) {
+            String formatted_suffix = String.format(suffix, repoName);
             String spec = rr.getBaseUrl();
 
-            if (spec.charAt(spec.length()-1) != '/' && suffix.charAt(0) != '/') {
-              spec = rr.getBaseUrl() + "/" + suffix;
-            } else if (spec.charAt(spec.length()-1) == '/' && suffix.charAt(0) == '/') {
-              spec = rr.getBaseUrl() + suffix.substring(1);
+            if (spec.charAt(spec.length() - 1) != '/' && formatted_suffix.charAt(0) != '/') {
+              spec = rr.getBaseUrl() + "/" + formatted_suffix;
+            } else if (spec.charAt(spec.length() - 1) == '/' && formatted_suffix.charAt(0) == '/') {
+              spec = rr.getBaseUrl() + formatted_suffix.substring(1);
             } else {
-              spec = rr.getBaseUrl() + suffix;
+              spec = rr.getBaseUrl() + formatted_suffix;
             }
 
             try {
@@ -3257,8 +2958,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
               errorMessage = "Could not access base url . " + rr.getBaseUrl() + " . ";
               if (LOG.isDebugEnabled()) {
                 errorMessage += ioe;
-              }
-              else {
+              } else {
                 errorMessage += ioe.getMessage();
               }
               bFound = false;

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java
index b577bb0..a616839 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostRequest.java
@@ -31,6 +31,9 @@ public class HostRequest {
   private String rackInfo;
   private List<ConfigurationRequest> desiredConfigs; // UPDATE
   private String maintenanceState; // UPDATE
+  private String blueprint;
+  private String hostgroup;
+  private String hostToClone;
 
   public HostRequest(String hostname, String clusterName, Map<String, String> hostAttributes) {
     this.hostname = hostname;
@@ -94,6 +97,30 @@ public class HostRequest {
     return maintenanceState;
   }
 
+  public void setBlueprintName(String blueprintName) {
+    blueprint = blueprintName;
+  }
+
+  public String getBlueprintName() {
+    return blueprint;
+  }
+
+  public void setHostGroupName(String hostgroupName) {
+    hostgroup = hostgroupName;
+  }
+
+  public String getHostGroupName() {
+    return hostgroup;
+  }
+
+  public void setHostToClone(String hostname) {
+    hostToClone = hostname;
+  }
+
+  public String getHostToClone() {
+    return hostToClone;
+  }
+
   public String toString() {
     StringBuilder sb = new StringBuilder();
     sb.append("{ hostname=").append(hostname).append(", clusterName=").append(clusterName);
@@ -105,7 +132,9 @@ public class HostRequest {
           sb.append(",");
         }
         ++i;
-        sb.append(attr.getKey() + "=" + attr.getValue());
+        sb.append(attr.getKey());
+        sb.append("=");
+        sb.append(attr.getValue());
       }
       sb.append(']');
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
index 2c9179d..69d13b5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
@@ -380,7 +380,7 @@ public abstract class AbstractResourceProvider extends BaseProvider implements R
       String absCategory = PropertyHelper.getPropertyCategory(entry.getKey());
       String propName = PropertyHelper.getPropertyName(entry.getKey());
 
-      if (absCategory.startsWith(desiredConfigKey)) {
+      if (absCategory != null && absCategory.startsWith(desiredConfigKey)) {
         config = (null == config) ? new ConfigurationRequest() : config;
 
         parseProperties(config, absCategory, propName, entry.getValue().toString());

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
index 9be73cc..9cfb635 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
@@ -24,8 +24,12 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.orm.dao.BlueprintDAO;
 import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
 import org.apache.ambari.server.orm.entities.BlueprintEntity;
@@ -37,6 +41,7 @@ import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DependencyInfo;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -442,6 +447,82 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     throw new IllegalArgumentException(msg);
   }
 
+  /**
+   * Create host and host_component resources.
+   *
+   * @param blueprintHostGroups  host groups specified in blueprint
+   * @param clusterName          cluster name
+   *
+   * @throws SystemException                an unexpected exception occurred
+   * @throws UnsupportedPropertyException   an invalid property was specified
+   * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists
+   * @throws NoSuchParentResourceException  a required parent resource is missing
+   */
+  protected void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups, String clusterName)
+      throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
+
+    createHostAndComponentResources(blueprintHostGroups, clusterName, getResourceProvider(Resource.Type.Host));
+  }
+
+  /**
+   * Create host and host_component resources via the specified host resource provider.
+   *
+   * @param blueprintHostGroups  host groups specified in blueprint
+   * @param clusterName          cluster name
+   * @param hostProvider         host resource provider
+   *
+   * @throws SystemException                an unexpected exception occurred
+   * @throws UnsupportedPropertyException   an invalid property was specified
+   * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists
+   * @throws NoSuchParentResourceException  a required parent resource is missing
+   */
+  protected void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups,
+                                                 String clusterName,
+                                                 ResourceProvider hostProvider)
+                                                 throws SystemException,
+                                                        UnsupportedPropertyException,
+                                                        ResourceAlreadyExistsException,
+                                                        NoSuchParentResourceException {
+
+    ResourceProvider hostComponentProvider = getResourceProvider(Resource.Type.HostComponent);
+    for (HostGroupImpl group : blueprintHostGroups.values()) {
+      for (String host : group.getHostInfo()) {
+        Map<String, Object> hostProperties = new HashMap<String, Object>();
+        hostProperties.put("Hosts/cluster_name", clusterName);
+        hostProperties.put("Hosts/host_name", host);
+
+        hostProvider.createResources(new RequestImpl(
+            null, Collections.singleton(hostProperties), null, null));
+
+        // create clusters/hosts/host_components
+        Set<Map<String, Object>> setHostComponentRequestProps = new HashSet<Map<String, Object>>();
+        for (String hostComponent : group.getComponents()) {
+          // AMBARI_SERVER is not recognized by Ambari as a component
+          if (! hostComponent.equals("AMBARI_SERVER")) {
+            Map<String, Object> hostComponentProperties = new HashMap<String, Object>();
+            hostComponentProperties.put("HostRoles/cluster_name", clusterName);
+            hostComponentProperties.put("HostRoles/host_name", host);
+            hostComponentProperties.put("HostRoles/component_name", hostComponent);
+            setHostComponentRequestProps.add(hostComponentProperties);
+          }
+        }
+        hostComponentProvider.createResources(new RequestImpl(
+            null, setHostComponentRequestProps, null, null));
+      }
+    }
+  }
+
+  /**
+   * Get a config group name based on a bp and host group.
+   *
+   * @param bpName         blueprint name
+   * @param hostGroupName  host group name
+   * @return  config group name
+   */
+  protected String getConfigurationGroupName(String bpName, String hostGroupName) {
+    return String.format("%s:%s", bpName, hostGroupName);
+  }
+
 
   // ----- Inner Classes -----------------------------------------------------
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
index 352e0ad..b075d25 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
@@ -339,9 +339,20 @@ public class ClusterControllerImpl implements ClusterController {
     return null;
   }
 
+
+  /**
+   * Provides a non-wrapped resource provider..
+   *
+   * @param type  type of resource provider to obtain
+   * @return a non-wrapped resource provider
+   */
   @Override
   public ResourceProvider ensureResourceProvider(Type type) {
-    return ensureResourceProviderWrapper(type);
+    //todo: in some cases it is necessary to down cast the returned resource provider
+    //todo: to a concrete type.  Perhaps we can provided a 'T getDelegate()' method
+    //todo: on the wrapper so no casting would be necessary.
+    ExtendedResourceProviderWrapper providerWrapper = ensureResourceProviderWrapper(type);
+    return providerWrapper == null ? null : providerWrapper.resourceProvider;
   }
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
index bb6a39c..15cd8de 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
@@ -599,49 +599,6 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
   }
 
   /**
-   * Create host and host_component resources.
-   *
-   * @param blueprintHostGroups  host groups specified in blueprint
-   * @param clusterName          cluster name
-   *
-   * @throws SystemException                an unexpected exception occurred
-   * @throws UnsupportedPropertyException   an invalid property was specified
-   * @throws ResourceAlreadyExistsException attempt to create a host or host_component which already exists
-   * @throws NoSuchParentResourceException  a required parent resource is missing
-   */
-  private void createHostAndComponentResources(Map<String, HostGroupImpl> blueprintHostGroups, String clusterName)
-      throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
-
-    ResourceProvider hostProvider = getResourceProvider(Resource.Type.Host);
-    ResourceProvider hostComponentProvider = getResourceProvider(Resource.Type.HostComponent);
-    for (HostGroupImpl group : blueprintHostGroups.values()) {
-      for (String host : group.getHostInfo()) {
-        Map<String, Object> hostProperties = new HashMap<String, Object>();
-        hostProperties.put("Hosts/cluster_name", clusterName);
-        hostProperties.put("Hosts/host_name", host);
-
-        hostProvider.createResources(new RequestImpl(
-            null, Collections.singleton(hostProperties), null, null));
-
-        // create clusters/hosts/host_components
-        Set<Map<String, Object>> setHostComponentRequestProps = new HashSet<Map<String, Object>>();
-        for (String hostComponent : group.getComponents()) {
-          // AMBARI_SERVER is not recognized by Ambari as a component
-          if (! hostComponent.equals("AMBARI_SERVER")) {
-            Map<String, Object> hostComponentProperties = new HashMap<String, Object>();
-            hostComponentProperties.put("HostRoles/cluster_name", clusterName);
-            hostComponentProperties.put("HostRoles/host_name", host);
-            hostComponentProperties.put("HostRoles/component_name", hostComponent);
-            setHostComponentRequestProps.add(hostComponentProperties);
-          }
-        }
-        hostComponentProvider.createResources(new RequestImpl(
-            null, setHostComponentRequestProps, null, null));
-      }
-    }
-  }
-
-  /**
    * Create component resources.
    *
    * @param blueprintHostGroups  host groups specified in blueprint
@@ -1158,8 +1115,9 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
       for (Map.Entry<String, Map<String, Config>> entry : groupConfigs.entrySet()) {
         String service = entry.getKey();
         Map<String, Config> serviceConfigs = entry.getValue();
+        String hostGroupName = getConfigurationGroupName(entity.getBlueprintName(), entity.getName());
         ConfigGroupRequest request = new ConfigGroupRequest(
-            null, clusterName, entity.getName(), service, "Host Group Configuration",
+            null, clusterName, hostGroupName, service, "Host Group Configuration",
             new HashSet<String>(group.getHostInfo()), serviceConfigs);
 
         ((ConfigGroupResourceProvider) getResourceProvider(Resource.Type.ConfigGroup)).

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
index c73a8d4..3fcb84b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
@@ -17,8 +17,6 @@
  */
 package org.apache.ambari.server.controller.internal;
 
-import com.google.common.collect.MapDifference;
-import com.google.common.collect.Maps;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.ClusterNotFoundException;
 import org.apache.ambari.server.ConfigGroupNotFoundException;
@@ -180,19 +178,13 @@ public class ConfigGroupResourceProvider extends
       for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) {
         requests.add(getConfigGroupRequest(propertyMap));
       }
-
-      modifyResources(new Command<Void>() {
-        @Override
-        public Void invoke() throws AmbariException {
-          updateConfigGroups(requests);
-          return null;
-        }
-      });
     }
 
+    RequestStatus status = updateResources(requests);
+
     notifyUpdate(Resource.Type.ConfigGroup, request, predicate);
 
-    return getRequestStatus(null);
+    return status;
   }
 
   @Override
@@ -265,6 +257,23 @@ public class ConfigGroupResourceProvider extends
     return getRequestStatus(null, associatedResources);
   }
 
+  public RequestStatus updateResources(final Set<ConfigGroupRequest> requests)
+      throws SystemException,
+      UnsupportedPropertyException,
+      NoSuchResourceException,
+      NoSuchParentResourceException {
+
+    modifyResources(new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        updateConfigGroups(requests);
+        return null;
+      }
+    });
+
+    return getRequestStatus(null);
+  }
+
   private synchronized  Set<ConfigGroupResponse> getConfigGroups
     (Set<ConfigGroupRequest> requests) throws AmbariException {
     Set<ConfigGroupResponse> responses = new HashSet<ConfigGroupResponse>();

http://git-wip-us.apache.org/repos/asf/ambari/blob/058dc168/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java
index 4600538..47d3f70 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostComponentResourceProvider.java
@@ -17,20 +17,31 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.inject.Inject;
 import com.google.inject.Injector;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
 import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.ServiceComponentHostRequest;
 import org.apache.ambari.server.controller.ServiceComponentHostResponse;
+import org.apache.ambari.server.controller.predicate.AndPredicate;
+import org.apache.ambari.server.controller.predicate.EqualsPredicate;
+import org.apache.ambari.server.controller.predicate.NotPredicate;
+import org.apache.ambari.server.controller.predicate.OrPredicate;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -45,7 +56,17 @@ import org.apache.ambari.server.controller.utilities.PropertyHelper;
 
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
-import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.ServiceComponentHostEvent;
+import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
+import org.apache.ambari.server.state.svccomphost.ServiceComponentHostDisableEvent;
+import org.apache.ambari.server.state.svccomphost.ServiceComponentHostRestoreEvent;
+import org.apache.commons.lang.StringUtils;
 
 /**
  * Resource provider for host component resources.
@@ -79,7 +100,7 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
       = PropertyHelper.getPropertyId("HostRoles", "desired_admin_state");
   protected static final String HOST_COMPONENT_MAINTENANCE_STATE_PROPERTY_ID
       = "HostRoles/maintenance_state";
-  
+
   //Component name mappings
   private final Map<String, PropertyProvider> HOST_COMPONENT_PROPERTIES_PROVIDER = new HashMap<String, PropertyProvider>();
   private static final int HOST_COMPONENT_HTTP_PROPERTY_REQUEST_CONNECT_TIMEOUT = 1500;   //milliseconds
@@ -95,6 +116,13 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
           HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID,
           HOST_COMPONENT_HOST_NAME_PROPERTY_ID}));
 
+  /**
+   * maintenance state helper
+   */
+  @Inject
+  private MaintenanceStateHelper maintenanceStateHelper;
+
+
   // ----- Constructors ----------------------------------------------------
 
   /**
@@ -153,7 +181,6 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
   }
 
   @Override
-  @Transactional
   public Set<Resource> getResources(Request request, Predicate predicate)
       throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
 
@@ -194,7 +221,7 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
       setResourceProperty(resource, HOST_COMPONENT_ACTUAL_CONFIGS_PROPERTY_ID,
           response.getActualConfigs(), requestedIds);
       setResourceProperty(resource, HOST_COMPONENT_STALE_CONFIGS_PROPERTY_ID,
-          Boolean.valueOf(response.isStaleConfig()), requestedIds);
+          response.isStaleConfig(), requestedIds);
       
       if (response.getAdminState() != null) {
         setResourceProperty(resource, HOST_COMPONENT_DESIRED_ADMIN_STATE_PROPERTY_ID,
@@ -222,26 +249,24 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
   @Override
   public RequestStatus updateResources(final Request request, Predicate predicate)
       throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
-    final Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
-    RequestStatusResponse response = null;
 
-    final boolean runSmokeTest = "true".equals(getQueryParameterValue(
-        QUERY_PARAMETERS_RUN_SMOKE_TEST_ID, predicate)) ? true : false;
+    if (request.getProperties().isEmpty()) {
+      throw new IllegalArgumentException("Received an update request with no properties");
+    }
 
-    Iterator<Map<String, Object>> iterator = request.getProperties().iterator();
-    if (iterator.hasNext()) {
-      for (Map<String, Object> propertyMap : getPropertyMaps(request.getProperties().iterator().next(), predicate)) {
-        requests.add(getRequest(propertyMap));
-      }
-      response = modifyResources(new Command<RequestStatusResponse>() {
-        @Override
-        public RequestStatusResponse invoke() throws AmbariException {
-          return getManagementController().updateHostComponents(requests, request.getRequestInfoProperties(), runSmokeTest);
-        }
-      });
+    RequestStageContainer requestStages = doUpdateResources(null, request, predicate);
 
+    RequestStatusResponse response = null;
+    if (requestStages != null) {
+      try {
+        requestStages.persist();
+      } catch (AmbariException e) {
+        throw new SystemException(e.getMessage(), e);
+      }
+      response = requestStages.getRequestStatusResponse();
       notifyUpdate(Resource.Type.HostComponent, request, predicate);
     }
+
     return getRequestStatus(response);
   }
 
@@ -284,14 +309,247 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
     return unsupportedProperties;
   }
 
+  RequestStatusResponse installAndStart(String cluster, Collection<String> hosts) throws  SystemException,
+      UnsupportedPropertyException, NoSuchParentResourceException {
 
-  // ----- utility methods -------------------------------------------------
+    final RequestStageContainer requestStages;
+    Map<String, Object> installProperties = new HashMap<String, Object>();
+
+    installProperties.put(HOST_COMPONENT_STATE_PROPERTY_ID, "INSTALLED");
+    Map<String, String> requestInfo = new HashMap<String, String>();
+    requestInfo.put("context", "Install and start components on added hosts");
+    Request installRequest = PropertyHelper.getUpdateRequest(installProperties, requestInfo);
+
+    Collection<EqualsPredicate> hostPredicates = new ArrayList<EqualsPredicate>();
+    for (String host : hosts) {
+      hostPredicates.add(new EqualsPredicate<String>(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, host));
+    }
+
+    Predicate statePredicate = new EqualsPredicate<String>(HOST_COMPONENT_STATE_PROPERTY_ID, "INIT");
+    Predicate clusterPredicate = new EqualsPredicate<String>(HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID, cluster);
+    Predicate hostPredicate = new OrPredicate(hostPredicates.toArray(new Predicate[hostPredicates.size()]));
+    Predicate hostAndStatePredicate = new AndPredicate(statePredicate, hostPredicate);
+    Predicate installPredicate = new AndPredicate(hostAndStatePredicate, clusterPredicate);
+
+    try {
+      LOG.info("Installing all components on added hosts");
+      requestStages = doUpdateResources(null, installRequest, installPredicate);
+      notifyUpdate(Resource.Type.HostComponent, installRequest, installPredicate);
+
+      Map<String, Object> startProperties = new HashMap<String, Object>();
+      startProperties.put(HOST_COMPONENT_STATE_PROPERTY_ID, "STARTED");
+      Request startRequest = PropertyHelper.getUpdateRequest(startProperties, requestInfo);
+      // Important to query against desired_state as this has been updated when install stage was created
+      // If I query against state, then the getRequest compares predicate prop against desired_state and then when the predicate
+      // is later applied explicitly, it gets compared to live_state. Since live_state == INSTALLED == INIT at this point and
+      // desired_state == INSTALLED, we will always get 0 matches since both comparisons can't be true :(
+      Predicate installedStatePredicate = new EqualsPredicate<String>(HOST_COMPONENT_DESIRED_STATE_PROPERTY_ID, "INSTALLED");
+      Predicate notClientPredicate = new NotPredicate(new ClientComponentPredicate());
+      Predicate clusterAndClientPredicate = new AndPredicate(clusterPredicate, notClientPredicate);
+      hostAndStatePredicate = new AndPredicate(installedStatePredicate, hostPredicate);
+      Predicate startPredicate = new AndPredicate(clusterAndClientPredicate, hostAndStatePredicate);
+
+      LOG.info("Starting all non-client components on added hosts");
+      //todo: if a host in in state HEARTBEAT_LOST, no stage will be created, so if this occurs during INSTALL
+      //todo: then no INSTALL stage will exist which will result in invalid state transition INIT->STARTED
+      doUpdateResources(requestStages, startRequest, startPredicate);
+      notifyUpdate(Resource.Type.HostComponent, startRequest, startPredicate);
+      try {
+        requestStages.persist();
+      } catch (AmbariException e) {
+        throw new SystemException(e.getMessage(), e);
+      }
+      return requestStages.getRequestStatusResponse();
+    } catch (NoSuchResourceException e) {
+      // shouldn't encounter this exception here
+      throw new SystemException("An unexpected exception occurred while processing add hosts",  e);
+    }
+  }
+
+
+  /**
+   * Update the host component identified by the given request object with the
+   * values carried by the given request object.
+   *
+   * @param stages             stages of the associated request
+   * @param requests           the request object which defines which host component to
+   *                           update and the values to set
+   * @param requestProperties  the request properties
+   * @param runSmokeTest       indicates whether or not to run a smoke test
+   *
+   * @return a track action response
+   *
+   * @throws AmbariException thrown if the resource cannot be updated
+   */
+  //todo: This was moved from AmbariManagementController and needs a lot of refactoring.
+  //todo: Look into using the predicate instead of Set<ServiceComponentHostRequest>
+  //todo: change to private access when all AMC tests have been moved.
+  protected synchronized RequestStageContainer updateHostComponents(RequestStageContainer stages,
+                                                                    Set<ServiceComponentHostRequest> requests,
+                                                                    Map<String, String> requestProperties,
+                                                                    boolean runSmokeTest) throws AmbariException {
+
+    Clusters clusters = getManagementController().getClusters();
+
+
+    Map<String, Map<State, List<ServiceComponentHost>>> changedScHosts = new HashMap<String, Map<State, List<ServiceComponentHost>>>();
+    Collection<ServiceComponentHost> ignoredScHosts = new ArrayList<ServiceComponentHost>();
+    Set<String> clusterNames = new HashSet<String>();
+    Map<String, Map<String, Map<String, Set<String>>>> requestClusters = new HashMap<String, Map<String, Map<String, Set<String>>>>();
+    Map<ServiceComponentHost, State> directTransitionScHosts = new HashMap<ServiceComponentHost, State>();
+
+    Resource.Type reqOpLvl = determineOperationLevel(requestProperties);
+
+
+    for (ServiceComponentHostRequest request : requests) {
+      validateServiceComponentHostRequest(request);
+
+      Cluster cluster = clusters.getCluster(request.getClusterName());
+
+      if (StringUtils.isEmpty(request.getServiceName())) {
+        request.setServiceName(getManagementController().findServiceName(cluster, request.getComponentName()));
+      }
+
+      ServiceComponent sc = getServiceComponent(
+          request.getClusterName(), request.getServiceName(), request.getComponentName());
+
+      logRequestInfo("Received a updateHostComponent request", request);
+
+      clusterNames.add(request.getClusterName());
+
+      if (clusterNames.size() > 1) {
+        throw new IllegalArgumentException("Updates to multiple clusters is not"
+            + " supported");
+      }
+
+      // maps of cluster->services, services->components, components->hosts
+      Map<String, Map<String, Set<String>>> clusterServices = requestClusters.get(request.getClusterName());
+      if (clusterServices == null) {
+        clusterServices = new HashMap<String, Map<String, Set<String>>>();
+        requestClusters.put(request.getClusterName(), clusterServices);
+      }
+
+      Map<String, Set<String>> serviceComponents = clusterServices.get(request.getServiceName());
+      if (serviceComponents == null) {
+        serviceComponents = new HashMap<String, Set<String>>();
+        clusterServices.put(request.getServiceName(), serviceComponents);
+      }
+
+      Set<String> componentHosts = serviceComponents.get(request.getComponentName());
+      if (componentHosts == null) {
+        componentHosts = new HashSet<String>();
+        serviceComponents.put(request.getComponentName(), componentHosts) ;
+      }
+
+      if (componentHosts.contains(request.getHostname())) {
+        throw new IllegalArgumentException("Invalid request contains duplicate hostcomponents");
+      }
+
+      componentHosts.add(request.getHostname());
+
+
+      ServiceComponentHost sch = sc.getServiceComponentHost(request.getHostname());
+      State oldState = sch.getState();
+      State newState = null;
+      if (request.getDesiredState() != null) {
+        // set desired state on host component
+        newState = State.valueOf(request.getDesiredState());
+        // throw exception if desired state isn't a valid desired state (static check)
+        if (!newState.isValidDesiredState()) {
+          throw new IllegalArgumentException("Invalid arguments, invalid"
+              + " desired state, desiredState=" + newState.toString());
+        }
+      }
+
+      // Setting Maintenance state for host component
+      if (null != request.getMaintenanceState()) {
+        MaintenanceState newMaint = MaintenanceState.valueOf(request.getMaintenanceState());
+        MaintenanceState oldMaint = maintenanceStateHelper.getEffectiveState(sch);
+
+        if (newMaint != oldMaint) {
+          if (sc.isClientComponent()) {
+            throw new IllegalArgumentException("Invalid arguments, cannot set maintenance state on a client component");
+          } else if (newMaint.equals(MaintenanceState.IMPLIED_FROM_HOST)  || newMaint.equals(MaintenanceState.IMPLIED_FROM_SERVICE)) {
+            throw new IllegalArgumentException("Invalid arguments, can only set maintenance state to one of " +
+                EnumSet.of(MaintenanceState.OFF, MaintenanceState.ON));
+          } else {
+            sch.setMaintenanceState(newMaint);
+          }
+        }
+      }
+
+      if (newState == null) {
+        logComponentInfo("Nothing to do for new updateServiceComponentHost", request, oldState, null);
+        continue;
+      }
+
+      if (sc.isClientComponent() &&
+          !newState.isValidClientComponentState()) {
+        throw new IllegalArgumentException("Invalid desired state for a client"
+            + " component");
+      }
+
+      State oldSchState = sch.getState();
+      // Client component reinstall allowed
+      if (newState == oldSchState && !sc.isClientComponent() &&
+          !requestProperties.containsKey(sch.getServiceComponentName().toLowerCase())) {
+
+        ignoredScHosts.add(sch);
+        logComponentInfo("Ignoring ServiceComponentHost", request, oldState, newState);
+        continue;
+      }
+
+      if (! maintenanceStateHelper.isOperationAllowed(reqOpLvl, sch)) {
+        ignoredScHosts.add(sch);
+        logComponentInfo("Ignoring ServiceComponentHost", request, oldState, newState);
+        continue;
+      }
+
+      if (! isValidStateTransition(stages, oldSchState, newState, sch)) {
+        throw new AmbariException("Invalid state transition for host component"
+            + ", clusterName=" + cluster.getClusterName()
+            + ", clusterId=" + cluster.getClusterId()
+            + ", serviceName=" + sch.getServiceName()
+            + ", componentName=" + sch.getServiceComponentName()
+            + ", hostname=" + sch.getHostName()
+            + ", currentState=" + oldSchState
+            + ", newDesiredState=" + newState);
+      }
+
+      if (isDirectTransition(oldSchState, newState)) {
+        logComponentInfo("Handling direct transition update to host component", request, oldState, newState);
+        directTransitionScHosts.put(sch, newState);
+      } else {
+        if (!changedScHosts.containsKey(sc.getName())) {
+          changedScHosts.put(sc.getName(),
+              new EnumMap<State, List<ServiceComponentHost>>(State.class));
+        }
+        if (!changedScHosts.get(sc.getName()).containsKey(newState)) {
+          changedScHosts.get(sc.getName()).put(newState,
+              new ArrayList<ServiceComponentHost>());
+        }
+        logComponentInfo("Handling update to host component", request, oldState, newState);
+        changedScHosts.get(sc.getName()).get(newState).add(sch);
+      }
+    }
+
+    doDirectTransitions(directTransitionScHosts);
+
+    // just getting the first cluster
+    Cluster cluster = clusters.getCluster(clusterNames.iterator().next());
+
+    return getManagementController().addStages(stages, cluster, requestProperties, null, null, null,
+        changedScHosts, ignoredScHosts, runSmokeTest, false);
+  }
 
   @Override
   protected Set<String> getPKPropertyIds() {
     return pkPropertyIds;
   }
 
+
+  // ----- utility methods -------------------------------------------------
+
   /**
    * Get a component request object from a map of property values.
    *
@@ -324,4 +582,248 @@ public class HostComponentResourceProvider extends AbstractControllerResourcePro
 
     return serviceComponentHostRequest;
   }
+
+  private RequestStageContainer doUpdateResources(final RequestStageContainer stages, final Request request, Predicate predicate)
+      throws UnsupportedPropertyException, SystemException, NoSuchResourceException, NoSuchParentResourceException {
+
+    final Set<ServiceComponentHostRequest> requests = new HashSet<ServiceComponentHostRequest>();
+
+    final boolean runSmokeTest = "true".equals(getQueryParameterValue(
+        QUERY_PARAMETERS_RUN_SMOKE_TEST_ID, predicate));
+
+    Set<String> queryIds = Collections.singleton(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID);
+
+    Request queryRequest = PropertyHelper.getReadRequest(queryIds);
+    // will take care of 404 exception
+    Set<Resource> matchingResources = getResources(queryRequest, predicate);
+
+    for (Resource queryResource : matchingResources) {
+      if (predicate.evaluate(queryResource)) {
+        Map<String, Object> updateRequestProperties = new HashMap<String, Object>();
+
+        // add props from query resource
+        updateRequestProperties.putAll(PropertyHelper.getProperties(queryResource));
+
+        // add properties from update request
+        //todo: should we flag value size > 1?
+        if (request.getProperties() != null && request.getProperties().size() != 0) {
+          updateRequestProperties.putAll(request.getProperties().iterator().next());
+        }
+        requests.add(getRequest(updateRequestProperties));
+      }
+    }
+
+    RequestStageContainer requestStages = modifyResources(new Command<RequestStageContainer>() {
+      @Override
+      public RequestStageContainer invoke() throws AmbariException {
+        return updateHostComponents(stages, requests, request.getRequestInfoProperties(),
+            runSmokeTest);
+      }
+    });
+    notifyUpdate(Resource.Type.HostComponent, request, predicate);
+
+    return requestStages;
+  }
+
+  /**
+   * Determine whether a host component state change is valid.
+   * Looks at projected state from the current stages associated with the request.
+   *
+   *
+   * @param stages        request stages
+   * @param startState    host component start state
+   * @param desiredState  host component desired state
+   * @param host          host where state change is occurring
+   *
+   * @return whether the state transition is valid
+   */
+  private boolean isValidStateTransition(RequestStageContainer stages, State startState,
+                                         State desiredState, ServiceComponentHost host) {
+
+    if (stages != null) {
+      State projectedState = stages.getProjectedState(host.getHostName(), host.getServiceComponentName());
+      startState = projectedState == null ? startState : projectedState;
+    }
+
+    return State.isValidStateTransition(startState, desiredState);
+  }
+
+  /**
+   * Checks if assigning new state does not require performing
+   * any additional actions
+   */
+  public boolean isDirectTransition(State oldState, State newState) {
+    switch (newState) {
+      case INSTALLED:
+        if (oldState == State.DISABLED) {
+          return true;
+        }
+        break;
+      case DISABLED:
+        if (oldState == State.INSTALLED ||
+            oldState == State.INSTALL_FAILED ||
+            oldState == State.UNKNOWN) {
+          return true;
+        }
+        break;
+      default:
+        break;
+    }
+    return false;
+  }
+
+  private ServiceComponent getServiceComponent(String clusterName, String serviceName, String componentName)
+      throws AmbariException {
+
+    Clusters clusters = getManagementController().getClusters();
+    return clusters.getCluster(clusterName).getService(serviceName).getServiceComponent(componentName);
+  }
+
+  // Perform direct transitions (without task generation)
+  private void doDirectTransitions(Map<ServiceComponentHost, State> directTransitionScHosts) throws AmbariException {
+    for (Map.Entry<ServiceComponentHost, State> entry : directTransitionScHosts.entrySet()) {
+      ServiceComponentHost componentHost = entry.getKey();
+      State newState = entry.getValue();
+      long timestamp = System.currentTimeMillis();
+      ServiceComponentHostEvent event;
+      componentHost.setDesiredState(newState);
+      switch (newState) {
+        case DISABLED:
+          event = new ServiceComponentHostDisableEvent(
+              componentHost.getServiceComponentName(),
+              componentHost.getHostName(),
+              timestamp);
+          break;
+        case INSTALLED:
+          event = new ServiceComponentHostRestoreEvent(
+              componentHost.getServiceComponentName(),
+              componentHost.getHostName(),
+              timestamp);
+          break;
+        default:
+          throw new AmbariException("Direct transition from " + componentHost.getState() + " to " + newState + " not supported");
+      }
+      try {
+        componentHost.handleEvent(event);
+      } catch (InvalidStateTransitionException e) {
+        //Should not occur, must be covered by previous checks
+        throw new AmbariException("Internal error - not supported transition", e);
+      }
+    }
+  }
+
+  /**
+   * Logs request info.
+   *
+   * @param msg      base log msg
+   * @param request  the request to log
+   */
+  private void logRequestInfo(String msg, ServiceComponentHostRequest request) {
+    LOG.info("{}, clusterName={}, serviceName={}, componentName={}, hostname={}, request={}",
+        msg,
+        request.getClusterName(),
+        request.getServiceName(),
+        request.getComponentName(),
+        request.getHostname(),
+        request);
+  }
+
+  /**
+   * Logs component info.
+   *
+   * @param msg              base log msg
+   * @param request          the request to log
+   * @param oldState         current state
+   * @param newDesiredState  new desired state
+   */
+  private void logComponentInfo(String msg, ServiceComponentHostRequest request, State oldState, State newDesiredState) {
+    LOG.debug("{}, clusterName={}, serviceName={}, componentName={}, hostname={}, currentState={}, newDesiredState={}",
+        msg,
+        request.getClusterName(),
+        request.getServiceName(),
+        request.getComponentName(),
+        request.getHostname(),
+        oldState == null ? "null" : oldState,
+        newDesiredState == null ? "null" : newDesiredState);
+  }
+
+  /**
+   * Get the "operation level" from the request.
+   *
+   * @param requestProperties  request properties
+   * @return  the "operation level"
+   */
+  private Resource.Type determineOperationLevel(Map<String, String> requestProperties) {
+    // Determine operation level
+    Resource.Type reqOpLvl;
+    if (requestProperties.containsKey(RequestOperationLevel.OPERATION_LEVEL_ID)) {
+      reqOpLvl = new RequestOperationLevel(requestProperties).getLevel();
+    } else {
+      String message = "Can not determine request operation level. " +
+          "Operation level property should " +
+          "be specified for this request.";
+      LOG.warn(message);
+      reqOpLvl = Resource.Type.Cluster;
+    }
+    return reqOpLvl;
+  }
+
+  /**
+   * Validate a host component request.
+   *
+   * @param request  request to validate
+   * @throws IllegalArgumentException if the request is invalid
+   */
+  private void validateServiceComponentHostRequest(ServiceComponentHostRequest request) {
+    if (request.getClusterName() == null
+        || request.getClusterName().isEmpty()
+        || request.getComponentName() == null
+        || request.getComponentName().isEmpty()
+        || request.getHostname() == null
+        || request.getHostname().isEmpty()) {
+      throw new IllegalArgumentException("Invalid arguments"
+          + ", cluster name, component name and host name should be"
+          + " provided");
+    }
+
+    if (request.getAdminState() != null) {
+      throw new IllegalArgumentException("Property adminState cannot be modified through update. Use service " +
+          "specific DECOMMISSION action to decommision/recommission components.");
+    }
+  }
+
+
+  // ----- inner classes ---------------------------------------------------
+
+  /**
+   * Predicate that identifies client components.
+   */
+  private  class ClientComponentPredicate implements Predicate {
+    @Override
+    public boolean evaluate(Resource resource) {
+      boolean isClient = false;
+
+      String componentName = (String) resource.getPropertyValue(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID);
+      try {
+        if (componentName != null && !componentName.isEmpty()) {
+          AmbariManagementController managementController = getManagementController();
+          String clusterName = (String) resource.getPropertyValue(HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID);
+          String serviceName = (String) resource.getPropertyValue(HOST_COMPONENT_SERVICE_NAME_PROPERTY_ID);
+          if (StringUtils.isEmpty(serviceName)) {
+            Cluster cluster = managementController.getClusters().getCluster(clusterName);
+            serviceName = managementController.findServiceName(cluster, componentName);
+          }
+
+          ServiceComponent sc = getServiceComponent((String) resource.getPropertyValue(
+              HOST_COMPONENT_CLUSTER_NAME_PROPERTY_ID), serviceName, componentName);
+          isClient = sc.isClientComponent();
+        }
+      } catch (AmbariException e) {
+        // this is really a system exception since cluster/service should have been already verified
+        throw new RuntimeException(
+            "An unexpected exception occurred while trying to determine if a component is a client", e);
+      }
+      return isClient;
+    }
+  }
 }