private CommandReport finalizeDowngrade(UpgradeContext upgradeContext)
      throws AmbariException, InterruptedException {

    StringBuilder outSB = new StringBuilder();
    StringBuilder errSB = new StringBuilder();

    try {
      Cluster cluster = upgradeContext.getCluster();
      RepositoryVersionEntity downgradeFromRepositoryVersion = upgradeContext.getRepositoryVersion();
      String downgradeFromVersion = downgradeFromRepositoryVersion.getVersion();
      Set<String> servicesInUpgrade = upgradeContext.getSupportedServices();

      String message;

      if (upgradeContext.getOrchestrationType() == RepositoryType.STANDARD) {
        message = MessageFormat.format(
            "Finalizing the downgrade from {0} for all cluster services.",
            downgradeFromVersion);
      } else {
        message = MessageFormat.format(
            "Finalizing the downgrade from {0} for the following services: {1}",
            downgradeFromVersion, StringUtils.join(servicesInUpgrade, ','));
      }

      outSB.append(message).append(System.lineSeparator());

      // iterate through all host components and make sure that they are on the
      // correct version; if they are not, then this will throw an exception
      Set<InfoTuple> errors = validateComponentVersions(upgradeContext);
      if (!errors.isEmpty()) {
        StrBuilder messageBuff = new StrBuilder(String.format(
            "The following %d host component(s) have not been downgraded to their desired versions:",
            errors.size())).append(System.lineSeparator());

        for (InfoTuple error : errors) {
          messageBuff.append(String.format("%s: %s (current = %s, desired = %s)", error.hostName,
              error.componentName, error.currentVersion, error.targetVersion));

          messageBuff.append(System.lineSeparator());
        }

        throw new AmbariException(messageBuff.toString());
      }

      finalizeHostVersionsNotDesired(cluster, upgradeContext);

      // for every repository being downgraded to, ensure the host versions are correct
      Map<String, RepositoryVersionEntity> targetVersionsByService = upgradeContext.getTargetVersions();
      Set<RepositoryVersionEntity> targetRepositoryVersions = new HashSet<>();
      for (String service : targetVersionsByService.keySet()) {
        targetRepositoryVersions.add(targetVersionsByService.get(service));
      }

      // move host versions in the downgrade back to CURRENT if they are not
      for (RepositoryVersionEntity targetRepositoryVersion : targetRepositoryVersions) {
        // find host versions
        List<HostVersionEntity> hostVersions = hostVersionDAO.findHostVersionByClusterAndRepository(
            cluster.getClusterId(), targetRepositoryVersion);

        outSB.append(String.format("Finalizing %d host(s) back to %s", hostVersions.size(),
            targetRepositoryVersion.getVersion())).append(System.lineSeparator());

        for (HostVersionEntity hostVersion : hostVersions) {
          if (hostVersion.getState() != RepositoryVersionState.CURRENT) {
            hostVersion.setState(RepositoryVersionState.CURRENT);
            hostVersionDAO.merge(hostVersion);
          }

          List<HostComponentStateEntity> hostComponentStates = hostComponentStateDAO.findByHost(
              hostVersion.getHostName());

          for (HostComponentStateEntity hostComponentState : hostComponentStates) {
            hostComponentState.setUpgradeState(UpgradeState.NONE);
            hostComponentStateDAO.merge(hostComponentState);
          }
        }
      }

      // remove any configurations for services which crossed a stack boundary
      for (String serviceName : servicesInUpgrade) {
        RepositoryVersionEntity sourceRepositoryVersion = upgradeContext.getSourceRepositoryVersion(serviceName);
        RepositoryVersionEntity targetRepositoryVersion = upgradeContext.getTargetRepositoryVersion(serviceName);
        StackId sourceStackId = sourceRepositoryVersion.getStackId();
        StackId targetStackId = targetRepositoryVersion.getStackId();
        // only work with configurations when crossing stacks
        if (!sourceStackId.equals(targetStackId)) {
          outSB.append(
              String.format("Removing %s configurations for %s", sourceStackId,
                  serviceName)).append(System.lineSeparator());

          cluster.removeConfigurations(sourceStackId, serviceName);
        }
      }

      // ensure that when downgrading, we set the desired back to the
      // original value
      versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));

      // Reset upgrade state
      cluster.setUpgradeEntity(null);

      message = String.format("The downgrade from %s has completed.", downgradeFromVersion);
      outSB.append(message).append(System.lineSeparator());

      return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", outSB.toString(), errSB.toString());
    } catch (Exception e) {
      StringWriter sw = new StringWriter();
      e.printStackTrace(new PrintWriter(sw));
      errSB.append(sw);

      return createCommandReport(-1, HostRoleStatus.FAILED, "{}", outSB.toString(), errSB.toString());
    }
  }
Execution path for downgrade.

@param upgradeContext
         the upgrade context (not {@code null}).
@return the command report
ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java