protected UpgradeEntity createUpgrade(UpgradeContext upgradeContext)
      throws AmbariException, AuthorizationException {

    UpgradePack pack = upgradeContext.getUpgradePack();
    Cluster cluster = upgradeContext.getCluster();
    Direction direction = upgradeContext.getDirection();

    ConfigHelper configHelper = getManagementController().getConfigHelper();

    List<UpgradeGroupHolder> groups = s_upgradeHelper.createSequence(pack, upgradeContext);

    if (groups.isEmpty()) {
      throw new AmbariException("There are no groupings available");
    }

    // Non Rolling Upgrades require a group with name "UPDATE_DESIRED_REPOSITORY_ID".
    // This is needed as a marker to indicate which version to use when an upgrade is paused.
    if (pack.getType() == UpgradeType.NON_ROLLING) {
      boolean foundUpdateDesiredRepositoryIdGrouping = false;
      for (UpgradeGroupHolder group : groups) {
        if (group.groupClass == UpdateStackGrouping.class) {
          foundUpdateDesiredRepositoryIdGrouping = true;
          break;
        }
      }

      if (!foundUpdateDesiredRepositoryIdGrouping) {
        throw new AmbariException(String.format(
            "Express upgrade packs are required to have a group of type %s. The upgrade pack %s is missing this grouping.",
            "update-stack", pack.getName()));
      }
    }

    List<UpgradeGroupEntity> groupEntities = new ArrayList<>();
    RequestStageContainer req = createRequest(upgradeContext);

    UpgradeEntity upgrade = new UpgradeEntity();
    upgrade.setRepositoryVersion(upgradeContext.getRepositoryVersion());
    upgrade.setClusterId(cluster.getClusterId());
    upgrade.setDirection(direction);
    upgrade.setUpgradePackage(pack.getName());
    upgrade.setUpgradePackStackId(pack.getOwnerStackId());
    upgrade.setUpgradeType(pack.getType());
    upgrade.setAutoSkipComponentFailures(upgradeContext.isComponentFailureAutoSkipped());
    upgrade.setAutoSkipServiceCheckFailures(upgradeContext.isServiceCheckFailureAutoSkipped());
    upgrade.setDowngradeAllowed(upgradeContext.isDowngradeAllowed());
    upgrade.setOrchestration(upgradeContext.getOrchestrationType());

    // create to/from history for this upgrade - this should be done before any
    // possible changes to the desired version for components
    addComponentHistoryToUpgrade(cluster, upgrade, upgradeContext);

    /*
    During a Rolling Upgrade, change the desired Stack Id if jumping across
    major stack versions (e.g., HDP 2.2 -> 2.3), and then set config changes
    so they are applied on the newer stack.

    During a {@link UpgradeType.NON_ROLLING} upgrade, the stack is applied during the middle of the upgrade (after
    stopping all services), and the configs are applied immediately before starting the services.
    The Upgrade Pack is responsible for calling {@link org.apache.ambari.server.serveraction.upgrades.UpdateDesiredRepositoryAction}
    at the appropriate moment during the orchestration.
    */
    if (pack.getType() == UpgradeType.ROLLING || pack.getType() == UpgradeType.HOST_ORDERED) {
      if (direction == Direction.UPGRADE) {
        StackEntity targetStack = upgradeContext.getRepositoryVersion().getStack();
        cluster.setDesiredStackVersion(
            new StackId(targetStack.getStackName(), targetStack.getStackVersion()));
      }
      s_upgradeHelper.updateDesiredRepositoriesAndConfigs(upgradeContext);
      s_upgradeHelper.publishDesiredRepositoriesUpdates(upgradeContext);
      if (direction == Direction.DOWNGRADE) {
        StackId targetStack = upgradeContext.getCluster().getCurrentStackVersion();
        cluster.setDesiredStackVersion(targetStack);
      }
    }

    // resolve or build a proper config upgrade pack - always start out with the config pack
    // for the current stack and merge into that
    //
    // HDP 2.2 to 2.3 should start with the config-upgrade.xml from HDP 2.2
    // HDP 2.2 to 2.4 should start with HDP 2.2 and merge in HDP 2.3's config-upgrade.xml
    ConfigUpgradePack configUpgradePack = ConfigurationPackBuilder.build(upgradeContext);

    // !!! effectiveStack in an EU must start as the source stack, since we're generating
    // commands for the "old" version
    StackId effectiveStack = upgradeContext.getTargetStack();
    if (upgradeContext.getType() == UpgradeType.NON_ROLLING) {
      effectiveStack = upgradeContext.getSourceStack();
    }

    // create the upgrade and request
    for (UpgradeGroupHolder group : groups) {

      if (upgradeContext.getType() == UpgradeType.NON_ROLLING
          && UpdateStackGrouping.class.equals(group.groupClass)) {
        effectiveStack = upgradeContext.getTargetStack();
      }

      List<UpgradeItemEntity> itemEntities = new ArrayList<>();

      for (StageWrapper wrapper : group.items) {

        switch(wrapper.getType()) {
          case SERVER_SIDE_ACTION:{
            // !!! each stage is guaranteed to be of one type. but because there
            // is a bug that prevents one stage with multiple tasks assigned for
            // the same host, break them out into individual stages.
            for (TaskWrapper taskWrapper : wrapper.getTasks()) {
              for (Task task : taskWrapper.getTasks()) {
                if (upgradeContext.isManualVerificationAutoSkipped()
                    && task.getType() == Task.Type.MANUAL) {
                  continue;
                }

                UpgradeItemEntity itemEntity = new UpgradeItemEntity();

                itemEntity.setText(wrapper.getText());
                itemEntity.setTasks(wrapper.getTasksJson());
                itemEntity.setHosts(wrapper.getHostsJson());

                injectVariables(configHelper, cluster, itemEntity);
                if (makeServerSideStage(group, upgradeContext, effectiveStack, req,
                    itemEntity, (ServerSideActionTask) task, configUpgradePack)) {
                  itemEntities.add(itemEntity);
                }
              }
            }
            break;
          }
          case REGENERATE_KEYTABS: {
            try {
              // remmeber how many stages we had before adding keytab stuff
              int stageCount = req.getStages().size();

              // build a map of request properties which say to
              //   - only regenerate missing tabs
              //   - allow all tasks which fail to be retried (so the upgrade doesn't abort)
              Map<String, String> requestProperties = new HashMap<>();
              requestProperties.put(SupportedCustomOperation.REGENERATE_KEYTABS.name().toLowerCase(), "missing");
              requestProperties.put(KerberosHelper.ALLOW_RETRY, Boolean.TRUE.toString().toLowerCase());
              requestProperties.put(KerberosHelper.DIRECTIVE_CONFIG_UPDATE_POLICY, UpdateConfigurationPolicy.NEW_AND_IDENTITIES.name());

              // add stages to the upgrade which will regenerate missing keytabs only
              req = s_kerberosHelper.get().executeCustomOperations(cluster, requestProperties, req, null);

              // for every stage which was added for kerberos stuff create an
              // associated upgrade item for it
              List<Stage> stages = req.getStages();
              int newStageCount = stages.size();
              for (int i = stageCount; i < newStageCount; i++) {
                Stage stage = stages.get(i);
                stage.setSkippable(group.skippable);
                stage.setAutoSkipFailureSupported(group.supportsAutoSkipOnFailure);

                UpgradeItemEntity itemEntity = new UpgradeItemEntity();
                itemEntity.setStageId(stage.getStageId());
                itemEntity.setText(stage.getRequestContext());
                itemEntity.setTasks(wrapper.getTasksJson());
                itemEntity.setHosts(wrapper.getHostsJson());
                itemEntities.add(itemEntity);
                injectVariables(configHelper, cluster, itemEntity);
              }
            } catch (KerberosOperationException kerberosOperationException) {
              throw new AmbariException("Unable to build keytab regeneration stage",
                  kerberosOperationException);
            }

            break;
          }
          default: {
            UpgradeItemEntity itemEntity = new UpgradeItemEntity();
            itemEntity.setText(wrapper.getText());
            itemEntity.setTasks(wrapper.getTasksJson());
            itemEntity.setHosts(wrapper.getHostsJson());
            itemEntities.add(itemEntity);

            injectVariables(configHelper, cluster, itemEntity);

            // upgrade items match a stage
            createStage(group, upgradeContext, effectiveStack, req, itemEntity, wrapper);

            break;
          }
        }
      }

      if(!itemEntities.isEmpty()) {
        UpgradeGroupEntity groupEntity = new UpgradeGroupEntity();
        groupEntity.setName(group.name);
        groupEntity.setTitle(group.title);
        groupEntity.setItems(itemEntities);
        groupEntities.add(groupEntity);
      }
    }

    // set all of the groups we just created
    upgrade.setUpgradeGroups(groupEntities);

    req.getRequestStatusResponse();
    return createUpgradeInsideTransaction(cluster, req, upgrade, upgradeContext);
  }
Creates the upgrade. All Request/Stage/Task and Upgrade entities will exist
in the database when this method completes.
<p/>
This method itself must not be wrapped in a transaction since it can
potentially make 1000's of database calls while building the entities
before persisting them. This would create a long-lived transaction which
could lead to database deadlock issues. Instead, only the creation of the
actual entities is wrapped in a {@link Transactional} block.

@param upgradeContext
@return
@throws AmbariException
@throws AuthorizationException
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java