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 2015/01/15 14:45:12 UTC

ambari git commit: AMBARI-9135. Upgrades: Downgrade for Failure Scenarios during upgrade process (ncole)

Repository: ambari
Updated Branches:
  refs/heads/trunk fe3b93c87 -> 0613eeb01


AMBARI-9135. Upgrades: Downgrade for Failure Scenarios during upgrade process (ncole)


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

Branch: refs/heads/trunk
Commit: 0613eeb010d947a11796203b999e4d2bbacb7389
Parents: fe3b93c
Author: Nate Cole <nc...@hortonworks.com>
Authored: Wed Jan 14 16:19:31 2015 -0500
Committer: Nate Cole <nc...@hortonworks.com>
Committed: Thu Jan 15 08:02:48 2015 -0500

----------------------------------------------------------------------
 .../internal/UpgradeResourceProvider.java       | 12 ++-
 .../server/orm/entities/UpgradeEntity.java      | 61 +++++++------
 .../ambari/server/stack/MasterHostResolver.java | 92 ++++++++++++++++++--
 .../ambari/server/state/UpgradeCheckHelper.java |  6 +-
 .../ambari/server/state/UpgradeHelper.java      | 17 ++--
 .../state/stack/upgrade/ClusterGrouping.java    | 10 +--
 .../server/upgrade/UpgradeCatalog200.java       | 20 +++--
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |  3 +-
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |  3 +-
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |  3 +-
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     |  3 +-
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |  3 +-
 .../internal/UpgradeResourceProviderTest.java   | 73 ++++++++++++++--
 .../ambari/server/orm/dao/UpgradeDAOTest.java   |  2 +
 .../server/upgrade/UpgradeCatalog200Test.java   | 60 +++++++------
 15 files changed, 270 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/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 0a11aca..9b17b33 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
@@ -97,6 +97,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
   protected static final String UPGRADE_VERSION = "Upgrade/repository_version";
   protected static final String UPGRADE_REQUEST_ID = "Upgrade/request_id";
   protected static final String UPGRADE_FORCE_DOWNGRADE = "Upgrade/force_downgrade";
+  protected static final String UPGRADE_FROM_VERSION = "Upgrade/from_version";
+  protected static final String UPGRADE_TO_VERSION = "Upgrade/to_version";
 
   private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
       Arrays.asList(UPGRADE_REQUEST_ID, UPGRADE_CLUSTER_NAME));
@@ -140,6 +142,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     PROPERTY_IDS.add(UPGRADE_VERSION);
     PROPERTY_IDS.add(UPGRADE_REQUEST_ID);
     PROPERTY_IDS.add(UPGRADE_FORCE_DOWNGRADE);
+    PROPERTY_IDS.add(UPGRADE_FROM_VERSION);
+    PROPERTY_IDS.add(UPGRADE_TO_VERSION);
 
     // !!! boo
     for (String requestPropertyId : RequestResourceProvider.PROPERTY_IDS) {
@@ -289,6 +293,8 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
 
     setResourceProperty(resource, UPGRADE_CLUSTER_NAME, clusterName, requestedIds);
     setResourceProperty(resource, UPGRADE_REQUEST_ID, entity.getRequestId(), requestedIds);
+    setResourceProperty(resource, UPGRADE_FROM_VERSION, entity.getFromVersion(), requestedIds);
+    setResourceProperty(resource, UPGRADE_TO_VERSION, entity.getToVersion(), requestedIds);
 
     return resource;
   }
@@ -379,7 +385,6 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     Cluster cluster = getManagementController().getClusters().getCluster(clusterName);
     ConfigHelper configHelper = getManagementController().getConfigHelper();
 
-    MasterHostResolver mhr = new MasterHostResolver(cluster);
     String forceDowngrade = (String) requestMap.get(UPGRADE_FORCE_DOWNGRADE);
 
     List<UpgradeGroupHolder> groups = null;
@@ -388,8 +393,10 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     final String version = (String) requestMap.get(UPGRADE_VERSION);
 
     if (null != forceDowngrade && Boolean.parseBoolean(forceDowngrade)) {
+      MasterHostResolver mhr = new MasterHostResolver(cluster, version);
       groups = s_upgradeHelper.createDowngrade(mhr, pack, version);
     } else {
+      MasterHostResolver mhr = new MasterHostResolver(cluster);
       groups = s_upgradeHelper.createUpgrade(mhr, pack, version);
     }
 
@@ -450,6 +457,9 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
     }
 
     UpgradeEntity entity = new UpgradeEntity();
+    // !!! FIXME not quite right here, upcoming patch is supposed to get this right
+    entity.setFromVersion(cluster.getCurrentClusterVersion().getRepositoryVersion().getVersion());
+    entity.setToVersion(version);
     entity.setUpgradeGroups(groupEntities);
     entity.setClusterId(Long.valueOf(cluster.getClusterId()));
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UpgradeEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UpgradeEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UpgradeEntity.java
index 2c19da3..5804a1d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UpgradeEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/UpgradeEntity.java
@@ -22,9 +22,6 @@ import java.util.List;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
@@ -33,9 +30,6 @@ import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.TableGenerator;
-import javax.persistence.TypedQuery;
-
-import org.apache.ambari.server.state.UpgradeState;
 
 /**
  * Models the data representation of an upgrade
@@ -61,13 +55,16 @@ public class UpgradeEntity {
   @Column(name = "cluster_id", nullable = false, insertable = true, updatable = false)
   private Long clusterId;
 
-  @Enumerated(value=EnumType.STRING)
-  @Column(name = "state", nullable = false)
-  private UpgradeState state = UpgradeState.NONE;
-
   @Column(name="request_id", nullable = false)
   private Long requestId;
 
+  @Column(name="from_version", nullable = false)
+  private String fromVersion = null;
+
+  @Column(name="to_version", nullable = false)
+  private String toVersion = null;
+
+
   @OneToMany(mappedBy = "upgradeEntity", cascade = { CascadeType.ALL })
   private List<UpgradeGroupEntity> upgradeGroupEntities;
 
@@ -101,21 +98,6 @@ public class UpgradeEntity {
   }
 
   /**
-   * @return the current state
-   */
-  public UpgradeState getState() {
-    return state;
-  }
-
-  /**
-   * @param state the new state
-   */
-  public void setState(UpgradeState state) {
-    this.state = state;
-  }
-
-
-  /**
    * @return the upgrade items
    */
   public List<UpgradeGroupEntity> getUpgradeGroups() {
@@ -146,4 +128,33 @@ public class UpgradeEntity {
     requestId = id;
   }
 
+  /**
+   * @return the "from" version
+   */
+  public String getFromVersion() {
+    return fromVersion;
+  }
+
+  /**
+   * @param version the "from" version
+   */
+  public void setFromVersion(String version) {
+    fromVersion = version;
+  }
+
+  /**
+   * @return the "to" version
+   */
+  public String getToVersion() {
+    return toVersion;
+  }
+
+  /**
+   * @param version the "to" version
+   */
+  public void setToVersion(String version) {
+    toVersion = version;
+  }
+
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/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 724bcdc..239c79c 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
@@ -25,7 +25,10 @@ import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.ambari.server.AmbariException;
 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.utils.HTTPUtils;
 import org.apache.ambari.server.utils.StageUtils;
 import org.slf4j.Logger;
@@ -38,12 +41,14 @@ public class MasterHostResolver {
 
   private static Logger LOG = LoggerFactory.getLogger(MasterHostResolver.class);
 
-  private Cluster cluster;
+  private Cluster m_cluster;
+  private String m_version;
 
   enum Service {
     HDFS,
     HBASE,
-    YARN
+    YARN,
+    OTHER
   }
 
   /**
@@ -54,8 +59,29 @@ public class MasterHostResolver {
     STANDBY
   }
 
+  /**
+   * 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 cluster the cluster
+   */
   public MasterHostResolver(Cluster cluster) {
-    this.cluster = cluster;
+    this(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 cluster the cluster
+   * @param version the version, or {@code null} to not compare versions
+   */
+  public MasterHostResolver(Cluster cluster, String version) {
+    m_cluster = cluster;
+    m_version = version;
   }
 
   /**
@@ -65,7 +91,7 @@ public class MasterHostResolver {
    * @return the cluster (not {@code null}).
    */
   public Cluster getCluster() {
-    return cluster;
+    return m_cluster;
   }
 
   /**
@@ -81,19 +107,18 @@ public class MasterHostResolver {
       return null;
     }
 
-    Set<String> componentHosts = cluster.getHosts(serviceName, componentName);
+    Set<String> componentHosts = m_cluster.getHosts(serviceName, componentName);
     if (0 == componentHosts.size()) {
       return null;
     }
 
     hostsType.hosts = componentHosts;
 
-    Service s = null;
+    Service s = Service.OTHER;
     try {
       s = Service.valueOf(serviceName.toUpperCase());
     } catch (Exception e) {
       // !!! nothing to do
-      return hostsType;
     }
 
     switch (s) {
@@ -106,23 +131,76 @@ public class MasterHostResolver {
           } else {
             hostsType.master = componentHosts.iterator().next();
           }
+        } else {
+          hostsType = filterSameVersion(hostsType, serviceName, componentName);
         }
         break;
       case YARN:
         if (componentName.equalsIgnoreCase("RESOURCEMANAGER")) {
           resolveResourceManagers(hostsType);
+        } else {
+          hostsType = filterSameVersion(hostsType, serviceName, componentName);
         }
         break;
       case HBASE:
         if (componentName.equalsIgnoreCase("HBASE_MASTER")) {
           resolveHBaseMasters(hostsType);
+        } else {
+          hostsType = filterSameVersion(hostsType, serviceName, componentName);
         }
         break;
+      case OTHER:
+        hostsType = filterSameVersion(hostsType, serviceName, componentName);
+        break;
     }
     return hostsType;
   }
 
   /**
+   * Compares the versions of a HostComponent to the version for the resolver.
+   * If version is unspecified for the object, the {@link HostsType} object is
+   * returned without change.
+   *
+   * @param hostsType the hosts to resolve
+   * @param service   the service name
+   * @param component the component name
+   * @return the modified hosts instance, or {@code null} if the filtering
+   *        results in no hosts to upgrade
+   */
+  private HostsType filterSameVersion(HostsType hostsType, String service, String component) {
+    if (null == m_version) {
+      return hostsType;
+    }
+
+    try {
+      org.apache.ambari.server.state.Service svc = m_cluster.getService(service);
+      ServiceComponent sc = svc.getServiceComponent(component);
+
+      Set<String> toUpgrade = new LinkedHashSet<String>();
+
+      for (String host : hostsType.hosts) {
+        ServiceComponentHost sch = sc.getServiceComponentHost(host);
+        if (null == sch.getVersion() || !sch.getVersion().equals(m_version)) {
+          toUpgrade.add(host);
+        }
+      }
+
+      if (toUpgrade.isEmpty()) {
+        return null;
+      } else {
+        hostsType.hosts = toUpgrade;
+        return hostsType;
+      }
+
+    } catch (AmbariException e) {
+      // !!! better not
+      LOG.warn("Could not determine host components to upgrade. Defaulting to saved hosts.", e);
+      return hostsType;
+    }
+  }
+
+
+  /**
    * Get mapping of the HDFS Namenodes from the state ("active" or "standby") to the hostname.
    * @param hosts Hosts to lookup.
    * @return Returns a map from the state ("active" or "standby" to the hostname with that state.

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeCheckHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeCheckHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeCheckHelper.java
index 8ea259d..d3b2df2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeCheckHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UpgradeCheckHelper.java
@@ -270,13 +270,15 @@ public class UpgradeCheckHelper {
     public void perform(UpgradeCheck upgradeCheck, PreUpgradeCheckRequest request) throws AmbariException {
       final String clusterName = request.getClusterName();
       final Cluster cluster = clustersProvider.get().getCluster(clusterName);
-      final MasterHostResolver masterHostResolver = new MasterHostResolver(cluster);
+      final MasterHostResolver masterHostResolver = new MasterHostResolver(cluster,
+          request.getRepositoryVersion());
       final Set<String> hostsWithMasterComponent = new HashSet<String>();
       for (Entry<String, Service> serviceEntry: cluster.getServices().entrySet()) {
         final Service service = serviceEntry.getValue();
         for (Entry<String, ServiceComponent> serviceComponentEntry: service.getServiceComponents().entrySet()) {
           final ServiceComponent serviceComponent = serviceComponentEntry.getValue();
-          final HostsType hostsType = masterHostResolver.getMasterAndHosts(service.getName(), serviceComponent.getName());
+          final HostsType hostsType = masterHostResolver.getMasterAndHosts(
+              service.getName(), serviceComponent.getName());
           if (hostsType != null && hostsType.master != null) {
             hostsWithMasterComponent.add(hostsType.master);
           }

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/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 04e8eb3..cdecac6 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
@@ -179,7 +179,8 @@ public class UpgradeHelper {
 
     for (Grouping group : upgradePack.getGroups()) {
       if (ClusterGrouping.class.isInstance(group)) {
-        UpgradeGroupHolder groupHolder = getClusterGroupHolder(cluster, (ClusterGrouping) group);
+        UpgradeGroupHolder groupHolder = getClusterGroupHolder(
+            cluster, (ClusterGrouping) group, forUpgrade ? null : version);
         if (null != groupHolder) {
           groups.add(groupHolder);
           idx++;
@@ -471,14 +472,18 @@ public class UpgradeHelper {
   }
 
   /**
-   * Special handling for ClusterGrouping.
-   * @param cluster the cluster
-   * @param grouping the grouping
+   * Special handling for ClusterGrouping that is used for tasks that are
+   * to run on a specific targeted HostComponent.
+   *
+   * @param cluster   the cluster
+   * @param grouping  the grouping
+   * @param version   the version used to create a {@link MasterHostResolver}
    * @return the holder, or {@code null} if there are no clustergrouping tasks.
    */
-  private UpgradeGroupHolder getClusterGroupHolder(Cluster cluster, ClusterGrouping grouping) {
+  private UpgradeGroupHolder getClusterGroupHolder(Cluster cluster,
+      ClusterGrouping grouping, String version) {
 
-    grouping.getBuilder().setHelpers(cluster);
+    grouping.getBuilder().setHelpers(new MasterHostResolver(cluster, version));
     List<StageWrapper> wrappers = grouping.getBuilder().build();
 
     if (wrappers.size() > 0) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
index ef605ec..f8af353 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
@@ -33,7 +33,6 @@ import javax.xml.bind.annotation.XmlType;
 
 import org.apache.ambari.server.stack.HostsType;
 import org.apache.ambari.server.stack.MasterHostResolver;
-import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
 
 /**
@@ -83,10 +82,10 @@ public class ClusterGrouping extends Grouping {
     private MasterHostResolver m_resolver = null;
 
     /**
-     * @param cluster the cluster to use with this builder
+     * @param resolver  the resolver to use
      */
-    public void setHelpers(Cluster cluster) {
-      m_resolver = new MasterHostResolver(cluster);
+    public void setHelpers(MasterHostResolver resolver) {
+      m_resolver = resolver;
     }
 
     @Override
@@ -109,7 +108,8 @@ public class ClusterGrouping extends Grouping {
 
           StageWrapper wrapper = null;
 
-          if (null != execution.service && !execution.service.isEmpty() && null != execution.component && !execution.component.isEmpty()) {
+          if (null != execution.service && !execution.service.isEmpty() &&
+              null != execution.component && !execution.component.isEmpty()) {
 
             HostsType hosts = m_resolver.getMasterAndHosts(execution.service, execution.component);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
index dae920e..f7bd080 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
@@ -18,8 +18,13 @@
 
 package org.apache.ambari.server.upgrade;
 
-import com.google.inject.Inject;
-import com.google.inject.Injector;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.orm.DBAccessor;
@@ -45,12 +50,8 @@ import org.apache.ambari.server.state.UpgradeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
 
 
 /**
@@ -224,7 +225,8 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
     columns.add(new DBAccessor.DBColumnInfo("upgrade_id", Long.class, null, null, false));
     columns.add(new DBAccessor.DBColumnInfo("cluster_id", Long.class, null, null, false));
     columns.add(new DBAccessor.DBColumnInfo("request_id", Long.class, null, null, false));
-    columns.add(new DBAccessor.DBColumnInfo("state", String.class, 255, UpgradeState.NONE.name(), false));
+    columns.add(new DBAccessor.DBColumnInfo("from_version", String.class, 255, "", false));
+    columns.add(new DBAccessor.DBColumnInfo("to_version", String.class, 255, "", false));
     dbAccessor.createTable("upgrade", columns, "upgrade_id");
     dbAccessor.addFKConstraint("upgrade", "fk_upgrade_cluster_id", "cluster_id", "clusters", "cluster_id", false);
     dbAccessor.addFKConstraint("upgrade", "fk_upgrade_request_id", "request_id", "request", "request_id", false);

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 4e61f36..6f3f094 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -708,7 +708,8 @@ CREATE TABLE upgrade (
   upgrade_id BIGINT NOT NULL,
   cluster_id BIGINT NOT NULL,
   request_id BIGINT NOT NULL,
-  state VARCHAR(255) DEFAULT 'NONE' NOT NULL,
+  from_version VARCHAR(255) DEFAULT '' NOT NULL,
+  to_version VARCHAR(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (upgrade_id),
   FOREIGN KEY (cluster_id) REFERENCES clusters(cluster_id),
   FOREIGN KEY (request_id) REFERENCES request(request_id)

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 59031f7..11d5de3 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -698,7 +698,8 @@ CREATE TABLE upgrade (
   upgrade_id NUMBER(19) NOT NULL,
   cluster_id NUMBER(19) NOT NULL,
   request_id NUMBER(19) NOT NULL,
-  state VARCHAR2(255) DEFAULT 'NONE' NOT NULL,
+  from_version VARCHAR2(255) DEFAULT '' NOT NULL,
+  to_version VARCHAR2(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (upgrade_id),
   FOREIGN KEY (cluster_id) REFERENCES clusters(cluster_id),
   FOREIGN KEY (request_id) REFERENCES request(request_id)

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 7b64212..7ce9fc0 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -695,7 +695,8 @@ CREATE TABLE upgrade (
   upgrade_id BIGINT NOT NULL,
   cluster_id BIGINT NOT NULL,
   request_id BIGINT NOT NULL,
-  state VARCHAR(255) DEFAULT 'NONE' NOT NULL,
+  from_version VARCHAR(255) DEFAULT '' NOT NULL,
+  to_version VARCHAR(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (upgrade_id),
   FOREIGN KEY (cluster_id) REFERENCES clusters(cluster_id),
   FOREIGN KEY (request_id) REFERENCES request(request_id)

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index bfb49e5..2ae175b 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -778,7 +778,8 @@ CREATE TABLE ambari.upgrade (
   upgrade_id BIGINT NOT NULL,
   cluster_id BIGINT NOT NULL,
   request_id BIGINT NOT NULL,
-  state VARCHAR(255) DEFAULT 'NONE' NOT NULL,
+  from_version VARCHAR(255) DEFAULT '' NOT NULL,
+  to_version VARCHAR(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (upgrade_id),
   FOREIGN KEY (cluster_id) REFERENCES ambari.clusters(cluster_id),
   FOREIGN KEY (request_id) REFERENCES ambari.request(request_id)

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index e6f2edb..1359fc1 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -279,7 +279,8 @@ CREATE TABLE upgrade (
   upgrade_id BIGINT NOT NULL,
   cluster_id BIGINT NOT NULL,
   request_id BIGINT NOT NULL,
-  state VARCHAR(255) DEFAULT 'NONE' NOT NULL,
+  from_version VARCHAR(255) DEFAULT '' NOT NULL,
+  to_version VARCHAR(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (upgrade_id),
   FOREIGN KEY (cluster_id) REFERENCES clusters(cluster_id),
   FOREIGN KEY (request_id) REFERENCES request(request_id)

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
index 2888eca..bd06c5f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
@@ -58,6 +58,7 @@ import org.apache.ambari.server.state.Host;
 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.view.ViewRegistry;
 import org.easymock.EasyMock;
@@ -116,14 +117,22 @@ public class UpgradeResourceProviderTest {
     ViewRegistry.initInstance(new ViewRegistry());
 
     RepositoryVersionEntity repoVersionEntity = new RepositoryVersionEntity();
-    repoVersionEntity.setDisplayName("My New Version");
+    repoVersionEntity.setDisplayName("My New Version 1");
     repoVersionEntity.setOperatingSystems("");
     repoVersionEntity.setStack("HDP-2.1.1");
     repoVersionEntity.setUpgradePackage("upgrade_test");
-    repoVersionEntity.setVersion("2.2.2.2");
+    repoVersionEntity.setVersion("2.2.2.1");
+    repoVersionDao.create(repoVersionEntity);
 
+    repoVersionEntity = new RepositoryVersionEntity();
+    repoVersionEntity.setDisplayName("My New Version 2");
+    repoVersionEntity.setOperatingSystems("");
+    repoVersionEntity.setStack("HDP-2.1.1");
+    repoVersionEntity.setUpgradePackage("upgrade_test");
+    repoVersionEntity.setVersion("2.2.2.2");
     repoVersionDao.create(repoVersionEntity);
 
+
     clusters = injector.getInstance(Clusters.class);
     clusters.addCluster("c1");
     Cluster cluster = clusters.getCluster("c1");
@@ -148,11 +157,12 @@ public class UpgradeResourceProviderTest {
     service.persist();
 
     ServiceComponent component = service.addServiceComponent("ZOOKEEPER_SERVER");
-    component.addServiceComponentHost("h1");
+    ServiceComponentHost sch = component.addServiceComponentHost("h1");
+    sch.setVersion("2.2.2.1");
 
     component = service.addServiceComponent("ZOOKEEPER_CLIENT");
-    component.addServiceComponentHost("h1");
-
+    sch = component.addServiceComponentHost("h1");
+    sch.setVersion("2.2.2.1");
   }
 
   @After
@@ -163,7 +173,6 @@ public class UpgradeResourceProviderTest {
 
   public org.apache.ambari.server.controller.spi.RequestStatus testCreateResources() throws Exception {
 
-
     Cluster cluster = clusters.getCluster("c1");
 
     List<UpgradeEntity> upgrades = upgradeDao.findUpgrades(cluster.getClusterId());
@@ -171,7 +180,7 @@ public class UpgradeResourceProviderTest {
 
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
-    requestProps.put(UpgradeResourceProvider.UPGRADE_VERSION, "2.2.2.2");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_VERSION, "2.2.2.1");
 
     ResourceProvider upgradeResourceProvider = createProvider(amc);
 
@@ -187,7 +196,6 @@ public class UpgradeResourceProviderTest {
     List<UpgradeGroupEntity> upgradeGroups = entity.getUpgradeGroups();
     assertEquals(4, upgradeGroups.size());
 
-
     UpgradeGroupEntity group = upgradeGroups.get(1);
     assertEquals(4, group.getItems().size());
 
@@ -320,9 +328,56 @@ public class UpgradeResourceProviderTest {
 
     assertEquals("Validate Partial Upgrade", res.getPropertyValue("UpgradeItem/context"));
     assertTrue(res.getPropertyValue("UpgradeItem/text").toString().startsWith("Please run"));
-
   }
 
+  @Test
+  public void testCreatePartialDowngrade() throws Exception {
+
+    clusters.addHost("h2");
+    Host host = clusters.getHost("h2");
+    Map<String, String> hostAttributes = new HashMap<String, String>();
+    hostAttributes.put("os_family", "redhat");
+    hostAttributes.put("os_release_version", "6.3");
+    host.setHostAttributes(hostAttributes);
+    host.persist();
+
+    clusters.mapHostToCluster("h2", "c1");
+    Cluster cluster = clusters.getCluster("c1");
+    Service service = cluster.getService("ZOOKEEPER");
+
+    // this should get skipped
+    ServiceComponent component = service.getServiceComponent("ZOOKEEPER_SERVER");
+    ServiceComponentHost sch = component.addServiceComponentHost("h2");
+    sch.setVersion("2.2.2.2");
+
+    List<UpgradeEntity> upgrades = upgradeDao.findUpgrades(cluster.getClusterId());
+    assertEquals(0, upgrades.size());
+
+    Map<String, Object> requestProps = new HashMap<String, Object>();
+    requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_VERSION, "2.2.2.1");
+    requestProps.put(UpgradeResourceProvider.UPGRADE_FORCE_DOWNGRADE, "true");
+
+    ResourceProvider upgradeResourceProvider = createProvider(amc);
+
+    Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
+    upgradeResourceProvider.createResources(request);
+
+    upgrades = upgradeDao.findUpgrades(cluster.getClusterId());
+    assertEquals(1, upgrades.size());
+
+
+    UpgradeEntity entity = upgrades.get(0);
+    assertEquals(cluster.getClusterId(), entity.getClusterId().longValue());
+
+    List<UpgradeGroupEntity> upgradeGroups = entity.getUpgradeGroups();
+    assertEquals(4, upgradeGroups.size());
+
+    UpgradeGroupEntity group = upgradeGroups.get(2);
+    assertEquals("ZOOKEEPER", group.getName());
+    assertEquals(4, group.getItems().size());
+
+  }
 
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/UpgradeDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/UpgradeDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/UpgradeDAOTest.java
index 113de2d..e5e78ef 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/UpgradeDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/UpgradeDAOTest.java
@@ -73,6 +73,8 @@ public class UpgradeDAOTest {
     UpgradeEntity entity = new UpgradeEntity();
     entity.setClusterId(Long.valueOf(1));
     entity.setRequestId(Long.valueOf(1));
+    entity.setFromVersion("");
+    entity.setToVersion("");
 
     UpgradeGroupEntity group = new UpgradeGroupEntity();
     group.setName("group_name");

http://git-wip-us.apache.org/repos/asf/ambari/blob/0613eeb0/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
index 73531ec..6bb8a95 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
@@ -18,12 +18,30 @@
 
 package org.apache.ambari.server.upgrade;
 
-import com.google.inject.Binder;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.persist.PersistService;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createMockBuilder;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.DBAccessor.DBColumnInfo;
@@ -49,28 +67,12 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import javax.persistence.EntityManager;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.easymock.EasyMock.capture;
-import static org.easymock.EasyMock.createMockBuilder;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.persist.PersistService;
 
 /**
  * {@link UpgradeCatalog200} unit tests.
@@ -269,7 +271,7 @@ public class UpgradeCatalog200Test {
     assertViewInstancePropertyColumns(valueColumnCapture);
     assertViewInstanceDataColumns(dataValueColumnCapture);
 
-    assertEquals(4, upgradeCapture.getValue().size());
+    assertEquals(5, upgradeCapture.getValue().size());
     assertEquals(4, upgradeGroupCapture.getValue().size());
     assertEquals(7, upgradeItemCapture.getValue().size());
   }