private void makeActionStage(UpgradeContext context, RequestStageContainer request,
      StackId stackId, UpgradeItemEntity entity,
      StageWrapper wrapper, boolean skippable, boolean supportsAutoSkipOnFailure,
      boolean allowRetry) throws AmbariException {

    if (0 == wrapper.getHosts().size()) {
      throw new AmbariException(
          String.format("Cannot create action for '%s' with no hosts", wrapper.getText()));
    }

    Cluster cluster = context.getCluster();

    LOG.debug("Analyzing upgrade item {} with tasks: {}.", entity.getText(), entity.getTasks());

    // 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();
    }

    Map<String, String> params = getNewParameterMap(request, context);
    params.put(UpgradeContext.COMMAND_PARAM_TASKS, entity.getTasks());

    // !!! when not scoped to a component (generic execution task)
    if (context.isScoped(UpgradeScope.COMPLETE) && null == componentName) {
      if (context.getDirection().isUpgrade()) {
        params.put(KeyNames.VERSION, context.getRepositoryVersion().getVersion());
      } else {
        // !!! in a full downgrade, the target version should be any of the history versions
        UpgradeEntity lastUpgrade = s_upgradeDAO.findLastUpgradeForCluster(
            cluster.getClusterId(), Direction.UPGRADE);

        @Experimental(feature = ExperimentalFeature.PATCH_UPGRADES,
            comment = "Shouldn't be getting the overall downgrade-to version.")
        UpgradeHistoryEntity lastHistory = lastUpgrade.getHistory().iterator().next();
        params.put(KeyNames.VERSION, lastHistory.getFromReposistoryVersion().getVersion());
      }
    }

    // Apply additional parameters to the command that come from the stage.
    applyAdditionalParameters(wrapper, params);


    // add each host to this stage
    RequestResourceFilter filter = new RequestResourceFilter(serviceName, componentName,
        new ArrayList<>(wrapper.getHosts()));

    ActionExecutionContext actionContext = buildActionExecutionContext(cluster, context,
        EXECUTE_TASK_ROLE, stackId, Collections.singletonList(filter), params,
        allowRetry, wrapper.getMaxTimeout(s_configuration));

    ExecuteCommandJson jsons = s_commandExecutionHelper.get().getCommandJson(actionContext,
        cluster, stackId, null);

    Stage stage = s_stageFactory.get().createNew(request.getId().longValue(), "/tmp/ambari",
        cluster.getClusterName(), cluster.getClusterId(), entity.getText(), jsons.getCommandParamsForStage(),
        jsons.getHostParamsForStage());

    stage.setSkippable(skippable);
    stage.setAutoSkipFailureSupported(supportsAutoSkipOnFailure);

    long stageId = request.getLastStageId() + 1;
    if (0L == stageId) {
      stageId = 1L;
    }

    stage.setStageId(stageId);
    entity.setStageId(Long.valueOf(stageId));

    s_actionExecutionHelper.get().addExecutionCommandsToStage(actionContext, stage, null);

    // need to set meaningful text on the command
    for (Map<String, HostRoleCommand> map : stage.getHostRoleCommands().values()) {
      for (HostRoleCommand hrc : map.values()) {
        hrc.setCommandDetail(entity.getText());
      }
    }

    request.addStages(Collections.singletonList(stage));
  }
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
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java