ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jonathanhur...@apache.org
Subject ambari git commit: AMBARI-18685 - Add validations and attributes to support HOST_ORDERED upgrades (jonathanhurley)
Date Wed, 02 Nov 2016 19:26:21 GMT
Repository: ambari
Updated Branches:
  refs/heads/branch-feature-AMBARI-18634 464d3d5ce -> cfe738d4b


AMBARI-18685 - Add validations and attributes to support HOST_ORDERED upgrades (jonathanhurley)


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

Branch: refs/heads/branch-feature-AMBARI-18634
Commit: cfe738d4bfeeb7a34681691b3f4ba13a4e29aa97
Parents: 464d3d5
Author: Jonathan Hurley <jhurley@hortonworks.com>
Authored: Wed Nov 2 09:38:37 2016 -0400
Committer: Jonathan Hurley <jhurley@hortonworks.com>
Committed: Wed Nov 2 15:25:41 2016 -0400

----------------------------------------------------------------------
 .../internal/UpgradeResourceProvider.java       | 562 ++++++++++++++-----
 .../ambari/server/state/UpgradeContext.java     | 128 ++++-
 .../ambari/server/state/UpgradeHelper.java      |   3 +-
 .../state/stack/upgrade/HostOrderGrouping.java  |  20 +
 .../state/stack/upgrade/HostOrderItem.java      | 101 ++++
 .../stack/upgrade/StageWrapperBuilder.java      |   3 +-
 .../AmbariManagementControllerTest.java         |   2 +-
 .../internal/UpgradeResourceProviderTest.java   |  56 ++
 .../ambari/server/state/UpgradeHelperTest.java  | 186 +++---
 .../stack/upgrade/StageWrapperBuilderTest.java  |   7 +-
 .../upgrades/upgrade_test_host_ordered.xml      |  44 ++
 11 files changed, 862 insertions(+), 250 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/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 d83aaa2..02c05d9 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
@@ -24,6 +24,7 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.VERSION;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -89,6 +90,7 @@ import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DesiredConfig;
+import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceInfo;
@@ -103,6 +105,9 @@ import org.apache.ambari.server.state.stack.UpgradePack;
 import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
 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.HostOrderGrouping;
+import org.apache.ambari.server.state.stack.upgrade.HostOrderItem;
+import org.apache.ambari.server.state.stack.upgrade.HostOrderItem.HostOrderActionType;
 import org.apache.ambari.server.state.stack.upgrade.ManualTask;
 import org.apache.ambari.server.state.stack.upgrade.ServerSideActionTask;
 import org.apache.ambari.server.state.stack.upgrade.StageWrapper;
@@ -175,6 +180,27 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
    */
   protected static final String UPGRADE_SKIP_MANUAL_VERIFICATION = "Upgrade/skip_manual_verification";
 
+  /**
+   * When creating an upgrade of type {@link UpgradeType#HOST_ORDERED}, this
+   * specifies the order in which the hosts are upgraded.
+   * </p>
+   *
+   * <pre>
+   * "host_order": [
+   *   { "hosts":
+   *       [ "c6401.ambari.apache.org, "c6402.ambari.apache.org", "c6403.ambari.apache.org" ],
+   *     "service_checks": ["ZOOKEEPER"]
+   *   },
+   *   {
+   *     "hosts": [ "c6404.ambari.apache.org, "c6405.ambari.apache.org"],
+   *     "service_checks": ["ZOOKEEPER", "KAFKA"]
+   *   }
+   * ]
+   * </pre>
+   *
+   */
+  protected static final String UPGRADE_HOST_ORDERED_HOSTS = "Upgrade/host_order";
+
   /*
    * Lifted from RequestResourceProvider
    */
@@ -279,6 +305,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     PROPERTY_IDS.add(UPGRADE_SKIP_MANUAL_VERIFICATION);
     PROPERTY_IDS.add(UPGRADE_SKIP_PREREQUISITE_CHECKS);
     PROPERTY_IDS.add(UPGRADE_FAIL_ON_CHECK_WARNINGS);
+    PROPERTY_IDS.add(UPGRADE_HOST_ORDERED_HOSTS);
 
     PROPERTY_IDS.add(REQUEST_CONTEXT_ID);
     PROPERTY_IDS.add(REQUEST_CREATE_TIME_ID);
@@ -336,23 +363,34 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     }
 
     final Map<String, String> requestInfoProps = request.getRequestInfoProperties();
+    final String forceDowngrade = requestInfoProps.get(UpgradeResourceDefinition.DOWNGRADE_DIRECTIVE);
+
+    final Direction direction = Boolean.parseBoolean(forceDowngrade) ? Direction.DOWNGRADE
+        : Direction.UPGRADE;
 
     UpgradeEntity entity = createResources(new Command<UpgradeEntity>() {
       @Override
       public UpgradeEntity invoke() throws AmbariException, AuthorizationException {
-        String forceDowngrade = requestInfoProps.get(UpgradeResourceDefinition.DOWNGRADE_DIRECTIVE);
 
-        if (null == clusterName) {
-          throw new AmbariException(String.format("%s is required", UPGRADE_CLUSTER_NAME));
+        // Default to ROLLING upgrade, but attempt to read from properties.
+        UpgradeType upgradeType = UpgradeType.ROLLING;
+        if (requestMap.containsKey(UPGRADE_TYPE)) {
+          try {
+            upgradeType = UpgradeType.valueOf(requestMap.get(UPGRADE_TYPE).toString());
+          } catch (Exception e) {
+            throw new AmbariException(String.format("Property %s has an incorrect value of %s.",
+                UPGRADE_TYPE, requestMap.get(UPGRADE_TYPE)));
+          }
         }
 
-        Direction direction = Boolean.parseBoolean(forceDowngrade) ? Direction.DOWNGRADE
-            : Direction.UPGRADE;
+        final UpgradeContext upgradeContext = new UpgradeContext(cluster, upgradeType, direction,
+            requestMap);
 
-        UpgradePack up = validateRequest(direction, requestMap);
+        UpgradePack upgradePack = validateRequest(upgradeContext);
+        upgradeContext.setUpgradePack(upgradePack);
 
         try {
-          return createUpgrade(cluster, direction, up, requestMap);
+          return createUpgrade(upgradeContext);
         } catch (Exception e) {
           LOG.error("Error appears during upgrade task submitting", e);
 
@@ -589,11 +627,11 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
    * @return the validated upgrade pack
    * @throws AmbariException
    */
-  private UpgradePack validateRequest(Direction direction, Map<String, Object> requestMap)
-      throws AmbariException {
-    String clusterName = (String) requestMap.get(UPGRADE_CLUSTER_NAME);
-    String version = (String) requestMap.get(UPGRADE_VERSION);
-    String versionForUpgradePack = (String) requestMap.get(UPGRADE_FROM_VERSION);
+  private UpgradePack validateRequest(UpgradeContext upgradeContext) throws AmbariException {
+    Cluster cluster = upgradeContext.getCluster();
+    Direction direction = upgradeContext.getDirection();
+    Map<String, Object> requestMap = upgradeContext.getUpgradeRequest();
+    UpgradeType upgradeType = upgradeContext.getType();
 
     /**
      * For the unit tests tests, there are multiple upgrade packs for the same type, so
@@ -601,119 +639,20 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
      */
     String preferredUpgradePackName = (String) requestMap.get(UPGRADE_PACK);
 
-    // Default to ROLLING upgrade, but attempt to read from properties.
-    UpgradeType upgradeType = UpgradeType.ROLLING;
-    if (requestMap.containsKey(UPGRADE_TYPE)) {
-      try {
-        upgradeType = UpgradeType.valueOf(requestMap.get(UPGRADE_TYPE).toString());
-      } catch(Exception e){
-        throw new AmbariException(String.format("Property %s has an incorrect value of %s.", UPGRADE_TYPE, requestMap.get(UPGRADE_TYPE)));
-      }
-    }
-
-    if (null == clusterName) {
-      throw new AmbariException(String.format("%s is required", UPGRADE_CLUSTER_NAME));
-    }
-
-    if (null == version) {
-      throw new AmbariException(String.format("%s is required", UPGRADE_VERSION));
-    }
+    String version = (String) requestMap.get(UPGRADE_VERSION);
+    String versionForUpgradePack = (String) requestMap.get(UPGRADE_FROM_VERSION);
 
-    Cluster cluster = getManagementController().getClusters().getCluster(clusterName);
-    UpgradePack pack = s_upgradeHelper.suggestUpgradePack(clusterName, versionForUpgradePack, version, direction, upgradeType, preferredUpgradePackName);
+    UpgradePack pack = s_upgradeHelper.suggestUpgradePack(cluster.getClusterName(),
+        versionForUpgradePack, version, direction, upgradeType, preferredUpgradePackName);
 
-    // Do not insert here additional checks! Wrap them to separate functions.
-    // Pre-req checks, function generate exceptions if something going wrong
-    validatePreRequest(cluster, direction, version, requestMap);
+    // the validator will throw an exception if the upgrade request is not valid
+    UpgradeRequestValidator upgradeRequestValidator = buildValidator(upgradeType);
+    upgradeRequestValidator.validate(upgradeContext, pack);
 
     return pack;
   }
 
   /**
-   * Pre-req checks.
-   * @param cluster Cluster
-   * @param direction Direction of upgrade
-   * @param repoVersion target repository version
-   * @param requestMap request arguments
-   * @throws AmbariException
-   */
-  private void validatePreRequest(Cluster cluster, Direction direction, String repoVersion, Map<String, Object> requestMap)
-      throws AmbariException {
-    boolean skipPrereqChecks = Boolean.parseBoolean((String) requestMap.get(UPGRADE_SKIP_PREREQUISITE_CHECKS));
-    boolean failOnCheckWarnings = Boolean.parseBoolean((String) requestMap.get(UPGRADE_FAIL_ON_CHECK_WARNINGS));
-    String preferredUpgradePack = requestMap.containsKey(UPGRADE_PACK) ? (String) requestMap.get(UPGRADE_PACK) : null;
-
-    UpgradeType upgradeType = UpgradeType.ROLLING;
-    if (requestMap.containsKey(UPGRADE_TYPE)) {
-      try {
-        upgradeType = UpgradeType.valueOf(requestMap.get(UPGRADE_TYPE).toString());
-      } catch(Exception e){
-        throw new AmbariException(String.format("Property %s has an incorrect value of %s.", UPGRADE_TYPE, requestMap.get(UPGRADE_TYPE)));
-      }
-    }
-
-    // Validate there isn't an direction == upgrade/downgrade already in progress.
-    List<UpgradeEntity> upgrades = s_upgradeDAO.findUpgrades(cluster.getClusterId());
-    for (UpgradeEntity entity : upgrades) {
-      if (entity.getDirection() == direction) {
-        Map<Long, HostRoleCommandStatusSummaryDTO> summary = s_hostRoleCommandDAO.findAggregateCounts(
-            entity.getRequestId());
-        CalculatedStatus calc = CalculatedStatus.statusFromStageSummary(summary, summary.keySet());
-        HostRoleStatus status = calc.getStatus();
-        if (!HostRoleStatus.getCompletedStates().contains(status)) {
-          throw new AmbariException(
-              String.format("Unable to perform %s as another %s is in progress. %s request %d is in %s",
-                  direction.getText(false), direction.getText(false), direction.getText(true),
-                  entity.getRequestId().longValue(), status)
-          );
-        }
-      }
-    }
-
-    if (direction.isUpgrade() && !skipPrereqChecks) {
-      // Validate pre-req checks pass
-      PreUpgradeCheckResourceProvider preUpgradeCheckResourceProvider = (PreUpgradeCheckResourceProvider)
-          getResourceProvider(Resource.Type.PreUpgradeCheck);
-      Predicate preUpgradeCheckPredicate = new PredicateBuilder().property(
-          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_CLUSTER_NAME_PROPERTY_ID).equals(cluster.getClusterName()).and().property(
-          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_REPOSITORY_VERSION_PROPERTY_ID).equals(repoVersion).and().property(
-          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_UPGRADE_TYPE_PROPERTY_ID).equals(upgradeType).and().property(
-          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_UPGRADE_PACK_PROPERTY_ID).equals(preferredUpgradePack).toPredicate();
-      Request preUpgradeCheckRequest = PropertyHelper.getReadRequest();
-
-      Set<Resource> preUpgradeCheckResources;
-      try {
-        preUpgradeCheckResources = preUpgradeCheckResourceProvider.getResources(
-            preUpgradeCheckRequest, preUpgradeCheckPredicate);
-      } catch (NoSuchResourceException|SystemException|UnsupportedPropertyException|NoSuchParentResourceException e) {
-        throw new AmbariException(
-            String.format("Unable to perform %s. Prerequisite checks could not be run",
-                direction.getText(false)));
-      }
-
-      List<Resource> failedResources = new LinkedList<Resource>();
-      if (preUpgradeCheckResources != null) {
-        for (Resource res : preUpgradeCheckResources) {
-          PrereqCheckStatus prereqCheckStatus = (PrereqCheckStatus) res.getPropertyValue(
-              PreUpgradeCheckResourceProvider.UPGRADE_CHECK_STATUS_PROPERTY_ID);
-
-          if (prereqCheckStatus == PrereqCheckStatus.FAIL
-              || (failOnCheckWarnings && prereqCheckStatus == PrereqCheckStatus.WARNING)) {
-            failedResources.add(res);
-          }
-        }
-      }
-
-      if (!failedResources.isEmpty()) {
-        Gson gson = new Gson();
-        throw new AmbariException(
-            String.format("Unable to perform %s. Prerequisite checks failed %s",
-                direction.getText(false), gson.toJson(failedResources)));
-      }
-    }
-  }
-
-  /**
    * Inject variables into the
    * {@link org.apache.ambari.server.orm.entities.UpgradeItemEntity}, whose
    * tasks may use strings like {{configType/propertyName}} that need to be
@@ -759,26 +698,19 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
    * could lead to database deadlock issues. Instead, only the creation of the
    * actual entities is wrapped in a {@link Transactional} block.
    *
-   * @param cluster
-   * @param direction
-   * @param pack
-   * @param requestMap
+   * @param upgradeContext
    * @return
    * @throws AmbariException
    * @throws AuthorizationException
    */
-  protected UpgradeEntity createUpgrade(Cluster cluster, Direction direction, UpgradePack pack,
-    Map<String, Object> requestMap) throws AmbariException, AuthorizationException  {
+  protected UpgradeEntity createUpgrade(UpgradeContext upgradeContext)
+      throws AmbariException, AuthorizationException {
 
-    // Default to ROLLING upgrade, but attempt to read from properties.
-    UpgradeType upgradeType = UpgradeType.ROLLING;
-    if (requestMap.containsKey(UPGRADE_TYPE)) {
-      try {
-        upgradeType = UpgradeType.valueOf(requestMap.get(UPGRADE_TYPE).toString());
-      } catch(Exception e){
-        throw new AmbariException(String.format("Property %s has an incorrect value of %s.", UPGRADE_TYPE, requestMap.get(UPGRADE_TYPE)));
-      }
-    }
+    UpgradePack pack = upgradeContext.getUpgradePack();
+    Cluster cluster = upgradeContext.getCluster();
+    Direction direction = upgradeContext.getDirection();
+    Map<String, Object> requestMap = upgradeContext.getUpgradeRequest();
+    UpgradeType upgradeType = upgradeContext.getType();
 
     ConfigHelper configHelper = getManagementController().getConfigHelper();
     String userName = getManagementController().getAuthName();
@@ -818,17 +750,18 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
         break;
     }
 
-    UpgradeContext ctx = new UpgradeContext(resolver, sourceStackId, targetStackId, version,
-        direction, pack.getType());
-    ctx.setSupportedServices(supportedServices);
-    ctx.setScope(scope);
+    upgradeContext.setResolver(resolver);
+    upgradeContext.setSourceAndTargetStacks(sourceStackId, targetStackId);
+    upgradeContext.setVersion(version);
+    upgradeContext.setSupportedServices(supportedServices);
+    upgradeContext.setScope(scope);
 
     if (direction.isDowngrade()) {
       if (requestMap.containsKey(UPGRADE_FROM_VERSION)) {
-        ctx.setDowngradeFromVersion((String) requestMap.get(UPGRADE_FROM_VERSION));
+        upgradeContext.setDowngradeFromVersion((String) requestMap.get(UPGRADE_FROM_VERSION));
       } else {
         UpgradeEntity lastUpgradeItemForCluster = s_upgradeDAO.findLastUpgradeForCluster(cluster.getClusterId());
-        ctx.setDowngradeFromVersion(lastUpgradeItemForCluster.getToVersion());
+        upgradeContext.setDowngradeFromVersion(lastUpgradeItemForCluster.getToVersion());
       }
     }
 
@@ -854,11 +787,11 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
       skipManualVerification = Boolean.parseBoolean((String) requestMap.get(UPGRADE_SKIP_MANUAL_VERIFICATION));
     }
 
-    ctx.setAutoSkipComponentFailures(skipComponentFailures);
-    ctx.setAutoSkipServiceCheckFailures(skipServiceCheckFailures);
-    ctx.setAutoSkipManualVerification(skipManualVerification);
+    upgradeContext.setAutoSkipComponentFailures(skipComponentFailures);
+    upgradeContext.setAutoSkipServiceCheckFailures(skipServiceCheckFailures);
+    upgradeContext.setAutoSkipManualVerification(skipManualVerification);
 
-    List<UpgradeGroupHolder> groups = s_upgradeHelper.createSequence(pack, ctx);
+    List<UpgradeGroupHolder> groups = s_upgradeHelper.createSequence(pack, upgradeContext);
 
     if (groups.isEmpty()) {
       throw new AmbariException("There are no groupings available");
@@ -934,9 +867,11 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
           // the same host, break them out into individual stages.
           for (TaskWrapper taskWrapper : wrapper.getTasks()) {
             for (Task task : taskWrapper.getTasks()) {
-              if(ctx.isManualVerificationAutoSkipped() && task.getType() == Task.Type.MANUAL) {
+              if (upgradeContext.isManualVerificationAutoSkipped()
+                  && task.getType() == Task.Type.MANUAL) {
                 continue;
               }
+
               UpgradeItemEntity itemEntity = new UpgradeItemEntity();
 
               itemEntity.setText(wrapper.getText());
@@ -947,17 +882,17 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
               // At this point, need to change the effective Stack Id so that subsequent tasks run on the newer value.
               if (upgradeType == UpgradeType.NON_ROLLING && UpdateStackGrouping.class.equals(group.groupClass)) {
                 if (direction.isUpgrade()) {
-                  ctx.setEffectiveStackId(ctx.getTargetStackId());
+                  upgradeContext.setEffectiveStackId(upgradeContext.getTargetStackId());
                 } else {
-                  ctx.setEffectiveStackId(ctx.getOriginalStackId());
+                  upgradeContext.setEffectiveStackId(upgradeContext.getOriginalStackId());
                 }
               } else if (UpdateStackGrouping.class.equals(group.groupClass)) {
-                ctx.setEffectiveStackId(ctx.getTargetStackId());
+                upgradeContext.setEffectiveStackId(upgradeContext.getTargetStackId());
               }
 
               injectVariables(configHelper, cluster, itemEntity);
-              makeServerSideStage(ctx, req, itemEntity, (ServerSideActionTask) task, skippable,
-                  supportsAutoSkipOnFailure, allowRetry, pack, configUpgradePack);
+              makeServerSideStage(upgradeContext, req, itemEntity, (ServerSideActionTask) task,
+                  skippable, supportsAutoSkipOnFailure, allowRetry, pack, configUpgradePack);
             }
           }
         } else {
@@ -970,8 +905,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
           injectVariables(configHelper, cluster, itemEntity);
 
           // upgrade items match a stage
-          createStage(ctx, req, itemEntity, wrapper, skippable, supportsAutoSkipOnFailure,
-              allowRetry);
+          createStage(upgradeContext, req, itemEntity, wrapper, skippable,
+              supportsAutoSkipOnFailure, allowRetry);
         }
       }
 
@@ -1331,9 +1266,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
    */
   private void applyAdditionalParameters(StageWrapper wrapper, Map<String, String> commandParams) {
     if (wrapper.getParams() != null) {
-      Iterator it = wrapper.getParams().entrySet().iterator();
-      while (it.hasNext()) {
-        Map.Entry<String, String> pair = (Map.Entry) it.next();
+      for (Map.Entry<String, String> pair : wrapper.getParams().entrySet()) {
         if (!commandParams.containsKey(pair.getKey())) {
           commandParams.put(pair.getKey(), pair.getValue());
         }
@@ -1830,6 +1763,57 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
   }
 
   /**
+   * Builds the list of {@link HostOrderItem}s from the upgrade request. If the
+   * upgrade request does not contain the hosts
+   *
+   * @param requestMap
+   *          the map of properties from the request (not {@code null}).
+   * @return the ordered list of actions to orchestrate for the
+   *         {@link UpgradeType#HOST_ORDERED} upgrade.
+   * @throws AmbariException
+   *           if the request properties are not valid.
+   */
+  @SuppressWarnings("unchecked")
+  private List<HostOrderItem> extractHostOrderItemsFromRequest(Map<String, Object> requestMap)
+      throws AmbariException {
+    // ewwww
+    Set<Map<String, List<String>>> hostsOrder = (Set<Map<String, List<String>>>) requestMap.get(
+        UPGRADE_HOST_ORDERED_HOSTS);
+
+    if (CollectionUtils.isEmpty(hostsOrder)) {
+      throw new AmbariException(
+          String.format("The %s property must be specified when using a %s upgrade type.",
+              UPGRADE_HOST_ORDERED_HOSTS, UpgradeType.HOST_ORDERED));
+    }
+
+    List<HostOrderItem> hostOrderItems = new ArrayList<>();
+
+    // extract all of the hosts so that we can ensure they are all accounted for
+    Iterator<Map<String, List<String>>> iterator = hostsOrder.iterator();
+    while (iterator.hasNext()) {
+      Map<String, List<String>> grouping = iterator.next();
+      List<String> hosts = grouping.get("hosts");
+      List<String> serviceChecks = grouping.get("service_checks");
+
+      if (CollectionUtils.isEmpty(hosts) && CollectionUtils.isEmpty(serviceChecks)) {
+        throw new AmbariException(String.format(
+            "The %s property must contain at least one object with either a %s or %s key",
+            UPGRADE_HOST_ORDERED_HOSTS, "hosts", "service_checks"));
+      }
+
+      if (CollectionUtils.isNotEmpty(hosts)) {
+        hostOrderItems.add(new HostOrderItem(HostOrderActionType.HOST_UPGRADE, hosts));
+      }
+
+      if (CollectionUtils.isNotEmpty(serviceChecks)) {
+        hostOrderItems.add(new HostOrderItem(HostOrderActionType.SERVICE_CHECK, serviceChecks));
+      }
+    }
+
+    return hostOrderItems;
+  }
+
+  /**
    * Builds the correct {@link ConfigUpgradePack} based on the upgrade and
    * source stack.
    * <ul>
@@ -1886,4 +1870,274 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
       return configUpgradePack;
     }
   }
+
+  /**
+   * Builds a chain of {@link UpgradeRequestValidator}s to ensure that the
+   * incoming request to create a new upgrade is valid.
+   *
+   * @param upgradeType
+   *          the type of upgrade to build the validator for.
+   * @return the validator which can check to ensure that the properties are
+   *         valid.
+   */
+  private UpgradeRequestValidator buildValidator(UpgradeType upgradeType){
+    UpgradeRequestValidator validator = new BasicUpgradePropertiesValidator();
+    UpgradeRequestValidator preReqValidator = new PreReqCheckValidator();
+    validator.setNextValidator(preReqValidator);
+
+    final UpgradeRequestValidator upgradeTypeValidator;
+    switch( upgradeType ){
+      case HOST_ORDERED:
+        upgradeTypeValidator = new HostOrderedUpgradeValidator();
+        break;
+      case NON_ROLLING:
+      case ROLLING:
+      default:
+        upgradeTypeValidator = null;
+        break;
+    }
+
+    preReqValidator.setNextValidator(upgradeTypeValidator);
+    return validator;
+  }
+
+  /**
+   * The {@link UpgradeRequestValidator} contains the logic to check for correct
+   * upgrade request properties and then pass the responsibility onto the next
+   * validator in the chain.
+   */
+  private abstract class UpgradeRequestValidator {
+    /**
+     * The next validator.
+     */
+    UpgradeRequestValidator m_nextValidator;
+
+    /**
+     * Sets the next validator in the chain.
+     *
+     * @param nextValidator
+     *          the next validator to run, or {@code null} for none.
+     */
+    void setNextValidator(UpgradeRequestValidator nextValidator) {
+      m_nextValidator = nextValidator;
+    }
+
+    /**
+     * Validates the upgrade request from this point in the chain.
+     *
+     * @param upgradeContext
+     * @param upgradePack
+     * @throws AmbariException
+     */
+    final void validate(UpgradeContext upgradeContext, UpgradePack upgradePack)
+        throws AmbariException {
+
+      // run this instance's check
+      check(upgradeContext, upgradePack);
+
+      // pass along to the next
+      if( null != m_nextValidator ) {
+        m_nextValidator.validate(upgradeContext, upgradePack);
+      }
+    }
+
+    /**
+     * Checks to ensure that upgrade request is valid given the specific
+     * arguments.
+     *
+     * @param upgradeContext
+     * @param upgradePack
+     *
+     * @throws AmbariException
+     */
+    abstract void check(UpgradeContext upgradeContext, UpgradePack upgradePack)
+        throws AmbariException;
+  }
+
+  /**
+   * The {@link BasicUpgradePropertiesValidator} ensures that the basic required
+   * properties are present on the upgrade request.
+   */
+  private final class BasicUpgradePropertiesValidator extends UpgradeRequestValidator {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void check(UpgradeContext upgradeContext, UpgradePack upgradePack)
+        throws AmbariException {
+      Map<String, Object> requestMap = upgradeContext.getUpgradeRequest();
+
+      String clusterName = (String) requestMap.get(UPGRADE_CLUSTER_NAME);
+      String version = (String) requestMap.get(UPGRADE_VERSION);
+
+      if (null == clusterName) {
+        throw new AmbariException(String.format("%s is required", UPGRADE_CLUSTER_NAME));
+      }
+
+      if (null == version) {
+        throw new AmbariException(String.format("%s is required", UPGRADE_VERSION));
+      }
+    }
+  }
+
+  /**
+   * The {@link PreReqCheckValidator} ensures that the upgrade pre-requisite
+   * checks have passed.
+   */
+  private final class PreReqCheckValidator extends UpgradeRequestValidator {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void check(UpgradeContext upgradeContext, UpgradePack upgradePack) throws AmbariException {
+      Cluster cluster = upgradeContext.getCluster();
+      Direction direction = upgradeContext.getDirection();
+      Map<String, Object> requestMap = upgradeContext.getUpgradeRequest();
+      UpgradeType upgradeType = upgradeContext.getType();
+
+      String version = (String) requestMap.get(UPGRADE_VERSION);
+      boolean skipPrereqChecks = Boolean.parseBoolean((String) requestMap.get(UPGRADE_SKIP_PREREQUISITE_CHECKS));
+      boolean failOnCheckWarnings = Boolean.parseBoolean((String) requestMap.get(UPGRADE_FAIL_ON_CHECK_WARNINGS));
+      String preferredUpgradePack = requestMap.containsKey(UPGRADE_PACK) ? (String) requestMap.get(UPGRADE_PACK) : null;
+
+      // Validate there isn't an direction == upgrade/downgrade already in progress.
+      List<UpgradeEntity> upgrades = s_upgradeDAO.findUpgrades(cluster.getClusterId());
+      for (UpgradeEntity entity : upgrades) {
+        if (entity.getDirection() == direction) {
+          Map<Long, HostRoleCommandStatusSummaryDTO> summary = s_hostRoleCommandDAO.findAggregateCounts(
+              entity.getRequestId());
+          CalculatedStatus calc = CalculatedStatus.statusFromStageSummary(summary, summary.keySet());
+          HostRoleStatus status = calc.getStatus();
+          if (!HostRoleStatus.getCompletedStates().contains(status)) {
+            throw new AmbariException(
+                String.format("Unable to perform %s as another %s is in progress. %s request %d is in %s",
+                    direction.getText(false), direction.getText(false), direction.getText(true),
+                    entity.getRequestId().longValue(), status)
+            );
+          }
+        }
+      }
+
+      // skip this check if it's a downgrade or we are instructed to skip it
+      if( direction.isDowngrade() || skipPrereqChecks ){
+        return;
+      }
+
+      // Validate pre-req checks pass
+      PreUpgradeCheckResourceProvider preUpgradeCheckResourceProvider = (PreUpgradeCheckResourceProvider)
+          getResourceProvider(Resource.Type.PreUpgradeCheck);
+
+      Predicate preUpgradeCheckPredicate = new PredicateBuilder().property(
+          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_CLUSTER_NAME_PROPERTY_ID).equals(cluster.getClusterName()).and().property(
+          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_REPOSITORY_VERSION_PROPERTY_ID).equals(version).and().property(
+          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_UPGRADE_TYPE_PROPERTY_ID).equals(upgradeType).and().property(
+          PreUpgradeCheckResourceProvider.UPGRADE_CHECK_UPGRADE_PACK_PROPERTY_ID).equals(preferredUpgradePack).toPredicate();
+
+      Request preUpgradeCheckRequest = PropertyHelper.getReadRequest();
+
+      Set<Resource> preUpgradeCheckResources;
+      try {
+        preUpgradeCheckResources = preUpgradeCheckResourceProvider.getResources(
+            preUpgradeCheckRequest, preUpgradeCheckPredicate);
+      } catch (NoSuchResourceException|SystemException|UnsupportedPropertyException|NoSuchParentResourceException e) {
+        throw new AmbariException(
+            String.format("Unable to perform %s. Prerequisite checks could not be run",
+                direction.getText(false), e));
+      }
+
+      List<Resource> failedResources = new LinkedList<Resource>();
+      if (preUpgradeCheckResources != null) {
+        for (Resource res : preUpgradeCheckResources) {
+          PrereqCheckStatus prereqCheckStatus = (PrereqCheckStatus) res.getPropertyValue(
+              PreUpgradeCheckResourceProvider.UPGRADE_CHECK_STATUS_PROPERTY_ID);
+
+          if (prereqCheckStatus == PrereqCheckStatus.FAIL
+              || (failOnCheckWarnings && prereqCheckStatus == PrereqCheckStatus.WARNING)) {
+            failedResources.add(res);
+          }
+        }
+      }
+
+      if (!failedResources.isEmpty()) {
+        Gson gson = new Gson();
+        throw new AmbariException(
+            String.format("Unable to perform %s. Prerequisite checks failed %s",
+                direction.getText(false), gson.toJson(failedResources)));
+      }
+    }
+  }
+
+  /**
+   * Ensures that for {@link UpgradeType#HOST_ORDERED}, the properties supplied
+   * are valid.
+   */
+  @SuppressWarnings("unchecked")
+  private final class HostOrderedUpgradeValidator extends UpgradeRequestValidator {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void check(UpgradeContext upgradeContext, UpgradePack upgradePack) throws AmbariException {
+      Cluster cluster = upgradeContext.getCluster();
+      Direction direction = upgradeContext.getDirection();
+      Map<String, Object> requestMap = upgradeContext.getUpgradeRequest();
+
+      String skipFailuresRequestProperty = (String) requestMap.get(UPGRADE_SKIP_FAILURES);
+      if (Boolean.parseBoolean(skipFailuresRequestProperty)) {
+        throw new AmbariException(
+            String.format("The %s property is not valid when creating a %s upgrade.",
+                UPGRADE_SKIP_FAILURES, UpgradeType.HOST_ORDERED));
+      }
+
+      String skipManualVerification = (String) requestMap.get(UPGRADE_SKIP_MANUAL_VERIFICATION);
+      if (Boolean.parseBoolean(skipManualVerification)) {
+        throw new AmbariException(
+            String.format("The %s property is not valid when creating a %s upgrade.",
+                UPGRADE_SKIP_MANUAL_VERIFICATION, UpgradeType.HOST_ORDERED));
+      }
+
+      if (!requestMap.containsKey(UPGRADE_HOST_ORDERED_HOSTS)) {
+        throw new AmbariException(
+            String.format("The %s property is required when creating a %s upgrade.",
+                UPGRADE_HOST_ORDERED_HOSTS, UpgradeType.HOST_ORDERED));
+      }
+
+      List<HostOrderItem> hostOrderItems = extractHostOrderItemsFromRequest(requestMap);
+      List<String> hostsFromRequest = new ArrayList<>(hostOrderItems.size());
+      for (HostOrderItem hostOrderItem : hostOrderItems) {
+        if (hostOrderItem.getType() == HostOrderActionType.HOST_UPGRADE) {
+          hostsFromRequest.addAll(hostOrderItem.getActionItems());
+        }
+      }
+
+      // ensure that all hosts for this cluster are accounted for
+      Collection<Host> hosts = cluster.getHosts();
+      Set<String> clusterHostNames = new HashSet<>(hosts.size());
+      for (Host host : hosts) {
+        clusterHostNames.add(host.getHostName());
+      }
+
+      Collection<String> disjunction = CollectionUtils.disjunction(hostsFromRequest,
+          clusterHostNames);
+
+      if (CollectionUtils.isNotEmpty(disjunction)) {
+        throw new AmbariException(String.format(
+            "The supplied list of hosts must match the cluster hosts in an upgrade of type %s. The following hosts are either missing or invalid: %s",
+            UpgradeType.HOST_ORDERED, StringUtils.join(disjunction, ", ")));
+      }
+
+      // verify that the upgradepack has the required grouping and set the
+      // action items on it
+      HostOrderGrouping hostOrderGrouping = null;
+      List<Grouping> groupings = upgradePack.getGroups(direction);
+      for (Grouping grouping : groupings) {
+        if (grouping instanceof HostOrderGrouping) {
+          hostOrderGrouping = (HostOrderGrouping) grouping;
+          hostOrderGrouping.setHostOrderItems(hostOrderItems);
+        }
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/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 32f5c42..8e7e5de 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
@@ -28,6 +28,7 @@ import org.apache.ambari.annotations.Experimental;
 import org.apache.ambari.annotations.ExperimentalFeature;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.stack.MasterHostResolver;
+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.UpgradeScope;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
@@ -37,6 +38,34 @@ import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
  */
 public class UpgradeContext {
 
+  /**
+   * The cluster that the upgrade is for.
+   */
+  final private Cluster m_cluster;
+
+  /**
+   * The direction of the upgrade.
+   */
+  final private Direction m_direction;
+
+  /**
+   * The type of upgrade.
+   */
+  final private UpgradeType m_type;
+
+  /**
+   * The request parameters from the REST API for creating this upgrade.
+   */
+  final private Map<String, Object> m_upgradeRequestMap;
+
+  /**
+   * The upgrade pack for this upgrade.
+   */
+  private UpgradePack m_upgradePack;
+
+  /**
+   * The version being upgrade to or downgraded to.
+   */
   private String m_version;
 
   /**
@@ -61,14 +90,12 @@ public class UpgradeContext {
    */
   private StackId m_targetStackId;
 
-  private Direction m_direction;
   private MasterHostResolver m_resolver;
   private AmbariMetaInfo m_metaInfo;
   private List<ServiceComponentHost> m_unhealthy = new ArrayList<ServiceComponentHost>();
   private Map<String, String> m_serviceNames = new HashMap<String, String>();
   private Map<String, String> m_componentNames = new HashMap<String, String>();
   private String m_downgradeFromVersion = null;
-  private UpgradeType m_type = null;
 
   /**
    * {@code true} if slave/client component failures should be automatically
@@ -96,8 +123,28 @@ public class UpgradeContext {
   /**
    * Constructor.
    *
-   * @param resolver
-   *          the resolver that also references the required cluster
+   * @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
+   */
+  public UpgradeContext(Cluster cluster, UpgradeType type, Direction direction,
+      Map<String, Object> upgradeRequestMap) {
+    m_cluster = cluster;
+    m_type = type;
+    m_direction = direction;
+    m_upgradeRequestMap = upgradeRequestMap;
+  }
+
+  /**
+   * Sets the source and target stack IDs. This will also set the effective
+   * stack ID based on the already-set {@link UpgradeType} and
+   * {@link Direction}.
+   *
    * @param sourceStackId
    *          the original "current" stack of the cluster before the upgrade
    *          started. This is the same regardless of whether the current
@@ -108,24 +155,18 @@ public class UpgradeContext {
    *          same regardless of whether the current direction is
    *          {@link Direction#UPGRADE} or {@link Direction#DOWNGRADE} (not
    *          {@code null}).
-   * @param version
-   *          the target version to upgrade to
-   * @param direction
-   *          the direction for the upgrade
-   * @param type
-   *          the type of upgrade, either rolling or non_rolling
+   *
+   * @see #getEffectiveStackId()
    */
-  public UpgradeContext(MasterHostResolver resolver, StackId sourceStackId,
-      StackId targetStackId, String version, Direction direction, UpgradeType type) {
-    m_version = version;
+  public void setSourceAndTargetStacks(StackId sourceStackId, StackId targetStackId) {
     m_originalStackId = sourceStackId;
 
-    switch (type) {
+    switch (m_type) {
       case ROLLING:
         m_effectiveStackId = targetStackId;
         break;
       case NON_ROLLING:
-        m_effectiveStackId = (direction.isUpgrade())? sourceStackId : targetStackId;
+        m_effectiveStackId = (m_direction.isUpgrade()) ? sourceStackId : targetStackId;
         break;
       default:
         m_effectiveStackId = targetStackId;
@@ -133,16 +174,45 @@ public class UpgradeContext {
     }
 
     m_targetStackId = targetStackId;
-    m_direction = direction;
-    m_resolver = resolver;
-    m_type = type;
   }
 
   /**
-   * @return the cluster from the {@link MasterHostResolver}
+   * Gets the original mapping of key/value pairs from the request which created
+   * the upgrade.
+   *
+   * @return the original mapping of key/value pairs from the request which
+   *         created the upgrade.
+   */
+  public Map<String, Object> getUpgradeRequest() {
+    return m_upgradeRequestMap;
+  }
+
+  /**
+   * Gets the upgrade pack for this upgrade.
+   *
+   * @return the upgrade pack
+   */
+  public UpgradePack getUpgradePack() {
+    return m_upgradePack;
+  }
+
+  /**
+   * Sets the upgrade pack for this upgrade
+   *
+   * @param upgradePack
+   *          the upgrade pack to set
+   */
+  public void setUpgradePack(UpgradePack upgradePack) {
+    m_upgradePack = upgradePack;
+  }
+
+  /**
+   * Gets the cluster that the upgrade is for.
+   *
+   * @return the cluster (never {@code null}).
    */
   public Cluster getCluster() {
-    return m_resolver.getCluster();
+    return m_cluster;
   }
 
   /**
@@ -153,6 +223,14 @@ public class UpgradeContext {
   }
 
   /**
+   * @param version
+   *          the target version to upgrade to
+   */
+  public void setVersion(String version) {
+    m_version = version;
+  }
+
+  /**
    * @return the direction of the upgrade
    */
   public Direction getDirection() {
@@ -167,6 +245,16 @@ public class UpgradeContext {
   }
 
   /**
+   * Sets the host resolver.
+   *
+   * @param resolver
+   *          the resolver that also references the required cluster
+   */
+  public void setResolver(MasterHostResolver resolver) {
+    m_resolver = resolver;
+  }
+
+  /**
    * @return the resolver
    */
   public MasterHostResolver getResolver() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
index ac0953f..52bf428 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
@@ -64,6 +64,7 @@ import org.apache.ambari.server.state.stack.upgrade.Task.Type;
 import org.apache.ambari.server.state.stack.upgrade.TaskWrapper;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeFunction;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -463,7 +464,7 @@ public class UpgradeHelper {
 
       List<StageWrapper> proxies = builder.build(context);
 
-      if (!proxies.isEmpty()) {
+      if (CollectionUtils.isNotEmpty(proxies)) {
         groupHolder.items = proxies;
         postProcess(context, groupHolder);
         groups.add(groupHolder);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/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 9c75344..f3dca62 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
@@ -33,11 +33,25 @@ import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
 public class HostOrderGrouping extends Grouping {
 
   /**
+   * Contains the ordered actions to schedule for this grouping.
+   */
+  private List<HostOrderItem> m_hostOrderItems;
+
+  /**
    * Constructor
    */
   public HostOrderGrouping() {
   }
 
+  /**
+   * Sets the {@link HostOrderItem}s on this grouping.
+   *
+   * @param hostOrderItems
+   */
+  public void setHostOrderItems(List<HostOrderItem> hostOrderItems) {
+    m_hostOrderItems = hostOrderItems;
+  }
+
   @Override
   public StageWrapperBuilder getBuilder() {
     return new HostBuilder(this);
@@ -54,12 +68,18 @@ public class HostOrderGrouping extends Grouping {
       super(grouping);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public void add(UpgradeContext upgradeContext, HostsType hostsType, String service,
         boolean clientOnly, ProcessingComponent pc, Map<String, String> params) {
 
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public List<StageWrapper> build(UpgradeContext upgradeContext,
         List<StageWrapper> stageWrappers) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderItem.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderItem.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderItem.java
new file mode 100644
index 0000000..bc46786
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/HostOrderItem.java
@@ -0,0 +1,101 @@
+/**
+ * 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.stack.upgrade;
+
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+
+import com.google.common.base.Objects;
+
+/**
+ * The {@link HostOrderItem} class represents the orchestration order of hosts
+ * and service checks in an {@link UpgradeType#HOST_ORDERED} upgrade.
+ */
+public class HostOrderItem {
+
+  /**
+   * The {@link HostOrderActionType} defines the type of action which should be
+   * taken on the list of action items.
+   */
+  public enum HostOrderActionType {
+    /**
+     * Represents that the action items are services which should have service
+     * checks scheduled.
+     */
+    SERVICE_CHECK,
+
+    /**
+     * Represents that the action items are hosts which need to be scheduled for
+     * upgrade.
+     */
+    HOST_UPGRADE;
+  }
+
+  /**
+   * The type of action.
+   */
+  private final HostOrderActionType m_type;
+
+  /**
+   * The items to take action on. If {@link HostOrderActionType#HOST_UPGRADE},
+   * then this should be a list of hosts. If
+   * {@link HostOrderActionType#SERVICE_CHECK}, then this should be a list of
+   * services.
+   */
+  private final List<String> m_actionItems;
+
+  /**
+   * Constructor.
+   *
+   * @param type
+   * @param actionItems
+   */
+  public HostOrderItem(HostOrderActionType type, List<String> actionItems) {
+    m_type = type;
+    m_actionItems = actionItems;
+  }
+
+  /**
+   * Gets the type of action to take.
+   *
+   * @return the type of action to take.
+   */
+  public HostOrderActionType getType() {
+    return m_type;
+  }
+
+  /**
+   * Gets the list of action items to take action on. This could be a list of
+   * host names or a list of services.
+   *
+   * @return the list of action items.
+   */
+  public List<String> getActionItems() {
+    return m_actionItems;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return Objects.toStringHelper(this).add("type", m_type).add("items",
+        StringUtils.join(m_actionItems, ", ")).omitNullValues().toString();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java
index ec7279c..267133b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilder.java
@@ -27,6 +27,7 @@ import org.apache.ambari.server.serveraction.upgrades.AutoSkipFailedSummaryActio
 import org.apache.ambari.server.stack.HostsType;
 import org.apache.ambari.server.state.UpgradeContext;
 import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
+import org.springframework.util.CollectionUtils;
 
 /**
  * Defines how to build stages for an Upgrade or Downgrade.
@@ -128,7 +129,7 @@ public abstract class StageWrapperBuilder {
   protected List<StageWrapper> afterBuild(UpgradeContext upgradeContext,
       List<StageWrapper> stageWrappers) {
 
-    if (stageWrappers.isEmpty()) {
+    if (CollectionUtils.isEmpty(stageWrappers)) {
       return stageWrappers;
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/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 e8b10a6..0fdaa46 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
@@ -7326,7 +7326,7 @@ public class AmbariManagementControllerTest {
     Assert.assertEquals(1, responsesWithParams.size());
     StackVersionResponse resp = responsesWithParams.iterator().next();
     assertNotNull(resp.getUpgradePacks());
-    assertEquals(10, resp.getUpgradePacks().size());
+    assertEquals(11, resp.getUpgradePacks().size());
     assertTrue(resp.getUpgradePacks().contains("upgrade_test"));
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
index 08507ef..17e4f2d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
@@ -35,6 +35,7 @@ import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,6 +57,7 @@ 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.spi.SystemException;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
@@ -111,6 +113,7 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
+import com.google.common.collect.Lists;
 import com.google.gson.Gson;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
@@ -1425,6 +1428,59 @@ public class UpgradeResourceProviderTest {
     assertEquals(0, requestIds.size());
   }
 
+  /**
+   * Tests that a {@link UpgradeType#HOST_ORDERED} upgrade throws an exception
+   * on missing hosts.
+   *
+   * @throws Exception
+   */
+  @Test()
+  public void testCreateHostOrderedUpgradeThrowsExceptions() throws Exception {
+    Cluster cluster = clusters.getCluster("c1");
+
+    Map<String, Object> requestProps = new HashMap<String, Object>();
+    requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_VERSION, "2.2.0.0");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_PACK, "upgrade_test_host_ordered");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_TYPE, UpgradeType.HOST_ORDERED.toString());
+    requestProps.put(UpgradeResourceProvider.UPGRADE_SKIP_PREREQUISITE_CHECKS, Boolean.TRUE.toString());
+
+    ResourceProvider upgradeResourceProvider = createProvider(amc);
+    Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
+
+    try {
+      upgradeResourceProvider.createResources(request);
+      Assert.fail("The request should have failed due to the missing Upgrade/host_order property");
+    } catch( SystemException systemException ){
+      // expected
+    }
+
+    // stick a bad host_ordered_hosts in there which has the wrong hosts
+    Set<Map<String, List<String>>> hostsOrder = new LinkedHashSet<>();
+    Map<String, List<String>> hostGrouping = new HashMap<>();
+    hostGrouping.put("hosts", Lists.newArrayList("invalid-host"));
+    hostsOrder.add(hostGrouping);
+
+    requestProps.put(UpgradeResourceProvider.UPGRADE_HOST_ORDERED_HOSTS, hostsOrder);
+
+    try {
+      upgradeResourceProvider.createResources(request);
+      Assert.fail("The request should have failed due to invalid hosts");
+    } catch (SystemException systemException) {
+      // expected
+    }
+
+    // use correct hosts now
+    hostsOrder = new LinkedHashSet<>();
+    hostGrouping = new HashMap<>();
+    hostGrouping.put("hosts", Lists.newArrayList("h1"));
+    hostsOrder.add(hostGrouping);
+
+    requestProps.put(UpgradeResourceProvider.UPGRADE_HOST_ORDERED_HOSTS, hostsOrder);
+    upgradeResourceProvider.createResources(request);
+  }
+
+
   private String parseSingleMessage(String msgStr){
     JsonParser parser = new JsonParser();
     JsonArray msgArray = (JsonArray) parser.parse(msgStr);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/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 9a0946b..52e6513 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
@@ -191,10 +191,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -270,10 +272,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test_partial");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
     context.setSupportedServices(Collections.singleton("ZOOKEEPER"));
     context.setScope(UpgradeScope.PARTIAL);
 
@@ -327,10 +331,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test_partial");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
     context.setSupportedServices(Collections.singleton("ZOOKEEPER"));
     context.setScope(UpgradeScope.COMPLETE);
 
@@ -382,10 +388,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_server_action_test");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -436,8 +444,10 @@ public class UpgradeHelperTest {
     // use a "real" master host resolver here so that we can actually test MM
     MasterHostResolver masterHostResolver = new MasterHostResolver(null, cluster, "");
 
-    UpgradeContext context = new UpgradeContext(masterHostResolver, HDP_21, HDP_21,
-        UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
     assertEquals(7, groups.size());
@@ -465,10 +475,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -515,9 +527,11 @@ public class UpgradeHelperTest {
     List<ServiceComponentHost> schs = cluster.getServiceComponentHosts("h4");
     assertEquals(1, schs.size());
     assertEquals(HostState.HEARTBEAT_LOST, schs.get(0).getHostState());
-
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -551,11 +565,13 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test");
     assertNotNull(upgrade);
 
-    makeCluster();
-
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, DOWNGRADE_VERSION, Direction.DOWNGRADE, UpgradeType.ROLLING);
+    Cluster cluster = makeCluster();
 
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.DOWNGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(DOWNGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
     assertEquals(7, groups.size());
@@ -591,11 +607,13 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_bucket_test");
     assertNotNull(upgrade);
 
-    makeCluster();
-
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    Cluster cluster = makeCluster();
 
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
     assertEquals(1, groups.size());
@@ -621,10 +639,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -651,8 +671,10 @@ public class UpgradeHelperTest {
 
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -722,8 +744,10 @@ public class UpgradeHelperTest {
 
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade,
         context);
@@ -833,8 +857,10 @@ public class UpgradeHelperTest {
 
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade,
         context);
@@ -895,8 +921,10 @@ public class UpgradeHelperTest {
 
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade,
         context);
@@ -960,8 +988,10 @@ public class UpgradeHelperTest {
     assertNotNull(upgrade);
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21, HDP_21,
-        UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -1036,9 +1066,11 @@ public class UpgradeHelperTest {
       numServiceChecksExpected++;
     }
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_22, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
-
+    UpgradeContext context = new UpgradeContext(c, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_22);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);    
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
     assertEquals(7, groups.size());
@@ -1079,11 +1111,13 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_test_checks");
     assertNotNull(upgrade);
 
-    makeCluster();
-
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, DOWNGRADE_VERSION, Direction.DOWNGRADE, UpgradeType.ROLLING);
+    Cluster cluster = makeCluster();
 
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.DOWNGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(DOWNGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);    
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
     assertEquals(5, groups.size());
@@ -1114,10 +1148,12 @@ public class UpgradeHelperTest {
     UpgradePack upgrade = upgrades.get("upgrade_to_new_stack");
     assertNotNull(upgrade);
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
@@ -1360,11 +1396,13 @@ public class UpgradeHelperTest {
     }
     assertTrue(foundService);
 
-    makeCluster();
-
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21,
-        HDP_21, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    Cluster cluster = makeCluster();
 
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);    
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
 
     // The upgrade pack has 2 tasks for Oozie in the pre-upgrade group.
@@ -1461,9 +1499,11 @@ public class UpgradeHelperTest {
     expect(m_masterHostResolver.getCluster()).andReturn(c).anyTimes();
     replay(m_masterHostResolver);
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21, HDP_21, DOWNGRADE_VERSION,
-        Direction.DOWNGRADE, UpgradeType.ROLLING);
-
+    UpgradeContext context = new UpgradeContext(c, UpgradeType.ROLLING, Direction.DOWNGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(DOWNGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);    
+    
     Map<String, UpgradePack> upgrades = ambariMetaInfo.getUpgradePacks("HDP", "2.1.1");
     assertTrue(upgrades.containsKey("upgrade_direction"));
     UpgradePack upgrade = upgrades.get("upgrade_direction");
@@ -1699,7 +1739,7 @@ public class UpgradeHelperTest {
     final Direction upgradeDirection = Direction.UPGRADE;
     final UpgradeType upgradeType = UpgradeType.ROLLING;
 
-    makeCluster();
+    Cluster cluster = makeCluster();
 
     // grab the right pack
     String preferredUpgradePackName = "upgrade_grouping_rolling";
@@ -1709,8 +1749,10 @@ public class UpgradeHelperTest {
     assertEquals(upgradeType, upgradePack.getType());
 
     // get an upgrade
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_21, HDP_21,
-        UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_21, HDP_21);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);
 
     context.setSupportedServices(Collections.singleton("ZOOKEEPER"));
     context.setScope(UpgradeScope.COMPLETE);
@@ -1809,9 +1851,12 @@ public class UpgradeHelperTest {
     };
 
     MasterHostResolver resolver = new MasterHostResolver(m_configHelper, c);
-    UpgradeContext context = new UpgradeContext(resolver, stackId, stackId2, "2.2.0",
-        Direction.UPGRADE, UpgradeType.NON_ROLLING);
 
+    UpgradeContext context = new UpgradeContext(c, UpgradeType.NON_ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(stackId, stackId2);
+    context.setVersion("2.2.0");
+    context.setResolver(resolver);    
+    
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgradePack, context);
 
     assertEquals(1, groups.size());
@@ -1819,9 +1864,12 @@ public class UpgradeHelperTest {
     sch1.setVersion("2.1.1");
     sch2.setVersion("2.1.1");
     resolver = new MasterHostResolver(m_configHelper, c, "2.1.1");
-    context = new UpgradeContext(resolver, stackId2, stackId, "2.1.1", Direction.DOWNGRADE,
-        UpgradeType.NON_ROLLING);
-
+    
+    context = new UpgradeContext(c, UpgradeType.NON_ROLLING, Direction.DOWNGRADE, null);
+    context.setSourceAndTargetStacks(stackId2, stackId);
+    context.setVersion("2.1.1");
+    context.setResolver(resolver);    
+    
     groups = m_upgradeHelper.createSequence(upgradePack, context);
 
     assertTrue(groups.isEmpty());
@@ -1842,8 +1890,10 @@ public class UpgradeHelperTest {
 
     Cluster cluster = makeCluster();
 
-    UpgradeContext context = new UpgradeContext(m_masterHostResolver, HDP_22,
-        HDP_22, UPGRADE_VERSION, Direction.UPGRADE, UpgradeType.ROLLING);
+    UpgradeContext context = new UpgradeContext(cluster, UpgradeType.ROLLING, Direction.UPGRADE, null);
+    context.setSourceAndTargetStacks(HDP_22, HDP_22);
+    context.setVersion(UPGRADE_VERSION);
+    context.setResolver(m_masterHostResolver);    
 
     // initially, no conditions should be met
     List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java
index a72dd13..f7f8325 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/upgrade/StageWrapperBuilderTest.java
@@ -44,8 +44,7 @@ public class StageWrapperBuilderTest {
    */
   @Test
   public void testBuildOrder() throws Exception {
-    UpgradeContext upgradeContext = new UpgradeContext(null, null, null, null, Direction.UPGRADE,
-        UpgradeType.ROLLING);
+    UpgradeContext upgradeContext = new UpgradeContext(null, UpgradeType.ROLLING, Direction.UPGRADE, null);    
 
     MockStageWrapperBuilder builder = new MockStageWrapperBuilder(null);
     List<StageWrapper> stageWrappers = builder.build(upgradeContext);
@@ -67,9 +66,7 @@ public class StageWrapperBuilderTest {
    */
   @Test
   public void testAutoSkipCheckInserted() throws Exception {
-    UpgradeContext upgradeContext = new UpgradeContext(null, null, null, null, Direction.UPGRADE,
-        UpgradeType.ROLLING);
-
+    UpgradeContext upgradeContext = new UpgradeContext(null, UpgradeType.ROLLING, Direction.UPGRADE, null);    
     upgradeContext.setAutoSkipComponentFailures(true);
     upgradeContext.setAutoSkipServiceCheckFailures(true);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cfe738d4/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_host_ordered.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_host_ordered.xml b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_host_ordered.xml
new file mode 100644
index 0000000..b7ffb89
--- /dev/null
+++ b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_host_ordered.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.
+-->
+<upgrade xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="upgrade-pack.xsd">
+  <target>2.2.*</target>
+  <target-stack>HDP-2.2</target-stack>
+  <type>HOST_ORDERED</type>
+  
+  <order>
+    <group xsi:type="host-order" name="HOST_GROUP" />
+    
+    <group xsi:type="cluster" name="MANUAL_TASK" title="A manual task just for fun">
+      <execute-stage title="You need to accept this task">
+        <task xsi:type="manual">
+          <message>Please confirm you are ready to accept this task</message>
+        </task>
+      </execute-stage>
+    </group>    
+  </order>
+
+  <processing>
+    <service name="ZOOKEEPER">
+      <component name="ZOOKEEPER_SERVER">
+        <upgrade>
+          <task xsi:type="restart-task"/>
+        </upgrade>
+      </component>
+    </service>
+  </processing>
+</upgrade>


Mime
View raw message