private void addCustomCommandAction(final ActionExecutionContext actionExecutionContext,
      final RequestResourceFilter resourceFilter, Stage stage, Map<String, String> additionalCommandParams,
      String commandDetail, Map<String, String> requestParams) throws AmbariException {
    final String serviceName = resourceFilter.getServiceName();
    final String componentName = resourceFilter.getComponentName();
    final String commandName = actionExecutionContext.getActionName();
    boolean retryAllowed = actionExecutionContext.isRetryAllowed();
    boolean autoSkipFailure = actionExecutionContext.isFailureAutoSkipped();

    String clusterName = stage.getClusterName();
    final Cluster cluster = clusters.getCluster(clusterName);

    // start with all hosts
    Set<String> candidateHosts = new HashSet<>(resourceFilter.getHostNames());

    // Filter hosts that are in MS
    Set<String> ignoredHosts = maintenanceStateHelper.filterHostsInMaintenanceState(
      candidateHosts, new MaintenanceStateHelper.HostPredicate() {
        @Override
        public boolean shouldHostBeRemoved(final String hostname)
            throws AmbariException {
          if (actionExecutionContext.isFutureCommand()) {
            return false;
          }

          return !maintenanceStateHelper.isOperationAllowed(
              cluster, actionExecutionContext.getOperationLevel(),
              resourceFilter, serviceName, componentName, hostname);
        }
      }
    );

    // Filter unhealthy hosts
    Set<String> unhealthyHosts = getUnhealthyHosts(candidateHosts, actionExecutionContext, resourceFilter);

    // log excluded hosts
    if (!ignoredHosts.isEmpty()) {
      if( LOG.isDebugEnabled() ){
        LOG.debug(
            "While building the {} custom command for {}/{}, the following hosts were excluded: unhealthy[{}], maintenance[{}]",
            commandName, serviceName, componentName, StringUtils.join(unhealthyHosts, ','),
            StringUtils.join(ignoredHosts, ','));
      }
    } else if (!unhealthyHosts.isEmpty()) {
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            "While building the {} custom command for {}/{}, the following hosts were excluded: unhealthy[{}], maintenance[{}]",
            commandName, serviceName, componentName, StringUtils.join(unhealthyHosts, ','),
            StringUtils.join(ignoredHosts, ','));
      }
    } else if (candidateHosts.isEmpty()) {
      String message = MessageFormat.format(
          "While building the {0} custom command for {1}/{2}, there were no healthy eligible hosts",
          commandName, serviceName, componentName);

      throw new AmbariException(message);
    }

    Service service = cluster.getService(serviceName);

    // grab the stack ID from the service first, and use the context's if it's set
    StackId stackId = service.getDesiredStackId();
    if (null != actionExecutionContext.getStackId()) {
      stackId = actionExecutionContext.getStackId();
    }

    AmbariMetaInfo ambariMetaInfo = managementController.getAmbariMetaInfo();
    ServiceInfo serviceInfo = ambariMetaInfo.getService(service);

    CustomCommandDefinition customCommandDefinition = null;
    ComponentInfo ci = serviceInfo.getComponentByName(componentName);
    if(ci != null){
      customCommandDefinition = ci.getCustomCommandByName(commandName);
    }

    long nowTimestamp = System.currentTimeMillis();

    for (String hostName : candidateHosts) {
      stage.addHostRoleExecutionCommand(hostName, Role.valueOf(componentName),
          RoleCommand.CUSTOM_COMMAND,
          new ServiceComponentHostOpInProgressEvent(componentName, hostName, nowTimestamp),
          cluster.getClusterName(), serviceName, retryAllowed, autoSkipFailure);

      ExecutionCommand execCmd = stage.getExecutionCommandWrapper(hostName,
          componentName).getExecutionCommand();

      // if the command should fetch brand new configuration tags before
      // execution, then we don't need to fetch them now
      if(actionExecutionContext.getParameters() != null && actionExecutionContext.getParameters().containsKey(KeyNames.OVERRIDE_CONFIGS)){
        execCmd.setOverrideConfigs(true);
      }

      HostRoleCommand cmd = stage.getHostRoleCommand(hostName, componentName);
      if (cmd != null) {
        cmd.setCommandDetail(commandDetail);
        cmd.setCustomCommandName(commandName);
        if (customCommandDefinition != null){
          cmd.setOpsDisplayName(customCommandDefinition.getOpsDisplayName());
        }
      }

      //set type background
      if(customCommandDefinition != null && customCommandDefinition.isBackground()){
        cmd.setBackgroundCommand(true);
        execCmd.setCommandType(AgentCommandType.BACKGROUND_EXECUTION_COMMAND);
      }

      execCmd.setComponentVersions(cluster);

      execCmd.setConfigurations(new TreeMap<>());

      // Get the value of credential store enabled from the DB
      Service clusterService = cluster.getService(serviceName);
      execCmd.setCredentialStoreEnabled(String.valueOf(clusterService.isCredentialStoreEnabled()));

      // Get the map of service config type to password properties for the service
      Map<String, Map<String, String>> configCredentials;
      configCredentials = configCredentialsForService.get(clusterService.getName());
      if (configCredentials == null) {
        configCredentials = configHelper.getCredentialStoreEnabledProperties(stackId, clusterService);
        configCredentialsForService.put(clusterService.getName(), configCredentials);
      }

      execCmd.setConfigurationCredentials(configCredentials);

      Map<String, String> hostLevelParams = new TreeMap<>();
      hostLevelParams.put(STACK_NAME, stackId.getStackName());
      hostLevelParams.put(STACK_VERSION, stackId.getStackVersion());

      Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();

      Set<String> userSet = configHelper.getPropertyValuesWithPropertyType(stackId, PropertyType.USER, cluster, desiredConfigs);
      String userList = gson.toJson(userSet);
      hostLevelParams.put(USER_LIST, userList);

      //Create a user_group mapping and send it as part of the hostLevelParams
      Map<String, Set<String>> userGroupsMap = configHelper.createUserGroupsMap(
        stackId, cluster, desiredConfigs);
      String userGroups = gson.toJson(userGroupsMap);
      hostLevelParams.put(USER_GROUPS, userGroups);

      Set<String> groupSet = configHelper.getPropertyValuesWithPropertyType(stackId, PropertyType.GROUP, cluster, desiredConfigs);
      String groupList = gson.toJson(groupSet);
      hostLevelParams.put(GROUP_LIST, groupList);

      Map<PropertyInfo, String> notManagedHdfsPathMap = configHelper.getPropertiesWithPropertyType(stackId, PropertyType.NOT_MANAGED_HDFS_PATH, cluster, desiredConfigs);
      Set<String> notManagedHdfsPathSet = configHelper.filterInvalidPropertyValues(notManagedHdfsPathMap, NOT_MANAGED_HDFS_PATH_LIST);
      String notManagedHdfsPathList = gson.toJson(notManagedHdfsPathSet);
      hostLevelParams.put(NOT_MANAGED_HDFS_PATH_LIST, notManagedHdfsPathList);

      execCmd.setHostLevelParams(hostLevelParams);

      Map<String, String> commandParams = new TreeMap<>();
      if (additionalCommandParams != null) {
        for (String key : additionalCommandParams.keySet()) {
          commandParams.put(key, additionalCommandParams.get(key));
        }
      }
      commandParams.put(CUSTOM_COMMAND, commandName);

      boolean isInstallCommand = commandName.equals(RoleCommand.INSTALL.toString());
      int commandTimeout = Integer.valueOf(configs.getDefaultAgentTaskTimeout(isInstallCommand));

      ComponentInfo componentInfo = ambariMetaInfo.getComponent(
          stackId.getStackName(), stackId.getStackVersion(),
          serviceName, componentName);

      if (serviceInfo.getSchemaVersion().equals(AmbariMetaInfo.SCHEMA_VERSION_2)) {
        // Service check command is not custom command
        CommandScriptDefinition script = componentInfo.getCommandScript();

        if (script != null) {
          commandParams.put(SCRIPT, script.getScript());
          commandParams.put(SCRIPT_TYPE, script.getScriptType().toString());
          if (script.getTimeout() > 0) {
            commandTimeout = script.getTimeout();
          }
        } else {
          String message = String.format("Component %s has not command script " +
              "defined. It is not possible to send command for " +
              "this service", componentName);
          throw new AmbariException(message);
        }
        // We don't need package/repo information to perform service check
      }

      // !!! the action execution context timeout is the final say, but make sure it's at least 60 seconds
      if (null != actionExecutionContext.getTimeout()) {
        commandTimeout = actionExecutionContext.getTimeout().intValue();
        commandTimeout = Math.max(60, commandTimeout);
      }

      if (requestParams != null && requestParams.containsKey(RequestResourceProvider.CONTEXT)) {
        String requestContext = requestParams.get(RequestResourceProvider.CONTEXT);
        if (StringUtils.isNotEmpty(requestContext) && requestContext.toLowerCase().contains("rolling-restart")) {
          Config clusterEnvConfig = cluster.getDesiredConfigByType("cluster-env");
          if (clusterEnvConfig != null) {
            String componentRollingRestartTimeout = clusterEnvConfig.getProperties().get("namenode_rolling_restart_timeout");
            if (StringUtils.isNotEmpty(componentRollingRestartTimeout)) {
              commandTimeout = Integer.parseInt(componentRollingRestartTimeout);
            }
          }
        }
      }

      commandParams.put(COMMAND_TIMEOUT, "" + commandTimeout);

      Map<String, String> roleParams = execCmd.getRoleParams();
      if (roleParams == null) {
        roleParams = new TreeMap<>();
      }

      // if there is a stack upgrade which is currently suspended then pass that
      // information down with the command as some components may need to know
      boolean isUpgradeSuspended = cluster.isUpgradeSuspended();
      if (isUpgradeSuspended) {
        cluster.addSuspendedUpgradeParameters(commandParams, roleParams);
      }
      StageUtils.useAmbariJdkInCommandParams(commandParams, configs);
      roleParams.put(COMPONENT_CATEGORY, componentInfo.getCategory());

      // set reconfigureAction in case of a RECONFIGURE command if there are any
      if (commandName.equals("RECONFIGURE")) {
        String refreshConfigsCommand = configHelper.getRefreshConfigsCommand(cluster, hostName, serviceName, componentName);
        if (refreshConfigsCommand != null && !refreshConfigsCommand.equals(RefreshCommandConfiguration.REFRESH_CONFIGS)) {
              LOG.info("Refreshing configs for {}/{} with command: ", componentName, hostName, refreshConfigsCommand);
          commandParams.put("reconfigureAction", refreshConfigsCommand);
          //execCmd.setForceRefreshConfigTagsBeforeExecution(true);
        }
      }

      execCmd.setCommandParams(commandParams);
      execCmd.setRoleParams(roleParams);

      // skip anything else
      if (actionExecutionContext.isFutureCommand()) {
        continue;
      }

      // perform any server side command related logic - eg - set desired states on restart
      applyCustomCommandBackendLogic(cluster, serviceName, componentName, commandName, hostName);
    }
  }
Called during the start/stop/restart of services, plus custom commands during Stack Upgrade.
@param actionExecutionContext Execution Context
@param resourceFilter Resource Filter
@param stage Command stage
@param additionalCommandParams Additional command params to add the the stage
@param commandDetail String for the command detail
@throws AmbariException
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java