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/13 19:14:46 UTC

[28/37] ambari git commit: AMBARI-21450. Initial cherry-picking for feature branch (ncole)

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
index 0e6f0c4..9d70546 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -20,11 +20,13 @@ package org.apache.ambari.server.serveraction.upgrades;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.text.MessageFormat;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.ambari.server.AmbariException;
@@ -33,35 +35,24 @@ import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.events.StackUpgradeFinishEvent;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
-import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.HostComponentStateDAO;
 import org.apache.ambari.server.orm.dao.HostVersionDAO;
-import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
-import org.apache.ambari.server.orm.dao.StackDAO;
-import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
-import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
-import org.apache.ambari.server.orm.entities.ServiceComponentDesiredStateEntity;
-import org.apache.ambari.server.orm.entities.ServiceComponentHistoryEntity;
-import org.apache.ambari.server.orm.entities.StackEntity;
-import org.apache.ambari.server.orm.entities.UpgradeEntity;
-import org.apache.ambari.server.serveraction.AbstractServerAction;
 import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
+import org.apache.ambari.server.state.RepositoryType;
 import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.UpgradeContext;
-import org.apache.ambari.server.state.UpgradeContextFactory;
 import org.apache.ambari.server.state.UpgradeState;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
-import org.apache.ambari.server.state.svccomphost.ServiceComponentHostSummary;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.text.StrBuilder;
 
 import com.google.inject.Inject;
@@ -69,228 +60,128 @@ import com.google.inject.Inject;
 /**
  * Action that represents finalizing the Upgrade by completing any database changes.
  */
-public class FinalizeUpgradeAction extends AbstractServerAction {
+public class FinalizeUpgradeAction extends AbstractUpgradeServerAction {
 
-  public static final String CLUSTER_NAME_KEY = "cluster_name";
-  public static final String UPGRADE_DIRECTION_KEY = "upgrade_direction";
-  public static final String VERSION_KEY = "version";
-  public static final String REQUEST_ID = "request_id";
   public static final String PREVIOUS_UPGRADE_NOT_COMPLETED_MSG = "It is possible that a previous upgrade was not finalized. " +
       "For this reason, Ambari will not remove any configs. Please ensure that all database records are correct.";
 
-  /**
-   * The original "current" stack of the cluster before the upgrade started.
-   * This is the same regardless of whether the current direction is
-   * {@link Direction#UPGRADE} or {@link Direction#DOWNGRADE}.
-   */
-  public static final String ORIGINAL_STACK_KEY = "original_stack";
-
-  /**
-   * The target upgrade stack before the upgrade started. This is the same
-   * regardless of whether the current direction is {@link Direction#UPGRADE} or
-   * {@link Direction#DOWNGRADE}.
-   */
-  public static final String TARGET_STACK_KEY = "target_stack";
-
-  /**
-   * The Cluster that this ServerAction implementation is executing on
-   */
-  @Inject
-  protected Clusters clusters;
-
-  @Inject
-  private ClusterVersionDAO clusterVersionDAO;
-
   @Inject
   private HostVersionDAO hostVersionDAO;
 
   @Inject
   private HostComponentStateDAO hostComponentStateDAO;
 
-  /**
-   * Gets {@link StackEntity} instances from {@link StackId}.
-   */
-  @Inject
-  private StackDAO stackDAO;
-
-  /**
-   * Gets desired state entities for service components.
-   */
-  @Inject
-  private ServiceComponentDesiredStateDAO serviceComponentDesiredStateDAO;
-
   @Inject
   private AmbariMetaInfo ambariMetaInfo;
 
   @Inject
-  VersionEventPublisher versionEventPublisher;
-
-  /**
-   * Used for building {@link UpgradeContext} instances.
-   */
-  @Inject
-  private UpgradeContextFactory upgradeContextFactory;
+  private VersionEventPublisher versionEventPublisher;
 
   @Override
   public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext)
       throws AmbariException, InterruptedException {
 
     String clusterName = getExecutionCommand().getClusterName();
-    Cluster cluster = clusters.getCluster(clusterName);
-    UpgradeContext context = upgradeContextFactory.create(cluster, cluster.getUpgradeInProgress());
+    Cluster cluster = m_clusters.getCluster(clusterName);
 
-    if (context.getDirection() == Direction.DOWNGRADE) {
-      return finalizeDowngrade(context);
+    UpgradeContext upgradeContext = getUpgradeContext(cluster);
+
+    if (upgradeContext.getDirection() == Direction.UPGRADE) {
+      return finalizeUpgrade(upgradeContext);
     } else {
-      return finalizeUpgrade(context);
+      return finalizeDowngrade(upgradeContext);
     }
   }
 
   /**
    * Execution path for upgrade.
-   *
-   * @param context
-   *          the upgrade context (not {@code null}).
    * @return the command report
    */
-  private CommandReport finalizeUpgrade(UpgradeContext context)
+  private CommandReport finalizeUpgrade(UpgradeContext upgradeContext)
     throws AmbariException, InterruptedException {
 
     StringBuilder outSB = new StringBuilder();
     StringBuilder errSB = new StringBuilder();
 
     try {
-      Cluster cluster = context.getCluster();
-      String clusterName = cluster.getClusterName();
-      RepositoryVersionEntity targetRepositoryVersion = context.getTargetRepositoryVersion();
-      String version = targetRepositoryVersion.getVersion();
+      Cluster cluster = upgradeContext.getCluster();
+      RepositoryVersionEntity repositoryVersion = upgradeContext.getRepositoryVersion();
+      String version = repositoryVersion.getVersion();
+
+      String message;
+      if (upgradeContext.getOrchestrationType() == RepositoryType.STANDARD) {
+        message = MessageFormat.format("Finalizing the upgrade to {0} for all cluster services.", version);
+      } else {
+        Set<String> servicesInUpgrade = upgradeContext.getSupportedServices();
 
-      outSB.append(MessageFormat.format(
-          "Begin finalizing the upgrade of cluster {0} to version {1}\n", clusterName,
-          targetRepositoryVersion.getVersion()));
+        message = MessageFormat.format(
+            "Finalizing the upgrade to {0} for the following services: {1}",
+            version, StringUtils.join(servicesInUpgrade, ','));
+      }
 
-      StackId clusterDesiredStackId = cluster.getDesiredStackVersion();
-      StackId clusterCurrentStackId = cluster.getCurrentStackVersion();
+      outSB.append(message).append(System.lineSeparator());
 
-      ClusterVersionEntity upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
-          clusterName, clusterDesiredStackId, version);
+      // 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 upgraded to version %s. Please install and upgrade "
+                + "the Stack Version on those hosts and try again.\nHost components:",
+            errors.size(), version)).append(System.lineSeparator());
 
-      if (upgradingClusterVersion == null) {
-        throw new AmbariException(MessageFormat.format(
-            "Cluster stack version {0} not found", version));
+        for (InfoTuple error : errors) {
+          messageBuff.append(String.format("%s on host %s\n", error.componentName, error.hostName));
+        }
+
+        throw new AmbariException(messageBuff.toString());
       }
 
-      // Validate that all of the hosts with a version in the cluster have the
-      // version being upgraded to, and it is in an allowed state.
-      List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(
-          clusterName, clusterDesiredStackId, version);
+      // find every host version for this upgrade and ensure it has transitioned
+      // to CURRENT if required
+      List<HostVersionEntity> hostVersions = hostVersionDAO.findHostVersionByClusterAndRepository(
+          cluster.getClusterId(), repositoryVersion);
 
-      // Will include hosts whose state is INSTALLED
       Set<HostVersionEntity> hostVersionsAllowed = new HashSet<>();
       Set<String> hostsWithoutCorrectVersionState = new HashSet<>();
-      Set<String> hostsToUpdate = new HashSet<>();
 
-      // It is important to only iterate over the hosts with a version, as
-      // opposed to all hosts, since some hosts may only have components that do
-      // not advertise a version, such as AMBARI_METRICS.
+      // for every host version for this repository, determine if any didn't
+      // transition correctly
       for (HostVersionEntity hostVersion : hostVersions) {
-        boolean hostHasCorrectVersionState = false;
         RepositoryVersionState hostVersionState = hostVersion.getState();
-        switch( hostVersionState ){
-          case CURRENT:{
-            // if the state is correct, then do nothing
-            hostHasCorrectVersionState = true;
-            break;
-          }
-          case NOT_REQUIRED:
-          case INSTALLED:{
-            // It is possible that the host version has a state of INSTALLED and it
-            // never changed if the host only has components that do not advertise a
-            // version.
-            HostEntity host = hostVersion.getHostEntity();
-
-            ServiceComponentHostSummary hostSummary = new ServiceComponentHostSummary(ambariMetaInfo,
-                host, clusterDesiredStackId);
-
-            // if all components have finished advertising their version, then
-            // this host can be considered upgraded
-            if (hostSummary.haveAllComponentsFinishedAdvertisingVersion()) {
-              // mark this as upgraded
-              hostHasCorrectVersionState = true;
-            } else {
-              hostsWithoutCorrectVersionState.add(hostVersion.getHostName());
-            }
-
+        switch (hostVersionState) {
+          case CURRENT:
+          case NOT_REQUIRED: {
+            hostVersionsAllowed.add(hostVersion);
             break;
           }
           default: {
-            // all other states are not allowed
             hostsWithoutCorrectVersionState.add(hostVersion.getHostName());
             break;
           }
         }
-
-        // keep track of this host version in order to transition it correctly
-        if (hostHasCorrectVersionState) {
-          hostVersionsAllowed.add(hostVersion);
-          hostsToUpdate.add(hostVersion.getHostName());
-        }
       }
 
-      // throw an exception if there are hosts which are not not fully upgraded
+      // throw an exception if there are hosts which did not transition the
+      // repository to CURRENT
       if (hostsWithoutCorrectVersionState.size() > 0) {
-        String message = String.format("The following %d host(s) have not been upgraded to version %s. " +
-                "Please install and upgrade the Stack Version on those hosts and try again.\nHosts: %s\n",
-            hostsWithoutCorrectVersionState.size(),
-            version,
+        message = String.format(
+            "The following %d host(s) have not been upgraded to version %s. "
+                + "Please install and upgrade the Stack Version on those hosts and try again.\nHosts: %s",
+            hostsWithoutCorrectVersionState.size(), version,
             StringUtils.join(hostsWithoutCorrectVersionState, ", "));
         outSB.append(message);
+        outSB.append(System.lineSeparator());
         throw new AmbariException(message);
       }
 
-      // 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
-      List<InfoTuple> errors = checkHostComponentVersions(cluster, version, clusterDesiredStackId);
-      if (! errors.isEmpty()) {
-        StrBuilder messageBuff = new StrBuilder(
-            String.format(
-                "The following %d host component(s) "
-                    + "have not been upgraded to version %s. Please install and upgrade "
-                    + "the Stack Version on those hosts and try again.\nHost components:\n",
-                errors.size(), version));
-
-        for (InfoTuple error : errors) {
-          messageBuff.append(String.format("%s on host %s\n", error.componentName, error.hostName));
-        }
-
-        throw new AmbariException(messageBuff.toString());
-      }
-
-
-      // we're guaranteed to be ready transition to upgraded now; ensure that
-      // the transition will be allowed if the cluster state is not upgraded
-      upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(clusterName,
-          clusterDesiredStackId, version);
-
-      if (RepositoryVersionState.INSTALLING == upgradingClusterVersion.getState()) {
-        cluster.transitionClusterVersion(clusterDesiredStackId, version, RepositoryVersionState.INSTALLED);
-
-        upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
-            clusterName, clusterDesiredStackId, version);
-      }
-
-      // we cannot finalize since the cluster was not ready to move into the
-      // upgraded state
-      if (RepositoryVersionState.INSTALLED != upgradingClusterVersion.getState()) {
-        throw new AmbariException(String.format("The cluster stack version state %s is not allowed to transition directly into %s",
-            upgradingClusterVersion.getState(), RepositoryVersionState.CURRENT.toString()));
-      }
-
       outSB.append(
-          String.format("Finalizing the upgraded state of host components in %d host(s).\n",
-              hostVersionsAllowed.size()));
+          String.format("Finalizing the upgrade state and repository version for %d host(s).",
+              hostVersionsAllowed.size())).append(System.lineSeparator());
 
-      // Reset the upgrade state
+      // at this point, all host versions are correct - do some cleanup like
+      // resetting the upgrade state
       for (HostVersionEntity hostVersion : hostVersionsAllowed) {
         Collection<HostComponentStateEntity> hostComponentStates = hostComponentStateDAO.findByHost(hostVersion.getHostName());
         for (HostComponentStateEntity hostComponentStateEntity: hostComponentStates) {
@@ -299,28 +190,18 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
         }
       }
 
-      // Impacts all hosts that have a version
-      outSB.append(
-          String.format("Finalizing the version for %d host(s).\n", hostVersionsAllowed.size()));
-      cluster.mapHostVersions(hostsToUpdate, upgradingClusterVersion, RepositoryVersionState.CURRENT);
-
-      versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));
-
-      // transitioning the cluster into CURRENT will update the current/desired
-      // stack values
-      outSB.append(String.format("Finalizing the version for cluster %s.\n", clusterName));
-      cluster.transitionClusterVersion(clusterDesiredStackId, version,
-          RepositoryVersionState.CURRENT);
-
-      UpgradeEntity upgradeEntity = cluster.getUpgradeInProgress();
-      outSB.append("Creating upgrade history.\n");
-      writeComponentHistory(cluster, upgradeEntity, clusterCurrentStackId, clusterDesiredStackId);
+      // move host versions from CURRENT to INSTALLED if their repos are no
+      // longer used
+      finalizeHostRepositoryVersions(cluster);
 
       // Reset upgrade state
       cluster.setUpgradeEntity(null);
 
-      outSB.append("Upgrade was successful!\n");
+      // the upgrade is done!
+      versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));
 
+      message = String.format("The upgrade to %s has completed.", version);
+      outSB.append(message).append(System.lineSeparator());
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", outSB.toString(), errSB.toString());
     } catch (Exception e) {
       errSB.append(e.getMessage());
@@ -331,223 +212,286 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
   /**
    * Execution path for downgrade.
    *
-   * @param context
+   * @param upgradeContext
    *          the upgrade context (not {@code null}).
    * @return the command report
    */
-  private CommandReport finalizeDowngrade(UpgradeContext context)
+  private CommandReport finalizeDowngrade(UpgradeContext upgradeContext)
       throws AmbariException, InterruptedException {
 
-    StringBuilder out = new StringBuilder();
-    StringBuilder err = new StringBuilder();
+    StringBuilder outSB = new StringBuilder();
+    StringBuilder errSB = new StringBuilder();
 
     try {
-      Cluster cluster = context.getCluster();
-      String clusterName = cluster.getClusterName();
-
-      StackId currentClusterStackId = cluster.getCurrentStackVersion();
-      StackId sourceStackId = context.getSourceStackId();
-      StackId targetStackId = context.getTargetStackId();
-      RepositoryVersionEntity targetRepositoryVersion = context.getTargetRepositoryVersion();
-
-      // Safety check that the cluster's stack (from clusterstate's current_stack_id) is equivalent to the
-      // cluster's CURRENT repo version's stack. This is to avoid deleting configs from the target stack if the customer
-      // ended up modifying their database manually after a stack upgrade and forgot to call "Save DB State".
-      ClusterVersionEntity currentClusterVersion = cluster.getCurrentClusterVersion();
-      RepositoryVersionEntity currentRepoVersion = currentClusterVersion.getRepositoryVersion();
-      StackId currentRepoStackId = currentRepoVersion.getStackId();
-      if (!currentRepoStackId.equals(targetStackId)) {
-        String msg = String.format(
-            "The stack of %s's CURRENT repository version is %s, yet the target stack for this downgrade is %s. %s",
-            clusterName, currentRepoStackId.getStackId(), targetStackId.getStackId(),
-            PREVIOUS_UPGRADE_NOT_COMPLETED_MSG);
-        out.append(msg);
-        err.append(msg);
-        throw new AmbariException(
-            "The target stack of this downgrade doesn't match the cluster's current stack.");
-      }
+      Cluster cluster = upgradeContext.getCluster();
+      RepositoryVersionEntity downgradeFromRepositoryVersion = upgradeContext.getRepositoryVersion();
+      String downgradeFromVersion = downgradeFromRepositoryVersion.getVersion();
+      Set<String> servicesInUpgrade = upgradeContext.getSupportedServices();
 
-      // This was a cross-stack upgrade, meaning that configurations were created that now need to be removed.
-      if (!sourceStackId.equals(targetStackId)) {
-        out.append(String.format(
-            "Configurations created for stack %s will be removed since this downgrade is to stack %s.",
-            sourceStackId.getStackId(), targetStackId.getStackId()));
+      String message;
 
-        out.append(System.lineSeparator());
-        cluster.removeConfigurations(sourceStackId);
+      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, ','));
       }
 
-      ClusterVersionEntity clusterVersion = clusterVersionDAO.findByClusterAndStateCurrent(clusterName);
-      if (null == clusterVersion) {
-        throw new AmbariException("Could not find current cluster version");
-      }
+      outSB.append(message).append(System.lineSeparator());
 
-      out.append(String.format(
-          "Comparing downgrade target version %s-%s to current cluster version %s-%s\n",
-          targetStackId.getStackName(), targetRepositoryVersion.getVersion(),
-          clusterVersion.getRepositoryVersion().getStackId().getStackName(),
-          clusterVersion.getRepositoryVersion().getVersion()));
-
-      if (!StringUtils.equals(targetRepositoryVersion.getVersion(),
-          clusterVersion.getRepositoryVersion().getVersion())) {
-        throw new AmbariException(
-            String.format("Downgrade version %s is not the current cluster version of %s. %s",
-                targetRepositoryVersion.getVersion(),
-                clusterVersion.getRepositoryVersion().getVersion(),
-                PREVIOUS_UPGRADE_NOT_COMPLETED_MSG));
-      } else {
-        out.append(String.format(
-            "Downgrade version is the same as current. Searching "
-                + "for cluster versions that do not match %s\n",
-            targetRepositoryVersion.getVersion()));
-      }
+      // 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());
 
-      Set<String> badVersions = new HashSet<>();
-
-      // update the cluster version
-      for (ClusterVersionEntity cve : clusterVersionDAO.findByCluster(clusterName)) {
-        switch (cve.getState()) {
-          case INSTALL_FAILED:
-          case INSTALLED:
-          case INSTALLING: {
-              badVersions.add(cve.getRepositoryVersion().getVersion());
-              cve.setState(RepositoryVersionState.INSTALLED);
-              clusterVersionDAO.merge(cve);
-              break;
-            }
-          default:
-            break;
+        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());
       }
 
-      out.append(String.format("Found %d other version(s) not matching downgrade: %s\n",
-          badVersions.size(), StringUtils.join(badVersions, ", ")));
 
-      Set<String> badHosts = new HashSet<>();
-      for (String badVersion : badVersions) {
-        List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(
-            clusterName, sourceStackId, badVersion);
+      if (upgradeContext.isPatchRevert()) {
+        finalizeHostRepositoryVersions(cluster);
+      } else {
+        // 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));
+        }
+
+        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 (HostVersionEntity hostVersion : hostVersions) {
-          badHosts.add(hostVersion.getHostName());
-          hostVersion.setState(RepositoryVersionState.INSTALLED);
-          hostVersionDAO.merge(hostVersion);
+            for (HostComponentStateEntity hostComponentState : hostComponentStates) {
+              hostComponentState.setUpgradeState(UpgradeState.NONE);
+              hostComponentStateDAO.merge(hostComponentState);
+            }
+          }
         }
       }
 
-      out.append(String.format("Found %d hosts not matching downgrade version: %s-%s\n",
-          badHosts.size(), targetStackId.getStackName(), targetRepositoryVersion.getVersion()));
-
-      for (String badHost : badHosts) {
-        List<HostComponentStateEntity> hostComponentStates = hostComponentStateDAO.findByHost(badHost);
-        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
-      cluster.setDesiredStackVersion(currentClusterStackId);
       versionEventPublisher.publish(new StackUpgradeFinishEvent(cluster));
 
       // Reset upgrade state
       cluster.setUpgradeEntity(null);
 
-      return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
-          out.toString(), err.toString());
+      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));
-      err.append(sw.toString());
+      errSB.append(sw);
 
-      return createCommandReport(-1, HostRoleStatus.FAILED, "{}",
-          out.toString(), err.toString());
+      return createCommandReport(-1, HostRoleStatus.FAILED, "{}", outSB.toString(), errSB.toString());
     }
   }
 
-
   /**
-   * Confirms that all host components that are able to provide hdp version,
-   * have been upgraded to the target version.
-   * @param cluster         the cluster the upgrade is for
-   * @param desiredVersion  the target version of the upgrade
-   * @param targetStackId     the target stack id for meta-info lookup
-   * @return the list of {@link InfoTuple} objects of host components in error
+   * Gets any host components which have not been propertly upgraded or
+   * downgraded.
+   *
+   * @param upgradeContext
+   *          the upgrade context (not {@code null}).
+   * @return a list of {@link InfoTuple} representing components which should
+   *         have been upgraded but did not.
    */
-  protected List<InfoTuple> checkHostComponentVersions(Cluster cluster, String desiredVersion, StackId targetStackId)
-          throws AmbariException {
+  protected Set<InfoTuple> validateComponentVersions(UpgradeContext upgradeContext)
+      throws AmbariException {
 
-    ArrayList<InfoTuple> errors = new ArrayList<>();
+    Set<InfoTuple> errors = new TreeSet<>();
+
+    Cluster cluster = upgradeContext.getCluster();
+    RepositoryVersionEntity repositoryVersionEntity = upgradeContext.getRepositoryVersion();
+    StackId targetStackId = repositoryVersionEntity.getStackId();
+
+    Set<String> servicesParticipating = upgradeContext.getSupportedServices();
+    for (String serviceName : servicesParticipating) {
+      Service service = cluster.getService(serviceName);
+      String targetVersion = upgradeContext.getTargetVersion(serviceName);
 
-    for (Service service : cluster.getServices().values()) {
       for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
         for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
           ComponentInfo componentInfo = ambariMetaInfo.getComponent(targetStackId.getStackName(),
                   targetStackId.getStackVersion(), service.getName(), serviceComponent.getName());
 
           if (!componentInfo.isVersionAdvertised()) {
-            StackId desired = serviceComponentHost.getDesiredStackVersion();
-            StackId actual = serviceComponentHost.getStackVersion();
-            if (!desired.equals(actual)) {
-              serviceComponentHost.setStackVersion(desired);
-            }
-          } else if (componentInfo.isVersionAdvertised()
-              && !serviceComponentHost.getVersion().equals(desiredVersion)) {
-            errors.add(new InfoTuple(
-                service.getName(), serviceComponent.getName(),
-                serviceComponentHost.getHostName(), serviceComponentHost.getVersion()));
+            continue;
+          }
+
+          if (!StringUtils.equals(targetVersion, serviceComponentHost.getVersion())) {
+            errors.add(new InfoTuple(service.getName(), serviceComponent.getName(),
+                serviceComponentHost.getHostName(), serviceComponentHost.getVersion(),
+                targetVersion));
           }
         }
       }
     }
 
+
     return errors;
   }
 
-  private void writeComponentHistory(Cluster cluster, UpgradeEntity upgradeEntity,
-      StackId fromStackId, StackId toStackId) {
+  /**
+   * Moves any {@link HostVersionEntity}s which are
+   * {@link RepositoryVersionState#CURRENT} to either
+   * {@link RepositoryVersionState#INSTALLED} or
+   * {@link RepositoryVersionState#NOT_REQUIRED} if their assocaited
+   * repositories are no longer in use.
+   *
+   * @param cluster
+   * @throws AmbariException
+   */
+  private void finalizeHostRepositoryVersions(Cluster cluster) throws AmbariException {
+    // create a set of all of the repos that the services are on
+    Set<RepositoryVersionEntity> desiredRepoVersions = new HashSet<>();
+    Set<String> serviceNames = cluster.getServices().keySet();
+    for (String serviceName : serviceNames) {
+      Service service = cluster.getService(serviceName);
+      desiredRepoVersions.add(service.getDesiredRepositoryVersion());
+    }
 
-    StackEntity fromStack = stackDAO.find(fromStackId.getStackName(), fromStackId.getStackVersion());
-    StackEntity toStack = stackDAO.find(toStackId.getStackName(), toStackId.getStackVersion());
+    // if any CURRENT host version is for a repo which is no longer desired by
+    // ANY service, move it to INSTALLED
+    List<HostVersionEntity> currentHostVersions = hostVersionDAO.findByClusterAndState(
+        cluster.getClusterName(), RepositoryVersionState.CURRENT);
 
-    // for every service component, if it was included in the upgrade then
-    // create a historical entry
-    for (Service service : cluster.getServices().values()) {
-      for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
-        if (serviceComponent.isVersionAdvertised()) {
-          // create the historical entry
-          ServiceComponentHistoryEntity historyEntity = new ServiceComponentHistoryEntity();
-          historyEntity.setUpgrade(upgradeEntity);
-          historyEntity.setFromStack(fromStack);
-          historyEntity.setToStack(toStack);
-
-          // get the service component
-          ServiceComponentDesiredStateEntity desiredStateEntity = serviceComponentDesiredStateDAO.findByName(
-              cluster.getClusterId(), serviceComponent.getServiceName(),
-              serviceComponent.getName());
-
-          // add the history to the component and save
-          desiredStateEntity.addHistory(historyEntity);
-          serviceComponentDesiredStateDAO.merge(desiredStateEntity);
-        }
+    for (HostVersionEntity hostVersion : currentHostVersions) {
+      RepositoryVersionEntity hostRepoVersion = hostVersion.getRepositoryVersion();
+      if (!desiredRepoVersions.contains(hostRepoVersion)) {
+        hostVersion.setState(RepositoryVersionState.INSTALLED);
+        hostVersion = hostVersionDAO.merge(hostVersion);
       }
     }
   }
 
-  protected static class InfoTuple {
+  protected static class InfoTuple implements Comparable<InfoTuple> {
     protected final String serviceName;
     protected final String componentName;
     protected final String hostName;
     protected final String currentVersion;
+    protected final String targetVersion;
 
-    protected InfoTuple(String service, String component, String host, String version) {
+    protected InfoTuple(String service, String component, String host, String version,
+        String desiredVersion) {
       serviceName = service;
       componentName = component;
       hostName = host;
       currentVersion = version;
+      targetVersion = desiredVersion;
     }
-  }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(InfoTuple that) {
+      int compare = hostName.compareTo(that.hostName);
+      if (compare != 0) {
+        return compare;
+      }
+
+      compare = serviceName.compareTo(that.serviceName);
+      if (compare != 0) {
+        return compare;
+      }
+
+      compare = componentName.compareTo(that.componentName);
+      if (compare != 0) {
+        return compare;
+      }
+
+      return compare;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(hostName, serviceName, componentName, currentVersion, targetVersion);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object object) {
+      if (this == object) {
+        return true;
+      }
+
+      if (object == null || getClass() != object.getClass()) {
+        return false;
+      }
+
+      InfoTuple that = (InfoTuple) object;
+
+      EqualsBuilder equalsBuilder = new EqualsBuilder();
+      equalsBuilder.append(hostName, that.hostName);
+      equalsBuilder.append(serviceName, that.serviceName);
+      equalsBuilder.append(componentName, that.componentName);
+      equalsBuilder.append(currentVersion, that.currentVersion);
+      equalsBuilder.append(targetVersion, that.targetVersion);
+      ;
+      return equalsBuilder.isEquals();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      return com.google.common.base.Objects.toStringHelper(this)
+          .add("host", hostName)
+          .add("component", componentName)
+          .add("current", currentVersion)
+          .add("target", targetVersion).toString();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
index 498fb49..ff0becc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/UpdateDesiredStackAction.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -16,31 +16,35 @@
  * limitations under the License.
  */
 package org.apache.ambari.server.serveraction.upgrades;
-import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.VERSION;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
-import org.apache.ambari.server.api.services.AmbariMetaInfo;
-import org.apache.ambari.server.controller.AmbariServer;
-import org.apache.ambari.server.controller.internal.UpgradeResourceProvider;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.dao.HostVersionDAO;
+import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
-import org.apache.ambari.server.serveraction.AbstractServerAction;
+import org.apache.ambari.server.serveraction.ServerAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
-import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.state.StackInfo;
+import org.apache.ambari.server.state.RepositoryType;
+import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.UpgradeContext;
-import org.apache.ambari.server.state.UpgradeContextFactory;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
+import com.google.inject.persist.Transactional;
 
 /**
  * Action that represents updating the Desired Stack Id during the middle of a stack upgrade (typically NonRolling).
@@ -48,15 +52,13 @@ import com.google.inject.Inject;
  * actually changed half-way through calculating the Actions, and this serves to update the database to make it
  * evident to the user at which point it changed.
  */
-public class UpdateDesiredStackAction extends AbstractServerAction {
+public class UpdateDesiredStackAction extends AbstractUpgradeServerAction {
 
   /**
    * Logger.
    */
   private static final Logger LOG = LoggerFactory.getLogger(UpdateDesiredStackAction.class);
 
-  public static final String COMMAND_PARAM_VERSION = VERSION;
-  public static final String COMMAND_PARAM_UPGRADE_PACK = "upgrade_pack";
 
   /**
    * The Cluster that this ServerAction implementation is executing on.
@@ -64,105 +66,122 @@ public class UpdateDesiredStackAction extends AbstractServerAction {
   @Inject
   private Clusters clusters;
 
+  /**
+   * The Ambari configuration.
+   */
   @Inject
-  private AmbariMetaInfo ambariMetaInfo;
+  private Configuration m_configuration;
 
   /**
-   * Used for building {@link UpgradeContext} instances.
+   * Used for restting host version states on downgrade.
    */
   @Inject
-  UpgradeContextFactory m_upgradeContextFactory;
+  private HostVersionDAO m_hostVersionDAO;
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext)
       throws AmbariException, InterruptedException {
+
     String clusterName = getExecutionCommand().getClusterName();
     Cluster cluster = clusters.getCluster(clusterName);
-    UpgradeContext context = m_upgradeContextFactory.create(cluster,
-        cluster.getUpgradeInProgress());
+    UpgradeContext upgradeContext = getUpgradeContext(cluster);
 
+    Map<String, String> roleParams = getExecutionCommand().getRoleParams();
 
-    CommandReport commandReport = updateDesiredStack(cluster, context);
-
-    // invalidate any cached effective ID
-    cluster.invalidateUpgradeEffectiveVersion();
+    // Make a best attempt at setting the username
+    String userName;
+    if (roleParams != null && roleParams.containsKey(ServerAction.ACTION_USER_NAME)) {
+      userName = roleParams.get(ServerAction.ACTION_USER_NAME);
+    } else {
+      userName = m_configuration.getAnonymousAuditName();
+      LOG.warn(String.format("Did not receive role parameter %s, will save configs using anonymous username %s", ServerAction.ACTION_USER_NAME, userName));
+    }
 
-    return commandReport;
+    return updateDesiredRepositoryVersion(cluster, upgradeContext, userName);
   }
 
   /**
-   * Set the cluster's Desired Stack Id during an upgrade.
+   * Sets the desired repository version for services participating in the
+   * upgrade.
    *
-   * @param context
-   *          the upgrade context (not {@code null}).
+   * @param cluster
+   *          the cluster
+   * @param upgradeContext
+   *          the upgrade context
+   * @param userName
+   *          username performing the action
    * @return the command report to return
    */
-  private CommandReport updateDesiredStack(Cluster cluster, UpgradeContext context)
+  @Transactional
+  CommandReport updateDesiredRepositoryVersion(
+      Cluster cluster, UpgradeContext upgradeContext, String userName)
       throws AmbariException, InterruptedException {
 
-    Direction direction = context.getDirection();
-    RepositoryVersionEntity fromRepositoryVersion = context.getSourceRepositoryVersion();
-    RepositoryVersionEntity toRepositoryVersion = context.getTargetRepositoryVersion();
-
-    String clusterName = cluster.getClusterName();
     StringBuilder out = new StringBuilder();
     StringBuilder err = new StringBuilder();
 
     try {
-      StackId clusterDesiredStackId = cluster.getDesiredStackVersion();
-      StackId currentClusterStackId = cluster.getCurrentStackVersion();
-      StackId targetStackId = toRepositoryVersion.getStackId();
-
-      out.append(String.format("%s %s from %s-%s to %s-%s\n",
-          direction.getVerb(true), clusterName,
-          fromRepositoryVersion.getStackId().getStackName(),
-          fromRepositoryVersion.getVersion(),
-          targetStackId.getStackName(),
-          toRepositoryVersion.getVersion()));
-
-      // Ensure that the target stack id exist
-      StackInfo desiredClusterStackInfo = ambariMetaInfo.getStack(targetStackId.getStackName(), targetStackId.getStackVersion());
-      if (null == desiredClusterStackInfo) {
-        String message = String.format("Invalid target stack of \n", targetStackId.getStackId());
-        err.append(message);
-        out.append(message);
-        return createCommandReport(-1, HostRoleStatus.FAILED, "{}", out.toString(), err.toString());
+      // the desired repository message to put in the command report - this will
+      // change based on the type of upgrade and the services participating
+      if (upgradeContext.getDirection() == Direction.UPGRADE) {
+        final String message;
+        RepositoryVersionEntity targetRepositoryVersion = upgradeContext.getRepositoryVersion();
+
+        if (upgradeContext.getOrchestrationType() == RepositoryType.STANDARD) {
+          message = MessageFormat.format(
+              "Updating the desired repository version to {0} for all cluster services.",
+              targetRepositoryVersion.getVersion());
+        } else {
+          Set<String> servicesInUpgrade = upgradeContext.getSupportedServices();
+          message = MessageFormat.format(
+              "Updating the desired repository version to {0} for the following services: {1}",
+              targetRepositoryVersion.getVersion(), StringUtils.join(servicesInUpgrade, ','));
+        }
+
+        out.append(message).append(System.lineSeparator());
       }
 
-      // Ensure that the current Stack Id coincides with the parameter that the user passed in.
-      if (direction == Direction.DOWNGRADE && !currentClusterStackId.equals(targetStackId)) {
-        String message = String.format(
-            "The cluster's current stack of %s doesn't match %s which is target stack of this downgrade",
-            currentClusterStackId, targetStackId);
+      if( upgradeContext.getDirection() == Direction.DOWNGRADE ){
+        String message = "Updating the desired repository back their original values for the following services:";
+        out.append(message).append(System.lineSeparator());
 
-        err.append(message);
-        out.append(message);
-        return createCommandReport(-1, HostRoleStatus.FAILED, "{}", out.toString(), err.toString());
+        Map<String, RepositoryVersionEntity> targetVersionsByService = upgradeContext.getTargetVersions();
+        for (String serviceName : targetVersionsByService.keySet()) {
+          RepositoryVersionEntity repositoryVersion = targetVersionsByService.get(serviceName);
+
+          message = String.format("  %s to %s", serviceName, repositoryVersion.getVersion());
+          out.append(message).append(System.lineSeparator());
+        }
       }
 
-      // Check for a no-op
-      if (clusterDesiredStackId.equals(targetStackId)) {
-        String message = String.format("The cluster's desired stack is already set to %s\n",
-            targetStackId.getStackId());
+      // move repositories to the right version and create/revert configs
+      m_upgradeHelper.updateDesiredRepositoriesAndConfigs(upgradeContext);
 
-        out.append(message);
-        return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", out.toString(), err.toString());
-      }
+      // a downgrade must force host versions back to INSTALLED for the
+      // repository which failed to be upgraded.
+      if (upgradeContext.getDirection() == Direction.DOWNGRADE) {
+        RepositoryVersionEntity downgradeFromRepositoryVersion = upgradeContext.getRepositoryVersion();
+        out.append(String.format("Setting host versions back to %s for repository version %s",
+            RepositoryVersionState.INSTALLED, downgradeFromRepositoryVersion.getVersion()));
 
-      // Create Create new configurations that are a merge between the current stack and the desired stack
-      // Also updates the desired stack version.
-      UpgradeResourceProvider upgradeResourceProvider = new UpgradeResourceProvider(AmbariServer.getController());
-      upgradeResourceProvider.applyStackAndProcessConfigurations(context);
-      String message = String.format("The cluster's desired stack was set to %s.\n",
-          targetStackId.getStackId());
+        List<HostVersionEntity> hostVersionsToReset = m_hostVersionDAO.findHostVersionByClusterAndRepository(
+            cluster.getClusterId(), downgradeFromRepositoryVersion);
 
-      out.append(message);
+        for (HostVersionEntity hostVersion : hostVersionsToReset) {
+          if( hostVersion.getState() != RepositoryVersionState.NOT_REQUIRED ){
+            hostVersion.setState(RepositoryVersionState.INSTALLED);
+          }
+        }
+      }
 
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", out.toString(), err.toString());
     } catch (Exception e) {
       StringWriter sw = new StringWriter();
       e.printStackTrace(new PrintWriter(sw));
-      err.append(sw.toString());
+      err.append(sw);
 
       return createCommandReport(-1, HostRoleStatus.FAILED, "{}", out.toString(), err.toString());
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
index f5eb007..fc657c1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -30,13 +30,16 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.UpgradeContext;
 import org.apache.ambari.server.state.UpgradeState;
+import org.apache.ambari.server.state.stack.upgrade.Direction;
 import org.apache.ambari.server.utils.HTTPUtils;
 import org.apache.ambari.server.utils.HostAndPort;
 import org.apache.ambari.server.utils.StageUtils;
@@ -49,11 +52,11 @@ import com.google.common.reflect.TypeToken;
 
 public class MasterHostResolver {
 
-  private static Logger LOG = LoggerFactory.getLogger(MasterHostResolver.class);
+  private static final Logger LOG = LoggerFactory.getLogger(MasterHostResolver.class);
 
-  private Cluster m_cluster;
-  private String m_version;
-  private ConfigHelper m_configHelper;
+  private final UpgradeContext m_upgradeContext;
+  private final Cluster m_cluster;
+  private final ConfigHelper m_configHelper;
 
   public enum Service {
     HDFS,
@@ -71,29 +74,17 @@ public class MasterHostResolver {
   }
 
   /**
-   * Create a resolver that does not consider HostComponents' version when
-   * resolving hosts.  Common use case is creating an upgrade that should
-   * include an entire cluster.
-   * @param configHelper Configuration Helper
-   * @param cluster the cluster
-   */
-  public MasterHostResolver(ConfigHelper configHelper, Cluster cluster) {
-    this(configHelper, cluster, null);
-  }
-
-  /**
-   * Create a resolver that compares HostComponents' version when calculating
-   * hosts for the stage.  Common use case is for downgrades when only some
-   * HostComponents need to be downgraded, and HostComponents already at the
-   * correct version are skipped.
-   * @param configHelper Configuration Helper
-   * @param cluster the cluster
-   * @param version the version, or {@code null} to not compare versions
+   * Constructor.
+   *
+   * @param configHelper
+   *          Configuration Helper
+   * @param upgradeContext
+   *          the upgrade context
    */
-  public MasterHostResolver(ConfigHelper configHelper, Cluster cluster, String version) {
+  public MasterHostResolver(Cluster cluster, ConfigHelper configHelper, UpgradeContext upgradeContext) {
     m_configHelper = configHelper;
+    m_upgradeContext = upgradeContext;
     m_cluster = cluster;
-    m_version = version;
   }
 
   /**
@@ -141,7 +132,7 @@ public class MasterHostResolver {
               return filterHosts(hostsType, serviceName, componentName);
             }
 
-            Map<Status, String> pair = getNameNodePair();
+            Map<Status, String> pair = getNameNodePair(componentHosts);
             if (pair != null) {
               hostsType.master = pair.containsKey(Status.ACTIVE) ? pair.get(Status.ACTIVE) :  null;
               hostsType.secondary = pair.containsKey(Status.STANDBY) ? pair.get(Status.STANDBY) :  null;
@@ -203,8 +194,8 @@ public class MasterHostResolver {
       ServiceComponent sc = svc.getServiceComponent(component);
 
       // !!! not really a fan of passing these around
-      List<ServiceComponentHost> unhealthyHosts = new ArrayList<ServiceComponentHost>();
-      LinkedHashSet<String> upgradeHosts = new LinkedHashSet<String>();
+      List<ServiceComponentHost> unhealthyHosts = new ArrayList<>();
+      LinkedHashSet<String> upgradeHosts = new LinkedHashSet<>();
 
       for (String hostName : hostsType.hosts) {
         ServiceComponentHost sch = sc.getServiceComponentHost(hostName);
@@ -216,10 +207,29 @@ public class MasterHostResolver {
         // possible
         if (maintenanceState != MaintenanceState.OFF) {
           unhealthyHosts.add(sch);
-        } else if (null == m_version || null == sch.getVersion() ||
-            !sch.getVersion().equals(m_version) ||
-            sch.getUpgradeState() == UpgradeState.FAILED) {
+          continue;
+        }
+
+        if (sch.getUpgradeState() == UpgradeState.FAILED) {
           upgradeHosts.add(hostName);
+          continue;
+        }
+
+        if(m_upgradeContext.getDirection() == Direction.UPGRADE){
+          RepositoryVersionEntity targetRepositoryVersion = m_upgradeContext.getRepositoryVersion();
+          if (!StringUtils.equals(targetRepositoryVersion.getVersion(), sch.getVersion())) {
+            upgradeHosts.add(hostName);
+          }
+
+          continue;
+        }
+
+        // it's a downgrade ...
+        RepositoryVersionEntity downgradeToRepositoryVersion = m_upgradeContext.getTargetRepositoryVersion(service);
+        String downgradeToVersion = downgradeToRepositoryVersion.getVersion();
+        if (!StringUtils.equals(downgradeToVersion, sch.getVersion())) {
+          upgradeHosts.add(hostName);
+          continue;
         }
       }
 
@@ -263,8 +273,8 @@ public class MasterHostResolver {
    * one active and one standby host were found, otherwise, return null.
    * The hostnames are returned in lowercase.
    */
-  private Map<Status, String> getNameNodePair() {
-    Map<Status, String> stateToHost = new HashMap<Status, String>();
+  private Map<Status, String> getNameNodePair(Set<String> componentHosts) throws AmbariException {
+    Map<Status, String> stateToHost = new HashMap<>();
     Cluster cluster = getCluster();
 
     String nameService = m_configHelper.getValueFromDesiredConfigurations(cluster, ConfigHelper.HDFS_SITE, "dfs.internal.nameservices");
@@ -297,6 +307,13 @@ public class MasterHostResolver {
           throw new MalformedURLException("Could not parse host and port from " + value);
         }
 
+        if (!componentHosts.contains(hp.host)){
+          //This may happen when NN HA is configured on dual network card machines with public/private FQDNs.
+          LOG.error(
+              String.format(
+                  "Hadoop NameNode HA configuration {0} contains host {1} that does not exist in the NameNode hosts list {3}",
+                  key, hp.host, componentHosts.toString()));
+        }
         String state = queryJmxBeanValue(hp.host, hp.port, "Hadoop:service=NameNode,name=NameNodeStatus", "State", true, encrypted);
 
         if (null != state && (state.equalsIgnoreCase(Status.ACTIVE.toString()) || state.equalsIgnoreCase(Status.STANDBY.toString()))) {
@@ -323,7 +340,7 @@ public class MasterHostResolver {
    * @throws MalformedURLException
    */
   private void resolveResourceManagers(Cluster cluster, HostsType hostType) throws MalformedURLException {
-    LinkedHashSet<String> orderedHosts = new LinkedHashSet<String>(hostType.hosts);
+    LinkedHashSet<String> orderedHosts = new LinkedHashSet<>(hostType.hosts);
 
     // IMPORTANT, for RM, only the master returns jmx
     String rmWebAppAddress = m_configHelper.getValueFromDesiredConfigurations(cluster, ConfigHelper.YARN_SITE, "yarn.resourcemanager.webapp.address");

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
index 3f17fd2..d43bdfa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
@@ -1,4 +1,4 @@
-  /**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -32,6 +32,7 @@ import org.apache.ambari.server.orm.entities.OperatingSystemEntity;
 import org.apache.ambari.server.orm.entities.RepositoryEntity;
 import org.apache.ambari.server.state.RepositoryInfo;
 import org.apache.ambari.server.state.stack.RepositoryXml;
+import org.apache.commons.collections.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -114,21 +115,23 @@ public class RepoUtil {
    *    service repository and will be added.
    * @param operatingSystems - A list of OperatingSystemEntity objects extracted from a RepositoryVersionEntity
    * @param stackReposByOs - Stack repositories loaded from the disk (including service repositories), grouped by os.
+   * @return {@code true} if there were added repositories
    */
-  public static void addServiceReposToOperatingSystemEntities(List<OperatingSystemEntity> operatingSystems,
+  public static boolean addServiceReposToOperatingSystemEntities(List<OperatingSystemEntity> operatingSystems,
       ListMultimap<String, RepositoryInfo> stackReposByOs) {
     Set<String> addedRepos = new HashSet<>();
     for (OperatingSystemEntity os : operatingSystems) {
       List<RepositoryInfo> serviceReposForOs = stackReposByOs.get(os.getOsType());
       ImmutableSet<String> repoNames = ImmutableSet.copyOf(Lists.transform(os.getRepositories(), REPO_ENTITY_TO_NAME));
-      for (RepositoryInfo repoInfo : serviceReposForOs) {
+      for (RepositoryInfo repoInfo : serviceReposForOs)
         if (!repoNames.contains(repoInfo.getRepoName())) {
           os.getRepositories().add(toRepositoryEntity(repoInfo));
           addedRepos.add(String.format("%s (%s)", repoInfo.getRepoId(), os.getOsType()));
         }
-      }
     }
     LOG.info("Added {} service repos: {}", addedRepos.size(),Iterables.toString(addedRepos));
+
+    return CollectionUtils.isNotEmpty(addedRepos);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/stack/UpdateActiveRepoVersionOnStartup.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/UpdateActiveRepoVersionOnStartup.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/UpdateActiveRepoVersionOnStartup.java
index 8a32a42..b7064df 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/UpdateActiveRepoVersionOnStartup.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/UpdateActiveRepoVersionOnStartup.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -19,25 +19,22 @@
 package org.apache.ambari.server.stack;
 
 import java.util.List;
-import javax.annotation.Nullable;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
-import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
-import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
+import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.OperatingSystemEntity;
-import org.apache.ambari.server.orm.entities.RepositoryEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.RepositoryInfo;
+import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Function;
 import com.google.common.collect.ListMultimap;
 import com.google.inject.Inject;
 import com.google.inject.persist.Transactional;
@@ -54,26 +51,17 @@ public class UpdateActiveRepoVersionOnStartup {
 
   private static final Logger LOG = LoggerFactory.getLogger(UpdateActiveRepoVersionOnStartup.class);
 
-
   ClusterDAO clusterDao;
-  ClusterVersionDAO clusterVersionDao;
   RepositoryVersionDAO repositoryVersionDao;
   RepositoryVersionHelper repositoryVersionHelper;
   StackManager stackManager;
 
-
-  private static final Function<RepositoryEntity, String> REPO_TO_ID = new Function<RepositoryEntity, String>() {
-    @Override  public String apply(@Nullable RepositoryEntity input) { return input.getRepositoryId(); }
-  };
-
   @Inject
   public UpdateActiveRepoVersionOnStartup(ClusterDAO clusterDao,
-      ClusterVersionDAO clusterVersionDao,
       RepositoryVersionDAO repositoryVersionDao,
       RepositoryVersionHelper repositoryVersionHelper,
       AmbariMetaInfo metaInfo) {
     this.clusterDao = clusterDao;
-    this.clusterVersionDao = clusterVersionDao;
     this.repositoryVersionDao = repositoryVersionDao;
     this.repositoryVersionHelper = repositoryVersionHelper;
     this.stackManager = metaInfo.getStackManager();
@@ -86,21 +74,20 @@ public class UpdateActiveRepoVersionOnStartup {
   @Transactional
   public void process() throws AmbariException {
     LOG.info("Updating existing repo versions with service repos.");
+
     try {
+
       List<ClusterEntity> clusters = clusterDao.findAll();
       for (ClusterEntity cluster: clusters) {
-        StackInfo stack =
-            stackManager.getStack(cluster.getDesiredStack().getStackName(), cluster.getDesiredStack().getStackVersion());
-        LOG.info("Updating existing repo versions for cluster {} on stack {}-{}",
-            cluster.getClusterName(), stack.getName(), stack.getVersion());
-        ClusterVersionEntity clusterVersion = clusterVersionDao.findByClusterAndStateCurrent(cluster.getClusterName());
-        if (null != clusterVersion) {
-          RepositoryVersionEntity repoVersion = clusterVersion.getRepositoryVersion();
-          updateRepoVersion(stack, repoVersion);
-          repositoryVersionDao.merge(repoVersion);
-        }
-        else {
-          LOG.warn("Missing cluster version for cluster {}", cluster.getClusterName());
+        for (ClusterServiceEntity service : cluster.getClusterServiceEntities()) {
+          RepositoryVersionEntity repositoryVersion = service.getServiceDesiredStateEntity().getDesiredRepositoryVersion();
+
+          StackId stackId = repositoryVersion.getStackId();
+          StackInfo stack = stackManager.getStack(stackId.getStackName(), stackId.getStackVersion());
+
+          if (updateRepoVersion(stack, repositoryVersion)) {
+            repositoryVersionDao.merge(repositoryVersion);
+          }
         }
       }
     }
@@ -111,13 +98,16 @@ public class UpdateActiveRepoVersionOnStartup {
     }
   }
 
-  private void updateRepoVersion(StackInfo stackInfo, RepositoryVersionEntity repoVersion) throws Exception {
+  private boolean updateRepoVersion(StackInfo stackInfo, RepositoryVersionEntity repoVersion) throws Exception {
     ListMultimap<String, RepositoryInfo> serviceReposByOs = stackInfo.getRepositoriesByOs();
 
     // Update repos in the JSON representation
     List<OperatingSystemEntity> operatingSystems = repoVersion.getOperatingSystems();
-    RepoUtil.addServiceReposToOperatingSystemEntities(operatingSystems, serviceReposByOs);
-    repoVersion.setOperatingSystems(repositoryVersionHelper.serializeOperatingSystemEntities(operatingSystems));
+    boolean changed = RepoUtil.addServiceReposToOperatingSystemEntities(operatingSystems, serviceReposByOs);
+    if (changed) {
+      repoVersion.setOperatingSystems(repositoryVersionHelper.serializeOperatingSystemEntities(operatingSystems));
+    }
+    return changed;
   }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/48f7fb22/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 9dc58fe..b4f7120 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -28,14 +28,12 @@ import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
-import org.apache.ambari.server.orm.entities.ClusterVersionEntity;
-import org.apache.ambari.server.orm.entities.HostEntity;
-import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
+import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.scheduler.RequestExecution;
 
 import com.google.common.collect.ListMultimap;
@@ -76,6 +74,14 @@ public interface Cluster {
   Service getService(String serviceName) throws AmbariException;
 
   /**
+   * Gets a service from the given component name.
+   * @param componentName
+   * @return
+   * @throws AmbariException
+   */
+  Service getServiceByComponentName(String componentName) throws AmbariException;
+
+  /**
    * Get all services
    * @return
    */
@@ -151,49 +157,6 @@ public interface Cluster {
   void removeServiceComponentHost(ServiceComponentHost svcCompHost)
       throws AmbariException;
 
-
-  /**
-   * Get the ClusterVersionEntity object whose state is CURRENT.
-   * @return Cluster Version entity to whose state is CURRENT.
-   */
-  ClusterVersionEntity getCurrentClusterVersion();
-
-  /**
-   * Gets the current stack version associated with the cluster.
-   * <ul>
-   * <li>if there is no upgrade in progress then get the
-   * {@link ClusterVersionEntity} object whose state is
-   * {@link RepositoryVersionState#CURRENT}.
-   * <li>If an upgrade is in progress then based on the direction and the
-   * desired stack determine which version to use. Assuming upgrading from HDP
-   * 2.2.0.0-1 to 2.3.0.0-2:
-   * <ul>
-   * <li>RU Upgrade: 2.3.0.0-2 (desired stack id)
-   * <li>RU Downgrade: 2.2.0.0-1 (desired stack id)
-   * <li>EU Upgrade: while stopping services and before changing desired stack,
-   * use 2.2.0.0-1, after, use 2.3.0.0-2
-   * <li>EU Downgrade: while stopping services and before changing desired
-   * stack, use 2.3.0.0-2, after, use 2.2.0.0-1
-   * </ul>
-   * </ul>
-   *
-   * This method must take into account both a running and a suspended upgrade.
-   *
-   * @return the effective cluster stack version given the current upgrading
-   *         conditions of the cluster.
-   * 
-   * @deprecated to be removed once the cluster tracks the desired repo instead
-   *             of desired stack id
-   */
-  @Deprecated
-  ClusterVersionEntity getEffectiveClusterVersion() throws AmbariException;
-
-  /**
-   * Get all of the ClusterVersionEntity objects for the cluster.
-   * @return
-   */
-  Collection<ClusterVersionEntity> getAllClusterVersions();
-
   /**
    * Get desired stack version
    * @return
@@ -207,15 +170,6 @@ public interface Cluster {
   void setDesiredStackVersion(StackId stackVersion) throws AmbariException;
 
   /**
-   * Sets the desired stack version, optionally setting all owned services,
-   * components, and host components
-   * @param stackId the stack id
-   * @param cascade {@code true} to cascade the desired version
-   */
-  public void setDesiredStackVersion(StackId stackId, boolean cascade) throws AmbariException;
-
-
-  /**
    * Get current stack version
    * @return
    */
@@ -228,99 +182,33 @@ public interface Cluster {
   void setCurrentStackVersion(StackId stackVersion) throws AmbariException;
 
   /**
-   * Create host versions for all of the hosts that don't already have the stack version.
-   * @param hostNames Collection of host names
-   * @param currentClusterVersion Entity that contains the cluster's current stack (with its name and version)
-   * @param desiredState Desired state must be {@link RepositoryVersionState#CURRENT} or {@link RepositoryVersionState#UPGRADING}
-   * @throws AmbariException
-   */
-  void mapHostVersions(Set<String> hostNames,
-      ClusterVersionEntity currentClusterVersion,
-      RepositoryVersionState desiredState) throws AmbariException;
-
-  /**
    * Creates or updates host versions for all of the hosts within a cluster
    * based on state of cluster stack version. This is used to transition all
-   * hosts into the specified state.
+   * hosts into the correct state (which may not be
+   * {@link RepositoryVersionState#INSTALLING}).
    * <p/>
-   * The difference between this method compared to
-   * {@link Cluster#mapHostVersions} is that it affects all hosts (not only
-   * missing hosts).
-   * <p/>
-   * Hosts that are in maintenance mode will be transititioned directly into
-   * {@link RepositoryVersionState#OUT_OF_SYNC} instead.
+   * Hosts that are in maintenance mode will be transitioned directly into
+   * {@link RepositoryVersionState#OUT_OF_SYNC} instead. Hosts which do not need
+   * the version distributed to them will move into the
+   * {@link RepositoryVersionState#NOT_REQUIRED} state.
    *
-   * @param sourceClusterVersion
-   *          cluster version to be queried for a stack name/version info and
-   *          desired RepositoryVersionState. The only valid state of a cluster
-   *          version is {@link RepositoryVersionState#INSTALLING}
-   * @param state
-   *          the state to transition the cluster's hosts to.
-   * @throws AmbariException
-   */
-  void transitionHosts(ClusterVersionEntity sourceClusterVersion, RepositoryVersionState state)
-      throws AmbariException;
-
-  /**
-   * For a given host, will either either update an existing Host Version Entity for the given version, or create
-   * one if it doesn't exist
-   *
-   * @param host Host Entity object
-   * @param repositoryVersion Repository Version that the host is transitioning to
-   * @param stack Stack information with the version
-   * @return Returns either the newly created or the updated Host Version Entity.
-   * @throws AmbariException
-   */
-  HostVersionEntity transitionHostVersionState(HostEntity host,
-      final RepositoryVersionEntity repositoryVersion, final StackId stack)
-      throws AmbariException;
-
-  /**
-   * Update state of a cluster stack version for cluster based on states of host versions and stackids.
-   * @param repositoryVersion the repository version entity whose version is a value like 2.2.1.0-100)
-   * @throws AmbariException
-   */
-  void recalculateClusterVersionState(RepositoryVersionEntity repositoryVersion) throws AmbariException;
-
-  /**
-   * Update state of all cluster stack versions for cluster based on states of host versions.
-   * @throws AmbariException
-   */
-  void recalculateAllClusterVersionStates() throws AmbariException;
-
-  /**
-   * Create a cluster version for the given stack and version, whose initial
-   * state must either be either {@link RepositoryVersionState#UPGRADING} (if no
-   * other cluster version exists) or {@link RepositoryVersionState#INSTALLING}
-   * (if at exactly one CURRENT cluster version already exists) or {@link RepositoryVersionState#INIT}
-   * (if the cluster is being created using a specific repository version).
-   *
-   * @param stackId
-   *          Stack ID
-   * @param version
-   *          Stack version
-   * @param userName
-   *          User performing the operation
-   * @param state
-   *          Initial state
-   * @throws AmbariException
-   */
-  void createClusterVersion(StackId stackId, String version,
-      String userName, RepositoryVersionState state) throws AmbariException;
-
-  /**
-   * Transition an existing cluster version from one state to another.
-   *
-   * @param stackId
-   *          Stack ID
-   * @param version
-   *          Stack version
-   * @param state
-   *          Desired state
+   * @param repoVersionEntity
+   *          the repository that the hosts are being transitioned for (not
+   *          {@code null}).
+   * @param versionDefinitionXml
+   *          the VDF, or {@code null} if none.
+   * @param forceInstalled
+   *          if {@code true}, then this will transition everything directly to
+   *          {@link RepositoryVersionState#INSTALLED} instead of
+   *          {@link RepositoryVersionState#INSTALLING}. Hosts which should
+   *          received other states (like
+   *          {@link RepositoryVersionState#NOT_REQUIRED} will continue to
+   *          receive those states.
+   * @return a list of hosts which need the repository installed.
    * @throws AmbariException
    */
-  void transitionClusterVersion(StackId stackId, String version,
-      RepositoryVersionState state) throws AmbariException;
+  List<Host> transitionHostsToInstalling(RepositoryVersionEntity repoVersionEntity,
+      VersionDefinitionXml versionDefinitionXml, boolean forceInstalled) throws AmbariException;
 
   /**
    * Gets whether the cluster is still initializing or has finished with its
@@ -449,7 +337,7 @@ public interface Cluster {
    * @param serviceName service name
    * @return
    */
-  public List<ServiceConfigVersionResponse> getActiveServiceConfigVersionResponse(String serviceName);
+  List<ServiceConfigVersionResponse> getActiveServiceConfigVersionResponse(String serviceName);
 
   /**
    * Get service config version history
@@ -529,11 +417,17 @@ public interface Cluster {
 
   /**
    * Add service to the cluster
+   *
    * @param serviceName
+   *          the name of the service to add (not {@code null}).
+   * @param repositoryVersion
+   *          the repository from which the service should be installed (not
+   *          {@code null}).
    * @return
    * @throws AmbariException
    */
-  Service addService(String serviceName) throws AmbariException;
+  Service addService(String serviceName, RepositoryVersionEntity repositoryVersion)
+      throws AmbariException;
 
   /**
    * Fetch desired configs for list of hosts in cluster
@@ -654,9 +548,8 @@ public interface Cluster {
   Map<String, Object> getSessionAttributes();
 
   /**
-   * Makes the most recent configurations in the specified stack the current set
-   * of configurations. This method will first ensure that the cluster's current
-   * stack matches that of the configuration stack specified.
+   * Makes the most recent configurations for the specified stack current. This
+   * will only modify configurations for the given service.
    * <p/>
    * When completed, all other configurations for any other stack will remain,
    * but will not be marked as selected.
@@ -664,18 +557,21 @@ public interface Cluster {
    * @param stackId
    *          the stack to use when finding the latest configurations (not
    *          {@code null}).
+   * @param serviceName
+   *          the service to modify configurations for (not {@code null}).
    */
-  void applyLatestConfigurations(StackId stackId);
+  void applyLatestConfigurations(StackId stackId, String serviceName);
 
   /**
-   * Removes all cluster configurations and service configurations that belong
-   * to the specified stack.
+   * Removes all configurations for the specified service and stack.
    *
    * @param stackId
    *          the stack to use when finding the configurations to remove (not
    *          {@code null}).
+   * @param serviceName
+   *          the service to rmeove configurations for (not {@code null}).
    */
-  void removeConfigurations(StackId stackId);
+  void removeConfigurations(StackId stackId, String serviceName);
 
   /**
    * Returns whether this cluster was provisioned by a Blueprint or not.
@@ -707,8 +603,7 @@ public interface Cluster {
    * Gets whether there is an upgrade which has been suspended and not yet
    * finalized.
    *
-   * @return {@code true} if the last upgrade is in the
-   *         {@link UpgradeState#SUSPENDED}.
+   * @return {@code true} if the last upgrade is suspended
    */
   boolean isUpgradeSuspended();
 
@@ -766,11 +661,4 @@ public interface Cluster {
    */
   void addSuspendedUpgradeParameters(Map<String, String> commandParams,
       Map<String, String> roleParams);
-
-  /**
-   * Invalidates any cached effective cluster versions for upgrades.
-   *
-   * @see #getEffectiveClusterVersion()
-   */
-  void invalidateUpgradeEffectiveVersion();
 }