You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/08/20 23:19:28 UTC

[2/2] ambari git commit: AMBARI-12755. Stop-and-Start Upgrade: UpgradeHelper to support nonrolling upgrade pack (alejandro)

AMBARI-12755. Stop-and-Start Upgrade: UpgradeHelper to support nonrolling upgrade pack (alejandro)


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

Branch: refs/heads/branch-dev-stop-all-upgrade
Commit: cca303d2a333d1ddc400b0d6a49bf536b218386e
Parents: 739cdec
Author: Alejandro Fernandez <af...@hortonworks.com>
Authored: Fri Aug 14 15:12:34 2015 -0700
Committer: Alejandro Fernandez <af...@hortonworks.com>
Committed: Thu Aug 20 14:17:24 2015 -0700

----------------------------------------------------------------------
 .../AmbariCustomCommandExecutionHelper.java     |   2 +-
 .../internal/UpgradeResourceProvider.java       |  61 ++-
 .../ambari/server/metadata/ActionMetadata.java  |   4 +-
 .../ambari/server/state/UpgradeContext.java     |  14 +-
 .../ambari/server/state/UpgradeHelper.java      |  78 +++-
 .../ambari/server/state/stack/UpgradePack.java  |  59 ++-
 .../state/stack/upgrade/ClusterGrouping.java    |  27 +-
 .../state/stack/upgrade/ConfigureTask.java      |  12 +
 .../server/state/stack/upgrade/ExecuteTask.java |  12 +
 .../server/state/stack/upgrade/Grouping.java    |  40 +-
 .../server/state/stack/upgrade/ManualTask.java  |   4 +
 .../state/stack/upgrade/RestartGrouping.java    |  36 ++
 .../server/state/stack/upgrade/RestartTask.java |  14 +-
 .../state/stack/upgrade/ServerActionTask.java   |   4 +
 .../stack/upgrade/ServerSideActionTask.java     |   7 +
 .../stack/upgrade/ServiceCheckGrouping.java     |  33 +-
 .../state/stack/upgrade/ServiceCheckTask.java   |  12 +
 .../state/stack/upgrade/StageWrapper.java       |   6 +-
 .../stack/upgrade/StageWrapperBuilder.java      |  12 +-
 .../state/stack/upgrade/StartGrouping.java      |  36 ++
 .../server/state/stack/upgrade/StartTask.java   |  53 +++
 .../state/stack/upgrade/StopGrouping.java       |  36 ++
 .../server/state/stack/upgrade/StopTask.java    |  53 +++
 .../ambari/server/state/stack/upgrade/Task.java |  22 +-
 .../state/stack/upgrade/UpgradeFunction.java    |  26 ++
 .../server/state/stack/upgrade/UpgradeType.java |  36 ++
 .../0.96.0.2.0/package/scripts/hbase_upgrade.py |   6 +-
 .../HDFS/2.1.0.2.0/package/scripts/namenode.py  |  15 +
 .../HDP/2.2/upgrades/nonrolling-upgrade-2.2.xml | 459 +++++++++++++++++++
 .../stacks/HDP/2.2/upgrades/upgrade-2.2.xml     |  86 ++--
 .../stacks/HDP/2.2/upgrades/upgrade-2.3.xml     |  99 ++--
 .../stacks/HDP/2.3/upgrades/upgrade-2.3.xml     | 108 ++---
 .../AmbariManagementControllerTest.java         |   2 +-
 .../ambari/server/state/UpgradeHelperTest.java  |  31 +-
 .../server/state/stack/UpgradePackTest.java     | 103 ++++-
 .../stacks/2.0.6/HBASE/test_hbase_master.py     |   2 +-
 .../HDP/2.1.1/upgrades/upgrade_bucket_test.xml  |   1 +
 .../HDP/2.1.1/upgrades/upgrade_direction.xml    |   3 +-
 .../stacks/HDP/2.1.1/upgrades/upgrade_test.xml  |   7 +-
 .../HDP/2.1.1/upgrades/upgrade_test_checks.xml  |   7 +-
 .../2.1.1/upgrades/upgrade_test_nonrolling.xml  | 162 +++++++
 .../HDP/2.1.1/upgrades/upgrade_to_new_stack.xml |   7 +-
 .../stacks/HDP/2.2.0/upgrades/upgrade_test.xml  |   7 +-
 .../HDP/2.2.0/upgrades/upgrade_test_checks.xml  |   7 +-
 44 files changed, 1543 insertions(+), 268 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
index 43bdbfe..6ac3ed7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
@@ -919,7 +919,7 @@ public class AmbariCustomCommandExecutionHelper {
 
         String commandDetail = getReadableCustomCommandDetail(actionExecutionContext, resourceFilter);
 
-        Map<String, String> extraParams = new HashMap<String, String>();;
+        Map<String, String> extraParams = new HashMap<String, String>();
         String componentName = (null == resourceFilter.getComponentName()) ? null :
             resourceFilter.getComponentName().toLowerCase();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/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 770cc04..fa743be 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
@@ -94,6 +94,7 @@ import org.apache.ambari.server.state.stack.upgrade.ServerSideActionTask;
 import org.apache.ambari.server.state.stack.upgrade.StageWrapper;
 import org.apache.ambari.server.state.stack.upgrade.Task;
 import org.apache.ambari.server.state.stack.upgrade.TaskWrapper;
+import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostServerActionEvent;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
@@ -137,6 +138,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
   private static final String COMMAND_PARAM_VERSION = VERSION;
   private static final String COMMAND_PARAM_CLUSTER_NAME = "clusterName";
   private static final String COMMAND_PARAM_DIRECTION = "upgrade_direction";
+  // TODO AMBARI-12698, change this variable name since it is no longer always a restart. Possible values are rolling_upgrade or nonrolling_upgrade
+  // This will involve changing Script.py
   private static final String COMMAND_PARAM_RESTART_TYPE = "restart_type";
   private static final String COMMAND_PARAM_TASKS = "tasks";
   private static final String COMMAND_PARAM_STRUCT_OUT = "structured_out";
@@ -567,7 +570,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     }
 
     UpgradeContext ctx = new UpgradeContext(resolver, sourceStackId, targetStackId, version,
-        direction);
+        direction, pack.getType());
 
     if (direction.isDowngrade()) {
       if (requestMap.containsKey(UPGRADE_FROM_VERSION)) {
@@ -721,6 +724,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     Map<String, Map<String, String>> newConfigurationsByType = null;
     ConfigHelper configHelper = getManagementController().getConfigHelper();
 
+    // TODO AMBARI-12698, handle jumping across several stacks and applying configs.
     if (direction == Direction.UPGRADE) {
       // populate a map of default configurations for the old stack (this is
       // used when determining if a property has been customized and should be
@@ -876,8 +880,10 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
           throws AmbariException {
 
     switch (wrapper.getType()) {
+      case START:
+      case STOP:
       case RESTART:
-        makeRestartStage(context, request, entity, wrapper, skippable, allowRetry);
+        makeCommandStage(context, request, entity, wrapper, skippable, allowRetry);
         break;
       case RU_TASKS:
         makeActionStage(context, request, entity, wrapper, skippable, allowRetry);
@@ -967,7 +973,17 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     request.addStages(Collections.singletonList(stage));
   }
 
-  private void makeRestartStage(UpgradeContext context, RequestStageContainer request,
+  /**
+   * Used to create a stage for restart, start, or stop.
+   * @param context Upgrade Context
+   * @param request Container for stage
+   * @param entity Upgrade Item
+   * @param wrapper Stage
+   * @param skippable Whether the item can be skipped
+   * @param allowRetry Whether the item is allowed to be retried
+   * @throws AmbariException
+   */
+  private void makeCommandStage(UpgradeContext context, RequestStageContainer request,
       UpgradeItemEntity entity, StageWrapper wrapper, boolean skippable, boolean allowRetry)
           throws AmbariException {
 
@@ -981,16 +997,36 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
           new ArrayList<String>(tw.getHosts())));
     }
 
-    Map<String, String> restartCommandParams = getNewParameterMap();
-    restartCommandParams.put(COMMAND_PARAM_RESTART_TYPE, "rolling_upgrade");
-    restartCommandParams.put(COMMAND_PARAM_VERSION, context.getVersion());
-    restartCommandParams.put(COMMAND_PARAM_DIRECTION, context.getDirection().name().toLowerCase());
-    restartCommandParams.put(COMMAND_PARAM_ORIGINAL_STACK,context.getOriginalStackId().getStackId());
-    restartCommandParams.put(COMMAND_PARAM_TARGET_STACK, context.getTargetStackId().getStackId());
-    restartCommandParams.put(COMMAND_DOWNGRADE_FROM_VERSION, context.getDowngradeFromVersion());
+    String function = null;
+    switch (wrapper.getType()) {
+      case START:
+      case STOP:
+      case RESTART:
+        function = wrapper.getType().name();
+        break;
+      default:
+        function = "UNKNOWN";
+        break;
+    }
+
+    Map<String, String> commandParams = getNewParameterMap();
+
+    // TODO AMBARI-12698, change COMMAND_PARAM_RESTART_TYPE to something that isn't "RESTART" specific.
+    if (context.getType() == UpgradeType.ROLLING) {
+      commandParams.put(COMMAND_PARAM_RESTART_TYPE, "rolling_upgrade");
+    }
+    if (context.getType() == UpgradeType.NONROLLING) {
+      commandParams.put(COMMAND_PARAM_RESTART_TYPE, "nonrolling_upgrade");
+    }
+
+    commandParams.put(COMMAND_PARAM_VERSION, context.getVersion());
+    commandParams.put(COMMAND_PARAM_DIRECTION, context.getDirection().name().toLowerCase());
+    commandParams.put(COMMAND_PARAM_ORIGINAL_STACK, context.getOriginalStackId().getStackId());
+    commandParams.put(COMMAND_PARAM_TARGET_STACK, context.getTargetStackId().getStackId());
+    commandParams.put(COMMAND_DOWNGRADE_FROM_VERSION, context.getDowngradeFromVersion());
 
     ActionExecutionContext actionContext = new ActionExecutionContext(cluster.getClusterName(),
-        "RESTART", filters, restartCommandParams);
+        function, filters, commandParams);
     actionContext.setTimeout(Short.valueOf(s_configuration.getDefaultAgentTaskTimeout(false)));
     actionContext.setIgnoreMaintenance(true);
 
@@ -1008,11 +1044,12 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     if (0L == stageId) {
       stageId = 1L;
     }
+
     stage.setStageId(stageId);
     entity.setStageId(Long.valueOf(stageId));
 
     Map<String, String> requestParams = new HashMap<String, String>();
-    requestParams.put("command", "RESTART");
+    requestParams.put("command", function);
 
     s_commandExecutionHelper.get().addExecutionCommandsToStage(actionContext, stage, requestParams,
         allowRetry);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/metadata/ActionMetadata.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/metadata/ActionMetadata.java b/ambari-server/src/main/java/org/apache/ambari/server/metadata/ActionMetadata.java
index e821827..f5642a0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/metadata/ActionMetadata.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/metadata/ActionMetadata.java
@@ -58,8 +58,10 @@ public class ActionMetadata {
 
   private void fillHostComponentCommands() {
     //Standart commands for any host component
-    // TODO: Add START/STOP/INSTALL commands
     defaultHostComponentCommands.add("RESTART");
+    defaultHostComponentCommands.add("START");
+    defaultHostComponentCommands.add("STOP");
+    defaultHostComponentCommands.add("INSTALL");
     defaultHostComponentCommands.add("CONFIGURE");
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/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 86dbccd..8b17d49 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
@@ -25,6 +25,7 @@ import java.util.Map;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.stack.MasterHostResolver;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 
 /**
  * Used to hold various helper objects required to process an upgrade pack.
@@ -54,6 +55,7 @@ public class UpgradeContext {
   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;
 
   /**
    * Constructor.
@@ -74,15 +76,18 @@ public class UpgradeContext {
    *          the target version to upgrade to
    * @param direction
    *          the direction for the upgrade
+   * @param type
+   *          the type of upgrade, either rolling or nonrolling
    */
   public UpgradeContext(MasterHostResolver resolver, StackId sourceStackId,
       StackId targetStackId, String version,
-      Direction direction) {
+      Direction direction, UpgradeType type) {
     m_version = version;
     m_originalStackId = sourceStackId;
     m_targetStackId = targetStackId;
     m_direction = direction;
     m_resolver = resolver;
+    m_type = type;
   }
 
   /**
@@ -107,6 +112,13 @@ public class UpgradeContext {
   }
 
   /**
+   * @return the type of upgrade.
+   */
+  public UpgradeType getType() {
+    return m_type;
+  }
+
+  /**
    * @return the resolver
    */
   public MasterHostResolver getResolver() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/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 5e63744..3e25d01 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
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.state;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashSet;
@@ -49,11 +50,18 @@ import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
 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.ManualTask;
+import org.apache.ambari.server.state.stack.upgrade.RestartGrouping;
+import org.apache.ambari.server.state.stack.upgrade.RestartTask;
 import org.apache.ambari.server.state.stack.upgrade.StageWrapper;
 import org.apache.ambari.server.state.stack.upgrade.StageWrapperBuilder;
+import org.apache.ambari.server.state.stack.upgrade.StartGrouping;
+import org.apache.ambari.server.state.stack.upgrade.StartTask;
+import org.apache.ambari.server.state.stack.upgrade.StopGrouping;
+import org.apache.ambari.server.state.stack.upgrade.StopTask;
 import org.apache.ambari.server.state.stack.upgrade.Task;
 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.UpgradeType;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -189,6 +197,7 @@ public class UpgradeHelper {
     Cluster cluster = context.getCluster();
     MasterHostResolver mhr = context.getResolver();
 
+    // Note, only a Rolling Upgrade uses processing tasks.
     Map<String, Map<String, ProcessingComponent>> allTasks = upgradePack.getTasks();
     List<UpgradeGroupHolder> groups = new ArrayList<UpgradeGroupHolder>();
 
@@ -205,29 +214,52 @@ public class UpgradeHelper {
         groupHolder.skippable = true;
       }
 
+      // NonRolling defaults to not performing service checks on a group.
+      // Of course, a Service Check Group does indeed run them.
+      if (upgradePack.getType() == UpgradeType.NONROLLING) {
+        group.performServiceCheck = false;
+      }
+
       StageWrapperBuilder builder = group.getBuilder();
 
       List<UpgradePack.OrderService> services = group.services;
 
-      if (context.getDirection().isDowngrade() && !services.isEmpty()) {
-        List<UpgradePack.OrderService> reverse = new ArrayList<UpgradePack.OrderService>(services);
-        Collections.reverse(reverse);
-        services = reverse;
+      // Rolling Downgrade must reverse the order of services.
+      if (upgradePack.getType() == UpgradeType.ROLLING) {
+        if (context.getDirection().isDowngrade() && !services.isEmpty()) {
+          List<UpgradePack.OrderService> reverse = new ArrayList<UpgradePack.OrderService>(services);
+          Collections.reverse(reverse);
+          services = reverse;
+        }
       }
 
       // !!! cluster and service checks are empty here
       for (UpgradePack.OrderService service : services) {
-
-        if (!allTasks.containsKey(service.serviceName)) {
+      
+        if (upgradePack.getType() == UpgradeType.ROLLING && !allTasks.containsKey(service.serviceName)) {
           continue;
         }
+        
+        // Attempt to get the function of the group, during a NonRolling Upgrade
+        Task.Type functionName = null;
+
+        if (RestartGrouping.class.isInstance(group)) {
+          functionName = ((RestartGrouping) group).getFunction();
+        }
+        if (StartGrouping.class.isInstance(group)) {
+          functionName = ((StartGrouping) group).getFunction();
+        }
+        if (StopGrouping.class.isInstance(group)) {
+          functionName = ((StopGrouping) group).getFunction();
+        }
 
         for (String component : service.components) {
-          if (!allTasks.get(service.serviceName).containsKey(component)) {
+          if (upgradePack.getType() == UpgradeType.ROLLING && !allTasks.get(service.serviceName).containsKey(component)) {
             continue;
           }
-
+          
           HostsType hostsType = mhr.getMasterAndHosts(service.serviceName, component);
+          // TODO AMBARI-12698, how does this impact SECONDARY NAMENODE if there's no NameNode HA?
           if (null == hostsType) {
             continue;
           }
@@ -237,7 +269,31 @@ public class UpgradeHelper {
           }
 
           Service svc = cluster.getService(service.serviceName);
-          ProcessingComponent pc = allTasks.get(service.serviceName).get(component);
+
+          ProcessingComponent pc = null;
+          if (upgradePack.getType() == UpgradeType.ROLLING) {
+            pc = allTasks.get(service.serviceName).get(component);
+          } else if (upgradePack.getType() == UpgradeType.NONROLLING) {
+            // Construct a processing task on-the-fly
+            if (null != functionName) {
+              pc = new ProcessingComponent();
+              pc.name = component;
+              pc.tasks = new ArrayList<Task>();
+
+              if (functionName == Type.START) {
+                pc.tasks.add(new StartTask());
+              } else if (functionName == Type.STOP) {
+                pc.tasks.add(new StopTask());
+              } else if (functionName == Type.RESTART) {
+                pc.tasks.add(new RestartTask());
+              }
+            }
+          }
+
+          if (pc == null) {
+            LOG.error(MessageFormat.format("Couldn't create a processing component for service {0} and component {1}.", service.serviceName, component));
+            continue;
+          }
 
           setDisplayNames(context, service.serviceName, component);
 
@@ -441,8 +497,6 @@ public class UpgradeHelper {
     public List<StageWrapper> items = new ArrayList<StageWrapper>();
   }
 
-
-
   /**
    * Gets a set of Stages resources to aggregate an UpgradeItem with Stage.
    *
@@ -509,8 +563,6 @@ public class UpgradeHelper {
     } catch (AmbariException e) {
       LOG.debug("Could not get service detail", e);
     }
-
-
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
index 9691292..79b8eb5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
@@ -36,6 +36,7 @@ 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.ServiceCheckGrouping;
 import org.apache.ambari.server.state.stack.upgrade.Task;
+import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 
 /**
  * Represents an upgrade pack.
@@ -50,11 +51,18 @@ public class UpgradePack {
   @XmlElement(name="target-stack")
   private String targetStack;
 
+  @XmlElement(name="type", defaultValue="rolling")
+  private UpgradeType type;
 
   @XmlElementWrapper(name="order")
   @XmlElement(name="group")
   private List<Grouping> groups;
 
+  /**
+   * In the case of a rolling upgrade, will specify processing logic for a particular component.
+   * Nonrolling upgrades are simpler so the "processing" is embedded into the  group's "type", which is a function like
+   * "stop" or "start".
+   */
   @XmlElementWrapper(name="processing")
   @XmlElement(name="service")
   private List<ProcessingService> processing;
@@ -81,13 +89,29 @@ public class UpgradePack {
   }
 
   /**
+   * @return the type of upgrade, e.g., "rolling" or "nonrolling"
+   */
+  public UpgradeType getType() {
+    return type;
+  }
+
+  /**
    * Gets the groups defined for the upgrade pack.  If a direction is defined
    * for a group, it must match the supplied direction to be returned
    * @param direction the direction to return the ordered groups
    * @return the list of groups
    */
   public List<Grouping> getGroups(Direction direction) {
-    List<Grouping> list = direction.isUpgrade() ? groups : getDowngradeGroups();
+    List<Grouping> list = new ArrayList<Grouping>();
+    if (direction.isUpgrade()) {
+      list = groups;
+    } else {
+      if (type == UpgradeType.ROLLING) {
+        list = getDowngradeGroupsForRolling();
+      } else if (type == UpgradeType.NONROLLING) {
+        list = getDowngradeGroupsForNonrolling();
+      }
+    }
 
     List<Grouping> checked = new ArrayList<Grouping>();
     for (Grouping group : list) {
@@ -100,7 +124,7 @@ public class UpgradePack {
   }
 
   /**
-   * Calculates the group orders when performing a downgrade
+   * Calculates the group orders when performing a rolling downgrade
    * <ul>
    *   <li>ClusterGroupings must remain at the same positions (first/last).</li>
    *   <li>When there is a ServiceCheck group, it must ALWAYS follow the same</li>
@@ -131,7 +155,7 @@ public class UpgradePack {
    * </ol>
    * @return the list of groups, reversed appropriately for a downgrade.
    */
-  private List<Grouping> getDowngradeGroups() {
+  private List<Grouping> getDowngradeGroupsForRolling() {
     List<Grouping> reverse = new ArrayList<Grouping>();
 
     int idx = 0;
@@ -161,6 +185,17 @@ public class UpgradePack {
     return reverse;
   }
 
+  private List<Grouping> getDowngradeGroupsForNonrolling() {
+    throw new UnsupportedOperationException("TODO AMBARI-12698");
+    /*
+    List<Grouping> list = new ArrayList<Grouping>();
+    for (Grouping g : groups) {
+      list.add(g);
+    }
+    return list;
+    */
+  }
+
   /**
    * Gets the tasks by which services and components should be upgraded.
    * @return a map of service_name -> map(component_name -> process).
@@ -170,15 +205,17 @@ public class UpgradePack {
     if (null == m_process) {
       m_process = new LinkedHashMap<String, Map<String, ProcessingComponent>>();
 
-      for (ProcessingService svc : processing) {
-        if (!m_process.containsKey(svc.name)) {
-          m_process.put(svc.name, new LinkedHashMap<String, ProcessingComponent>());
-        }
+      if (processing != null) {
+        for (ProcessingService svc : processing) {
+          if (!m_process.containsKey(svc.name)) {
+            m_process.put(svc.name, new LinkedHashMap<String, ProcessingComponent>());
+          }
 
-        Map<String, ProcessingComponent> componentMap = m_process.get(svc.name);
+          Map<String, ProcessingComponent> componentMap = m_process.get(svc.name);
 
-        for (ProcessingComponent pc : svc.components) {
-          componentMap.put(pc.name, pc);
+          for (ProcessingComponent pc : svc.components) {
+            componentMap.put(pc.name, pc);
+          }
         }
       }
     }
@@ -210,8 +247,6 @@ public class UpgradePack {
     public List<ProcessingComponent> components;
   }
 
-
-
   /**
    * A component definition in the 'processing/service' path.
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
index ad84210..648e7a4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
@@ -52,7 +52,7 @@ import com.google.gson.JsonPrimitive;
 public class ClusterGrouping extends Grouping {
 
   /**
-   * Stages against a Service and Component, or the Server
+   * Stages against a Service and Component, or the Server, that doesn't need a Processing Component.
    */
   @XmlElement(name="execute-stage")
   public List<ExecuteStage> executionStages;
@@ -153,6 +153,12 @@ public class ClusterGrouping extends Grouping {
     }
   }
 
+  /**
+   * Return a Stage Wrapper for a manual task that runs on the server.
+   * @param ctx Upgrade Context
+   * @param execution Execution Stage
+   * @return Returns a Stage Wrapper
+   */
   private StageWrapper getManualStageWrapper(UpgradeContext ctx, ExecuteStage execution) {
 
     String service   = execution.service;
@@ -191,6 +197,12 @@ public class ClusterGrouping extends Grouping {
         new TaskWrapper(service, component, realHosts, task));
   }
 
+  /**
+   * Return a Stage Wrapper for a task meant to execute code, typically on Ambari Server.
+   * @param ctx Upgrade Context
+   * @param execution Execution Stage
+   * @return Returns a Stage Wrapper, or null if a valid one could not be created.
+   */
   private StageWrapper getExecuteStageWrapper(UpgradeContext ctx, ExecuteStage execution) {
     String service   = execution.service;
     String component = execution.component;
@@ -234,15 +246,18 @@ public class ClusterGrouping extends Grouping {
       return new StageWrapper(
           StageWrapper.Type.RU_TASKS, execution.title,
           new TaskWrapper(service, component, hostNames, et));
-
     }
     return null;
   }
 
-  private void fillHostDetails(ManualTask mt, Map<String, List<String>> unhealthy) {
-
+  /**
+   * Populates the manual task, mt, with information about the list of hosts.
+   * @param mt Manual Task
+   * @param hostToComponents Map from host name to list of components
+   */
+  private void fillHostDetails(ManualTask mt, Map<String, List<String>> hostToComponents) {
     JsonArray arr = new JsonArray();
-    for (Entry<String, List<String>> entry : unhealthy.entrySet()) {
+    for (Entry<String, List<String>> entry : hostToComponents.entrySet()) {
       JsonObject hostObj = new JsonObject();
       hostObj.addProperty("host", entry.getKey());
 
@@ -259,7 +274,5 @@ public class ClusterGrouping extends Grouping {
     obj.add("unhealthy", arr);
 
     mt.structuredOut = obj.toString();
-
   }
-
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
index 8a9e2e5..8361ea6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
@@ -109,6 +109,8 @@ public class ConfigureTask extends ServerSideActionTask {
    */
   public static final String PARAMETER_REPLACEMENTS = "configure-task-replacements";
 
+  public static final String actionVerb = "Configuring";
+  
   /**
    * Gson
    */
@@ -148,6 +150,16 @@ public class ConfigureTask extends ServerSideActionTask {
     return type;
   }
 
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.SERVER_SIDE_ACTION;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
+
   /**
    * @return the config type
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ExecuteTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ExecuteTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ExecuteTask.java
index a0afdfb..d175a13 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ExecuteTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ExecuteTask.java
@@ -66,8 +66,20 @@ public class ExecuteTask extends Task {
   @XmlElement(name="command")
   public String command;
 
+  public static final String actionVerb = "Executing";
+
   @Override
   public Task.Type getType() {
     return type;
   }
+
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.RU_TASKS;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
index a1e1fcd..9d89b7a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
@@ -36,7 +36,7 @@ import org.apache.commons.lang.StringUtils;
 /**
  *
  */
-@XmlSeeAlso(value = { ColocatedGrouping.class, ClusterGrouping.class, ServiceCheckGrouping.class })
+@XmlSeeAlso(value = { ColocatedGrouping.class, ClusterGrouping.class, ServiceCheckGrouping.class, RestartGrouping.class, StartGrouping.class, StopGrouping.class })
 public class Grouping {
 
   @XmlAttribute(name="name")
@@ -60,7 +60,6 @@ public class Grouping {
   @XmlElement(name="direction")
   public Direction intendedDirection = null;
 
-
   /**
    * Gets the default builder.
    */
@@ -68,11 +67,11 @@ public class Grouping {
     return new DefaultBuilder(performServiceCheck);
   }
 
-
   private static class DefaultBuilder extends StageWrapperBuilder {
 
     private List<StageWrapper> m_stages = new ArrayList<StageWrapper>();
     private Set<String> m_servicesToCheck = new HashSet<String>();
+
     private boolean m_serviceCheck = true;
 
     private DefaultBuilder(boolean serviceCheck) {
@@ -89,9 +88,9 @@ public class Grouping {
     @Override
     public void add(UpgradeContext ctx, HostsType hostsType, String service,
        boolean clientOnly, ProcessingComponent pc) {
-
       boolean forUpgrade = ctx.getDirection().isUpgrade();
 
+      // Construct the pre tasks during Upgrade/Downgrade direction.
       List<TaskBucket> buckets = buckets(resolveTasks(forUpgrade, true, pc));
       for (TaskBucket bucket : buckets) {
         List<TaskWrapper> preTasks = TaskWrapperBuilder.getTaskList(service, pc.name, hostsType, bucket.tasks);
@@ -106,20 +105,20 @@ public class Grouping {
         }
       }
 
-      // !!! FIXME upgrade definition have only one step, and it better be a restart
+      // Add the processing component
       if (null != pc.tasks && 1 == pc.tasks.size()) {
         Task t = pc.tasks.get(0);
-        if (RestartTask.class.isInstance(t)) {
-          for (String hostName : hostsType.hosts) {
-            StageWrapper stage = new StageWrapper(
-                StageWrapper.Type.RESTART,
-                getStageText("Restarting", ctx.getComponentDisplay(service, pc.name), Collections.singleton(hostName)),
-                new TaskWrapper(service, pc.name, Collections.singleton(hostName), t));
-            m_stages.add(stage);
-          }
+
+        for (String hostName : hostsType.hosts) {
+          StageWrapper stage = new StageWrapper(
+              t.getStageWrapperType(),
+              getStageText(t.getActionVerb(), ctx.getComponentDisplay(service, pc.name), Collections.singleton(hostName)),
+              new TaskWrapper(service, pc.name, Collections.singleton(hostName), t));
+          m_stages.add(stage);
         }
       }
 
+      // Construct the post tasks during Upgrade/Downgrade direction.
       buckets = buckets(resolveTasks(forUpgrade, false, pc));
       for (TaskBucket bucket : buckets) {
         List<TaskWrapper> postTasks = TaskWrapperBuilder.getTaskList(service, pc.name, hostsType, bucket.tasks);
@@ -134,11 +133,17 @@ public class Grouping {
         }
       }
 
-      if (!clientOnly) {
+      // Potentially add a service check
+      if (this.m_serviceCheck && !clientOnly) {
         m_servicesToCheck.add(service);
       }
     }
 
+    /**
+     * Determine if service checks need to be ran after the stages.
+     * @param ctx the upgrade context
+     * @return Return the stages, which may potentially be followed by service checks.
+     */
     @Override
     public List<StageWrapper> build(UpgradeContext ctx) {
 
@@ -191,7 +196,6 @@ public class Grouping {
     }
 
     return holders;
-
   }
 
   private static class TaskBucket {
@@ -210,6 +214,12 @@ public class Grouping {
         case RESTART:
           type = StageWrapper.Type.RESTART;
           break;
+        case START:
+          type = StageWrapper.Type.START;
+          break;
+        case STOP:
+          type = StageWrapper.Type.STOP;
+          break;
         case SERVICE_CHECK:
           type = StageWrapper.Type.SERVICE_CHECK;
           break;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ManualTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ManualTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ManualTask.java
index 2b1ba56..a0a347a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ManualTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ManualTask.java
@@ -52,4 +52,8 @@ public class ManualTask extends ServerSideActionTask {
     return type;
   }
 
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.SERVER_SIDE_ACTION;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartGrouping.java
new file mode 100644
index 0000000..529cadd
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartGrouping.java
@@ -0,0 +1,36 @@
+/**
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Used for a group that restarts services.
+ */
+@XmlType(name="restart")
+public class RestartGrouping extends Grouping implements UpgradeFunction {
+
+  private static Logger LOG = LoggerFactory.getLogger(RestartGrouping.class);
+
+  @Override
+  public Task.Type getFunction() {
+    return Task.Type.RESTART;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartTask.java
index 1b69b5b..fac0179 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RestartTask.java
@@ -28,14 +28,26 @@ import javax.xml.bind.annotation.XmlType;
  */
 @XmlRootElement
 @XmlAccessorType(XmlAccessType.FIELD)
-@XmlType(name="restart")
+@XmlType(name="restart-task")
 public class RestartTask extends Task {
 
   @XmlTransient
   private Task.Type type = Task.Type.RESTART;
 
+  public static final String actionVerb = "Restarting";
+
   @Override
   public Task.Type getType() {
     return type;
   }
+
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.RESTART;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java
index 7a42c3b..ea59d65 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerActionTask.java
@@ -41,4 +41,8 @@ public class ServerActionTask extends ServerSideActionTask {
     return type;
   }
 
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.SERVER_SIDE_ACTION;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerSideActionTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerSideActionTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerSideActionTask.java
index 97981ae..595465d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerSideActionTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServerSideActionTask.java
@@ -27,7 +27,14 @@ public abstract class ServerSideActionTask extends Task {
   @XmlAttribute(name="class")
   protected String implClass;
 
+  public static final String actionVerb = "Executing";
+
   public String getImplementationClass() {
     return implClass;
   }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java
index 4fe5e98..68fec03 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckGrouping.java
@@ -50,10 +50,17 @@ public class ServiceCheckGrouping extends Grouping {
 
   private static Logger LOG = LoggerFactory.getLogger(ServiceCheckGrouping.class);
 
+  /**
+   * During a Rolling Upgrade, the priority services are ran first, then the remaining services in the cluster.
+   * During a Stop-and-Start Upgrade, only the priority services are ran.
+   */
   @XmlElementWrapper(name="priority")
   @XmlElement(name="service")
   private Set<String> priorityServices = new HashSet<String>();
 
+  /**
+   * During a Rolling Upgrade, exclude certain services.
+   */
   @XmlElementWrapper(name="exclude")
   @XmlElement(name="service")
   private Set<String> excludeServices = new HashSet<String>();
@@ -107,18 +114,20 @@ public class ServiceCheckGrouping extends Grouping {
         }
       }
 
-      // create stages for everything else, as long it is valid
-      for (String service : clusterServices) {
-        if (ServiceCheckGrouping.this.excludeServices.contains(service)) {
-          continue;
-        }
-        if (checkServiceValidity(ctx, service, serviceMap)) {
-          StageWrapper wrapper = new StageWrapper(
-              StageWrapper.Type.SERVICE_CHECK,
-              "Service Check " + ctx.getServiceDisplay(service),
-              new TaskWrapper(service, "", Collections.<String>emptySet(),
-                  new ServiceCheckTask()));
-          result.add(wrapper);
+      if (ctx.getType() == UpgradeType.ROLLING) {
+        // During Rolling Upgrade, create stages for everything else, as long it is valid
+        for (String service : clusterServices) {
+          if (ServiceCheckGrouping.this.excludeServices.contains(service)) {
+            continue;
+          }
+          if (checkServiceValidity(ctx, service, serviceMap)) {
+            StageWrapper wrapper = new StageWrapper(
+                StageWrapper.Type.SERVICE_CHECK,
+                "Service Check " + ctx.getServiceDisplay(service),
+                new TaskWrapper(service, "", Collections.<String>emptySet(),
+                    new ServiceCheckTask()));
+            result.add(wrapper);
+          }
         }
       }
       return result;

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckTask.java
index 5893edf..d6c19b8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckTask.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ServiceCheckTask.java
@@ -34,8 +34,20 @@ public class ServiceCheckTask extends Task {
   @XmlTransient
   private Task.Type type = Task.Type.SERVICE_CHECK;
 
+  public static final String actionVerb = "Running";
+
   @Override
   public Task.Type getType() {
     return type;
   }
+
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.SERVICE_CHECK;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/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 eac5ce5..92df3b5 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
@@ -92,7 +92,7 @@ public class StageWrapper {
   }
 
   /**
-   * @param text the new text for the stage
+   * @param newText the new text for the stage
    */
   public void setText(String newText) {
     text = newText;
@@ -113,6 +113,8 @@ public class StageWrapper {
     SERVER_SIDE_ACTION,
     RESTART,
     RU_TASKS,
-    SERVICE_CHECK
+    SERVICE_CHECK,
+    STOP,
+    START
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/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 f7b37ab..c9c6b8c 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
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.state.stack.upgrade;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -25,7 +26,7 @@ import org.apache.ambari.server.state.UpgradeContext;
 import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
 
 /**
- * Defines how to build stages.
+ * Defines how to build stages for an Upgrade or Downgrade.
  */
 public abstract class StageWrapperBuilder {
 
@@ -73,9 +74,14 @@ public abstract class StageWrapperBuilder {
    * @param forUpgrade  {@code true} if resolving for an upgrade, {@code false} for downgrade
    * @param preTasks    {@code true} if loading pre-upgrade or pre-downgrade
    * @param pc          the processing component holding task definitions
-   * @return
+   * @return A collection, potentially empty, of the tasks to run, which may contain either
+   * pre or post tasks if they exist, and the order depends on whether it's an upgrade or downgrade.
    */
   protected List<Task> resolveTasks(boolean forUpgrade, boolean preTasks, ProcessingComponent pc) {
+    if (null == pc) {
+      return Collections.emptyList();
+    }
+
     if (forUpgrade) {
       return preTasks ? pc.preTasks : pc.postTasks;
     } else {
@@ -84,6 +90,4 @@ public abstract class StageWrapperBuilder {
         (null == pc.postDowngradeTasks ? pc.postTasks : pc.postDowngradeTasks);
     }
   }
-
-
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartGrouping.java
new file mode 100644
index 0000000..7237599
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartGrouping.java
@@ -0,0 +1,36 @@
+/**
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ *  Used for a group that starts services.
+ */
+@XmlType(name="start")
+public class StartGrouping extends Grouping implements UpgradeFunction {
+
+  private static Logger LOG = LoggerFactory.getLogger(StartGrouping.class);
+
+  @Override
+  public Task.Type  getFunction() {
+    return Task.Type.START;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartTask.java
new file mode 100644
index 0000000..4d05dcb
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StartTask.java
@@ -0,0 +1,53 @@
+/**
+ * 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 javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Used to represent a start of a component.
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name="start-task")
+public class StartTask extends Task {
+
+  @XmlTransient
+  private Type type = Type.START;
+
+  public static final String actionVerb = "Starting";
+
+  @Override
+  public Type getType() {
+    return type;
+  }
+
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.START;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopGrouping.java
new file mode 100644
index 0000000..5cf1149
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopGrouping.java
@@ -0,0 +1,36 @@
+/**
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ *  Used for a group that stops services.
+ */
+@XmlType(name="stop")
+public class StopGrouping extends Grouping implements UpgradeFunction {
+
+  private static Logger LOG = LoggerFactory.getLogger(StopGrouping.class);
+
+  @Override
+  public Task.Type getFunction() {
+    return Task.Type.STOP;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopTask.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopTask.java
new file mode 100644
index 0000000..30a557f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StopTask.java
@@ -0,0 +1,53 @@
+/**
+ * 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 javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Used to represent a stop of a component.
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name="stop-task")
+public class StopTask extends Task {
+
+  @XmlTransient
+  private Type type = Type.STOP;
+
+  public static final String actionVerb = "Stopping";
+
+  @Override
+  public Type getType() {
+    return type;
+  }
+
+  @Override
+  public StageWrapper.Type getStageWrapperType() {
+    return StageWrapper.Type.STOP;
+  }
+
+  @Override
+  public String getActionVerb() {
+    return actionVerb;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
index 6416b57..f443e53 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
@@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlSeeAlso;
 /**
  * Base class to identify the items that could possibly occur during an upgrade
  */
-@XmlSeeAlso(value={ExecuteTask.class, ConfigureTask.class, ManualTask.class, RestartTask.class, ServerActionTask.class})
+@XmlSeeAlso(value={ExecuteTask.class, ConfigureTask.class, ManualTask.class, RestartTask.class, StartTask.class, StopTask.class, ServerActionTask.class})
 public abstract class Task {
 
   /**
@@ -38,6 +38,16 @@ public abstract class Task {
    */
   public abstract Type getType();
 
+  /**
+   * @return when a single Task is constructed, this is the type of stage it should belong to.
+   */
+  public abstract StageWrapper.Type getStageWrapperType();
+
+  /**
+   * @return a verb to display that describes the type of task, e.g., "executing".
+   */
+  public abstract String getActionVerb();
+
   @Override
   public String toString() {
     return getType().toString();
@@ -64,6 +74,14 @@ public abstract class Task {
      */
     RESTART,
     /**
+     * Task that is a start command.
+     */
+    START,
+    /**
+     * Task that is a stop command.
+     */
+    STOP,
+    /**
      * Task that is a service check
      */
     SERVICE_CHECK,
@@ -83,7 +101,7 @@ public abstract class Task {
      * @return {@code true} if the task is a command type (as opposed to an action)
      */
     public boolean isCommand() {
-      return this == RESTART || this == SERVICE_CHECK;
+      return this == RESTART || this == START || this == STOP || this == SERVICE_CHECK;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeFunction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeFunction.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeFunction.java
new file mode 100644
index 0000000..d58316d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeFunction.java
@@ -0,0 +1,26 @@
+/**
+ * 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;
+
+public interface UpgradeFunction {
+
+  /**
+   * @return Return the function that the group must provide.
+   */
+  public Task.Type getFunction();
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeType.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeType.java
new file mode 100644
index 0000000..17ee22c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/UpgradeType.java
@@ -0,0 +1,36 @@
+/**
+ * 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 javax.xml.bind.annotation.XmlEnumValue;
+
+/**
+ * Indicates the type of Upgrade performed.
+ */
+public enum UpgradeType {
+  /**
+   * Services are up the entire time
+   */
+  @XmlEnumValue("rolling")
+  ROLLING,
+  /**
+   * All services are stopped, then started
+   */
+  @XmlEnumValue("nonrolling")
+  NONROLLING;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/package/scripts/hbase_upgrade.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/package/scripts/hbase_upgrade.py b/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/package/scripts/hbase_upgrade.py
index 610f527..2dc9883 100644
--- a/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/package/scripts/hbase_upgrade.py
+++ b/ambari-server/src/main/resources/common-services/HBASE/0.96.0.2.0/package/scripts/hbase_upgrade.py
@@ -24,7 +24,7 @@ from resource_management.core.resources.system import Execute
 
 class HbaseMasterUpgrade(Script):
 
-  def snapshot(self, env):
+  def take_snapshot(self, env):
     import params
 
     snap_cmd = "echo 'snapshot_all' | {0} shell".format(params.hbase_cmd)
@@ -33,5 +33,9 @@ class HbaseMasterUpgrade(Script):
 
     Execute(exec_cmd, user=params.hbase_user)
 
+  def restore_snapshot(self, env):
+    import params
+    print "TODO AMBARI-12698"
+
 if __name__ == "__main__":
   HbaseMasterUpgrade().execute()

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py
index 1415367..09d4d56 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py
+++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/package/scripts/namenode.py
@@ -107,9 +107,24 @@ class NameNodeDefault(NameNode):
   def get_stack_to_component(self):
     return {"HDP": "hadoop-hdfs-namenode"}
 
+  def restore_snapshot(self, env):
+    """
+    Restore the snapshot during a Downgrade.
+    """
+    print "TODO AMBARI-12698"
+    pass
+
+  def prepare_nonrolling_upgrade(self, env):
+    print "TODO AMBARI-12698"
+    pass
+
   def prepare_rolling_upgrade(self, env):
     namenode_upgrade.prepare_rolling_upgrade()
 
+  def finalize_nonrolling_upgrade(self, env):
+    print "TODO AMBARI-12698"
+    pass
+
   def finalize_rolling_upgrade(self, env):
     namenode_upgrade.finalize_rolling_upgrade()
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cca303d2/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/nonrolling-upgrade-2.2.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/nonrolling-upgrade-2.2.xml b/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/nonrolling-upgrade-2.2.xml
new file mode 100644
index 0000000..bf237c6
--- /dev/null
+++ b/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/nonrolling-upgrade-2.2.xml
@@ -0,0 +1,459 @@
+<?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">
+  <target>2.2.*.*</target>
+  <type>nonrolling</type>
+
+  <order>
+    <group xsi:type="cluster" name="PRE_CLUSTER" title="Prepare Upgrade">
+      <skippable>true</skippable>
+      <direction>UPGRADE</direction>
+      <execute-stage service="YARN" component="RESOURCEMANAGER" title="Stop YARN Queues">
+        <task xsi:type="manual">
+          <message>Before continuing, please stop all YARN queues. If yarn-site's yarn.resourcemanager.work-preserving-recovery.enabled is set to true, then you can skip this step since the clients will retry on their own.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="SLIDER" component="SLIDER" title="Stop Long Running Applications on Slider">
+        <task xsi:type="manual">
+          <message>Before continuing, please stop all long-running applications deployed using Slider. E.g., su - yarn "/usr/hdp/current/slider-client/bin/slider stop &lt;app_name&gt;"</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="STORM" component="NIMBUS" title="Stop Storm Topologies">
+        <task xsi:type="manual">
+          <message>Before continuing, please deactivate and kill any currently running topologies.</message>
+        </task>
+      </execute-stage>
+    </group>
+
+    <group xsi:type="stop" name="Stop High-Level Daemons" title="Stop Daemons for High-Level Services">
+      <skippable>true</skippable>
+      <service-check>false</service-check>
+
+      <service name="FLUME">
+        <component>FLUME_HANDLER</component>
+      </service>
+
+      <service name="STORM">
+        <component>DRPC_SERVER</component>
+        <component>STORM_UI_SERVER</component>
+        <component>SUPERVISOR</component>
+        <component>STORM_REST_API</component>
+        <component>NIMBUS</component>
+      </service>
+
+      <service name="KNOX">
+        <component>KNOX_GATEWAY</component>
+      </service>
+
+      <service name="KAFKA">
+        <component>KAFKA_BROKER</component>
+      </service>
+
+      <service name="FALCON">
+        <component>FALCON_SERVER</component>
+      </service>
+
+      <service name="OOZIE">
+        <component>OOZIE_SERVER</component>
+      </service>
+
+      <service name="SPARK">
+        <component>SPARK_JOBHISTORYSERVER</component>
+      </service>
+
+      <service name="HIVE">
+        <component>WEBHCAT_SERVER</component>
+        <component>HIVE_SERVER</component>
+        <component>HIVE_METASTORE</component>
+      </service>
+
+      <service name="YARN">
+        <component>NODEMANAGER</component>        <!-- TODO, parallelize -->
+        <component>RESOURCEMANAGER</component>
+        <component>APP_TIMELINE_SERVER</component>
+      </service>
+
+      <service name="MAPREDUCE2">
+        <component>HISTORYSERVER</component>
+      </service>
+    </group>
+
+    <group xsi:type="cluster" name="Backups" title="Take Backups">
+      <direction>UPGRADE</direction>
+      <skippable>true</skippable>
+      <execute-stage service="OOZIE" component="OOZIE_SERVER" title="Backup Oozie Database">
+        <task xsi:type="manual">
+          <message>Before continuing, please backup the Oozie Server database on {{oozie-env/oozie_hostname}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HIVE" component="HIVE_METASTORE" title="Backup Hive Metastore">
+        <task xsi:type="manual">
+          <message>Before continuing, please backup the Hive Metastore database located on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="KNOX" component="KNOX_GATEWAY" title="Backup Knox Data">
+        <task xsi:type="manual">
+          <message>Before continuing, please backup the Knox data. E.g., "cp -RL /etc/knox/data/security ~/knox_backup" on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HBASE" component="HBASE_MASTER" title="Snapshot HBASE">
+        <task xsi:type="execute" hosts="master">
+          <script>scripts/hbase_upgrade.py</script>
+          <function>take_snapshot</function>        <!-- TODO, this function used to be called just "snapshot" -->
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HDFS" component="NAMENODE" title="Snapshot HDFS">
+        <task xsi:type="execute" hosts="master">  <!-- TODO, this can be any NameNode, not just the active. -->
+          <script>scripts/namenode.py</script>
+          <function>prepare_nonrolling_upgrade</function>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="RANGER" component="RANGER_ADMIN" title="Backup Ranger">
+        <task xsi:type="manual">
+          <message>Before continuing, please backup the Ranger Admin database and Ranger Audit database on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+    </group>
+
+    <group xsi:type="stop" name="Stop Low-Level Daemons" title="Stop Daemons for Low-Level Services">
+      <skippable>true</skippable>
+      <service-check>false</service-check>
+
+      <service name="HBASE">
+        <component>HBASE_REGIONSERVER</component>
+        <component>HBASE_MASTER</component>
+      </service>
+
+      <service name="HDFS">
+        <component>DATANODE</component>
+        <component>NAMENODE</component>
+        <component>SECONDARY_NAMENODE</component>   <!-- TODO, may not be present. -->
+        <component>ZKFC</component>                 <!-- TODO, may not be present. -->
+        <component>JOURNALNODE</component>          <!-- TODO, may not be present. -->
+      </service>
+
+      <service name="RANGER">
+        <component>RANGER_USERSYNC</component>
+        <component>RANGER_ADMIN</component>
+      </service>
+
+      <service name="ZOOKEEPER">
+        <component>ZOOKEEPER_SERVER</component>
+      </service>
+    </group>
+
+    <group name="Marker for Downgrade" title="Marker for Downgrade">
+      <direction>UPGRADE</direction>
+      <!-- TODO, if the user attempts a downgrade before this step, they can simply abort. -->
+    </group>
+
+    <group xsi:type="cluster" name="Restore Backups" title="Restore Backups">
+      <direction>DOWNGRADE</direction>
+      <skippable>true</skippable>
+
+      <!-- If the user attempts a downgrade after this point, they will need to restore backups
+      before starting any of the services. -->
+
+      <execute-stage service="OOZIE" component="OOZIE_SERVER" title="Backup Oozie Database">
+        <task xsi:type="manual">
+          <message>Before continuing, please restore the Oozie Server database on {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HIVE" component="HIVE_METASTORE" title="Backup Hive Metastore">
+        <task xsi:type="manual">
+          <message>Before continuing, please restore the Hive Metastore database located on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="KNOX" component="KNOX_GATEWAY" title="Backup Knox Data">
+        <task xsi:type="manual">
+          <message>Before continuing, please restore the Knox data. E.g., "cp -RL ~/knox_backup/* /etc/knox/data/security/" on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HBASE" component="HBASE_MASTER" title="Snapshot HBASE">
+        <task xsi:type="execute" hosts="master">
+          <script>scripts/hbase_upgrade.py</script>
+          <function>restore_snapshot</function>   <!-- TODO, this function name is new. -->
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HDFS" component="NAMENODE" title="Snapshot HDFS">
+        <task xsi:type="execute" hosts="master">  <!-- TODO, this can be any NameNode, not just the active. -->
+          <script>scripts/namenode.py</script>
+          <function>restore_snapshot</function>    <!-- TODO, this function doesn't exist yet. -->
+        </task>
+      </execute-stage>
+
+      <execute-stage service="RANGER" component="RANGER_ADMIN" title="Backup Ranger">
+        <task xsi:type="manual">
+          <message>Before continuing, please restore the Ranger Admin database and Ranger Audit database on the following host(s): {{hosts.all}}.</message>
+        </task>
+      </execute-stage>
+    </group>
+
+    <group xsi:type="cluster" name="ALL_HOST_OPS" title="Set Version On All Hosts">
+      <skippable>true</skippable>
+      <execute-stage title="Update stack to {{version}}">
+        <task xsi:type="execute">
+          <script>scripts/ru_set_all.py</script>
+          <function>actionexecute</function>      <!-- TODO, parallelize -->
+        </task>
+      </execute-stage>
+    </group>
+
+    <!-- Now, restart all of the services. -->
+
+    <group xsi:type="restart" name="ZOOKEEPER" title="Zookeeper">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="ZOOKEEPER">
+        <service-check>false</service-check>        <!-- TODO, enable service-check once done testing -->
+        <component>ZOOKEEPER_SERVER</component>
+        <component>ZOOKEEPER_CLIENT</component>     <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="RANGER" title="Ranger">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="RANGER">
+        <component>RANGER_ADMIN</component>
+        <component>RANGER_USERSYNC</component>
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="HDFS" title="HDFS">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="HDFS">
+        <component>JOURNALNODE</component>
+        <component>ZKFC</component>
+        <component>NAMENODE</component>
+        <component>SECONDARY_NAMENODE</component>   <!-- TODO, may not be present -->
+        <component>DATANODE</component>             <!-- TODO, parallelize -->
+        <component>HDFS_CLIENT</component>          <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="MR and YARN" title="MR and YARN">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="MAPREDUCE2">
+        <component>HISTORYSERVER</component>
+        <component>MAPREDUCE2_CLIENT</component>    <!-- TODO, parallelize -->
+      </service>
+      <service name="YARN">
+        <component>APP_TIMELINE_SERVER</component>
+        <component>RESOURCEMANAGER</component>
+        <component>NODEMANAGER</component>          <!-- TODO, parallelize -->
+        <component>YARN_CLIENT</component>          <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="HBASE" title="HBASE">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="HBASE">
+        <component>HBASE_MASTER</component>
+        <component>HBASE_REGIONSERVER</component>   <!-- TODO, parallelize -->
+        <component>HBASE_CLIENT</component>         <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="CLIENTS" title="Tez, Pig, Sqoop Clients">  <!-- TODO, parallelize -->
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="TEZ">
+        <component>TEZ_CLIENT</component>
+      </service>
+
+      <service name="PIG">
+        <component>PIG</component>
+      </service>
+
+      <service name="SQOOP">
+        <component>SQOOP</component>
+      </service>
+    </group>
+
+    <group name="SERVICE_CHECK" title="All Service Checks" xsi:type="service-check">
+      <skippable>true</skippable>
+      <direction>UPGRADE</direction>
+      <priority>
+        <!-- TODO, for some reason, it flips the order. -->
+        <service>HBASE</service>
+        <service>MAPREDUCE2</service>
+        <service>YARN</service>
+        <service>HDFS</service>
+      </priority>
+    </group>
+
+    <group xsi:type="restart" name="HIVE" title="Hive">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="HIVE">
+        <component>HIVE_METASTORE</component>
+        <component>HIVE_SERVER</component>
+        <component>WEBHCAT_SERVER</component>
+        <component>HIVE_CLIENT</component>          <!-- TODO, parallelize -->
+        <component>HCAT</component>                 <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="SPARK" title="Spark">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="SPARK">
+        <component>SPARK_JOBHISTORYSERVER</component>
+        <component>SPARK_CLIENT</component>         <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <!-- Upgrade Oozie DB only on Upgrade direction, and always create a new ShareLib. -->
+    <group name="Upgrade Oozie" title="Upgrade Oozie Database">
+      <direction>UPGRADE</direction>
+      <skippable>true</skippable>
+      <execute-stage service="OOZIE" component="OOZIE_SERVER" title="Upgrade Oozie Database">
+        <task xsi:type="execute" hosts="any" summary="Upgrading the database and creating a new sharelib">
+          <script>scripts/oozie_server_upgrade.py</script>
+          <function>upgrade_oozie_database_and_sharelib</function>
+        </task>
+      </execute-stage>
+    </group>
+
+    <!-- Only create the ShareLib folder during a Downgrade. -->
+    <group name="Downgrade Oozie" title="Downgrade Oozie ShareLib">
+      <direction>DOWNGRADE</direction>
+      <skippable>true</skippable>
+      <execute-stage service="OOZIE" component="OOZIE_SERVER" title="Downgrade Oozie ShareLib">
+        <task xsi:type="execute" hosts="any" summary="Upgrading the database and creating a new sharelib">
+          <script>scripts/oozie_server_upgrade.py</script>
+          <function>create_sharelib</function>
+        </task>
+      </execute-stage>
+    </group>
+
+    <group xsi:type="restart" name="OOZIE" title="Oozie">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="OOZIE">
+        <component>OOZIE_SERVER</component>
+        <component>OOZIE_CLIENT</component>         <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="FALCON" title="Falcon">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="FALCON">
+        <component>FALCON_SERVER</component>
+        <component>FALCON_CLIENT</component>        <!-- TODO, parallelize -->
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="KAFKA" title="Kafka">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="KAFKA">
+        <component>KAFKA_BROKER</component>
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="KNOX" title="Knox">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="KNOX">
+        <component>KNOX_GATEWAY</component>
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="STORM" title="Storm">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="STORM">
+        <component>NIMBUS</component>
+        <component>STORM_REST_API</component>
+        <component>SUPERVISOR</component>
+        <component>STORM_UI_SERVER</component>
+        <component>DRPC_SERVER</component>
+      </service>
+
+      <!-- TODO, does this work? -->
+      <execute-stage service="STORM" component="DRPC_SERVER" title="Rebuild Storm Topology">
+        <task xsi:type="manual">
+          <message>Please rebuild your topology using the new Storm version dependencies and resubmit it using the newly created jar.</message>
+        </task>
+      </execute-stage>
+    </group>
+
+    <group xsi:type="restart" name="SLIDER" title="Slider">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="SLIDER">
+        <component>SLIDER</component>
+      </service>
+    </group>
+
+    <group xsi:type="restart" name="FLUME" title="Flume">
+      <service-check>false</service-check>
+      <skippable>true</skippable>
+      <service name="FLUME">
+        <component>FLUME_HANDLER</component>
+      </service>
+    </group>
+
+    <group xsi:type="cluster" name="POST_CLUSTER" title="Finalize {{direction.text.proper}}">
+      <skippable>true</skippable>
+      <execute-stage title="Check Unhealthy Hosts" id="unhealthy-hosts">
+        <task xsi:type="manual">
+          <message>The following hosts were unhealthy and should be resolved before finalizing can be completed: {{hosts.unhealthy}}</message>
+        </task>
+      </execute-stage>
+      
+      <execute-stage title="Confirm Finalize">
+        <direction>UPGRADE</direction>
+        <task xsi:type="manual">
+          <message>Please confirm you are ready to finalize.</message>
+        </task>
+      </execute-stage>
+
+      <execute-stage service="HDFS" component="NAMENODE" title="Execute HDFS Finalize">
+        <task xsi:type="execute" hosts="master">      <!-- TODO, what happens if there's no HA. -->
+          <script>scripts/namenode.py</script>
+          <function>finalize_nonrolling_upgrade</function>
+        </task>
+      </execute-stage>
+
+      <execute-stage title="Save Cluster State" service="" component="">
+        <task xsi:type="server_action" class="org.apache.ambari.server.serveraction.upgrades.FinalizeUpgradeAction">
+        </task>
+      </execute-stage>
+    </group>
+  </order>
+</upgrade>
\ No newline at end of file