You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2018/11/09 16:38:37 UTC

[ambari] branch trunk updated: [AMBARI-24873] Rolling Upgrade Orchestration Changes For Client Performance (#2591)

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

ncole pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 791631e  [AMBARI-24873] Rolling Upgrade Orchestration Changes For Client Performance (#2591)
791631e is described below

commit 791631eea7e05e33ae754d404da24bb91f47b88d
Author: ncole <nc...@hortonworks.com>
AuthorDate: Fri Nov 9 11:38:31 2018 -0500

    [AMBARI-24873] Rolling Upgrade Orchestration Changes For Client Performance (#2591)
---
 .../internal/UpgradeResourceProvider.java          |   4 +-
 .../ambari/server/stack/upgrade/Grouping.java      |   2 +-
 .../stack/upgrade/ParallelClientGrouping.java      |  46 ++++
 .../server/stack/upgrade/ServiceCheckGrouping.java |   9 +-
 .../orchestrate/ParallelClientGroupingBuilder.java | 186 ++++++++++++++++
 ambari-server/src/main/resources/upgrade-pack.xsd  |   9 +-
 .../upgrade/orchestrate/UpgradeHelperTest.java     |  48 ++++
 .../upgrades/upgrade_test_parallel_client.xml      | 243 +++++++++++++++++++++
 8 files changed, 543 insertions(+), 4 deletions(-)

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 5d1886b..db29145 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
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.annotations.Experimental;
 import org.apache.ambari.annotations.ExperimentalFeature;
@@ -1236,7 +1237,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     List<RequestResourceFilter> filters = new ArrayList<>();
 
     for (TaskWrapper tw : wrapper.getTasks()) {
-      filters.add(new RequestResourceFilter(tw.getService(), "", Collections.emptyList()));
+      List<String> hosts = tw.getHosts().stream().collect(Collectors.toList());
+      filters.add(new RequestResourceFilter(tw.getService(), "", hosts));
     }
 
     Cluster cluster = context.getCluster();
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/Grouping.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/Grouping.java
index 19e826e..f5e7d08 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/Grouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/Grouping.java
@@ -49,7 +49,7 @@ import com.google.common.base.Objects;
  */
 @XmlSeeAlso(value = { ColocatedGrouping.class, ClusterGrouping.class,
     UpdateStackGrouping.class, ServiceCheckGrouping.class, RestartGrouping.class,
-    StartGrouping.class, StopGrouping.class, HostOrderGrouping.class })
+    StartGrouping.class, StopGrouping.class, HostOrderGrouping.class, ParallelClientGrouping.class })
 public class Grouping {
 
   @XmlAttribute(name="name")
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ParallelClientGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ParallelClientGrouping.java
new file mode 100644
index 0000000..fefe75a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ParallelClientGrouping.java
@@ -0,0 +1,46 @@
+/*
+ * 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.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.XmlType;
+
+import org.apache.ambari.server.stack.upgrade.orchestrate.ParallelClientGroupingBuilder;
+import org.apache.ambari.server.stack.upgrade.orchestrate.StageWrapperBuilder;
+
+/**
+ * Grouping for clients:
+ * <ul>
+ *   <li>The first client will be upgraded.</li>
+ *   <li>Run a service check on the first client.</li>
+ *   <li>Run the remaining clients in parallel.</li>
+ * </ul>
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name="parallel-client")
+public class ParallelClientGrouping extends Grouping {
+
+  @Override
+  public StageWrapperBuilder getBuilder() {
+    return new ParallelClientGroupingBuilder(this);
+  }
+
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ServiceCheckGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ServiceCheckGrouping.java
index d21c1d0..ccdb90b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ServiceCheckGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/ServiceCheckGrouping.java
@@ -280,10 +280,17 @@ public class ServiceCheckGrouping extends Grouping {
     public String service;
     public boolean priority;
 
+
     ServiceCheckStageWrapper(String service, String serviceDisplay, boolean priority) {
+      this(service, serviceDisplay, priority, null);
+    }
+
+    public ServiceCheckStageWrapper(String service, String serviceDisplay, boolean priority, String host) {
       super(StageWrapper.Type.SERVICE_CHECK,
           String.format("Service Check %s", serviceDisplay),
-          new TaskWrapper(service, "", Collections.emptySet(), new ServiceCheckTask()));
+          new TaskWrapper(service, "",
+              null == host ? Collections.emptySet() : Collections.singleton(host),
+              new ServiceCheckTask()));
 
       this.service = service;
       this.priority = priority;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/ParallelClientGroupingBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/ParallelClientGroupingBuilder.java
new file mode 100644
index 0000000..9411995
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/ParallelClientGroupingBuilder.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.stack.upgrade.orchestrate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.stack.HostsType;
+import org.apache.ambari.server.stack.upgrade.ExecuteHostType;
+import org.apache.ambari.server.stack.upgrade.ExecuteTask;
+import org.apache.ambari.server.stack.upgrade.Grouping;
+import org.apache.ambari.server.stack.upgrade.ParallelClientGrouping;
+import org.apache.ambari.server.stack.upgrade.ServiceCheckGrouping.ServiceCheckStageWrapper;
+import org.apache.ambari.server.stack.upgrade.Task;
+import org.apache.ambari.server.stack.upgrade.Task.Type;
+import org.apache.ambari.server.stack.upgrade.UpgradePack.ProcessingComponent;
+import org.apache.ambari.server.utils.StageUtils;
+import org.apache.commons.collections.CollectionUtils;
+
+/**
+ * Responsible for building the stages for {@link ParallelClientGrouping}
+ */
+public class ParallelClientGroupingBuilder extends StageWrapperBuilder {
+
+  private Map<String, HostHolder> serviceToHostMap = new HashMap<>();
+
+  /**
+   * @param grouping
+   */
+  public ParallelClientGroupingBuilder(Grouping grouping) {
+    super(grouping);
+  }
+
+  @Override
+  public void add(UpgradeContext upgradeContext, HostsType hostsType, String service,
+      boolean clientOnly, ProcessingComponent pc, Map<String, String> params) {
+
+    if (null == hostsType || CollectionUtils.isEmpty(hostsType.getHosts())) {
+      return;
+    }
+
+    Iterator<String> hostIterator = hostsType.getHosts().iterator();
+    HostHolder holder = new HostHolder();
+    holder.m_firstHost = hostIterator.next();
+
+    while (hostIterator.hasNext()) {
+      holder.m_remainingHosts.add(hostIterator.next());
+    }
+
+    holder.m_component = pc;
+    holder.m_tasks = new ArrayList<>();
+    holder.m_tasks.addAll(resolveTasks(upgradeContext, true, pc));
+    holder.m_tasks.add(resolveTask(upgradeContext, pc));
+    holder.m_tasks.addAll(resolveTasks(upgradeContext, false, pc));
+
+    serviceToHostMap.put(service, holder);
+  }
+
+  @Override
+  public List<StageWrapper> build(UpgradeContext upgradeContext, List<StageWrapper> stageWrappers) {
+
+    if (0 == serviceToHostMap.size()) {
+      return stageWrappers;
+    }
+
+    List<StageWrapper> starterUpgrades = new ArrayList<>();
+    List<StageWrapper> finisherUpgrades = new ArrayList<>();
+
+    // !!! create a stage wrapper for the first host tasks
+    // !!! create a stage wrapper for the service check on the first host
+    // !!! create a stage wrapper for the remaining hosts
+
+    serviceToHostMap.forEach((service, holder) -> {
+      String component = holder.m_component.name;
+
+      List<TaskWrapper> wrappers = buildWrappers(service, component, holder.m_tasks,
+          Collections.singleton(holder.m_firstHost), true);
+
+      String text = getStageText("Upgrading",
+          upgradeContext.getComponentDisplay(service, component),
+          Collections.singleton(holder.m_firstHost));
+
+      // !!! this is a poor assumption
+      StageWrapper.Type type = wrappers.get(0).getTasks().get(0).getStageWrapperType();
+
+      StageWrapper stage = new StageWrapper(type, text, new HashMap<>(), wrappers);
+
+      // !!! force the service check on the first host
+      StageWrapper serviceCheck = new ServiceCheckStageWrapper(service,
+          upgradeContext.getServiceDisplay(service), false, holder.m_firstHost);
+
+      starterUpgrades.add(stage);
+      starterUpgrades.add(serviceCheck);
+
+      wrappers = buildWrappers(service, component, holder.m_tasks, holder.m_remainingHosts, false);
+
+      text = getStageText("Upgrade Remaining",
+          upgradeContext.getComponentDisplay(service, component),
+          holder.m_remainingHosts);
+      stage = new StageWrapper(type, text, new HashMap<>(), wrappers);
+
+      finisherUpgrades.add(stage);
+    });
+
+    List<StageWrapper> results = new ArrayList<>(stageWrappers);
+
+    results.addAll(starterUpgrades);
+    results.addAll(finisherUpgrades);
+
+    return results;
+  }
+
+  /**
+   * Build the wrappers for the tasks.
+   *
+   * @param tasks
+   *          the tasks to wrap
+   * @param hosts
+   *          the hosts where the tasks should run
+   * @return
+   */
+  private List<TaskWrapper> buildWrappers(String service, String component,
+      List<Task> tasks, Set<String> hosts, boolean firstHost) {
+
+    List<TaskWrapper> results = new ArrayList<>();
+
+    String ambariServerHostname = StageUtils.getHostName();
+
+    for (Task task : tasks) {
+
+      // only add the server-side task if there are actual hosts for the service/component
+      if (task.getType().isServerAction()) {
+        results.add(new TaskWrapper(service, component, Collections.singleton(ambariServerHostname), task));
+        continue;
+      }
+
+      // FIXME how to handle master-only types
+
+      // !!! the first host has already run tasks that are singular
+      if (!firstHost && task.getType() == Type.EXECUTE) {
+        ExecuteTask et = (ExecuteTask) task;
+
+        // !!! singular types have already run when firstHost is true
+        if (et.hosts != ExecuteHostType.ALL) {
+          continue;
+        }
+      }
+
+      results.add(new TaskWrapper(service, component, hosts, task));
+    }
+
+    return results;
+  }
+
+  /**
+   * Temporary holder for building stage wrappers
+   */
+  private static class HostHolder {
+    private ProcessingComponent m_component;
+    private String m_firstHost;
+    private Set<String> m_remainingHosts = new HashSet<>();
+    private List<Task> m_tasks;
+  }
+
+}
diff --git a/ambari-server/src/main/resources/upgrade-pack.xsd b/ambari-server/src/main/resources/upgrade-pack.xsd
index e936ab4..b743095 100644
--- a/ambari-server/src/main/resources/upgrade-pack.xsd
+++ b/ambari-server/src/main/resources/upgrade-pack.xsd
@@ -22,7 +22,7 @@
     This document describes the schema for an Upgrade Pack
     </xs:documentation>
   </xs:annotation>
-
+  
   <!-- FIXME case sensitivity -->
   <xs:simpleType name="upgrade-kind-type">
     <xs:restriction base="xs:string">
@@ -174,6 +174,7 @@
           <xs:attribute name="name" type="xs:NMTOKEN" />
         </xs:complexType>
       </xs:element>
+      
     </xs:sequence>
     <xs:attribute name="name" type="xs:string" use="required" />
     <xs:attribute name="title" type="xs:string" use="required" />
@@ -241,6 +242,12 @@
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
+  
+  <xs:complexType name="parallel-client">
+    <xs:complexContent>
+      <xs:extension base="abstract-group-type" />
+    </xs:complexContent>
+  </xs:complexType>
 
   <xs:complexType name="start">
     <xs:complexContent>
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeHelperTest.java
index 865e180..e78af4c 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeHelperTest.java
@@ -44,6 +44,7 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -1502,6 +1503,7 @@ public class UpgradeHelperTest extends EasyMockSupport {
 
     HostsType type = HostsType.normal("h1", "h2", "h3");
     expect(m_masterHostResolver.getMasterAndHosts("ZOOKEEPER", "ZOOKEEPER_SERVER")).andReturn(type).anyTimes();
+    expect(m_masterHostResolver.getMasterAndHosts("ZOOKEEPER", "ZOOKEEPER_CLIENT")).andReturn(type).anyTimes();
     expect(m_masterHostResolver.getMasterAndHosts("HDFS", "NAMENODE")).andReturn(namenodeHosts).anyTimes();
 
     if (clean) {
@@ -2822,6 +2824,52 @@ public class UpgradeHelperTest extends EasyMockSupport {
     ambariMetaInfo.init();
   }
 
+  @Test
+  public void testParallelClients() throws Exception {
+    Map<String, UpgradePack> upgrades = ambariMetaInfo.getUpgradePacks("HDP", "2.1.1");
+
+    assertTrue(upgrades.containsKey("upgrade_test_parallel_client"));
+    UpgradePack upgrade = upgrades.get("upgrade_test_parallel_client");
+    assertNotNull(upgrade);
+
+    Cluster cluster = makeCluster();
+
+    Service s = cluster.getService("ZOOKEEPER");
+    ServiceComponent sc = s.addServiceComponent("ZOOKEEPER_CLIENT");
+    sc.addServiceComponentHost("h1");
+    sc.addServiceComponentHost("h2");
+    sc.addServiceComponentHost("h3");
+
+    UpgradeContext context = getMockUpgradeContext(cluster, Direction.UPGRADE, UpgradeType.NON_ROLLING);
+
+    List<UpgradeGroupHolder> groups = m_upgradeHelper.createSequence(upgrade, context);
+
+    Optional<UpgradeGroupHolder> optional = groups.stream()
+        .filter(g -> g.name.equals("ZK_CLIENTS"))
+        .findAny();
+    assertTrue(optional.isPresent());
+
+    UpgradeGroupHolder holder = optional.get();
+
+    assertEquals(3, holder.items.size());
+    assertEquals(StageWrapper.Type.RESTART, holder.items.get(0).getType());
+    assertEquals(StageWrapper.Type.SERVICE_CHECK, holder.items.get(1).getType());
+    assertEquals(StageWrapper.Type.RESTART, holder.items.get(2).getType());
+
+    // !!! this is a known issue - tasks wrappers should only wrap one task
+    TaskWrapper taskWrapper = holder.items.get(0).getTasks().get(0);
+    assertEquals(1, taskWrapper.getHosts().size());
+    String host1 = taskWrapper.getHosts().iterator().next();
+
+    taskWrapper = holder.items.get(1).getTasks().get(0);
+    assertEquals(1, taskWrapper.getHosts().size());
+    String host2 = taskWrapper.getHosts().iterator().next();
+    assertEquals(host1, host2);
+
+    taskWrapper = holder.items.get(2).getTasks().get(0);
+    assertEquals(2, taskWrapper.getHosts().size());
+  }
+
   /**
    * Builds a mock upgrade context using the following parameters:
    * <ul>
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_parallel_client.xml b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_parallel_client.xml
new file mode 100644
index 0000000..e030ae5
--- /dev/null
+++ b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_parallel_client.xml
@@ -0,0 +1,243 @@
+<?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.0</target-stack>
+  <type>ROLLING</type>
+  <prerequisite-checks>
+    <check>org.apache.ambari.server.checks.HiveMultipleMetastoreCheck</check>
+    <check>org.apache.ambari.server.checks.MapReduce2JobHistoryStatePreservingCheck</check>
+    <check>org.apache.ambari.server.checks.SecondaryNamenodeDeletedCheck</check>
+    <check>org.apache.ambari.server.checks.ServicesMapReduceDistributedCacheCheck</check>
+    <check>org.apache.ambari.server.checks.ServicesNamenodeHighAvailabilityCheck</check>
+    <check>org.apache.ambari.server.checks.ServicesNamenodeTruncateCheck</check>
+    <check>org.apache.ambari.server.checks.ServicesTezDistributedCacheCheck</check>
+    <check>org.apache.ambari.server.checks.ServicesYarnWorkPreservingCheck</check>
+    <check>org.apache.ambari.server.checks.YarnRMHighAvailabilityCheck</check>
+    <check>org.apache.ambari.server.checks.YarnTimelineServerStatePreservingCheck</check>
+
+    <configuration>
+      <property name="global-property-1">global-value-1</property>
+      <check-properties name="org.apache.ambari.server.checks.ServicesMapReduceDistributedCacheCheck">
+        <property name="dfs-protocols-regex">^([^:]*dfs|wasb|ecs):.*</property>
+      </check-properties>
+      <check-properties name="org.apache.ambari.server.checks.ServicesTezDistributedCacheCheck">
+        <property name="dfs-protocols-regex">^([^:]*dfs|wasb|ecs):.*</property>
+      </check-properties>
+    </configuration>
+  </prerequisite-checks>
+
+  <order>
+    <group xsi:type="cluster" name="PRE_CLUSTER" title="Pre Upgrade">
+      <execute-stage title="Confirm 1">
+        <task xsi:type="manual">
+          <message>Foo</message>
+        </task>
+      </execute-stage>
+      <execute-stage service="HDFS" component="NAMENODE" title="Finalize HDFS">
+        <task xsi:type="execute">
+          <script>foo</script>
+          <function>list</function>
+        </task>
+      </execute-stage>
+      <execute-stage title="Confirm 2">
+        <task xsi:type="manual">
+          <message>Foo</message>
+        </task>
+      </execute-stage>
+    </group>
+  
+    <group name="ZOOKEEPER" title="Zookeeper">
+      <service name="ZOOKEEPER">
+        <component>ZOOKEEPER_SERVER</component>
+      </service>
+    </group>
+
+    <group name="ZK_CLIENTS" title="Zookeeper" xsi:type="parallel-client">
+      <service name="ZOOKEEPER">
+        <component>ZOOKEEPER_CLIENT</component>
+      </service>
+    </group>
+    
+    <group name="CORE_MASTER" title="Core Masters">
+      <service name="HDFS">
+        <component>JOURNALNODE</component>
+        <component>NAMENODE</component>
+      </service>
+      <service name="YARN">
+        <component>RESOURCEMANAGER</component>
+      </service>
+      <service-check>false</service-check>
+    </group>
+    
+    <group name="SERVICE_CHECK_1" title="Post-Master Service Checks" xsi:type="service-check">
+      <priority>
+        <service>HDFS</service>
+        <service>HBASE</service>
+        <service>YARN</service>
+      </priority>
+    </group>
+    
+    <group name="CORE_SLAVES" title="Core Slaves" xsi:type="colocated">
+      <skippable>true</skippable>      <!-- set skippable for test -->
+      <allow-retry>false</allow-retry> <!-- set no retry for test -->
+      <service name="HDFS">
+        <component>DATANODE</component>
+      </service>
+      <service name="HBASE">
+        <component>REGIONSERVER</component>
+      </service>
+      <service name="YARN">
+        <component>NODEMANAGER</component>
+      </service>
+      
+      <batch>
+        <percent>20</percent>
+        <message>Please run additional tests</message>
+      </batch>
+    </group>
+    
+    <group name="SERVICE_CHECK_2" title="Post-Slave Service Checks" xsi:type="service-check">
+      <priority>
+        <service>ZOOKEEPER</service>
+        <service>HDFS</service>
+        <service>YARN</service>
+      </priority>
+    </group>
+    
+    <group name="OOZIE" title="Oozie">
+      <service name="OOZIE">
+        <component>OOZIE_SERVER</component>
+      </service>
+    </group>
+    
+    
+    <group xsi:type="cluster" name="POST_CLUSTER" title="Finalize Upgrade">
+      <execute-stage title="Confirm Finalize">
+        <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">
+          <script>foo</script>
+          <function>list</function>
+        </task>
+      </execute-stage>
+      <execute-stage title="Save Cluster State">
+        <task xsi:type="server_action" class="org.apache.ambari.server.serveraction.upgrades.FinalizeUpgradeAction">
+        </task>
+      </execute-stage>
+    </group>
+        
+  </order>
+  
+
+  <processing>
+  
+    <service name="ZOOKEEPER">
+      <component name="ZOOKEEPER_SERVER">
+        <pre-upgrade>
+          <task xsi:type="manual">
+            <summary>SUMMARY OF PREPARE</summary>
+            <message>This is a manual task with a placeholder of {{foo/bar}}</message>
+          </task>
+        </pre-upgrade>
+        <pre-downgrade copy-upgrade="true" />
+        <upgrade>
+          <task xsi:type="restart-task" />
+        </upgrade>
+        <post-upgrade>
+          <task xsi:type="configure" id="hdp_2_1_1_zk_post_upgrade"/>
+        </post-upgrade>
+        <post-downgrade copy-upgrade="true" />
+      </component>
+      
+      <component name="ZOOKEEPER_CLIENT">
+        <upgrade>
+          <task xsi:type="restart-task" />
+        </upgrade>
+      </component>      
+    </service>
+    <service name="HDFS">
+      <component name="NAMENODE">
+        <pre-upgrade>
+          <task xsi:type="execute" hosts="master">
+            <script>foo</script>
+            <function>list</function>
+          </task>
+          <task xsi:type="configure" id="hdp_2_1_1_nn_pre_upgrade"/>
+          <task xsi:type="manual">
+            <message>Update your database</message>
+          </task>
+        </pre-upgrade>
+        <pre-downgrade copy-upgrade="true" />
+        <upgrade>
+          <task xsi:type="restart-task" />
+        </upgrade>
+        <post-upgrade>
+          <task xsi:type="execute">
+            <script>foo</script>
+            <function>list</function>
+          </task>
+        </post-upgrade>
+        <post-downgrade copy-upgrade="true" />
+      </component>
+      <component name="DATANODE">
+        <pre-downgrade />
+        <upgrade>
+          <task xsi:type="restart-task" />
+        </upgrade>
+        <post-downgrade>
+          <task xsi:type="manual">
+            <message>Manual Downgrade</message>
+          </task>
+        </post-downgrade>
+      </component>
+    </service>
+    <service name="YARN">
+      <component name="RESOURCEMANAGER">
+        <pre-upgrade>
+          <task xsi:type="execute">
+            <script>foo</script>
+            <function>list</function>
+          </task>
+        </pre-upgrade>
+        <pre-downgrade copy-upgrade="true" />
+        <upgrade />
+      </component>
+      <component name="NODEMANAGER">
+        <pre-upgrade>
+          <task xsi:type="execute">
+            <script>foo</script>
+            <function>list</function>
+          </task>
+        </pre-upgrade>
+        <pre-downgrade copy-upgrade="true" />
+        <upgrade />
+      </component>
+    </service>
+    <service name="OOZIE">
+      <component name="OOZIE_SERVER">
+        <upgrade>
+          <task xsi:type="restart-task" />
+        </upgrade>
+      </component>
+	</service>
+  </processing>
+</upgrade>