You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by dm...@apache.org on 2016/02/23 16:20:10 UTC

[3/3] ambari git commit: AMBARI-14996. Component should support a desired version (dlysnichenko)

AMBARI-14996. Component should support a desired version (dlysnichenko)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/cbef0c14
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/cbef0c14
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/cbef0c14

Branch: refs/heads/branch-dev-patch-upgrade
Commit: cbef0c14682ae24f2754063c48ce521c58cdca27
Parents: 50547c5
Author: Lisnichenko Dmitro <dl...@hortonworks.com>
Authored: Tue Feb 23 17:17:27 2016 +0200
Committer: Lisnichenko Dmitro <dl...@hortonworks.com>
Committed: Tue Feb 23 17:18:57 2016 +0200

----------------------------------------------------------------------
 .gitignore                                      |   3 +
 .../ambari/server/agent/HeartBeatHandler.java   |  47 +-----
 .../AmbariCustomCommandExecutionHelper.java     |   2 +-
 .../RepositoryVersionResourceProvider.java      |   4 +-
 .../internal/UpgradeResourceProvider.java       |  37 ++++-
 .../HostComponentVersionAdvertisedEvent.java    |  73 +++++++++
 .../events/HostComponentVersionEvent.java       |  65 --------
 .../listeners/upgrade/StackVersionListener.java |  99 +++++++++++-
 .../publishers/VersionEventPublisher.java       |   8 +-
 .../ambari/server/orm/dao/UpgradeDAO.java       |   8 +-
 .../server/orm/entities/ClusterEntity.java      |  15 ++
 .../orm/entities/HostComponentStateEntity.java  |   5 +-
 .../ServiceComponentDesiredStateEntity.java     |  15 ++
 .../upgrades/FinalizeUpgradeAction.java         |  38 ++---
 .../ambari/server/stack/MasterHostResolver.java |   3 +-
 .../org/apache/ambari/server/state/Cluster.java |  15 ++
 .../server/state/RepositoryVersionState.java    |  39 +----
 .../ambari/server/state/ServiceComponent.java   |   4 +
 .../server/state/ServiceComponentHost.java      |  83 +++++-----
 .../server/state/ServiceComponentImpl.java      |  28 ++++
 .../ambari/server/state/UpgradeHelper.java      |  22 +++
 .../ambari/server/state/UpgradeState.java       |  22 ++-
 .../server/state/cluster/ClusterImpl.java       | 135 ++++++++--------
 .../svccomphost/ServiceComponentHostImpl.java   |   7 +-
 .../ServiceComponentHostSummary.java            |  53 +++----
 .../main/resources/Ambari-DDL-Derby-CREATE.sql  | 109 ++++++-------
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  | 108 ++++++-------
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql | 109 ++++++-------
 .../resources/Ambari-DDL-Postgres-CREATE.sql    | 109 ++++++-------
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     | 117 +++++++-------
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql | 105 +++++++------
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   | 109 ++++++-------
 .../ambari/server/StateRecoveryManagerTest.java |   6 -
 .../server/agent/TestHeartbeatHandler.java      |   2 +-
 .../server/agent/TestHeartbeatMonitor.java      |  10 +-
 .../AmbariManagementControllerTest.java         |  18 +--
 .../StackDefinedPropertyProviderTest.java       |   2 +-
 .../UpgradeResourceProviderHDP22Test.java       |   2 +-
 .../internal/UpgradeResourceProviderTest.java   |   2 +-
 .../apache/ambari/server/events/EventsTest.java |   2 +-
 .../HostVersionOutOfSyncListenerTest.java       |  10 +-
 .../upgrade/StackVersionListenerTest.java       |  25 ++-
 .../publishers/VersionEventPublisherTest.java   |  10 +-
 .../apache/ambari/server/orm/OrmTestHelper.java |   2 +-
 .../server/orm/dao/ClusterVersionDAOTest.java   |  34 ++--
 .../server/orm/dao/HostVersionDAOTest.java      |  16 +-
 .../ambari/server/orm/dao/UpgradeDAOTest.java   |   2 +-
 .../ComponentVersionCheckActionTest.java        |  18 +--
 .../upgrades/ConfigureActionTest.java           |   6 +-
 .../upgrades/UpgradeActionTest.java             |  36 ++---
 .../server/state/ServiceComponentTest.java      |   2 +-
 .../ambari/server/state/UpgradeHelperTest.java  |   8 +-
 .../state/cluster/ClusterDeadlockTest.java      |   2 +-
 .../server/state/cluster/ClusterTest.java       | 155 +------------------
 .../state/cluster/ClustersDeadlockTest.java     |   2 +-
 .../server/state/cluster/ClustersTest.java      |  10 +-
 .../ConcurrentServiceConfigVersionTest.java     |   2 +-
 ...omponentHostConcurrentWriteDeadlockTest.java |   2 +-
 .../ambari/server/state/host/HostTest.java      |   4 +-
 .../svccomphost/ServiceComponentHostTest.java   |  10 +-
 60 files changed, 1039 insertions(+), 957 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index a8c4aab..d9f23de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ pass.txt
 ambari-agent/src/test/python/ambari_agent/dummy_files/current-stack
 velocity.log*
 ambari-metrics/ambari-metrics-host-monitoring/src/main/python/psutil/build/
+rebel.xml
+rebel-remote.xml
+out

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index 248ce4b..a4136ee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -47,7 +47,7 @@ import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.events.ActionFinalReportReceivedEvent;
 import org.apache.ambari.server.events.AlertEvent;
 import org.apache.ambari.server.events.AlertReceivedEvent;
-import org.apache.ambari.server.events.HostComponentVersionEvent;
+import org.apache.ambari.server.events.HostComponentVersionAdvertisedEvent;
 import org.apache.ambari.server.events.publishers.AlertEventPublisher;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
@@ -562,10 +562,8 @@ public class HeartBeatHandler {
 
               String newVersion = structuredOutput == null ? null : structuredOutput.version;
 
-              // Pass true to always publish a version event.  It is safer to recalculate the version even if we don't
-              // detect a difference in the value.  This is useful in case that a manual database edit is done while
-              // ambari-server is stopped.
-              handleComponentVersionReceived(cl, scHost, newVersion, true);
+              HostComponentVersionAdvertisedEvent event = new HostComponentVersionAdvertisedEvent(cl, scHost, newVersion);
+              versionEventPublisher.publish(event);
             }
 
             // Updating stack version, if needed (this is not actually for express/rolling upgrades!)
@@ -618,7 +616,8 @@ public class HeartBeatHandler {
               try {
                 ComponentVersionStructuredOut structuredOutput = gson.fromJson(report.getStructuredOut(), ComponentVersionStructuredOut.class);
 
-                if (null != structuredOutput.upgradeDirection && structuredOutput.upgradeDirection.isUpgrade()) {
+                if (null != structuredOutput.upgradeDirection) {
+                  // TODO: backward compatibility: now state is set to FAILED also during downgrade
                   scHost.setUpgradeState(UpgradeState.FAILED);
                 }
               } catch (JsonSyntaxException ex) {
@@ -726,7 +725,8 @@ public class HeartBeatHandler {
                   if (extra.containsKey("version")) {
                     String version = extra.get("version").toString();
 
-                    handleComponentVersionReceived(cl, scHost, version, false);
+                    HostComponentVersionAdvertisedEvent event = new HostComponentVersionAdvertisedEvent(cl, scHost, version);
+                    versionEventPublisher.publish(event);
                   }
 
                 } catch (Exception e) {
@@ -782,39 +782,6 @@ public class HeartBeatHandler {
   }
 
   /**
-   * Updates the version of the given service component, sets the upgrade state (if needed)
-   * and publishes a version event through the version event publisher.
-   *
-   * @param cluster        the cluster
-   * @param scHost         service component host
-   * @param newVersion     new version of service component
-   * @param alwaysPublish  if true, always publish a version event; if false,
-   *                       only publish if the component version was updated
-   */
-  private void handleComponentVersionReceived(Cluster cluster, ServiceComponentHost scHost,
-                                              String newVersion, boolean alwaysPublish) {
-
-    boolean updated = false;
-
-    if (StringUtils.isNotBlank(newVersion)) {
-      final String previousVersion = scHost.getVersion();
-      if (!StringUtils.equals(previousVersion, newVersion)) {
-        scHost.setVersion(newVersion);
-        scHost.setStackVersion(cluster.getDesiredStackVersion());
-        if (previousVersion != null && !previousVersion.equalsIgnoreCase(State.UNKNOWN.toString())) {
-          scHost.setUpgradeState(UpgradeState.COMPLETE);
-        }
-        updated = true;
-      }
-    }
-
-    if (updated || alwaysPublish) {
-      HostComponentVersionEvent event = new HostComponentVersionEvent(cluster, scHost);
-      versionEventPublisher.publish(event);
-    }
-  }
-
-  /**
    * Adds commands from action queue to a heartbeat response.
    */
   protected void sendCommands(String hostname, HeartBeatResponse response)

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
index b62f4d1..24728bf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
@@ -1152,7 +1152,7 @@ public class AmbariCustomCommandExecutionHelper {
     ClusterVersionEntity clusterVersionEntity = clusterVersionDAO.findByClusterAndStateCurrent(cluster.getClusterName());
     if (clusterVersionEntity == null) {
       List<ClusterVersionEntity> clusterVersionEntityList = clusterVersionDAO
-              .findByClusterAndState(cluster.getClusterName(), RepositoryVersionState.UPGRADING);
+              .findByClusterAndState(cluster.getClusterName(), RepositoryVersionState.INSTALLING);
       if (!clusterVersionEntityList.isEmpty()) {
         clusterVersionEntity = clusterVersionEntityList.iterator().next();
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
index 87731c4..c298e0a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RepositoryVersionResourceProvider.java
@@ -403,9 +403,7 @@ public class RepositoryVersionResourceProvider extends AbstractAuthorizedResourc
       final List<RepositoryVersionState> forbiddenToDeleteStates = Lists.newArrayList(
           RepositoryVersionState.CURRENT,
           RepositoryVersionState.INSTALLED,
-          RepositoryVersionState.INSTALLING,
-          RepositoryVersionState.UPGRADED,
-          RepositoryVersionState.UPGRADING);
+          RepositoryVersionState.INSTALLING);
       for (ClusterVersionEntity clusterVersionEntity : clusterVersionEntities) {
         if (clusterVersionEntity.getRepositoryVersion().getId().equals(id) && forbiddenToDeleteStates.contains(clusterVersionEntity.getState())) {
           throw new SystemException("Repository version can't be deleted as it is " +

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 0190014..08665dd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -84,6 +84,9 @@ import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.DesiredConfig;
+import org.apache.ambari.server.state.RepositoryType;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
@@ -813,6 +816,20 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
       configUpgradePack = ConfigUpgradePack.merge(intermediateConfigUpgradePacks);
     }
 
+    // TODO: for now, all service components are transitioned to upgrading state
+    // TODO: When performing patch upgrade, we should only target supported services/components
+    // from upgrade pack
+    Set<Service> services = new HashSet<>(cluster.getServices().values());
+    Map<Service, Set<ServiceComponent>> targetComponents = new HashMap<>();
+    for (Service service: services) {
+      Set<ServiceComponent> serviceComponents =
+        new HashSet<>(service.getServiceComponents().values());
+      targetComponents.put(service, serviceComponents);
+    }
+    // TODO: is there any extreme case when we need to set component upgrade state back to NONE
+    // from IN_PROGRESS (e.g. canceled downgrade)
+    s_upgradeHelper.putComponentsToUpgradingState(version, targetComponents);
+
     for (UpgradeGroupHolder group : groups) {
       boolean skippable = group.skippable;
       boolean supportsAutoSkipOnFailure = group.supportsAutoSkipOnFailure;
@@ -894,6 +911,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     req.persist();
 
     s_upgradeDAO.create(entity);
+    cluster.setUpgradeEntity(entity);
 
     return entity;
   }
@@ -1600,11 +1618,19 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
               HostRoleStatus.ABORTED, internalStatus));
     }
 
+    Long clusterId = internalRequest.getClusterId();
     if (HostRoleStatus.ABORTED == status) {
       if (!internalStatus.isCompletedState()) {
         actionManager.cancelRequest(internalRequest.getRequestId(), reason);
+        // Remove relevant upgrade entity
+        try {
+          Cluster cluster = clusters.get().getClusterById(clusterId);
+          cluster.setUpgradeEntity(null);
+        } catch (AmbariException e) {
+          LOG.warn("Could not clear upgrade entity for cluster with id {}", clusterId, e);
+        }
       }
-    } else {
+    } else { // Processing PENDING
       List<Long> taskIds = new ArrayList<Long>();
 
       for (HostRoleCommand hrc : internalRequest.getCommands()) {
@@ -1615,6 +1641,15 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
       }
 
       actionManager.resubmitTasks(taskIds);
+
+      try {
+        Cluster cluster = clusters.get().getClusterById(clusterId);
+        UpgradeEntity lastUpgradeItemForCluster = s_upgradeDAO.findLastUpgradeForCluster(cluster.getClusterId());
+        cluster.setUpgradeEntity(lastUpgradeItemForCluster);
+      } catch (AmbariException e) {
+        LOG.warn("Could not clear upgrade entity for cluster with id {}", clusterId, e);
+      }
+
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionAdvertisedEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionAdvertisedEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionAdvertisedEvent.java
new file mode 100644
index 0000000..11ca72b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionAdvertisedEvent.java
@@ -0,0 +1,73 @@
+/**
+ * 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
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.events;
+
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.ServiceComponentHost;
+
+/**
+ * The {@link HostComponentVersionAdvertisedEvent}
+ * occurs when a Host Component advertises it's current version value.
+ */
+public class HostComponentVersionAdvertisedEvent extends ClusterEvent {
+
+  protected Cluster cluster;
+  protected ServiceComponentHost sch;
+  protected String version;
+
+  /**
+   * Constructor.
+   *
+   * @param cluster: cluster.
+   * @param sch: the service component host
+   */
+  public HostComponentVersionAdvertisedEvent(Cluster cluster, ServiceComponentHost sch,
+                                             String version) {
+    super(AmbariEventType.HOST_COMPONENT_VERSION_ADVERTISED, cluster.getClusterId());
+    this.cluster = cluster;
+    this.sch = sch;
+    this.version = version;
+  }
+
+  public ServiceComponentHost getServiceComponentHost() {
+    return sch;
+  }
+
+  public Cluster getCluster() {
+    return cluster;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder buffer = new StringBuilder("HostComponentVersionAdvertisedEvent{");
+    buffer.append("cluserId=").append(m_clusterId);
+    buffer.append(", serviceName=").append(sch.getServiceName());
+    buffer.append(", componentName=").append(sch.getServiceComponentName());
+    buffer.append(", hostName=").append(sch.getHostName());
+    buffer.append(", version=").append(version);
+    buffer.append("}");
+    return buffer.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionEvent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionEvent.java
deleted file mode 100644
index ee65d3d..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/HostComponentVersionEvent.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * 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
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.ambari.server.events;
-
-import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.ServiceComponentHost;
-
-/**
- * The {@link org.apache.ambari.server.events.HostComponentVersionEvent} represents all events in Ambari that relate
- * to a Host Component advertising a version.
- */
-public class HostComponentVersionEvent extends ClusterEvent {
-
-  protected Cluster cluster;
-  protected ServiceComponentHost sch;
-
-  /**
-   * Constructor.
-   *
-   * @param cluster: cluster.
-   * @param sch: the service component host
-   */
-  public HostComponentVersionEvent(Cluster cluster, ServiceComponentHost sch) {
-    super(AmbariEventType.HOST_COMPONENT_VERSION_ADVERTISED, cluster.getClusterId());
-    this.cluster = cluster;
-    this.sch = sch;
-  }
-
-  public ServiceComponentHost getServiceComponentHost() {
-    return sch;
-  }
-
-  public Cluster getCluster() {
-    return cluster;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public String toString() {
-    StringBuilder buffer = new StringBuilder("HostComponentVersionEvent{");
-    buffer.append("cluserId=").append(m_clusterId);
-    buffer.append(", serviceName=").append(sch.getServiceName());
-    buffer.append(", componentName=").append(sch.getServiceComponentName());
-    buffer.append(", hostName=").append(sch.getHostName());
-    buffer.append("}");
-    return buffer.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
index 74d4f4b..e2a7795 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
@@ -20,13 +20,17 @@ package org.apache.ambari.server.events.listeners.upgrade;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
-import org.apache.ambari.server.events.HostComponentVersionEvent;
+import org.apache.ambari.server.events.HostComponentVersionAdvertisedEvent;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.state.Cluster;
+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.State;
+import org.apache.ambari.server.state.UpgradeState;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,19 +73,35 @@ public class StackVersionListener {
 
   @Subscribe
   @AllowConcurrentEvents
-  public void onAmbariEvent(HostComponentVersionEvent event) {
+  public void onAmbariEvent(HostComponentVersionAdvertisedEvent event) {
     LOG.debug("Received event {}", event);
 
     Cluster cluster = event.getCluster();
 
     ServiceComponentHost sch = event.getServiceComponentHost();
+    String newVersion = event.getVersion();
 
     m_stackVersionLock.lock();
 
+    // Update host component version value if needed
     try {
-      RepositoryVersionEntity repoVersion = sch.recalculateHostVersionState();
-      if (null != repoVersion) {
-        cluster.recalculateClusterVersionState(repoVersion);
+      ServiceComponent sc = cluster.getService(sch.getServiceName()).getServiceComponent(sch.getServiceComponentName());
+      if (newVersion == null) {
+        processComponentVersionNotAdvertised(sch);
+      } else if (sc.getDesiredVersion().equals(State.UNKNOWN.toString())) {
+        processUnknownDesiredVersion(cluster, sc, sch, newVersion);
+      } else if (StringUtils.isNotBlank(newVersion)) {
+        String previousVersion = sch.getVersion();
+        String unknownVersion = State.UNKNOWN.toString();
+        if (previousVersion == null || previousVersion.equalsIgnoreCase(unknownVersion)) {
+          // value may be "UNKNOWN" when upgrading from older Ambari versions
+          // or if host component reports it's version for the first time
+          sch.setUpgradeState(UpgradeState.NONE);
+          sch.setVersion(newVersion);
+          bootstrapVersion(cluster, sch);
+        } else if (!StringUtils.equals(previousVersion, newVersion)) { //
+          processComponentVersionChange(cluster, sc, sch, newVersion);
+        }
       }
     } catch (Exception e) {
       LOG.error(
@@ -91,4 +111,71 @@ public class StackVersionListener {
       m_stackVersionLock.unlock();
     }
   }
+
+  /**
+   * Bootstrap cluster/repo version when version is reported for the first time
+   * @param cluster target cluster
+   * @param sch target host component
+   * @throws AmbariException
+   */
+  private void bootstrapVersion(Cluster cluster, ServiceComponentHost sch) throws AmbariException {
+    RepositoryVersionEntity repoVersion = sch.recalculateHostVersionState();
+    if (null != repoVersion) {
+      cluster.recalculateClusterVersionState(repoVersion);
+    }
+  }
+
+  /**
+   * Possible situation after upgrade from older Ambari version. Just use
+   * reported component version as desired version
+   * @param cluster target cluster
+   * @param sc target service component
+   * @param sch target host component
+   * @param newVersion advertised version
+   */
+  private void processUnknownDesiredVersion(Cluster cluster, ServiceComponent sc,
+                                            ServiceComponentHost sch,
+                                            String newVersion) throws AmbariException {
+    sc.setDesiredVersion(newVersion);
+    sch.setUpgradeState(UpgradeState.NONE);
+    sch.setVersion(newVersion);
+    bootstrapVersion(cluster, sch);
+  }
+
+  /**
+   * Focuses on cases when host component version really changed
+   * @param cluster target cluster
+   * @param sc target service component
+   * @param sch target host component
+   * @param newVersion advertised version
+   */
+  private void processComponentVersionChange(Cluster cluster, ServiceComponent sc,
+                                             ServiceComponentHost sch,
+                                             String newVersion) {
+    if (sch.getUpgradeState().equals(UpgradeState.IN_PROGRESS)) {
+      // Component status update is received during upgrade process
+      if (sc.getDesiredVersion().equals(newVersion)) {
+        sch.setUpgradeState(UpgradeState.COMPLETE);  // Component upgrade confirmed
+        sch.setStackVersion(cluster.getDesiredStackVersion());
+      } else { // Unexpected (wrong) version received
+        // Even during failed upgrade, we should not receive wrong version
+        // That's why mark as VERSION_MISMATCH
+        sch.setUpgradeState(UpgradeState.VERSION_MISMATCH);
+      }
+    } else { // No upgrade in progress, unexpected version change
+      sch.setUpgradeState(UpgradeState.VERSION_MISMATCH);
+    }
+    sch.setVersion(newVersion);
+  }
+
+  /**
+   * Focuses on cases when component does not advertise it's version
+   */
+  private void processComponentVersionNotAdvertised(ServiceComponentHost sch) {
+    if (UpgradeState.ONGOING_UPGRADE_STATES.contains(sch.getUpgradeState())) {
+      sch.setUpgradeState(UpgradeState.FAILED);
+    } else {
+      sch.setUpgradeState(UpgradeState.VERSION_MISMATCH);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/VersionEventPublisher.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/VersionEventPublisher.java b/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/VersionEventPublisher.java
index 3a11f38..5b32c4e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/VersionEventPublisher.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/publishers/VersionEventPublisher.java
@@ -20,11 +20,11 @@ package org.apache.ambari.server.events.publishers;
 
 import com.google.common.eventbus.EventBus;
 import com.google.inject.Singleton;
-import org.apache.ambari.server.events.HostComponentVersionEvent;
+import org.apache.ambari.server.events.HostComponentVersionAdvertisedEvent;
 
 /**
  * The {@link VersionEventPublisher} is used to publish instances of
- * {@link HostComponentVersionEvent} to any {@link com.google.common.eventbus.Subscribe} interested.
+ * {@link HostComponentVersionAdvertisedEvent} to any {@link com.google.common.eventbus.Subscribe} interested.
  * It uses a single-threaded, serial {@link EventBus}.
  */
 @Singleton
@@ -44,11 +44,11 @@ public class VersionEventPublisher {
   /**
    * Publishes the specified event to all registered listeners that
    * {@link com.google.common.eventbus.Subscribe} to any of the
-   * {@link org.apache.ambari.server.events.HostComponentVersionEvent} instances.
+   * {@link HostComponentVersionAdvertisedEvent} instances.
    *
    * @param event the event
    */
-  public void publish(HostComponentVersionEvent event) {
+  public void publish(HostComponentVersionAdvertisedEvent event) {
     m_eventBus.post(event);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UpgradeDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UpgradeDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UpgradeDAO.java
index 06f6ac1..4a923be 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UpgradeDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/UpgradeDAO.java
@@ -104,7 +104,13 @@ public class UpgradeDAO {
    */
   @Transactional
   public void create(UpgradeEntity entity) {
-    entityManagerProvider.get().persist(entity);
+    EntityManager entityManager = entityManagerProvider.get();
+    // This is required because since none of the entities
+    // for the request are actually persisted yet,
+    // JPA ordering could allow foreign key entities
+    // to be created after this statement.
+    entityManager.flush();
+    entityManager.persist(entity);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
index 2c4d695..2e0a15d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
@@ -145,6 +145,13 @@ public class ClusterEntity {
   })
   private ResourceEntity resource;
 
+  @OneToOne(cascade = CascadeType.ALL)
+  @JoinColumn(name = "upgrade_id", referencedColumnName = "upgrade_id")
+  /**
+   * {@code null} when there is no upgrade/downgrade in progress.
+   */
+  private UpgradeEntity upgradeEntity = null;
+
   public Long getClusterId() {
     return clusterId;
   }
@@ -351,4 +358,12 @@ public class ClusterEntity {
   public void setResource(ResourceEntity resource) {
     this.resource = resource;
   }
+
+  public UpgradeEntity getUpgradeEntity() {
+    return upgradeEntity;
+  }
+
+  public void setUpgradeEntity(UpgradeEntity upgradeEntity) {
+    this.upgradeEntity = upgradeEntity;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
index f92f645..1555321 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentStateEntity.java
@@ -85,8 +85,11 @@ public class HostComponentStateEntity {
   @Column(name = "component_name", nullable = false, insertable = false, updatable = false)
   private String componentName;
 
+  /**
+   * Version reported by host component during last status update.
+   */
   @Column(name = "version", nullable = false, insertable = true, updatable = true)
-  private String version = "UNKNOWN";
+  private String version = State.UNKNOWN.toString();
 
   @Enumerated(value = EnumType.STRING)
   @Column(name = "current_state", nullable = false, insertable = true, updatable = true)

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
index 519e4e6..7281c01 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceComponentDesiredStateEntity.java
@@ -91,6 +91,13 @@ public class ServiceComponentDesiredStateEntity {
   @JoinColumn(name = "desired_stack_id", unique = false, nullable = false, insertable = true, updatable = true)
   private StackEntity desiredStack;
 
+  /**
+   * Version string that should be followed by instances
+   * of component on hosts. Includes both stack version and build
+   */
+  @Column(name = "desired_version", nullable = false, insertable = true, updatable = true)
+  private String desiredVersion = State.UNKNOWN.toString();
+
   @ManyToOne
   @JoinColumns({@javax.persistence.JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false), @JoinColumn(name = "service_name", referencedColumnName = "service_name", nullable = false)})
   private ClusterServiceEntity clusterServiceEntity;
@@ -154,6 +161,14 @@ public class ServiceComponentDesiredStateEntity {
     this.desiredStack = desiredStack;
   }
 
+  public String getDesiredVersion() {
+    return desiredVersion;
+  }
+
+  public void setDesiredVersion(String desiredVersion) {
+    this.desiredVersion = desiredVersion;
+  }
+
   /**
    * Adds a historical entry for the version of this service component. New
    * entries are automatically created when this entities is merged via a

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/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 0c8df78..9088c1c 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
@@ -181,7 +181,7 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(
           clusterName, clusterDesiredStackId, version);
 
-      // Will include hosts whose state is UPGRADED, and potentially INSTALLED
+      // Will include hosts whose state is INSTALLED
       Set<HostVersionEntity> hostVersionsAllowed = new HashSet<HostVersionEntity>();
       Set<String> hostsWithoutCorrectVersionState = new HashSet<String>();
       Set<String> hostsToUpdate = new HashSet<String>();
@@ -193,12 +193,12 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
         boolean hostHasCorrectVersionState = false;
         RepositoryVersionState hostVersionState = hostVersion.getState();
         switch( hostVersionState ){
-          case UPGRADED:
           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
@@ -209,12 +209,10 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
                 host, clusterDesiredStackId);
 
             // if all components have finished advertising their version, then
-            // this host can be considered UPGRADED
+            // this host can be considered upgraded
             if (hostSummary.haveAllComponentsFinishedAdvertisingVersion()) {
-              // mark this as UPGRADED
+              // mark this as upgraded
               hostHasCorrectVersionState = true;
-              hostVersion.setState(RepositoryVersionState.UPGRADED);
-              hostVersion = hostVersionDAO.merge(hostVersion);
             } else {
               hostsWithoutCorrectVersionState.add(hostVersion.getHostName());
             }
@@ -235,7 +233,7 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
         }
       }
 
-      // throw an exception if there are hosts which are not not fully UPGRADED
+      // throw an exception if there are hosts which are not not fully upgraded
       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",
@@ -265,22 +263,21 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       }
 
 
-      // we're guaranteed to be ready transition to UPGRADED now; ensure that
-      // the transition will be allowed if the cluster state is not UPGRADED
+      // 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.UPGRADING == upgradingClusterVersion.getState()) {
-        cluster.transitionClusterVersion(clusterDesiredStackId, version,
-            RepositoryVersionState.UPGRADED);
+      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.UPGRADED != upgradingClusterVersion.getState()) {
+      // 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()));
       }
@@ -303,6 +300,9 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
           String.format("Finalizing the version for %d host(s).\n", hostVersionsAllowed.size()));
       cluster.mapHostVersions(hostsToUpdate, upgradingClusterVersion, RepositoryVersionState.CURRENT);
 
+      // Reset upgrade state
+      cluster.setUpgradeEntity(null);
+
       // transitioning the cluster into CURRENT will update the current/desired
       // stack values
       outSB.append(String.format("Finalizing the version for cluster %s.\n", clusterName));
@@ -405,9 +405,9 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       // update the cluster version
       for (ClusterVersionEntity cve : clusterVersionDAO.findByCluster(clusterName)) {
         switch (cve.getState()) {
-          case UPGRADE_FAILED:
-          case UPGRADED:
-          case UPGRADING: {
+          case INSTALL_FAILED:
+          case INSTALLED:
+          case INSTALLING: {
               badVersions.add(cve.getRepositoryVersion().getVersion());
               cve.setState(RepositoryVersionState.INSTALLED);
               clusterVersionDAO.merge(cve);
@@ -447,6 +447,8 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       // ensure that when downgrading, we set the desired back to the
       // original value
       cluster.setDesiredStackVersion(currentClusterStackId);
+      // Reset upgrade state
+      cluster.setUpgradeEntity(null);
 
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
           out.toString(), err.toString());
@@ -467,7 +469,7 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
    * have been upgraded to the target version.
    * @param cluster         the cluster the upgrade is for
    * @param desiredVersion  the target version of the upgrade
-   * @param targetStack     the target stack id for meta-info lookup
+   * @param targetStackId     the target stack id for meta-info lookup
    * @return the list of {@link InfoTuple} objects of host components in error
    */
   protected List<InfoTuple> checkHostComponentVersions(Cluster cluster, String desiredVersion, StackId targetStackId)

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/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 360f2b8..b813625 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
@@ -208,7 +208,8 @@ public class MasterHostResolver {
         if (maintenanceState != MaintenanceState.OFF) {
           unhealthyHosts.add(sch);
         } else if (null == m_version || null == sch.getVersion() ||
-            !sch.getVersion().equals(m_version) || sch.getUpgradeState() == UpgradeState.FAILED) {
+            !sch.getVersion().equals(m_version) ||
+            sch.getUpgradeState() == UpgradeState.FAILED) {
           upgradeHosts.add(hostName);
         }
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/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 b430525..f26f471 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
@@ -32,6 +32,7 @@ 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.scheduler.RequestExecution;
@@ -630,4 +631,18 @@ public interface Cluster {
    * @return true if the cluster was deployed with a Blueprint otherwise false.
    */
   boolean isBluePrintDeployed();
+
+  /**
+   * @return upgrade that is in progress for a cluster. If no upgrade is going
+   * on, a null is returned.
+   */
+  UpgradeEntity getUpgradeEntity();
+
+  /**
+   * The value is explicitly set on the ClusterEntity when Creating,
+   * Aborting (switching to downgrade), Resuming, or Finalizing an upgrade.
+   * @param upgradeEntity the upgrade entity to set for cluster
+   * @throws AmbariException
+   */
+  void setUpgradeEntity(UpgradeEntity upgradeEntity) throws AmbariException;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryVersionState.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryVersionState.java b/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryVersionState.java
index 344f358..119205a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryVersionState.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryVersionState.java
@@ -21,8 +21,7 @@ package org.apache.ambari.server.state;
 /**
  * There must be exactly one repository version that is in a CURRENT state for a particular cluster or host.
  * There may be 0 or more repository versions in an INSTALLED or INSTALLING state.
- * A repository version state transitions from UPGRADING -> UPGRADED | UPGRADE_FAILED
- * The operation to transition a repository version state from UPGRADED into CURRENT must be atomic and change the existing
+ * The operation to transition a repository version state from INSTALLED into CURRENT must be atomic and change the existing
  * relation between repository version and cluster or host from CURRENT to INSTALLED.
  *
  * <pre>
@@ -42,40 +41,25 @@ package org.apache.ambari.server.state;
  * Version 1: CURRENT
  * Version 2: INSTALL_FAILED (a retry can set this back to INSTALLING)
  *
- * Step 4: Start an upgrade from Version 1 to Version 2
- * Version 1: CURRENT
- * Version 2: UPGRADING
- *
- * Step 5: Upgrade can either complete successfully or fail
- * Version 1: CURRENT
- * Version 2: UPGRADE_FAILED (a retry can set this back to UPGRADING)
- *
- * or
- *
+ * Step 4: Perform an upgrade from Version 1 to Version 2
  * Version 1: INSTALLED
  * Version 2: CURRENT
  *
- * Step 4: May revert to the original version via a downgrade, which is technically still an upgrade to a version.
- * Version 1: UPGRADING
- * Version 2: CURRENT
- *
+ * Step 4: May revert to the original version via a downgrade, which is technically still an upgrade to a version
  * and eventually becomes
  *
  * Version 1: CURRENT
  * Version 2: INSTALLED
  *
  * *********************************************
- * Start states: CURRENT, UPGRADING, INSTALLING
+ * Start states: CURRENT, INSTALLING
  * Allowed Transitions:
- * UPGRADING -> UPGRADED | UPGRADE_FAILED
- * UPGRADE_FAILED -> UPGRADING
- * UPGRADED -> CURRENT
+ * INSTALLED -> CURRENT
  * INSTALLING -> INSTALLED | INSTALL_FAILED | OUT_OF_SYNC
  * INSTALLED -> INSTALLED | INSTALLING | OUT_OF_SYNC
  * OUT_OF_SYNC -> INSTALLING
  * INSTALL_FAILED -> INSTALLING
  * CURRENT -> INSTALLED
- * INSTALLED -> UPGRADING
  * </pre>
  */
 public enum RepositoryVersionState {
@@ -103,18 +87,5 @@ public enum RepositoryVersionState {
    * Repository version that is installed and supported and is the active version.
    */
   CURRENT,
-  /**
-   * Repository version that is in the process of upgrading to become the CURRENT active version,
-   * and the previous active version transitions to an INSTALLED state.
-   */
-  UPGRADING,
-  /**
-   * Repository version that during the upgrade process failed to become the active version and must be remedied.
-   */
-  UPGRADE_FAILED,
-  /**
-   * Repository version that finished upgrading and should be finalized to become CURRENT.
-   */
-  UPGRADED
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
index dcb7cf6..983cbdf 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponent.java
@@ -56,6 +56,10 @@ public interface ServiceComponent {
 
   void setDesiredStackVersion(StackId stackVersion);
 
+  String getDesiredVersion();
+
+  void setDesiredVersion(String version);
+
   Map<String, ServiceComponentHost> getServiceComponentHosts();
 
   ServiceComponentHost getServiceComponentHost(String hostname)

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
index f1e8d62..2a062a7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentHost.java
@@ -32,30 +32,30 @@ public interface ServiceComponentHost {
   /**
    * Get the Cluster that this object maps to
    */
-  public long getClusterId();
+  long getClusterId();
 
   /**
    * Get the Cluster that this object maps to
    */
-  public String getClusterName();
+  String getClusterName();
 
   /**
    * Get the Service this object maps to
    * @return Name of the Service
    */
-  public String getServiceName();
+  String getServiceName();
 
   /**
    * Get the ServiceComponent this object maps to
    * @return Name of the ServiceComponent
    */
-  public String getServiceComponentName();
+  String getServiceComponentName();
 
   /**
    * Get the Host this object maps to
    * @return Host's hostname
    */
-  public String getHostName();
+  String getHostName();
 
   /**
    * Get the Host this object maps to
@@ -68,20 +68,20 @@ public interface ServiceComponentHost {
    * @param event Event to handle
    * @throws InvalidStateTransitionException
    */
-  public void handleEvent(ServiceComponentHostEvent event)
+  void handleEvent(ServiceComponentHostEvent event)
       throws InvalidStateTransitionException;
 
-  public State getDesiredState();
+  State getDesiredState();
 
-  public void setDesiredState(State state);
+  void setDesiredState(State state);
 
-  public StackId getDesiredStackVersion();
+  StackId getDesiredStackVersion();
 
-  public void setDesiredStackVersion(StackId stackVersion);
+  void setDesiredStackVersion(StackId stackVersion);
 
-  public State getState();
+  State getState();
 
-  public void setState(State state);
+  void setState(State state);
 
   /**
    * Gets the current security state for this ServiceComponent
@@ -90,7 +90,7 @@ public interface ServiceComponentHost {
    *
    * @return the current SecurityState for this ServiceComponent
    */
-  public SecurityState getSecurityState();
+  SecurityState getSecurityState();
 
   /**
    * Sets the current security state for this ServiceComponent
@@ -99,21 +99,21 @@ public interface ServiceComponentHost {
    *
    * @param state the current SecurityState for this ServiceComponent
    */
-  public void setSecurityState(SecurityState state);
+  void setSecurityState(SecurityState state);
 
   /**
    * Gets the version of the component.
    *
    * @return component version
    */
-  public String getVersion();
+  String getVersion();
 
   /**
    * Sets the version of the component from the stack.
    *
    * @param version component version (e.g. 2.2.0.0-2041)
    */
-  public void setVersion(String version);
+  void setVersion(String version);
 
   /**
    * Gets the desired security state for this ServiceComponent
@@ -123,7 +123,7 @@ public interface ServiceComponentHost {
    *
    * @return the desired SecurityState for this ServiceComponent
    */
-  public SecurityState getDesiredSecurityState();
+  SecurityState getDesiredSecurityState();
 
   /**
    * Sets the desired security state for this ServiceComponent
@@ -134,27 +134,32 @@ public interface ServiceComponentHost {
    * @param securityState the desired SecurityState for this ServiceComponent
    * @throws AmbariException if the new state is not an endpoint state
    */
-  public void setDesiredSecurityState(SecurityState securityState) throws AmbariException;
+  void setDesiredSecurityState(SecurityState securityState) throws AmbariException;
 
   /**
    * @param upgradeState the upgrade state
    */
-  public void setUpgradeState(UpgradeState upgradeState);
+  void setUpgradeState(UpgradeState upgradeState);
 
   /**
-   * @return the upgrade state
+   * @return the upgrade state. Valid values:
+   * NONE  - means that component is installed and good to go, no upgrade in progress
+   * IN_PROGRESS - means that component is being upgraded
+   * COMPLETE - means that component has reported a correct new version during upgrade
+   * FAILED - means that failed and component did not get upgraded
+   * VERSION_MISMATCH - means that component reported unexpected version
    */
-  public UpgradeState getUpgradeState();
+  UpgradeState getUpgradeState();
 
-  public StackId getStackVersion();
+  StackId getStackVersion();
 
-  public void setStackVersion(StackId stackVersion);
+  void setStackVersion(StackId stackVersion);
 
-  public HostComponentAdminState getComponentAdminState();
+  HostComponentAdminState getComponentAdminState();
 
-  public void setComponentAdminState(HostComponentAdminState attribute);
+  void setComponentAdminState(HostComponentAdminState attribute);
 
-  public ServiceComponentHostResponse convertToResponse();
+  ServiceComponentHostResponse convertToResponse();
 
   boolean isPersisted();
 
@@ -162,62 +167,62 @@ public interface ServiceComponentHost {
 
   void refresh();
 
-  public void debugDump(StringBuilder sb);
+  void debugDump(StringBuilder sb);
 
-  public boolean canBeRemoved();
+  boolean canBeRemoved();
 
-  public void delete() throws AmbariException;
+  void delete() throws AmbariException;
 
   /**
    * Updates the tags that have been recognized by a START action.
    * @param configTags
    */
-  public void updateActualConfigs(Map<String, Map<String, String>> configTags);
+  void updateActualConfigs(Map<String, Map<String, String>> configTags);
 
   /**
    * Gets the actual config tags, if known.
    * @return the actual config map
    */
-  public Map<String, HostConfig> getActualConfigs();
+  Map<String, HostConfig> getActualConfigs();
 
-  public HostState getHostState();
+  HostState getHostState();
 
   /**
    * @param state the maintenance state
    */
-  public void setMaintenanceState(MaintenanceState state);
+  void setMaintenanceState(MaintenanceState state);
 
   /**
    * @return the maintenance state
    */
-  public MaintenanceState getMaintenanceState();
+  MaintenanceState getMaintenanceState();
 
   /**
    * @param procs a list containing a map describing each process
    */
-  public void setProcesses(List<Map<String, String>> procs);
+  void setProcesses(List<Map<String, String>> procs);
 
 
   /**
    * @return the list of maps describing each process
    */
-  public List<Map<String, String>> getProcesses();
+  List<Map<String, String>> getProcesses();
 
   /**
    * @return whether restart required
    */
-  public boolean isRestartRequired();
+  boolean isRestartRequired();
 
   /**
    * @param restartRequired the restartRequired flag
    */
-  public void setRestartRequired(boolean restartRequired);
+  void setRestartRequired(boolean restartRequired);
 
   /**
    * Changes host version state according to state of the components installed on the host.
    * @return The Repository Version Entity with that component in the host
    * @throws AmbariException if host is detached from the cluster
    */
-  public RepositoryVersionEntity recalculateHostVersionState() throws AmbariException;
+  RepositoryVersionEntity recalculateHostVersionState() throws AmbariException;
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
index eca911d..197f4cd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceComponentImpl.java
@@ -93,6 +93,7 @@ public class ServiceComponentImpl implements ServiceComponent {
     desiredStateEntity = new ServiceComponentDesiredStateEntity();
     desiredStateEntity.setComponentName(componentName);
     desiredStateEntity.setDesiredState(State.INIT);
+    desiredStateEntity.setDesiredVersion(State.UNKNOWN.toString());
     desiredStateEntity.setServiceName(service.getName());
     desiredStateEntity.setClusterId(service.getClusterId());
     desiredStateEntity.setRecoveryEnabled(false);
@@ -467,6 +468,33 @@ public class ServiceComponentImpl implements ServiceComponent {
         LOG.warn("Setting a member on an entity object that may have been " +
           "previously deleted, serviceName = " + (service != null ? service.getName() : ""));
       }
+    } finally {
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  @Override
+  public String getDesiredVersion() {
+    readWriteLock.readLock().lock();
+    try {
+      return getDesiredStateEntity().getDesiredVersion();
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  @Override
+  public void setDesiredVersion(String version) {
+    readWriteLock.writeLock().lock();
+    try {
+      ServiceComponentDesiredStateEntity desiredStateEntity = getDesiredStateEntity();
+      if (desiredStateEntity != null) {
+        desiredStateEntity.setDesiredVersion(version);
+        saveIfPersisted(desiredStateEntity);
+      } else {
+        LOG.warn("Setting a member on an entity object that may have been " +
+          "previously deleted, serviceName = " + (service != null ? service.getName() : ""));
+      }
 
     } finally {
       readWriteLock.writeLock().unlock();

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
index 5143bfa..05d49c4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeHelper.java
@@ -722,4 +722,26 @@ public class UpgradeHelper {
       LOG.debug("Could not get service detail", e);
     }
   }
+
+  /**
+   * Transitions all affected components to upgrading state. Transition is performed
+   * only for components that advertise their version. Service component desired
+   * version is set to one passed as an argument
+   * @param version desired version (like 2.2.1.0-1234) for upgrade
+   * @param targetServices targets for upgrade
+   */
+  public void putComponentsToUpgradingState(String version,
+                                            Map<Service, Set<ServiceComponent>> targetServices) throws AmbariException {
+    // TODO: generalize method?
+    for (Map.Entry<Service, Set<ServiceComponent>> entry: targetServices.entrySet()) {
+      for (ServiceComponent serviceComponent: entry.getValue()) {
+        if (serviceComponent.isVersionAdvertised()) {
+          for (ServiceComponentHost serviceComponentHost: serviceComponent.getServiceComponentHosts().values()) {
+            serviceComponentHost.setUpgradeState(UpgradeState.IN_PROGRESS);
+          }
+          serviceComponent.setDesiredVersion(version);
+        }
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeState.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeState.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeState.java
index ced1dd3..889e92d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeState.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeState.java
@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.state;
 
+import java.util.EnumSet;
+
 /**
  * Indicates the upgrade state
  */
@@ -30,16 +32,26 @@ public enum UpgradeState {
    */
   COMPLETE,
   /**
-   * Upgrade is pending
-   */
-  PENDING,
-  /**
    * Upgrade is in progress
    */
   IN_PROGRESS,
   /**
    * Upgrade has failed
    */
-  FAILED
+  FAILED,
+  /**
+   * Component reported unexpected/wrong version
+   */
+  VERSION_MISMATCH;
+
+  /**
+   * States when new/correct version has not been yet advertised
+   */
+  public static final EnumSet<UpgradeState> VERSION_NON_ADVERTISED_STATES = EnumSet.of(IN_PROGRESS, FAILED, VERSION_MISMATCH);
+
+  /**
+   * States when component is believed to participate in upgrade
+   */
+  public static final EnumSet<UpgradeState> ONGOING_UPGRADE_STATES = EnumSet.of(IN_PROGRESS, FAILED, COMPLETE);
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 07addfc..727eaf3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -22,6 +22,7 @@ import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -91,6 +92,7 @@ import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.orm.entities.TopologyRequestEntity;
+import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.state.Cluster;
@@ -150,6 +152,8 @@ public class ClusterImpl implements Cluster {
    * Prefix for cluster session attributes name.
    */
   private static final String CLUSTER_SESSION_ATTRIBUTES_PREFIX = "cluster_session_attributes:";
+  private static final Set<RepositoryVersionState> ALLOWED_REPOSITORY_STATES =
+      EnumSet.of(RepositoryVersionState.INSTALLING);
 
   @Inject
   private Clusters clusters;
@@ -1154,10 +1158,10 @@ public class ClusterImpl implements Cluster {
   }
 
   /**
-   * During the Finalize Action, want to transition all Host Versions from UPGRADED to CURRENT, and the last CURRENT one to INSTALLED.
+   * During the Finalize Action, want to transition all Host Versions from INSTALLED to CURRENT, and the last CURRENT one to INSTALLED.
    * @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}
+   * @param desiredState Desired state must be {@link RepositoryVersionState#CURRENT}
    * @throws AmbariException
    */
   @Override
@@ -1221,7 +1225,7 @@ public class ClusterImpl implements Cluster {
               && desiredState == RepositoryVersionState.CURRENT
               && currentHostVersionEntity.getState() == RepositoryVersionState.CURRENT) {
             currentHostVersionEntity.setState(RepositoryVersionState.INSTALLED);
-            currentHostVersionEntity = hostVersionDAO.merge(currentHostVersionEntity);
+            hostVersionDAO.merge(currentHostVersionEntity);
           }
         }
       }
@@ -1329,10 +1333,6 @@ public class ClusterImpl implements Cluster {
    * Calculate the effective Cluster Version State based on the state of its hosts.
    *
    * CURRENT: all hosts are CURRENT
-   * UPGRADE_FAILED: at least one host in UPGRADE_FAILED
-   * UPGRADED: all hosts are UPGRADED
-   * UPGRADING: at least one host is UPGRADING, and the rest in UPGRADING|INSTALLED
-   * UPGRADING: at least one host is UPGRADED, and the rest in UPGRADING|INSTALLED
    * INSTALLED: all hosts in INSTALLED
    * INSTALL_FAILED: at least one host in INSTALL_FAILED
    * INSTALLING: all hosts in INSTALLING -or- INSTALLING and NOT_REQUIRED. Notice that if one host is CURRENT and another is INSTALLING, then the
@@ -1354,22 +1354,6 @@ public class ClusterImpl implements Cluster {
     if (stateToHosts.containsKey(RepositoryVersionState.CURRENT) && stateToHosts.get(RepositoryVersionState.CURRENT).size() == totalHosts) {
       return RepositoryVersionState.CURRENT;
     }
-    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADE_FAILED) && !stateToHosts.get(RepositoryVersionState.UPGRADE_FAILED).isEmpty()) {
-      return RepositoryVersionState.UPGRADE_FAILED;
-    }
-    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADED) && stateToHosts.get(RepositoryVersionState.UPGRADED).size() == totalHosts) {
-      return RepositoryVersionState.UPGRADED;
-    }
-    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADING) && !stateToHosts.get(RepositoryVersionState.UPGRADING).isEmpty()) {
-      return RepositoryVersionState.UPGRADING;
-    }
-    if (stateToHosts.containsKey(RepositoryVersionState.UPGRADED)
-        && !stateToHosts.get(RepositoryVersionState.UPGRADED).isEmpty()
-        && stateToHosts.get(RepositoryVersionState.UPGRADED).size() != totalHosts) {
-      // It is possible that a host has transitioned to UPGRADED state even before any other host has transitioned to UPGRADING state.
-      // Example: Host with single component ZOOKEEPER Server on it which is the first component to be upgraded.
-      return RepositoryVersionState.UPGRADING;
-    }
     if (stateToHosts.containsKey(RepositoryVersionState.INSTALLED) && stateToHosts.get(RepositoryVersionState.INSTALLED).size() == totalHosts) {
       return RepositoryVersionState.INSTALLED;
     }
@@ -1404,9 +1388,9 @@ public class ClusterImpl implements Cluster {
       }
     }
 
-    // Also returns when have a mix of CURRENT and INSTALLING|INSTALLED|UPGRADING|UPGRADED
-    LOG.warn("have a mix of CURRENT and INSTALLING|INSTALLED|UPGRADING|UPGRADED host versions, " +
-      "returning OUT_OF_SYNC as cluster version. Host version states: " + stateToHosts.toString());
+    // Also returns when have a mix of CURRENT and INSTALLING|INSTALLED
+    LOG.warn("have a mix of CURRENT and INSTALLING|INSTALLED host versions, " +
+      "returning OUT_OF_SYNC as cluster version. Host version states: {}", stateToHosts);
     return RepositoryVersionState.OUT_OF_SYNC;
   }
 
@@ -1444,7 +1428,7 @@ public class ClusterImpl implements Cluster {
               stackId,
               version,
               AuthorizationHelper.getAuthenticatedName(configuration.getAnonymousAuditName()),
-              RepositoryVersionState.UPGRADING);
+              RepositoryVersionState.INSTALLING);
           clusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
               getClusterName(), stackId, version);
 
@@ -1461,14 +1445,11 @@ public class ClusterImpl implements Cluster {
           return;
         }
       }
-
       // Ignore if cluster version is CURRENT or UPGRADE_FAILED
       if (clusterVersion.getState() != RepositoryVersionState.INSTALL_FAILED &&
               clusterVersion.getState() != RepositoryVersionState.OUT_OF_SYNC &&
               clusterVersion.getState() != RepositoryVersionState.INSTALLING &&
-              clusterVersion.getState() != RepositoryVersionState.INSTALLED &&
-              clusterVersion.getState() != RepositoryVersionState.UPGRADING &&
-              clusterVersion.getState() != RepositoryVersionState.UPGRADED) {
+              clusterVersion.getState() != RepositoryVersionState.INSTALLED) {
         // anything else is not supported as of now
         return;
       }
@@ -1584,7 +1565,7 @@ public class ClusterImpl implements Cluster {
           // That is an initial bootstrap
           performingInitialBootstrap = true;
         }
-        hostVersionEntity = new HostVersionEntity(host, repositoryVersion, RepositoryVersionState.UPGRADING);
+        hostVersionEntity = new HostVersionEntity(host, repositoryVersion, RepositoryVersionState.INSTALLING);
         hostVersionDAO.create(hostVersionEntity);
       }
 
@@ -1595,22 +1576,17 @@ public class ClusterImpl implements Cluster {
       if (!isCurrentPresent) {
         // Transition from UPGRADING -> CURRENT. This is allowed because Host Version Entity is bootstrapped in an UPGRADING state.
         // Alternatively, transition to CURRENT during initial bootstrap if at least one host component advertised a version
-        if (hostSummary.isUpgradeFinished() && hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADING) || performingInitialBootstrap) {
+        if (hostSummary.isUpgradeFinished() || performingInitialBootstrap) {
           hostVersionEntity.setState(RepositoryVersionState.CURRENT);
           hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
         }
       } else {
         // Handle transitions during a Stack Upgrade
+        if (hostSummary.isUpgradeFinished() && hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED)) {
+          currentVersionEntity.setState(RepositoryVersionState.INSTALLED);
+          hostVersionEntity.setState(RepositoryVersionState.CURRENT);
 
-        // If a host only has one Component to update, that single report can still transition the host version from
-        // INSTALLED->UPGRADING->UPGRADED in one shot.
-        if (hostSummary.isUpgradeInProgress(currentVersionEntity.getRepositoryVersion().getVersion()) && hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED)) {
-          hostVersionEntity.setState(RepositoryVersionState.UPGRADING);
-          hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
-        }
-
-        if (hostSummary.isUpgradeFinished() && hostVersionEntity.getState().equals(RepositoryVersionState.UPGRADING)) {
-          hostVersionEntity.setState(RepositoryVersionState.UPGRADED);
+          hostVersionDAO.merge(currentVersionEntity);
           hostVersionEntity = hostVersionDAO.merge(hostVersionEntity);
         }
       }
@@ -1658,16 +1634,8 @@ public class ClusterImpl implements Cluster {
    */
   private void createClusterVersionInternal(StackId stackId, String version,
       String userName, RepositoryVersionState state) throws AmbariException {
-    Set<RepositoryVersionState> allowedStates = new HashSet<RepositoryVersionState>();
-    Collection<ClusterVersionEntity> allClusterVersions = getAllClusterVersions();
-    if (allClusterVersions == null || allClusterVersions.isEmpty()) {
-      allowedStates.add(RepositoryVersionState.UPGRADING);
-    } else {
-      allowedStates.add(RepositoryVersionState.INSTALLING);
-    }
-
-    if (!allowedStates.contains(state)) {
-      throw new AmbariException("The allowed state for a new cluster version must be within " + allowedStates);
+    if (!ALLOWED_REPOSITORY_STATES.contains(state)) {
+      throw new AmbariException("The allowed state for a new cluster version must be within " + ALLOWED_REPOSITORY_STATES);
     }
 
     ClusterVersionEntity existing = clusterVersionDAO.findByClusterAndStackAndVersion(
@@ -1701,10 +1669,10 @@ public class ClusterImpl implements Cluster {
    * following are some of the steps that are taken when transitioning between
    * specific states:
    * <ul>
-   * <li>UPGRADING/UPGRADED --> CURRENT</lki>: Set the current stack to the
+   * <li>INSTALLING/INSTALLED --> CURRENT</lki>: Set the current stack to the
    * desired stack, ensure all hosts with the desired stack are CURRENT as well.
    * </ul>
-   * <li>UPGRADING/UPGRADED --> CURRENT</lki>: Set the current stack to the
+   * <li>INSTALLING/INSTALLED --> CURRENT</lki>: Set the current stack to the
    * desired stack. </ul>
    *
    * @param stackId
@@ -1751,31 +1719,21 @@ public class ClusterImpl implements Cluster {
             allowedStates.add(RepositoryVersionState.INSTALLED);
             allowedStates.add(RepositoryVersionState.INSTALL_FAILED);
             allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
+            if (clusterVersionDAO.findByClusterAndStateCurrent(getClusterName()) == null) {
+              allowedStates.add(RepositoryVersionState.CURRENT);
+            }
             break;
           case INSTALL_FAILED:
             allowedStates.add(RepositoryVersionState.INSTALLING);
             break;
           case INSTALLED:
             allowedStates.add(RepositoryVersionState.INSTALLING);
-            allowedStates.add(RepositoryVersionState.UPGRADING);
             allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
+            allowedStates.add(RepositoryVersionState.CURRENT);
             break;
           case OUT_OF_SYNC:
             allowedStates.add(RepositoryVersionState.INSTALLING);
             break;
-          case UPGRADING:
-            allowedStates.add(RepositoryVersionState.UPGRADED);
-            allowedStates.add(RepositoryVersionState.UPGRADE_FAILED);
-            if (clusterVersionDAO.findByClusterAndStateCurrent(getClusterName()) == null) {
-              allowedStates.add(RepositoryVersionState.CURRENT);
-            }
-            break;
-          case UPGRADED:
-            allowedStates.add(RepositoryVersionState.CURRENT);
-            break;
-          case UPGRADE_FAILED:
-            allowedStates.add(RepositoryVersionState.UPGRADING);
-            break;
         }
 
         if (!allowedStates.contains(state)) {
@@ -3465,4 +3423,45 @@ public class ClusterImpl implements Cluster {
   private ClusterEntity getClusterEntity() {
     return clusterDAO.findById(clusterId);
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public UpgradeEntity getUpgradeEntity() {
+    clusterGlobalLock.readLock().lock();
+    try {
+      ClusterEntity clusterEntity = getClusterEntity();
+      if (clusterEntity != null) {
+        return clusterEntity.getUpgradeEntity();
+      } else {
+        return null;
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  @Transactional
+  public void setUpgradeEntity(UpgradeEntity upgradeEntity) throws AmbariException {
+    clusterGlobalLock.writeLock().lock();
+    try {
+      ClusterEntity clusterEntity = getClusterEntity();
+      if (clusterEntity != null) {
+        clusterEntity.setUpgradeEntity(upgradeEntity);
+        clusterDAO.merge(clusterEntity);
+      }
+    } catch (RollbackException e) {
+      String msg = "Unable to set upgrade entiry " + upgradeEntity + " for cluster "
+        + getClusterName();
+      LOG.warn(msg);
+      throw new AmbariException(msg, e);
+    } finally {
+      clusterGlobalLock.writeLock().unlock();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
index 92828af..2b926b4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
@@ -1790,8 +1790,11 @@ public class ServiceComponentHostImpl implements ServiceComponentHost {
   public RepositoryVersionEntity recalculateHostVersionState() throws AmbariException {
     RepositoryVersionEntity repositoryVersion = null;
     String version = getVersion();
-    if (version == null || version.isEmpty() || version.equalsIgnoreCase(State.UNKNOWN.toString())) {
-      // Recalculate only if some particular version is set
+    if (getUpgradeState().equals(UpgradeState.IN_PROGRESS) ||
+      getUpgradeState().equals(UpgradeState.VERSION_MISMATCH) ||
+        State.UNKNOWN.toString().equals(version)) {
+      // TODO: we still recalculate host version if upgrading component failed. It seems to be ok
+      // Recalculate only if no upgrade in progress/no version mismatch
       return null;
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/cbef0c14/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
index 1c36143..bccdb25 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostSummary.java
@@ -23,10 +23,11 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
-import org.apache.commons.lang.StringUtils;
+import org.apache.ambari.server.state.UpgradeState;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -36,7 +37,7 @@ import java.util.Set;
 /**
  * Represents a summary of the versions of the components installed on a host.
  */
-public class ServiceComponentHostSummary  {
+public class ServiceComponentHostSummary {
 
   private Collection<HostComponentStateEntity> allHostComponents;
   private Collection<HostComponentStateEntity> haveAdvertisedVersion;
@@ -44,15 +45,14 @@ public class ServiceComponentHostSummary  {
   private Collection<HostComponentStateEntity> noVersionToAdvertise;
   private Set<String> versions;
 
-
   public ServiceComponentHostSummary(AmbariMetaInfo ambariMetaInfo, HostEntity host, String stackName, String stackVersion) throws AmbariException {
     allHostComponents = host.getHostComponentStateEntities();
-    haveAdvertisedVersion = new HashSet<HostComponentStateEntity>();
-    waitingToAdvertiseVersion = new HashSet<HostComponentStateEntity>();
-    noVersionToAdvertise = new HashSet<HostComponentStateEntity>();
-    versions = new HashSet<String>();
+    haveAdvertisedVersion = new HashSet<>();
+    waitingToAdvertiseVersion = new HashSet<>();
+    noVersionToAdvertise = new HashSet<>();
+    versions = new HashSet<>();
 
-    for (HostComponentStateEntity hostComponentStateEntity: allHostComponents) {
+    for (HostComponentStateEntity hostComponentStateEntity : allHostComponents) {
       ComponentInfo compInfo = ambariMetaInfo.getComponent(
           stackName, stackVersion, hostComponentStateEntity.getServiceName(),
           hostComponentStateEntity.getComponentName());
@@ -61,12 +61,13 @@ public class ServiceComponentHostSummary  {
         // Some Components cannot advertise a version. E.g., ZKF, AMBARI_METRICS, Kerberos
         noVersionToAdvertise.add(hostComponentStateEntity);
       } else {
-        if (hostComponentStateEntity.getVersion() == null || hostComponentStateEntity.getVersion().isEmpty() || hostComponentStateEntity.getVersion().equalsIgnoreCase(State.UNKNOWN.toString())) {
+        if (hostComponentStateEntity.getUpgradeState().equals(UpgradeState.IN_PROGRESS) ||
+            hostComponentStateEntity.getVersion().equalsIgnoreCase(State.UNKNOWN.toString())) {
           waitingToAdvertiseVersion.add(hostComponentStateEntity);
         } else {
           haveAdvertisedVersion.add(hostComponentStateEntity);
           versions.add(hostComponentStateEntity.getVersion());
-        }
+        } // TODO: what if component reported wrong version?
       }
     }
   }
@@ -80,20 +81,20 @@ public class ServiceComponentHostSummary  {
   }
 
   public boolean isUpgradeFinished() {
-    return haveAllComponentsFinishedAdvertisingVersion() && haveSameVersion(getHaveAdvertisedVersion());
+    return haveAllComponentsFinishedAdvertisingVersion() && noComponentVersionMismatches(getHaveAdvertisedVersion());
   }
 
   /**
-   * @param currentRepoVersion Repo Version that is CURRENT for this host
+   * @param upgradeEntity Upgrade info about update on given host
    * @return Return true if multiple component versions are found for this host, or if it does not coincide with the
    * CURRENT repo version.
    */
-  public boolean isUpgradeInProgress(String currentRepoVersion) {
+  public boolean isUpgradeInProgress(UpgradeEntity upgradeEntity) {
     // Exactly one CURRENT version must exist
     // We can only detect an upgrade if the Host has at least one component that advertises a version and has done so already
     // If distinct versions have been advertises, then an upgrade is in progress.
     // If exactly one version has been advertises, but it doesn't coincide with the CURRENT HostVersion, then an upgrade is in progress.
-    return currentRepoVersion != null && (versions.size() > 1 || (versions.size() == 1 && !versions.iterator().next().equals(currentRepoVersion)));
+    return upgradeEntity != null;
   }
 
   /**
@@ -101,30 +102,20 @@ public class ServiceComponentHostSummary  {
    * @return Return a bool indicating if all components that can report a version have done so.
    */
   public boolean haveAllComponentsFinishedAdvertisingVersion() {
-    return waitingToAdvertiseVersion.size() == 0;
+    return waitingToAdvertiseVersion.isEmpty();
   }
 
   /**
-   * Checks that every component has the same version
+   * Checks that every component has really advertised version (in other words, we are not waiting
+   * for version advertising), and that no version mismatch occurred
    *
    * @param hostComponents host components
-   * @return true if components have the same version, or collection is empty, false otherwise.
+   * @return true if components have advertised the same version, or collection is empty, false otherwise.
    */
-  public static boolean haveSameVersion(Collection<HostComponentStateEntity> hostComponents) {
-    // It is important to return true even if the collection is empty because technically, there are no conflicts.
-    if (hostComponents.isEmpty()) {
-      return true;
-    }
-    String firstVersion = null;
+  public static boolean noComponentVersionMismatches(Collection<HostComponentStateEntity> hostComponents) {
     for (HostComponentStateEntity hostComponent : hostComponents) {
-      if (!hostComponent.getVersion().isEmpty()) {
-        if (firstVersion == null) {
-          firstVersion = hostComponent.getVersion();
-        } else {
-          if (!StringUtils.equals(firstVersion, hostComponent.getVersion())) {
-            return false;
-          }
-        }
+      if (UpgradeState.VERSION_NON_ADVERTISED_STATES.contains(hostComponent.getUpgradeState())) {
+        return false;
       }
     }
     return true;