You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/01/10 17:05:33 UTC

ambari git commit: AMBARI-19435 - NodeManager restart fails during HOU if it is on same host as RM (jonathanhurley)

Repository: ambari
Updated Branches:
  refs/heads/trunk e15531981 -> e10f6922e


AMBARI-19435 - NodeManager restart fails during HOU if it is on same host as RM (jonathanhurley)


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

Branch: refs/heads/trunk
Commit: e10f6922eb972c581102b75d7fa50eabe0487fa2
Parents: e155319
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Mon Jan 9 20:51:20 2017 -0500
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Tue Jan 10 12:05:26 2017 -0500

----------------------------------------------------------------------
 .../server/controller/ControllerModule.java     |   2 +
 .../internal/UpgradeResourceProvider.java       |  13 +-
 .../ambari/server/stageplanner/RoleGraph.java   | 120 +++++++++++++++++--
 .../org/apache/ambari/server/state/Cluster.java |   8 ++
 .../ambari/server/state/UpgradeContext.java     |  43 ++++++-
 .../server/state/UpgradeContextFactory.java     |  47 ++++++++
 .../server/state/cluster/ClusterImpl.java       |  17 +++
 .../state/stack/upgrade/HostOrderGrouping.java  | 114 +++++++++++++++---
 .../state/stack/upgrade/StageWrapper.java       |  11 ++
 .../server/state/stack/upgrade/TaskWrapper.java |  14 ++-
 .../ambari/server/agent/AgentResourceTest.java  |   3 +
 .../server/controller/KerberosHelperTest.java   |   6 +
 .../ActiveWidgetLayoutResourceProviderTest.java |   3 +
 .../StackUpgradeConfigurationMergeTest.java     |   6 +-
 .../UserAuthorizationResourceProviderTest.java  |   3 +
 .../internal/UserResourceProviderTest.java      |   3 +
 .../ambari/server/metadata/RoleGraphTest.java   |  73 +++++++++++
 .../ambari/server/state/UpgradeHelperTest.java  |  74 ++++++++----
 .../cluster/ClusterEffectiveVersionTest.java    |   4 +
 .../HDP/2.1.1/services/HBASE/metainfo.xml       |  44 +++++++
 20 files changed, 553 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index 9c93c60..b7c9e85 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -124,6 +124,7 @@ import org.apache.ambari.server.state.ServiceComponentHostFactory;
 import org.apache.ambari.server.state.ServiceComponentImpl;
 import org.apache.ambari.server.state.ServiceFactory;
 import org.apache.ambari.server.state.ServiceImpl;
+import org.apache.ambari.server.state.UpgradeContextFactory;
 import org.apache.ambari.server.state.cluster.ClusterFactory;
 import org.apache.ambari.server.state.cluster.ClusterImpl;
 import org.apache.ambari.server.state.cluster.ClustersImpl;
@@ -477,6 +478,7 @@ public class ControllerModule extends AbstractModule {
     install(new FactoryModuleBuilder().build(StackManagerFactory.class));
     install(new FactoryModuleBuilder().build(ExecutionCommandWrapperFactory.class));
     install(new FactoryModuleBuilder().build(MetricPropertyProviderFactory.class));
+    install(new FactoryModuleBuilder().build(UpgradeContextFactory.class));
 
     bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
     bind(SecurityHelper.class).toInstance(SecurityHelperImpl.getInstance());

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 5191e83..4c01964 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -97,6 +97,7 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.UpgradeContext;
+import org.apache.ambari.server.state.UpgradeContextFactory;
 import org.apache.ambari.server.state.UpgradeHelper;
 import org.apache.ambari.server.state.UpgradeHelper.UpgradeGroupHolder;
 import org.apache.ambari.server.state.stack.ConfigUpgradePack;
@@ -290,6 +291,13 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
   @Inject
   private static Gson s_gson;
 
+  /**
+   * Used to create instances of {@link UpgradeContext} with injected
+   * dependencies.
+   */
+  @Inject
+  private static UpgradeContextFactory s_upgradeContextFactory;
+
   static {
     // properties
     PROPERTY_IDS.add(UPGRADE_CLUSTER_NAME);
@@ -385,8 +393,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
           }
         }
 
-        final UpgradeContext upgradeContext = new UpgradeContext(cluster, upgradeType, direction,
-            requestMap);
+        final UpgradeContext upgradeContext = s_upgradeContextFactory.create(cluster, upgradeType,
+            direction, requestMap);
 
         UpgradePack upgradePack = validateRequest(upgradeContext);
         upgradeContext.setUpgradePack(upgradePack);
@@ -1475,7 +1483,6 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     }
 
     s_commandExecutionHelper.get().addExecutionCommandsToStage(actionContext, stage, requestParams);
-
     request.addStages(Collections.singletonList(stage));
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/stageplanner/RoleGraph.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stageplanner/RoleGraph.java b/ambari-server/src/main/java/org/apache/ambari/server/stageplanner/RoleGraph.java
index 404e4ff..65a86c0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stageplanner/RoleGraph.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stageplanner/RoleGraph.java
@@ -18,13 +18,16 @@
 package org.apache.ambari.server.stageplanner;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
+import org.apache.ambari.server.Role;
 import org.apache.ambari.server.RoleCommand;
 import org.apache.ambari.server.actionmanager.CommandExecutionType;
 import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
@@ -48,13 +51,20 @@ public class RoleGraph {
   @Inject
   private StageFactory stageFactory;
 
+  /**
+   * Used for created {@link HostRoleCommand}s when building structures to
+   * represent an ordered set of stages.
+   */
+  @Inject
+  private HostRoleCommandFactory hrcFactory;
+
   @AssistedInject
   public RoleGraph() {
   }
 
   @AssistedInject
   public RoleGraph(@Assisted RoleCommandOrder rd) {
-    this.roleDependencies = rd;
+    roleDependencies = rd;
   }
 
   public CommandExecutionType getCommandExecutionType() {
@@ -67,15 +77,41 @@ public class RoleGraph {
 
   /**
    * Given a stage builds a DAG of all execution commands within the stage.
+   *
+   * @see #getStages()
    */
   public void build(Stage stage) {
     if (stage == null) {
       throw new IllegalArgumentException("Null stage");
     }
-    graph = new TreeMap<String, RoleGraphNode>();
+
+    if (commandExecutionType == CommandExecutionType.DEPENDENCY_ORDERED) {
+      LOG.info("Build stage with DEPENDENCY_ORDERED commandExecutionType: {} ",
+          stage.getRequestContext());
+    }
+
     initialStage = stage;
 
     Map<String, Map<String, HostRoleCommand>> hostRoleCommands = stage.getHostRoleCommands();
+    build(hostRoleCommands);
+  }
+
+  /**
+   * Initializes {@link #graph} with the supplied unordered commands. The
+   * commands specified are in the following format: Input:
+   * 
+   * <pre>
+   * {c6401={NAMENODE=STOP}, c6402={DATANODE=STOP}, NODEMANAGER=STOP}}
+   * </pre>
+   *
+   * @param hostRoleCommands
+   *          the unordered commands to build a DAG from. The map is keyed first
+   *          by host and the for each host it is keyed by {@link Role} to
+   *          {@link RoleCommand}.
+   */
+  private void build(Map<String, Map<String, HostRoleCommand>> hostRoleCommands) {
+    graph = new TreeMap<String, RoleGraphNode>();
+
     for (String host : hostRoleCommands.keySet()) {
       for (String role : hostRoleCommands.get(host).keySet()) {
         HostRoleCommand hostRoleCommand = hostRoleCommands.get(host).get(role);
@@ -110,10 +146,9 @@ public class RoleGraph {
           }
         }
       }
-    } else {
-      LOG.info("Build stage with DEPENDENCY_ORDERED commandExecutionType: {} ", stage.getRequestContext());
     }
   }
+
   /**
    * This method return more detailed RoleCommand type. For now, i've added code
    * only for RESTART name of CUSTOM COMMAND, but in future i think all other will be added too.
@@ -136,7 +171,7 @@ public class RoleGraph {
     List<RoleGraphNode> firstStageNodes = new ArrayList<RoleGraphNode>();
     while (!graph.isEmpty()) {
       if (LOG.isDebugEnabled()) {
-        LOG.debug(this.stringifyGraph());
+        LOG.debug(stringifyGraph());
       }
 
       for (String role: graph.keySet()) {
@@ -151,7 +186,7 @@ public class RoleGraph {
       //Remove first stage nodes from the graph, we know that none of
       //these nodes have an incoming edges.
       for (RoleGraphNode rgn : firstStageNodes) {
-        if (this.sameHostOptimization) {
+        if (sameHostOptimization) {
           //Perform optimization
         }
         removeZeroInDegreeNode(rgn.getRole().toString());
@@ -162,11 +197,82 @@ public class RoleGraph {
   }
 
   /**
+   * Gets a representation of the role ordering of the specified commands
+   * without constructing {@link Stage} instances. The commands to order are
+   * supplied as mapping of host to role/command. Each item of the returned list
+   * represents a single stage. The map is of host to commands. For example:
+   * <br/>
+   * <br/>
+   * Input:
+   * <pre>
+   * {c6401={NAMENODE=STOP}, c6402={DATANODE=STOP}, NODEMANAGER=STOP}}
+   * </pre>
+   *
+   * Output:
+   * <pre>
+   * [{c6402=[NODEMANAGER/STOP, DATANODE-STOP]}, c6401=[NAMENODE/STOP]]
+   *
+   * <pre>
+   *
+   * @param unorderedCommands
+   *          a mapping of {@link Role} to {@link HostRoleCommand} by host.
+   * @return and ordered list where each item represents a single stage and each
+   *         stage's commands are mapped by host.
+   */
+  public List<Map<String, List<HostRoleCommand>>> getOrderedHostRoleCommands(
+      Map<String, Map<String, HostRoleCommand>> unorderedCommands) {
+    build(unorderedCommands);
+
+    // represents an ordered list of stages
+    List<Map<String, List<HostRoleCommand>>> orderedCommands = new ArrayList<>();
+
+    List<RoleGraphNode> firstStageNodes = new ArrayList<RoleGraphNode>();
+    while (!graph.isEmpty()) {
+      for (String role : graph.keySet()) {
+        RoleGraphNode rgn = graph.get(role);
+        if (rgn.getInDegree() == 0) {
+          firstStageNodes.add(rgn);
+        }
+      }
+
+      // represents a stage
+      Map<String, List<HostRoleCommand>> commandsPerHost = new HashMap<>();
+
+      for (RoleGraphNode rgn : firstStageNodes) {
+        // for every host for this stage, create the ordered commands
+        for (String host : rgn.getHosts()) {
+          List<HostRoleCommand> commands = commandsPerHost.get(host);
+          if (null == commands) {
+            commands = new ArrayList<>();
+            commandsPerHost.put(host, commands);
+          }
+
+          HostRoleCommand hrc = hrcFactory.create(host, rgn.getRole(), null, rgn.getCommand());
+          commands.add(hrc);
+        }
+      }
+
+      // add the stage to the list of stages
+      orderedCommands.add(commandsPerHost);
+
+      // Remove first stage nodes from the graph, we know that none of
+      // these nodes have an incoming edges.
+      for (RoleGraphNode rgn : firstStageNodes) {
+        removeZeroInDegreeNode(rgn.getRole().toString());
+      }
+
+      firstStageNodes.clear();
+    }
+
+    return orderedCommands;
+  }
+
+  /**
    * Assumes there are no incoming edges.
    */
   private synchronized void removeZeroInDegreeNode(String role) {
     RoleGraphNode nodeToRemove = graph.remove(role);
-    for (RoleGraphNode edgeNode: nodeToRemove.getEdges()) {
+    for (RoleGraphNode edgeNode : nodeToRemove.getEdges()) {
       edgeNode.decrementInDegree();
     }
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/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 c6ae050..4e37c92 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
@@ -27,6 +27,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
+import org.apache.ambari.server.metadata.RoleCommandOrder;
 import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
@@ -738,4 +739,11 @@ public interface Cluster {
    * @return number of hosts that form the cluster
    */
   int  getClusterSize();
+
+  /**
+   * Gets a new instance of a {@link RoleCommandOrder} for this cluster.
+   *
+   * @return the role command order instance (not {@code null}).
+   */
+  RoleCommandOrder getRoleCommandOrder();
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
index 8e7e5de..1d51b0d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContext.java
@@ -26,13 +26,19 @@ import java.util.Set;
 
 import org.apache.ambari.annotations.Experimental;
 import org.apache.ambari.annotations.ExperimentalFeature;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.stack.MasterHostResolver;
+import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.stack.UpgradePack;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.ambari.server.state.stack.upgrade.Grouping;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeScope;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
 /**
  * Used to hold various helper objects required to process an upgrade pack.
  */
@@ -121,6 +127,20 @@ public class UpgradeContext {
   private UpgradeScope m_scope = UpgradeScope.ANY;
 
   /**
+   * Used by some {@link Grouping}s to generate commands. It is exposed here
+   * mainly for injection purposes since the XML is not created by Guice.
+   */
+  @Inject
+  private HostRoleCommandFactory m_hrcFactory;
+
+  /**
+   * Used by some {@link Grouping}s to determine command ordering. It is exposed
+   * here mainly for injection purposes since the XML is not created by Guice.
+   */
+  @Inject
+  private RoleGraphFactory m_roleGraphFactory;
+
+  /**
    * Constructor.
    *
    * @param cluster
@@ -132,8 +152,9 @@ public class UpgradeContext {
    * @param upgradeRequestMap
    *          the original map of paramters used to create the upgrade
    */
-  public UpgradeContext(Cluster cluster, UpgradeType type, Direction direction,
-      Map<String, Object> upgradeRequestMap) {
+  @Inject
+  public UpgradeContext(@Assisted Cluster cluster, @Assisted UpgradeType type,
+      @Assisted Direction direction, @Assisted Map<String, Object> upgradeRequestMap) {
     m_cluster = cluster;
     m_type = type;
     m_direction = direction;
@@ -482,4 +503,22 @@ public class UpgradeContext {
   public boolean isScoped(UpgradeScope scope) {
     return m_scope.isScoped(scope);
   }
+
+  /**
+   * Gets the injected instance of a {@link RoleGraphFactory}.
+   *
+   * @return a {@link RoleGraphFactory} instance (never {@code null}).
+   */
+  public RoleGraphFactory getRoleGraphFactory() {
+    return m_roleGraphFactory;
+  }
+
+  /**
+   * Gets the injected instance of a {@link HostRoleCommandFactory}.
+   *
+   * @return a {@link HostRoleCommandFactory} instance (never {@code null}).
+   */
+  public HostRoleCommandFactory getHostRoleCommandFactory() {
+    return m_hrcFactory;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContextFactory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContextFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContextFactory.java
new file mode 100644
index 0000000..4b988e8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeContextFactory.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.state;
+
+import java.util.Map;
+
+import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
+
+/**
+ * The {@link UpgradeContextFactory} is used to create dependency-injected
+ * instances of {@link UpgradeContext}s.
+ */
+public interface UpgradeContextFactory {
+
+  /**
+   * Creates an {@link UpgradeContext} which is injected with dependencies.
+   *
+   * @param cluster
+   *          the cluster that the upgrade is for
+   * @param type
+   *          the type of upgrade, either rolling or non_rolling
+   * @param direction
+   *          the direction for the upgrade
+   * @param upgradeRequestMap
+   *          the original map of paramters used to create the upgrade
+   *
+   * @return an initialized {@link UpgradeContext}.
+   */
+  UpgradeContext create(Cluster cluster, UpgradeType type, Direction direction,
+      Map<String, Object> upgradeRequestMap);
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/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 46e2f8e..6455d6e 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
@@ -47,6 +47,7 @@ import org.apache.ambari.server.ConfigGroupNotFoundException;
 import org.apache.ambari.server.DuplicateResourceException;
 import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.ParentObjectNotFoundException;
+import org.apache.ambari.server.RoleCommand;
 import org.apache.ambari.server.ServiceComponentHostNotFoundException;
 import org.apache.ambari.server.ServiceComponentNotFoundException;
 import org.apache.ambari.server.ServiceNotFoundException;
@@ -68,6 +69,8 @@ import org.apache.ambari.server.events.jpa.JPAEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.JPAEventPublisher;
 import org.apache.ambari.server.logging.LockFactory;
+import org.apache.ambari.server.metadata.RoleCommandOrder;
+import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
 import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.cache.HostConfigMapping;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
@@ -307,6 +310,12 @@ public class ClusterImpl implements Cluster {
   private JPAEventPublisher jpaEventPublisher;
 
   /**
+   * Used for getting instances of {@link RoleCommand} for this cluster.
+   */
+  @Inject
+  private RoleCommandOrderProvider roleCommandOrderProvider;
+
+  /**
    * A simple cache for looking up {@code cluster-env} properties for a cluster.
    * This map is changed whenever {{cluster-env}} is changed and we receive a
    * {@link ClusterConfigChangedEvent}.
@@ -3474,4 +3483,12 @@ public class ClusterImpl implements Cluster {
 
     m_clusterPropertyCache.clear();
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public RoleCommandOrder getRoleCommandOrder() {
+    return roleCommandOrderProvider.getRoleCommandOrder(this);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderGrouping.java
index 5d723f5..abb2aab 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderGrouping.java
@@ -19,13 +19,22 @@ package org.apache.ambari.server.state.stack.upgrade;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import javax.xml.bind.annotation.XmlType;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.Role;
+import org.apache.ambari.server.RoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.metadata.RoleCommandOrder;
 import org.apache.ambari.server.stack.HostsType;
+import org.apache.ambari.server.stageplanner.RoleGraph;
+import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.ServiceComponentHost;
@@ -114,9 +123,16 @@ public class HostOrderGrouping extends Grouping {
     }
 
     /**
-     * @param upgradeContext  the context
-     * @param hosts           the list of hostnames
-     * @return  the wrappers for a host
+     * Builds the stages for each host which typically consist of a STOP, a
+     * manual wait, and a START. The starting of components can be a single
+     * stage or may consist of several stages if the host components have
+     * dependencies on each other.
+     *
+     * @param upgradeContext
+     *          the context
+     * @param hosts
+     *          the list of hostnames
+     * @return the wrappers for a host
      */
     private List<StageWrapper> buildHosts(UpgradeContext upgradeContext, List<String> hosts) {
       if (CollectionUtils.isEmpty(hosts)) {
@@ -126,11 +142,20 @@ public class HostOrderGrouping extends Grouping {
       Cluster cluster = upgradeContext.getCluster();
       List<StageWrapper> wrappers = new ArrayList<>();
 
-      for (String hostName : hosts) {
+      HostRoleCommandFactory hrcFactory = upgradeContext.getHostRoleCommandFactory();
 
+      for (String hostName : hosts) {
+        // initialize the collection for all stop tasks for every component on
+        // the host
         List<TaskWrapper> stopTasks = new ArrayList<>();
-        List<TaskWrapper> upgradeTasks = new ArrayList<>();
 
+        // initialize the collection which will be passed into the RoleGraph for
+        // ordering
+        Map<String, Map<String, HostRoleCommand>> restartCommandsForHost = new HashMap<>();
+        Map<String, HostRoleCommand> restartCommandsByRole = new HashMap<>();
+        restartCommandsForHost.put(hostName, restartCommandsByRole);
+
+        // iterating over every host component, build the commands
         for (ServiceComponentHost sch : cluster.getServiceComponentHosts(hostName)) {
           if (!isVersionAdvertised(upgradeContext, sch)) {
             continue;
@@ -149,31 +174,90 @@ public class HostOrderGrouping extends Grouping {
             continue;
           }
 
+          // create a STOP task for this host component
           if (!sch.isClientComponent()) {
             stopTasks.add(new TaskWrapper(sch.getServiceName(), sch.getServiceComponentName(),
                 Collections.singleton(hostName), new StopTask()));
           }
 
-          // !!! simple restart will do
-          upgradeTasks.add(new TaskWrapper(sch.getServiceName(), sch.getServiceComponentName(),
-              Collections.singleton(hostName), new RestartTask()));
+          // generate a placeholder HRC that can be used to generate the
+          // dependency graph - we must use START here since that's what the
+          // role command order is defined with - each of these will turn into a
+          // RESTART when we create the wrappers later on
+          Role role = Role.valueOf(sch.getServiceComponentName());
+          HostRoleCommand hostRoleCommand = hrcFactory.create(hostName, role, null,
+              RoleCommand.START);
+
+          // add the newly created HRC RESTART
+          restartCommandsByRole.put(role.name(), hostRoleCommand);
         }
 
-        if (stopTasks.isEmpty() && upgradeTasks.isEmpty()) {
-          LOG.info("No tasks for {}", hostName);
+        // short circuit and move to the next host if there are no commands
+        if (stopTasks.isEmpty() && restartCommandsByRole.isEmpty()) {
+          LOG.info("There were no {} commands generated for {}",
+              upgradeContext.getDirection().getText(false), hostName);
+
           continue;
         }
 
+        // build the single STOP stage
         StageWrapper stopWrapper = new StageWrapper(StageWrapper.Type.STOP, String.format("Stop on %s", hostName),
             stopTasks.toArray(new TaskWrapper[stopTasks.size()]));
 
-        StageWrapper startWrapper = new StageWrapper(StageWrapper.Type.RESTART, String.format("Start on %s", hostName),
-            upgradeTasks.toArray(new TaskWrapper[upgradeTasks.size()]));
+        // now process the HRCs created so that we can create the appropriate
+        // stage/task wrappers for the RESTARTs
+        RoleGraphFactory roleGraphFactory = upgradeContext.getRoleGraphFactory();
+        RoleCommandOrder roleCommandOrder = cluster.getRoleCommandOrder();
+        RoleGraph roleGraph = roleGraphFactory.createNew(roleCommandOrder);
+        List<Map<String, List<HostRoleCommand>>> stages = roleGraph.getOrderedHostRoleCommands(
+            restartCommandsForHost);
+
+        // initialize the list of stage wrappers
+        List<StageWrapper> stageWrappers = new ArrayList<>();
+
+        // for every stage, create a stage wrapper around the tasks
+        int phaseCounter = 1;
+        for (Map<String, List<HostRoleCommand>> stage : stages) {
+          List<HostRoleCommand> stageCommandsForHost = stage.get(hostName);
+          String stageTitle = String.format("Starting components on %s (phase %d)", hostName,
+              phaseCounter++);
+
+          // create task wrappers
+          List<TaskWrapper> taskWrappers = new ArrayList<>();
+          for (HostRoleCommand command : stageCommandsForHost) {
+            StackId stackId = upgradeContext.getEffectiveStackId();
+            String componentName = command.getRole().name();
+
+            String serviceName = null;
+
+            try {
+              AmbariMetaInfo ambariMetaInfo = upgradeContext.getAmbariMetaInfo();
+              serviceName = ambariMetaInfo.getComponentToService(stackId.getStackName(),
+                  stackId.getStackVersion(), componentName);
+            } catch (AmbariException ambariException) {
+              LOG.error("Unable to lookup service by component {} for stack {}-{}", componentName,
+                  stackId.getStackName(), stackId.getStackVersion());
+            }
+
+            TaskWrapper taskWrapper = new TaskWrapper(serviceName, componentName,
+                Collections.singleton(hostName), new RestartTask());
+
+            taskWrappers.add(taskWrapper);
+          }
 
-        String message = String.format("Please acknowledge that host %s has been prepared.", hostName);
+          if (!taskWrappers.isEmpty()) {
+            StageWrapper startWrapper = new StageWrapper(StageWrapper.Type.RESTART, stageTitle,
+                taskWrappers.toArray(new TaskWrapper[taskWrappers.size()]));
+
+            stageWrappers.add(startWrapper);
+          }
+        }
 
+        // create the manual task between the STOP and START stages
         ManualTask mt = new ManualTask();
+        String message = String.format("Please acknowledge that host %s has been prepared.", hostName);
         mt.messages.add(message);
+
         JsonObject structuredOut = new JsonObject();
         structuredOut.addProperty(TYPE, HostOrderItem.HostOrderActionType.HOST_UPGRADE.toString());
         structuredOut.addProperty(HOST, hostName);
@@ -184,9 +268,9 @@ public class HostOrderGrouping extends Grouping {
 
         wrappers.add(stopWrapper);
         wrappers.add(manualWrapper);
-        // !!! TODO install_packages for hdp and conf-select changes.  Hopefully these will no-op.
-        wrappers.add(startWrapper);
 
+        // !!! TODO install_packages for hdp and conf-select changes.  Hopefully these will no-op.
+        wrappers.addAll(stageWrappers);
       }
 
       return wrappers;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
index 5ec7ddb..669d50f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.common.base.Objects;
 import com.google.gson.Gson;
 
 /**
@@ -152,4 +153,14 @@ public class StageWrapper {
     START,
     CONFIGURE
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return Objects.toStringHelper(this).add("type", type)
+        .add("text",text)
+        .omitNullValues().toString();
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
index 5fdf91c..11e27cf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
@@ -23,6 +23,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.common.base.Objects;
+
 /**
  * Aggregates all upgrade tasks for a HostComponent into one wrapper.
  */
@@ -43,7 +45,7 @@ public class TaskWrapper {
   public TaskWrapper(String s, String c, Set<String> hosts, Task... tasks) {
     this(s, c, hosts, null, Arrays.asList(tasks));
   }
-  
+
   /**
    * @param s the service name for the tasks
    * @param c the component name for the tasks
@@ -92,10 +94,16 @@ public class TaskWrapper {
     return hosts;
   }
 
-
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public String toString() {
-    return service + ":" + component + ":" + tasks + ":" + hosts;
+    return Objects.toStringHelper(this).add("service", service)
+        .add("component", component)
+        .add("tasks", tasks)
+        .add("hosts", hosts)
+        .omitNullValues().toString();
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
index 674025c..17b1e27 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
@@ -36,6 +36,8 @@ import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.agent.rest.AgentResource;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
+import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
@@ -316,6 +318,7 @@ public class AgentResourceTest extends RandomPortJerseyTest {
       bind(HostDAO.class).toInstance(createNiceMock(HostDAO.class));
       bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
       bind(PersistedState.class).toInstance(createNiceMock(PersistedState.class));
+      bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
     }
 
     private void installDependencies() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index 8a70f0c..91cd608 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -58,6 +58,8 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.Role;
 import org.apache.ambari.server.actionmanager.ActionManager;
 import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
 import org.apache.ambari.server.actionmanager.RequestFactory;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
@@ -70,7 +72,9 @@ import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.RequestStageContainer;
 import org.apache.ambari.server.controller.spi.ClusterController;
 import org.apache.ambari.server.controller.utilities.KerberosChecker;
+import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
+import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.ArtifactDAO;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
@@ -247,6 +251,8 @@ public class KerberosHelperTest extends EasyMockSupport {
         bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
         bind(ArtifactDAO.class).toInstance(createNiceMock(ArtifactDAO.class));
         bind(KerberosPrincipalDAO.class).toInstance(createNiceMock(KerberosPrincipalDAO.class));
+        bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
+        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
 
         requestStaticInjection(KerberosChecker.class);
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ActiveWidgetLayoutResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ActiveWidgetLayoutResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ActiveWidgetLayoutResourceProviderTest.java
index 4b3782f..5cce3fc 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ActiveWidgetLayoutResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ActiveWidgetLayoutResourceProviderTest.java
@@ -34,6 +34,8 @@ import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.actionmanager.ActionDBAccessor;
 import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
@@ -405,6 +407,7 @@ public class ActiveWidgetLayoutResourceProviderTest extends EasyMockSupport {
         bind(HostRoleCommandDAO.class).toInstance(createMock(HostRoleCommandDAO.class));
         bind(HookContextFactory.class).toInstance(createMock(HookContextFactory.class));
         bind(HookService.class).toInstance(createMock(HookService.class));
+        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
       }
     });
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackUpgradeConfigurationMergeTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackUpgradeConfigurationMergeTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackUpgradeConfigurationMergeTest.java
index 27d3d7b..1c45589 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackUpgradeConfigurationMergeTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackUpgradeConfigurationMergeTest.java
@@ -24,6 +24,8 @@ import java.util.Map;
 import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
 import org.apache.ambari.server.actionmanager.RequestFactory;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -53,6 +55,7 @@ import org.apache.ambari.server.state.ServiceComponentHostFactory;
 import org.apache.ambari.server.state.ServiceFactory;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.UpgradeContextFactory;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
 import org.apache.ambari.server.state.stack.OsFamily;
@@ -295,7 +298,8 @@ public class StackUpgradeConfigurationMergeTest extends EasyMockSupport {
       binder.bind(RepositoryVersionDAO.class).toInstance(createNiceMock(RepositoryVersionDAO.class));
       binder.bind(HookContextFactory.class).toInstance(createMock(HookContextFactory.class));
       binder.bind(HookService.class).toInstance(createMock(HookService.class));
-
+      binder.install(new FactoryModuleBuilder().build(UpgradeContextFactory.class));
+      binder.bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
 
       binder.requestStaticInjection(UpgradeResourceProvider.class);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
index 37c48c3..fd96c8e 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
@@ -32,6 +32,8 @@ import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.actionmanager.ActionDBAccessor;
 import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
@@ -417,6 +419,7 @@ public class UserAuthorizationResourceProviderTest extends EasyMockSupport {
         bind(HostRoleCommandDAO.class).toInstance(createMock(HostRoleCommandDAO.class));
         bind(HookContextFactory.class).toInstance(createMock(HookContextFactory.class));
         bind(HookService.class).toInstance(createMock(HookService.class));
+        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
       }
     });
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
index b8e027f..cc0f2b6 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
@@ -34,6 +34,8 @@ import javax.persistence.EntityManager;
 
 import org.apache.ambari.server.actionmanager.ActionDBAccessor;
 import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactoryImpl;
 import org.apache.ambari.server.actionmanager.RequestFactory;
 import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -252,6 +254,7 @@ public class UserResourceProviderTest extends EasyMockSupport {
         bind(HostRoleCommandDAO.class).toInstance(createMock(HostRoleCommandDAO.class));
         bind(HookService.class).toInstance(createMock(HookService.class));
         bind(HookContextFactory.class).toInstance(createMock(HookContextFactory.class));
+        bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
       }
     });
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/metadata/RoleGraphTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/metadata/RoleGraphTest.java b/ambari-server/src/test/java/org/apache/ambari/server/metadata/RoleGraphTest.java
index 53686aa..f04efde 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/metadata/RoleGraphTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/metadata/RoleGraphTest.java
@@ -22,16 +22,25 @@ package org.apache.ambari.server.metadata;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.Role;
 import org.apache.ambari.server.RoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.apache.ambari.server.stageplanner.RoleGraph;
+import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphNode;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.cluster.ClusterImpl;
+import org.apache.hadoop.metrics2.sink.relocated.google.common.collect.Lists;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -46,12 +55,16 @@ public class RoleGraphTest {
 
   private Injector injector;
   private RoleCommandOrderProvider roleCommandOrderProvider;
+  private RoleGraphFactory roleGraphFactory;
+  private HostRoleCommandFactory hrcFactory;
 
   @Before
   public void setup() throws Exception {
     injector = Guice.createInjector(new InMemoryDefaultTestModule());
     injector.getInstance(GuiceJpaInitializer.class);
     roleCommandOrderProvider = injector.getInstance(RoleCommandOrderProvider.class);
+    roleGraphFactory = injector.getInstance(RoleGraphFactory.class);
+    hrcFactory = injector.getInstance(HostRoleCommandFactory.class);
   }
 
   @After
@@ -139,4 +152,64 @@ public class RoleGraphTest {
     Assert.assertEquals(1, rco.order(nn_start, zk_server_start));
     Assert.assertEquals(1, rco.order(zkfc_start, nn_start));
   }
+
+  /**
+   * Tests the ordering of
+   * {@link RoleGraph#getOrderedHostRoleCommands(java.util.Map)}.
+   *
+   * @throws AmbariException
+   */
+  @Test
+  public void testGetOrderedHostRoleCommands() throws AmbariException {
+    ClusterImpl cluster = mock(ClusterImpl.class);
+    when(cluster.getCurrentStackVersion()).thenReturn(new StackId("HDP-2.0.6"));
+    when(cluster.getClusterId()).thenReturn(1L);
+
+    RoleCommandOrder rco = roleCommandOrderProvider.getRoleCommandOrder(cluster);
+    RoleGraph roleGraph = roleGraphFactory.createNew(rco);
+
+    Map<String, Map<String, HostRoleCommand>> unorderedCommands = new HashMap<>();
+    Map<String, HostRoleCommand> c6401Commands = new HashMap<>();
+    Map<String, HostRoleCommand> c6402Commands = new HashMap<>();
+    Map<String, HostRoleCommand> c6403Commands = new HashMap<>();
+
+    HostRoleCommand hrcNameNode = hrcFactory.create("c6041", Role.NAMENODE, null, RoleCommand.START);
+    HostRoleCommand hrcZooKeeperHost1 = hrcFactory.create("c6041", Role.ZOOKEEPER_SERVER, null, RoleCommand.START);
+    HostRoleCommand hrcHBaseMaster = hrcFactory.create("c6042", Role.HBASE_MASTER, null, RoleCommand.START);
+    HostRoleCommand hrcZooKeeperHost3 = hrcFactory.create("c6043", Role.ZOOKEEPER_SERVER, null, RoleCommand.START);
+
+    c6401Commands.put(hrcNameNode.getRole().name(), hrcNameNode);
+    c6401Commands.put(hrcZooKeeperHost1.getRole().name(), hrcZooKeeperHost1);
+    c6402Commands.put(hrcHBaseMaster.getRole().name(), hrcHBaseMaster);
+    c6403Commands.put(hrcZooKeeperHost3.getRole().name(), hrcZooKeeperHost3);
+
+    unorderedCommands.put("c6401", c6401Commands);
+    unorderedCommands.put("c6402", c6402Commands);
+    unorderedCommands.put("c6403", c6403Commands);
+
+    List<Map<String, List<HostRoleCommand>>> stages = roleGraph.getOrderedHostRoleCommands(unorderedCommands);
+
+    Assert.assertEquals(2, stages.size());
+
+    Map<String, List<HostRoleCommand>> stage1 = stages.get(0);
+    Map<String, List<HostRoleCommand>> stage2 = stages.get(1);
+
+    Assert.assertEquals(2, stage1.size());
+    Assert.assertEquals(1, stage2.size());
+
+    List<HostRoleCommand> stage1CommandsHost1 = stage1.get("c6401");
+    List<HostRoleCommand> stage1CommandsHost3 = stage1.get("c6403");
+    List<HostRoleCommand> stage2CommandsHost2 = stage2.get("c6402");
+
+    Assert.assertEquals(3, stage1CommandsHost1.size() + stage1CommandsHost3.size());
+    Assert.assertEquals(1, stage2CommandsHost2.size());
+
+    List<Role> stage1Roles = Lists.newArrayList(stage1CommandsHost1.get(0).getRole(),
+        stage1CommandsHost1.get(1).getRole(), stage1CommandsHost3.get(0).getRole());
+
+    Assert.assertTrue(stage1Roles.contains(Role.NAMENODE));
+    Assert.assertTrue(stage1Roles.contains(Role.ZOOKEEPER_SERVER));
+    Assert.assertEquals(Role.ZOOKEEPER_SERVER, stage1CommandsHost3.get(0).getRole());
+    Assert.assertEquals(Role.HBASE_MASTER, stage2CommandsHost2.get(0).getRole());
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
index ea1f18a..0d1a2fa 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/UpgradeHelperTest.java
@@ -107,6 +107,7 @@ public class UpgradeHelperTest {
   private ConfigHelper m_configHelper;
   private AmbariManagementController m_managementController;
   private Gson m_gson = new Gson();
+  private UpgradeContextFactory m_upgradeContextFactory;
 
   /**
    * Because test cases need to share config mocks, put common ones in this function.
@@ -147,6 +148,7 @@ public class UpgradeHelperTest {
     m_upgradeHelper = injector.getInstance(UpgradeHelper.class);
     m_masterHostResolver = EasyMock.createMock(MasterHostResolver.class);
     m_managementController = injector.getInstance(AmbariManagementController.class);
+    m_upgradeContextFactory = injector.getInstance(UpgradeContextFactory.class);
 
     // Set the authenticated user
     // TODO: remove this or replace the authenticated user to test authorization rules
@@ -1879,9 +1881,15 @@ public class UpgradeHelperTest {
     assertTrue(groups.isEmpty());
   }
 
+  /**
+   * Tests {@link UpgradeType#HOST_ORDERED}, specifically that the orchestration
+   * can properly expand the single {@link HostOrderGrouping} and create the
+   * correct stages based on the dependencies of the components.
+   *
+   * @throws Exception
+   */
   @Test
   public void testHostGroupingOrchestration() throws Exception {
-
     Clusters clusters = injector.getInstance(Clusters.class);
     ServiceFactory serviceFactory = injector.getInstance(ServiceFactory.class);
 
@@ -1894,12 +1902,13 @@ public class UpgradeHelperTest {
 
     helper.getOrCreateRepositoryVersion(stackId,
         c.getDesiredStackVersion().getStackVersion());
-    helper.getOrCreateRepositoryVersion(stackId2,"2.2.0");
+    helper.getOrCreateRepositoryVersion(stackId2, "2.2.0");
 
     c.createClusterVersion(stackId,
         c.getDesiredStackVersion().getStackVersion(), "admin",
         RepositoryVersionState.INSTALLING);
 
+    // create 2 hosts
     for (int i = 0; i < 2; i++) {
       String hostName = "h" + (i+1);
       clusters.addHost(hostName);
@@ -1914,19 +1923,24 @@ public class UpgradeHelperTest {
       clusters.mapHostToCluster(hostName, clusterName);
     }
 
-    // !!! add storm
+    // add ZK Server to both hosts, and then Nimbus to only 1 - this will test
+    // how the HOU breaks out dependencies into stages
     c.addService(serviceFactory.createNew(c, "ZOOKEEPER"));
-
-    Service s = c.getService("ZOOKEEPER");
-    ServiceComponent sc = s.addServiceComponent("ZOOKEEPER_SERVER");
-    ServiceComponentHost sch1 = sc.addServiceComponentHost("h1");
-    ServiceComponentHost sch2 = sc.addServiceComponentHost("h2");
+    c.addService(serviceFactory.createNew(c, "HBASE"));
+    Service zookeeper = c.getService("ZOOKEEPER");
+    Service hbase = c.getService("HBASE");
+    ServiceComponent zookeeperServer = zookeeper.addServiceComponent("ZOOKEEPER_SERVER");
+    ServiceComponentHost zookeeperServer1 = zookeeperServer.addServiceComponentHost("h1");
+    ServiceComponentHost zookeeperServer2 = zookeeperServer.addServiceComponentHost("h2");
+    ServiceComponent hbaseMaster = hbase.addServiceComponent("HBASE_MASTER");
+    ServiceComponentHost hbaseMaster1 = hbaseMaster.addServiceComponentHost("h1");
 
     // !!! make a custom grouping
     HostOrderItem hostItem = new HostOrderItem(HostOrderActionType.HOST_UPGRADE,
         Lists.newArrayList("h1", "h2"));
+
     HostOrderItem checkItem = new HostOrderItem(HostOrderActionType.SERVICE_CHECK,
-        Lists.newArrayList("ZOOKEEPER", "STORM"));
+        Lists.newArrayList("ZOOKEEPER", "HBASE"));
 
     Grouping g = new HostOrderGrouping();
     ((HostOrderGrouping) g).setHostOrderItems(Lists.newArrayList(hostItem, checkItem));
@@ -1943,9 +1957,11 @@ public class UpgradeHelperTest {
     field.setAccessible(true);
     field.set(upgradePack, UpgradeType.HOST_ORDERED);
 
-
     MasterHostResolver resolver = new MasterHostResolver(m_configHelper, c);
-    UpgradeContext context = new UpgradeContext(c, UpgradeType.HOST_ORDERED, Direction.UPGRADE, new HashMap<String, Object>());
+
+    UpgradeContext context = m_upgradeContextFactory.create(c, UpgradeType.HOST_ORDERED,
+        Direction.UPGRADE, new HashMap<String, Object>());
+
     context.setResolver(resolver);
     context.setSourceAndTargetStacks(stackId, stackId2);
     context.setVersion("2.2.0");
@@ -1954,13 +1970,13 @@ public class UpgradeHelperTest {
     assertEquals(1, groups.size());
 
     UpgradeGroupHolder holder = groups.get(0);
-    assertEquals(7, holder.items.size());
+    assertEquals(9, holder.items.size());
 
-    for (int i = 0; i < 6; i++) {
+    for (int i = 0; i < 7; i++) {
       StageWrapper w = holder.items.get(i);
-      if (i == 0 || i == 3) {
+      if (i == 0 || i == 4) {
         assertEquals(StageWrapper.Type.STOP, w.getType());
-      } else if (i == 1 || i == 4) {
+      } else if (i == 1 || i == 5) {
         assertEquals(StageWrapper.Type.SERVER_SIDE_ACTION, w.getType());
         assertEquals(1, w.getTasks().size());
         assertEquals(1, w.getTasks().get(0).getTasks().size());
@@ -1976,33 +1992,43 @@ public class UpgradeHelperTest {
         assertEquals(StageWrapper.Type.RESTART, w.getType());
       }
     }
-    assertEquals(StageWrapper.Type.SERVICE_CHECK, holder.items.get(6).getType());
+
+    assertEquals(StageWrapper.Type.SERVICE_CHECK, holder.items.get(7).getType());
+    assertEquals(StageWrapper.Type.SERVICE_CHECK, holder.items.get(8).getType());
 
     // !!! test downgrade when all host components have failed
-    sch1.setVersion("2.1.1");
-    sch2.setVersion("2.1.1");
+    zookeeperServer1.setVersion("2.1.1");
+    zookeeperServer2.setVersion("2.1.1");
+    hbaseMaster1.setVersion("2.1.1");
     resolver = new MasterHostResolver(m_configHelper, c, "2.1.1");
-    context = new UpgradeContext(c, UpgradeType.HOST_ORDERED, Direction.DOWNGRADE, new HashMap<String, Object>());
+
+    m_upgradeContextFactory.create(c, UpgradeType.HOST_ORDERED, Direction.DOWNGRADE,
+        new HashMap<String, Object>());
+
     context.setResolver(resolver);
     context.setSourceAndTargetStacks(stackId2, stackId);
     context.setVersion("2.1.1");
     groups = m_upgradeHelper.createSequence(upgradePack, context);
 
     assertEquals(1, groups.size());
-    assertEquals(1, groups.get(0).items.size());
+    assertEquals(2, groups.get(0).items.size());
 
     // !!! test downgrade when one of the hosts had failed
-    sch1.setVersion("2.1.1");
-    sch2.setVersion("2.2.0");
+    zookeeperServer1.setVersion("2.1.1");
+    zookeeperServer2.setVersion("2.2.0");
+    hbaseMaster1.setVersion("2.1.1");
     resolver = new MasterHostResolver(m_configHelper, c, "2.1.1");
-    context = new UpgradeContext(c, UpgradeType.HOST_ORDERED, Direction.DOWNGRADE, new HashMap<String, Object>());
+
+    m_upgradeContextFactory.create(c, UpgradeType.HOST_ORDERED, Direction.DOWNGRADE,
+        new HashMap<String, Object>());
+
     context.setResolver(resolver);
     context.setSourceAndTargetStacks(stackId2, stackId);
     context.setVersion("2.1.1");
     groups = m_upgradeHelper.createSequence(upgradePack, context);
 
     assertEquals(1, groups.size());
-    assertEquals(4, groups.get(0).items.size());
+    assertEquals(5, groups.get(0).items.size());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java
index 8ba891a..d01249d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterEffectiveVersionTest.java
@@ -34,6 +34,8 @@ import org.apache.ambari.server.controller.spi.ClusterController;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
+import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
+import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
@@ -277,6 +279,8 @@ public class ClusterEffectiveVersionTest extends EasyMockSupport {
       binder.install(new FactoryModuleBuilder().implement(
           Cluster.class, ClusterImpl.class).build(ClusterFactory.class));
 
+      binder.bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
+
       try {
         AmbariMetaInfo ambariMetaInfo = EasyMock.createNiceMock(AmbariMetaInfo.class);
         EasyMock.expect(

http://git-wip-us.apache.org/repos/asf/ambari/blob/e10f6922/ambari-server/src/test/resources/stacks/HDP/2.1.1/services/HBASE/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.1.1/services/HBASE/metainfo.xml b/ambari-server/src/test/resources/stacks/HDP/2.1.1/services/HBASE/metainfo.xml
new file mode 100644
index 0000000..5725d57
--- /dev/null
+++ b/ambari-server/src/test/resources/stacks/HDP/2.1.1/services/HBASE/metainfo.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<metainfo>
+  <schemaVersion>2.0</schemaVersion>
+  <services>
+    <service>
+      <name>HBASE</name>
+      <comment>Non-relational distributed database and centralized service for configuration management &amp; synchronization</comment>
+      <version>0.95.2.2.0.5.0</version>
+
+      <components>
+        <component>
+          <name>HBASE_MASTER</name>
+          <versionAdvertised>true</versionAdvertised>
+        </component>
+
+        <component>
+          <name>HBASE_REGIONSERVER</name>
+          <versionAdvertised>true</versionAdvertised>
+        </component>
+
+        <component>
+          <name>HBASE_CLIENT</name>
+          <versionAdvertised>false</versionAdvertised>
+        </component>
+      </components>
+    </service>
+  </services>
+</metainfo>