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 2017/07/24 15:27:06 UTC

[40/50] [abbrv] ambari git commit: AMBARI-21530 - Service Checks During Upgrades Should Use Desired Stack (jonathanhurley)

AMBARI-21530 - Service Checks During Upgrades Should Use Desired Stack (jonathanhurley)


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

Branch: refs/heads/branch-feature-AMBARI-12556
Commit: e87a3e31a9a18c5178f1170cef15c4de47f6808e
Parents: 9c45110
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Wed Jul 19 22:04:07 2017 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Thu Jul 20 17:15:21 2017 -0400

----------------------------------------------------------------------
 .../actionmanager/ExecutionCommandWrapper.java  | 34 +++++++-
 .../AmbariCustomCommandExecutionHelper.java     |  7 +-
 .../AmbariManagementControllerImpl.java         |  7 +-
 .../internal/UpgradeResourceProvider.java       | 91 +++++++++++++++-----
 .../2.1.0.2.0/package/scripts/historyserver.py  |  2 +-
 .../2.1.0.2.0/package/scripts/params_linux.py   | 11 ++-
 .../2.1.0.2.0/package/scripts/service_check.py  |  6 +-
 .../src/test/python/TestStackFeature.py         | 61 +++++++++++++
 8 files changed, 174 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapper.java b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapper.java
index efd609a..91db7d0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapper.java
@@ -17,6 +17,9 @@
  */
 package org.apache.ambari.server.actionmanager;
 
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.HOOKS_FOLDER;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_PACKAGE_FOLDER;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
@@ -27,6 +30,7 @@ import org.apache.ambari.server.ServiceNotFoundException;
 import org.apache.ambari.server.agent.AgentCommand.AgentCommandType;
 import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.agent.ExecutionCommand.KeyNames;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.Cluster;
@@ -35,6 +39,9 @@ import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DesiredConfig;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.StackInfo;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
@@ -63,6 +70,12 @@ public class ExecutionCommandWrapper {
   @Inject
   private Gson gson;
 
+  /**
+   * Used for injecting hooks and common-services into the command.
+   */
+  @Inject
+  private AmbariMetaInfo ambariMetaInfo;
+
   @AssistedInject
   public ExecutionCommandWrapper(@Assisted String jsonExecutionCommand) {
     this.jsonExecutionCommand = jsonExecutionCommand;
@@ -208,9 +221,28 @@ public class ExecutionCommandWrapper {
           }
         }
 
+        Map<String, String> commandParams = executionCommand.getCommandParams();
+
         if (null != repositoryVersion) {
-          executionCommand.getCommandParams().put(KeyNames.VERSION, repositoryVersion.getVersion());
+          commandParams.put(KeyNames.VERSION, repositoryVersion.getVersion());
           executionCommand.getHostLevelParams().put(KeyNames.CURRENT_VERSION, repositoryVersion.getVersion());
+
+          StackId stackId = repositoryVersion.getStackId();
+          StackInfo stackInfo = ambariMetaInfo.getStack(stackId.getStackName(),
+              stackId.getStackVersion());
+
+          if (!commandParams.containsKey(HOOKS_FOLDER)) {
+            commandParams.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
+          }
+
+          if (!commandParams.containsKey(SERVICE_PACKAGE_FOLDER)) {
+            if (!StringUtils.isEmpty(serviceName)) {
+              ServiceInfo serviceInfo = ambariMetaInfo.getService(stackId.getStackName(),
+                  stackId.getStackVersion(), serviceName);
+
+              commandParams.put(SERVICE_PACKAGE_FOLDER, serviceInfo.getServicePackageFolder());
+            }
+          }
         }
       } catch (ServiceNotFoundException serviceNotFoundException) {
         // it's possible that there are commands specified for a service where

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/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 5180870..0b140e4 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
@@ -27,7 +27,6 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.CUSTOM_CO
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.DB_DRIVER_FILENAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.DB_NAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.GROUP_LIST;
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.HOOKS_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.HOST_SYS_PREPPED;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.JDK_LOCATION;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.MYSQL_JDBC_URL;
@@ -36,7 +35,6 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.ORACLE_JD
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.REPO_INFO;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT_TYPE;
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_PACKAGE_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_NAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_VERSION;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_GROUPS;
@@ -492,8 +490,6 @@ public class AmbariCustomCommandExecutionHelper {
       }
 
       commandParams.put(COMMAND_TIMEOUT, "" + commandTimeout);
-      commandParams.put(SERVICE_PACKAGE_FOLDER, serviceInfo.getServicePackageFolder());
-      commandParams.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
 
       Map<String, String> roleParams = execCmd.getRoleParams();
       if (roleParams == null) {
@@ -811,8 +807,7 @@ public class AmbariCustomCommandExecutionHelper {
       actualTimeout = actualTimeout < MIN_STRICT_SERVICE_CHECK_TIMEOUT ? MIN_STRICT_SERVICE_CHECK_TIMEOUT : actualTimeout;
       commandParams.put(COMMAND_TIMEOUT, Integer.toString(actualTimeout));
     }
-    commandParams.put(SERVICE_PACKAGE_FOLDER, serviceInfo.getServicePackageFolder());
-    commandParams.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
+
     StageUtils.useAmbariJdkInCommandParams(commandParams, configs);
 
     execCmd.setCommandParams(commandParams);

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 4229d34..44943c7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -28,7 +28,6 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.COMMAND_T
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.CUSTOM_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.DB_DRIVER_FILENAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.GROUP_LIST;
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.HOOKS_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.MAX_DURATION_OF_RETRIES;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.NOT_MANAGED_HDFS_PATH_LIST;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.PACKAGE_LIST;
@@ -36,7 +35,6 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.PACKAGE_V
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.REPO_INFO;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT_TYPE;
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_PACKAGE_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_REPO_INFO;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.UNLIMITED_KEY_JCE_REQUIRED;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_GROUPS;
@@ -1536,7 +1534,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         Map.Entry<String, String> pair = (Map.Entry) it.next();
         // Check the value if both keys exist
         if (newConfigValues.containsKey(pair.getKey())) {
-          if (!newConfigValues.get((String) pair.getKey()).equals(pair.getValue())) {
+          if (!newConfigValues.get(pair.getKey()).equals(pair.getValue())) {
             configsChanged.put(pair.getKey(), "changed");
           }
         } else {
@@ -2459,9 +2457,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     }
 
     commandParams.put(COMMAND_TIMEOUT, actualTimeout);
-    commandParams.put(SERVICE_PACKAGE_FOLDER,
-      serviceInfo.getServicePackageFolder());
-    commandParams.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
 
     String customCacheDirectory = componentInfo.getCustomFolder();
     if (customCacheDirectory != null) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/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 22858dd..e8cd220 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
@@ -856,6 +856,63 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     }
   }
 
+  /**
+   * Adds the hooks and service folders based on the effective stack ID and the
+   * name of the service from the wrapper.
+   *
+   * @param wrapper
+   *          the stage wrapper to use when detemrining the service name.
+   * @param effectiveStackId
+   *          the stack ID to use when getting the hooks and service folders.
+   * @param commandParams
+   *          the params to update with the new values
+   * @throws AmbariException
+   */
+  private void applyRepositoryAssociatedParameters(StageWrapper wrapper, StackId effectiveStackId,
+      Map<String, String> commandParams) throws AmbariException {
+    if (CollectionUtils.isNotEmpty(wrapper.getTasks())
+        && wrapper.getTasks().get(0).getService() != null) {
+
+      AmbariMetaInfo ambariMetaInfo = s_metaProvider.get();
+
+      StackInfo stackInfo = ambariMetaInfo.getStack(effectiveStackId.getStackName(),
+          effectiveStackId.getStackVersion());
+
+      String serviceName = wrapper.getTasks().get(0).getService();
+      ServiceInfo serviceInfo = ambariMetaInfo.getService(effectiveStackId.getStackName(),
+          effectiveStackId.getStackVersion(), serviceName);
+
+      commandParams.put(SERVICE_PACKAGE_FOLDER, serviceInfo.getServicePackageFolder());
+      commandParams.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
+    }
+  }
+
+  /**
+   * Creates an action stage using the {@link #EXECUTE_TASK_ROLE} custom action
+   * to execute some Python command.
+   *
+   * @param context
+   *          the upgrade context.
+   * @param request
+   *          the request object to add the stage to.
+   * @param effectiveRepositoryVersion
+   *          the stack/version to use when generating content for the command.
+   *          On some upgrade types, this may change during the course of the
+   *          upgrade orchestration. An express upgrade changes this after
+   *          stopping all services.
+   * @param entity
+   *          the upgrade entity to set the stage information on
+   * @param wrapper
+   *          the stage wrapper containing information to generate the stage.
+   * @param skippable
+   *          {@code true} to mark the stage as being skippable if a failure
+   *          occurs.
+   * @param supportsAutoSkipOnFailure
+   *          {@code true} to automatically skip on a failure.
+   * @param allowRetry
+   *          {@code true} to be able to retry the failed stage.
+   * @throws AmbariException
+   */
   private void makeActionStage(UpgradeContext context, RequestStageContainer request,
       RepositoryVersionEntity effectiveRepositoryVersion, UpgradeItemEntity entity,
       StageWrapper wrapper, boolean skippable, boolean supportsAutoSkipOnFailure,
@@ -869,38 +926,28 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     Cluster cluster = context.getCluster();
 
     LOG.debug("Analyzing upgrade item {} with tasks: {}.", entity.getText(), entity.getTasks());
-    Map<String, String> params = getNewParameterMap(request, context);
-    params.put(UpgradeContext.COMMAND_PARAM_TASKS, entity.getTasks());
-
-    // Apply additional parameters to the command that come from the stage.
-    applyAdditionalParameters(wrapper, params);
-
-    // Because custom task may end up calling a script/function inside a
-    // service, it is necessary to set the
-    // service_package_folder and hooks_folder params.
-    AmbariMetaInfo ambariMetaInfo = s_metaProvider.get();
-    StackId stackId = effectiveRepositoryVersion.getStackId();
-
-    StackInfo stackInfo = ambariMetaInfo.getStack(stackId.getStackName(),
-        stackId.getStackVersion());
 
     // if the service/component are specified, then make sure to grab them off
     // of the wrapper so they can be stored on the command for use later
     String serviceName = null;
     String componentName = null;
-
     if (wrapper.getTasks() != null && wrapper.getTasks().size() > 0
         && wrapper.getTasks().get(0).getService() != null) {
       TaskWrapper taskWrapper = wrapper.getTasks().get(0);
       serviceName = taskWrapper.getService();
       componentName = taskWrapper.getComponent();
+    }
 
-      ServiceInfo serviceInfo = ambariMetaInfo.getService(stackId.getStackName(),
-          stackId.getStackVersion(), serviceName);
+    Map<String, String> params = getNewParameterMap(request, context);
+    params.put(UpgradeContext.COMMAND_PARAM_TASKS, entity.getTasks());
 
-      params.put(SERVICE_PACKAGE_FOLDER, serviceInfo.getServicePackageFolder());
-      params.put(HOOKS_FOLDER, stackInfo.getStackHooksFolder());
-    }
+    // Apply additional parameters to the command that come from the stage.
+    applyAdditionalParameters(wrapper, params);
+
+    // the ru_execute_tasks invokes scripts - it needs information about where
+    // the scripts live and for that it should always use the target repository
+    // stack
+    applyRepositoryAssociatedParameters(wrapper, effectiveRepositoryVersion.getStackId(), params);
 
     // add each host to this stage
     RequestResourceFilter filter = new RequestResourceFilter(serviceName, componentName,
@@ -1048,6 +1095,10 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     // Apply additional parameters to the command that come from the stage.
     applyAdditionalParameters(wrapper, commandParams);
 
+    // add things like hooks and service folders based on effective repo
+    applyRepositoryAssociatedParameters(wrapper, effectiveRepositoryVersion.getStackId(),
+        commandParams);
+
     ActionExecutionContext actionContext = new ActionExecutionContext(cluster.getClusterName(),
         "SERVICE_CHECK", filters, commandParams);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/historyserver.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/historyserver.py b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/historyserver.py
index d886244..0b03af4 100644
--- a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/historyserver.py
+++ b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/historyserver.py
@@ -90,7 +90,7 @@ class HistoryServerDefault(HistoryServer):
     env.set_params(params)
     self.configure(env) # FOR SECURITY
 
-    if params.stack_version_formatted_major and check_stack_feature(StackFeature.COPY_TARBALL_TO_HDFS, params.stack_version_formatted_major):
+    if check_stack_feature(StackFeature.COPY_TARBALL_TO_HDFS, params.version_for_stack_feature_checks):
       # MC Hammer said, "Can't touch this"
       resource_created = copy_to_hdfs(
         "mapreduce",

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/params_linux.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/params_linux.py b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/params_linux.py
index 67931c6..6f75852 100644
--- a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/params_linux.py
+++ b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/params_linux.py
@@ -66,21 +66,20 @@ tarball_map = default("/configurations/cluster-env/tarball_map", None)
 config_path = os.path.join(stack_root, "current/hadoop-client/conf")
 config_dir = os.path.realpath(config_path)
 
+# get the correct version to use for checking stack features
+version_for_stack_feature_checks = get_stack_feature_version(config)
+
 # This is expected to be of the form #.#.#.#
 stack_version_unformatted = config['hostLevelParams']['stack_version']
-stack_version_formatted_major = format_stack_version(stack_version_unformatted)
 stack_version_formatted = functions.get_stack_version('hadoop-yarn-resourcemanager')
 
-stack_supports_ru = stack_version_formatted_major and check_stack_feature(StackFeature.ROLLING_UPGRADE, stack_version_formatted_major)
-stack_supports_timeline_state_store = stack_version_formatted_major and check_stack_feature(StackFeature.TIMELINE_STATE_STORE, stack_version_formatted_major)
+stack_supports_ru = check_stack_feature(StackFeature.ROLLING_UPGRADE, version_for_stack_feature_checks)
+stack_supports_timeline_state_store = check_stack_feature(StackFeature.TIMELINE_STATE_STORE, version_for_stack_feature_checks)
 
 # New Cluster Stack Version that is defined during the RESTART of a Stack Upgrade.
 # It cannot be used during the initial Cluser Install because the version is not yet known.
 version = default("/commandParams/version", None)
 
-# get the correct version to use for checking stack features
-version_for_stack_feature_checks = get_stack_feature_version(config)
-
 stack_supports_ranger_kerberos = check_stack_feature(StackFeature.RANGER_KERBEROS_SUPPORT, version_for_stack_feature_checks)
 stack_supports_ranger_audit_db = check_stack_feature(StackFeature.RANGER_AUDIT_DB_SUPPORT, version_for_stack_feature_checks)
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/service_check.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/service_check.py b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/service_check.py
index b934767..bf52ee6 100644
--- a/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/service_check.py
+++ b/ambari-server/src/main/resources/common-services/YARN/2.1.0.2.0/package/scripts/service_check.py
@@ -99,11 +99,7 @@ class ServiceCheckDefault(ServiceCheck):
                         mode=params.smoke_hdfs_user_mode,
                         )
 
-    if params.stack_version_formatted_major and check_stack_feature(StackFeature.ROLLING_UPGRADE, params.stack_version_formatted_major):
-      path_to_distributed_shell_jar = format("{stack_root}/current/hadoop-yarn-client/hadoop-yarn-applications-distributedshell.jar")
-    else:
-      path_to_distributed_shell_jar = "/usr/lib/hadoop-yarn/hadoop-yarn-applications-distributedshell*.jar"
-
+    path_to_distributed_shell_jar = format("{stack_root}/current/hadoop-yarn-client/hadoop-yarn-applications-distributedshell.jar")
     yarn_distrubuted_shell_check_params = ["yarn org.apache.hadoop.yarn.applications.distributedshell.Client",
                                            "-shell_command", "ls", "-num_containers", "{number_of_nm}",
                                            "-jar", "{path_to_distributed_shell_jar}", "-timeout", "300000",

http://git-wip-us.apache.org/repos/asf/ambari/blob/e87a3e31/ambari-server/src/test/python/TestStackFeature.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/TestStackFeature.py b/ambari-server/src/test/python/TestStackFeature.py
index 230734c..6e8bcec 100644
--- a/ambari-server/src/test/python/TestStackFeature.py
+++ b/ambari-server/src/test/python/TestStackFeature.py
@@ -21,10 +21,13 @@ limitations under the License.
 
 from resource_management.core.logger import Logger
 from resource_management.libraries.functions.stack_features import get_stack_feature_version
+from resource_management.libraries.functions.stack_features import check_stack_feature
 from resource_management.libraries.script import Script
 from resource_management.core.exceptions import Fail
 from unittest import TestCase
 
+import json
+
 Logger.initialize_logger()
 
 class TestStackFeature(TestCase):
@@ -115,6 +118,34 @@ class TestStackFeature(TestCase):
     stack_feature_version = get_stack_feature_version(command_json)
     self.assertEqual("2.5.9.9-9999", stack_feature_version)
 
+
+  def test_get_stack_feature(self):
+    """
+    Tests the stack feature version calculated during a STOP command in a downgrade.
+    :return:
+    """
+    command_json = TestStackFeature._get_cluster_upgrade_restart_json()
+    Script.config = command_json
+
+    Script.config["configurations"] = {}
+    Script.config["configurations"]["cluster-env"] = {}
+    Script.config["configurations"]["cluster-env"]["stack_features"] = {}
+    Script.config["configurations"]["cluster-env"]["stack_features"] = json.dumps(TestStackFeature._get_stack_feature_json())
+
+    stack_feature_version = get_stack_feature_version(command_json)
+    self.assertTrue(check_stack_feature("stack-feature-1", stack_feature_version))
+    self.assertTrue(check_stack_feature("stack-feature-2", stack_feature_version))
+    self.assertFalse(check_stack_feature("stack-feature-3", stack_feature_version))
+
+    command_json = TestStackFeature._get_cluster_install_command_json()
+    Script.config.update(command_json)
+
+    stack_feature_version = get_stack_feature_version(command_json)
+    self.assertTrue(check_stack_feature("stack-feature-1", stack_feature_version))
+    self.assertTrue(check_stack_feature("stack-feature-2", stack_feature_version))
+    self.assertFalse(check_stack_feature("stack-feature-3", stack_feature_version))
+
+
   @staticmethod
   def _get_cluster_install_command_json():
     """
@@ -221,4 +252,34 @@ class TestStackFeature(TestCase):
         "version":"2.5.9.9-9999",
         "downgrade_from_version":"2.5.9.9-9999"
       }
+    }
+
+  @staticmethod
+  def _get_stack_feature_json():
+    """
+    A STOP command during a downgrade.
+    :return:
+    """
+    return {
+      "HDP": {
+        "stack_features":[
+          {
+            "name":"stack-feature-1",
+            "description":"Stack Feature 1",
+            "min_version":"2.2.0.0"
+          },
+          {
+            "name":"stack-feature-2",
+            "description":"Stack Feature 2",
+            "min_version":"2.2.0.0",
+            "max_version":"2.6.0.0"
+          },
+          {
+            "name":"stack-feature-3",
+            "description":"Stack Feature 3",
+            "min_version":"2.2.0.0",
+            "max_version":"2.3.0.0"
+          }
+        ]
+      }
     }
\ No newline at end of file