ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject [07/13] ambari git commit: AMBARI-12885: Dynamic stack extensions - install and upgrade support for custom services (tthorpe via dili)
Date Mon, 11 Jan 2016 19:15:43 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterExtensionVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterExtensionVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterExtensionVersionDAO.java
new file mode 100644
index 0000000..2de810a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterExtensionVersionDAO.java
@@ -0,0 +1,192 @@
+/**
+ * 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.orm.dao;
+
+import java.util.List;
+
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ClusterExtensionVersionEntity;
+import org.apache.ambari.server.state.RepositoryVersionState;
+import org.apache.ambari.server.state.ExtensionId;
+
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+
+/**
+ * The {@link ClusterExtensionVersionDAO} class manages the {@link ClusterExtensionVersionEntity} instances associated with a cluster.
+ * Each cluster can have multiple extension versions {@link org.apache.ambari.server.state.RepositoryVersionState#INSTALLED},
+ * exactly one extension version for each extension name that is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, and at most one
+ * extension version for each extension name that is {@link org.apache.ambari.server.state.RepositoryVersionState#UPGRADING}.
+ */
+@Singleton
+public class ClusterExtensionVersionDAO extends CrudDAO<ClusterExtensionVersionEntity, Long>{
+  /**
+   * Constructor.
+   */
+  public ClusterExtensionVersionDAO() {
+    super(ClusterExtensionVersionEntity.class);
+  }
+
+  /**
+   * Retrieve all of the cluster versions for the given extension and version.
+   *
+   * @param extensionName
+   *          the extension name (e.g., EXT)
+   * @param extensionVersion
+   *          the extension version (e.g., 1.0)
+   * @param version
+   *          Repository version (e.g., 1.0.0.1-995)
+   * @return Return a list of cluster versions that match the extension and version.
+   */
+  @RequiresSession
+  public List<ClusterExtensionVersionEntity> findByExtensionAndVersion(String extensionName,
+      String extensionVersion, String version) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get().createNamedQuery("clusterExtensionVersionByExtensionVersion", ClusterExtensionVersionEntity.class);
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("extensionVersion", extensionVersion);
+    query.setParameter("version", version);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Get the cluster version for the given cluster name, extension name, and extension
+   * version.
+   *
+   * @param clusterName
+   *          Cluster name
+   * @param extensionId
+   *          Extension id (e.g., EXT-1.0)
+   * @param version
+   *          Repository version (e.g., 1.0.0.1-995)
+   * @return Return all of the cluster versions associated with the given
+   *         cluster.
+   */
+  @RequiresSession
+  public ClusterExtensionVersionEntity findByClusterAndExtensionAndVersion(
+      String clusterName, ExtensionId extensionId, String version) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterExtensionVersionByClusterAndExtensionAndVersion", ClusterExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("extensionName", extensionId.getExtensionName());
+    query.setParameter("extensionVersion", extensionId.getExtensionVersion());
+    query.setParameter("version", version);
+
+    return daoUtils.selectSingle(query);
+  }
+
+  /**
+   * Retrieve all of the cluster versions for the given cluster.
+   *
+   * @param clusterName Cluster name
+   * @return Return all of the cluster versions associated with the given cluster.
+   */
+  @RequiresSession
+  public List<ClusterExtensionVersionEntity> findByCluster(String clusterName) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterExtensionVersionByCluster", ClusterExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the cluster versions for the given cluster.
+   *
+   * @param clusterName Cluster name
+   * @return Return all of the cluster versions associated with the given cluster.
+   */
+  @RequiresSession
+  public List<ClusterExtensionVersionEntity> findByClusterAndExtensionName(String clusterName, String extensionName) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterExtensionVersionByClusterAndExtensionName", ClusterExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("extensionName", extensionName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve the single cluster extension version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
+   * for the given cluster and extension name.
+   *
+   * @param clusterName Cluster name
+   * @param extensionName Extension name
+   * @return Returns the single cluster extension version for the cluster's extension with the specified name whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, or {@code null} otherwise.
+   */
+  @RequiresSession
+  public ClusterExtensionVersionEntity findByClusterAndExtensionNameAndStateCurrent(String clusterName, String extensionName) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterExtensionVersionByClusterAndExtensionNameAndState", ClusterExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("state", RepositoryVersionState.CURRENT);
+
+    try {
+      List results = query.getResultList();
+      if (results.isEmpty()) {
+        return null;
+      } else {
+        if (results.size() == 1) {
+          return (ClusterExtensionVersionEntity) results.get(0);
+        }
+      }
+      throw new NonUniqueResultException();
+    } catch (NoResultException ignored) {
+      return null;
+    }
+  }
+
+  /**
+   * Retrieve all of the cluster versions for the cluster with the given name and a state.
+   *
+   * @param clusterName Cluster name
+   * @param state Cluster version state
+   * @return Returns a list of cluster versions for the given cluster and a state.
+   */
+  @RequiresSession
+  public List<ClusterExtensionVersionEntity> findByClusterAndState(String clusterName, RepositoryVersionState state) {
+    final TypedQuery<ClusterExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("clusterExtensionVersionByClusterAndState", ClusterExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("state", state);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Construct a Cluster Extension Version. Additionally this will update parent connection relations without
+   * forcing refresh of parent entity
+   * @param entity entity to create
+   */
+  @Override
+  @Transactional
+  public void create(ClusterExtensionVersionEntity entity) throws IllegalArgumentException {
+    // check if repository version is not missing, to avoid NPE
+    if (entity.getRepositoryVersion() == null) {
+      throw new IllegalArgumentException("RepositoryVersion argument is not set for the entity");
+    }
+
+    super.create(entity);
+    //entity.getRepositoryVersion().updateClusterVersionEntityRelation(entity);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java
new file mode 100644
index 0000000..617b659
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionDAO.java
@@ -0,0 +1,164 @@
+/**
+ * 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.orm.dao;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ExtensionEntity;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+
+/**
+ * The {@link ExtensionDAO} class is used to manage the persistence and retrieval of
+ * {@link ExtensionEntity} instances.
+ */
+@Singleton
+public class ExtensionDAO {
+
+  /**
+   * JPA entity manager
+   */
+  @Inject
+  private Provider<EntityManager> entityManagerProvider;
+
+  /**
+   * DAO utilities for dealing mostly with {@link TypedQuery} results.
+   */
+  @Inject
+  private DaoUtils daoUtils;
+
+  /**
+   * Gets a extension with the specified ID.
+   *
+   * @param extensionId
+   *          the ID of the extension to retrieve.
+   * @return the extension or {@code null} if none exists.
+   */
+  @RequiresSession
+  public ExtensionEntity findById(long extensionId) {
+    return entityManagerProvider.get().find(ExtensionEntity.class, extensionId);
+  }
+
+  /**
+   * Gets all of the defined extensions.
+   *
+   * @return all of the extensions loaded from resources or an empty list (never
+   *         {@code null}).
+   */
+  @RequiresSession
+  public List<ExtensionEntity> findAll() {
+    TypedQuery<ExtensionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionEntity.findAll", ExtensionEntity.class);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets the extension that matches the specified name and version.
+   *
+   * @return the extension matching the specified name and version or {@code null}
+   *         if none.
+   */
+  @RequiresSession
+  public ExtensionEntity find(String extensionName, String extensionVersion) {
+    TypedQuery<ExtensionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionEntity.findByNameAndVersion", ExtensionEntity.class);
+
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("extensionVersion", extensionVersion);
+
+    return daoUtils.selectOne(query);
+  }
+
+  /**
+   * Persists a new extension instance.
+   *
+   * @param extension
+   *          the extension to persist (not {@code null}).
+   */
+  @Transactional
+  public void create(ExtensionEntity extension)
+      throws AmbariException {
+    EntityManager entityManager = entityManagerProvider.get();
+    entityManager.persist(extension);
+  }
+
+  /**
+   * Refresh the state of the extension instance from the database.
+   *
+   * @param extension
+   *          the extension to refresh (not {@code null}).
+   */
+  @Transactional
+  public void refresh(ExtensionEntity extension) {
+    entityManagerProvider.get().refresh(extension);
+  }
+
+  /**
+   * Merge the specified extension with the existing extension in the database.
+   *
+   * @param extension
+   *          the extension to merge (not {@code null}).
+   * @return the updated extension with merged content (never {@code null}).
+   */
+  @Transactional
+  public ExtensionEntity merge(ExtensionEntity extension) {
+    return entityManagerProvider.get().merge(extension);
+  }
+
+  /**
+   * Creates or updates the specified entity. This method will check
+   * {@link ExtensionEntity#getStackId()} in order to determine whether the entity
+   * should be created or merged.
+   *
+   * @param extension
+   *          the extension to create or update (not {@code null}).
+   */
+  public void createOrUpdate(ExtensionEntity extension)
+      throws AmbariException {
+    if (null == extension.getExtensionId()) {
+      create(extension);
+    } else {
+      merge(extension);
+    }
+  }
+
+  /**
+   * Removes the specified extension and all related clusters, services and
+   * components.
+   *
+   * @param extension
+   *          the extension to remove.
+   */
+  @Transactional
+  public void remove(ExtensionEntity extension) {
+    EntityManager entityManager = entityManagerProvider.get();
+    extension = findById(extension.getExtensionId());
+    if (null != extension) {
+      entityManager.remove(extension);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java
new file mode 100644
index 0000000..f74eca2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionLinkDAO.java
@@ -0,0 +1,236 @@
+/**
+ * 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.orm.dao;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.ExtensionLinkRequest;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ExtensionLinkEntity;
+import org.apache.ambari.server.orm.entities.ExtensionLinkEntity;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+
+/**
+ * The {@link ExtensionLinkDAO} class is used to manage the persistence and retrieval of
+ * {@link ExtensionLinkEntity} instances.
+ */
+@Singleton
+public class ExtensionLinkDAO {
+
+  /**
+   * JPA entity manager
+   */
+  @Inject
+  private Provider<EntityManager> entityManagerProvider;
+
+  /**
+   * DAO utilities for dealing mostly with {@link TypedQuery} results.
+   */
+  @Inject
+  private DaoUtils daoUtils;
+
+
+  /**
+   * Gets the extension links that match the specified stack name and version.
+   *
+   * @return the extension links  matching the specified stack name and version if any.
+   */
+  @RequiresSession
+  public List<ExtensionLinkEntity> find(ExtensionLinkRequest request) {
+    if (request.getLinkId() != null) {
+      ExtensionLinkEntity entity = findById(new Long(request.getLinkId()));
+      List<ExtensionLinkEntity> list = new ArrayList<ExtensionLinkEntity>();
+      list.add(entity);
+      return list;
+    }
+
+    String stackName = request.getStackName();
+    String stackVersion = request.getStackName();
+    String extensionName = request.getStackName();
+    String extensionVersion = request.getStackName();
+
+    if (stackName != null && stackVersion != null) {
+      if (extensionName != null && extensionVersion != null) {
+        ExtensionLinkEntity entity = findByStackAndExtension(stackName, stackVersion, extensionName, extensionVersion);
+        List<ExtensionLinkEntity> list = new ArrayList<ExtensionLinkEntity>();
+        list.add(entity);
+        return list;
+      }
+      return findByStack(stackName, stackVersion);
+    }
+    if (extensionName != null && extensionVersion != null) {
+      return findByExtension(extensionName, extensionVersion);
+    }
+
+    return findAll();
+  }
+
+  /**
+   * Gets an extension link with the specified ID.
+   *
+   * @param linkId
+   *          the ID of the extension link to retrieve.
+   * @return the extension or {@code null} if none exists.
+   */
+  @RequiresSession
+  public ExtensionLinkEntity findById(long linkId) {
+    return entityManagerProvider.get().find(ExtensionLinkEntity.class, linkId);
+  }
+
+  /**
+   * Gets all of the defined extension links.
+   *
+   * @return all of the extension links loaded from resources or an empty list (never
+   *         {@code null}).
+   */
+  @RequiresSession
+  public List<ExtensionLinkEntity> findAll() {
+    TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionLinkEntity.findAll", ExtensionLinkEntity.class);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets the extension links that match the specified extension name and version.
+   *
+   * @return the extension links matching the specified extension name and version if any.
+   */
+  @RequiresSession
+  public List<ExtensionLinkEntity> findByExtension(String extensionName, String extensionVersion) {
+    TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionLinkEntity.findByExtension", ExtensionLinkEntity.class);
+
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("extensionVersion", extensionVersion);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets the extension links that match the specified stack name and version.
+   *
+   * @return the extension links  matching the specified stack name and version if any.
+   */
+  @RequiresSession
+  public List<ExtensionLinkEntity> findByStack(String stackName, String stackVersion) {
+    TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionLinkEntity.findByStack", ExtensionLinkEntity.class);
+
+    query.setParameter("stackName", stackName);
+    query.setParameter("stackVersion", stackVersion);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets the extension link that match the specified stack name, stack version, extension name and extension version.
+   *
+   * @return the extension link matching the specified stack name, stack version, extension name and extension version if any.
+   */
+  @RequiresSession
+  public ExtensionLinkEntity findByStackAndExtension(String stackName, String stackVersion, String extensionName, String extensionVersion) {
+    TypedQuery<ExtensionLinkEntity> query = entityManagerProvider.get().createNamedQuery(
+        "ExtensionLinkEntity.findByStackAndExtension", ExtensionLinkEntity.class);
+
+    query.setParameter("stackName", stackName);
+    query.setParameter("stackVersion", stackVersion);
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("extensionVersion", extensionVersion);
+
+    return daoUtils.selectOne(query);
+  }
+
+  /**
+   * Persists a new extension link instance.
+   *
+   * @param link
+   *          the extension link to persist (not {@code null}).
+   */
+  @Transactional
+  public void create(ExtensionLinkEntity link)
+      throws AmbariException {
+    EntityManager entityManager = entityManagerProvider.get();
+    entityManager.persist(link);
+  }
+
+  /**
+   * Refresh the state of the extension instance from the database.
+   *
+   * @param link
+   *          the extension link to refresh (not {@code null}).
+   */
+  @Transactional
+  public void refresh(ExtensionLinkEntity link) {
+    entityManagerProvider.get().refresh(link);
+  }
+
+  /**
+   * Merge the specified extension link with the existing extension link in the database.
+   *
+   * @param link
+   *          the extension link to merge (not {@code null}).
+   * @return the updated extension link with merged content (never {@code null}).
+   */
+  @Transactional
+  public ExtensionLinkEntity merge(ExtensionLinkEntity link) {
+    return entityManagerProvider.get().merge(link);
+  }
+
+  /**
+   * Creates or updates the specified entity. This method will check
+   * {@link ExtensionLinkEntity#getLinkId()} in order to determine whether the entity
+   * should be created or merged.
+   *
+   * @param extension
+   *          the link to create or update (not {@code null}).
+   */
+  public void createOrUpdate(ExtensionLinkEntity link)
+      throws AmbariException {
+    if (null == link.getLinkId()) {
+      create(link);
+    } else {
+      merge(link);
+    }
+  }
+
+  /**
+   * Removes the specified extension link
+   *
+   * @param link
+   *          the extension link to remove.
+   */
+  @Transactional
+  public void remove(ExtensionLinkEntity link) {
+    EntityManager entityManager = entityManagerProvider.get();
+    link = findById(link.getLinkId());
+    if (null != link) {
+      entityManager.remove(link);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionRepositoryVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionRepositoryVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionRepositoryVersionDAO.java
new file mode 100644
index 0000000..48abc8c
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ExtensionRepositoryVersionDAO.java
@@ -0,0 +1,171 @@
+/**
+ * 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.orm.dao;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import javax.persistence.TypedQuery;
+
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ExtensionRepositoryVersionEntity;
+import org.apache.ambari.server.orm.entities.ExtensionEntity;
+import org.apache.ambari.server.state.ExtensionId;
+
+import com.google.inject.Singleton;
+
+/**
+ * DAO for repository versions.
+ *
+ */
+@Singleton
+public class ExtensionRepositoryVersionDAO extends CrudDAO<ExtensionRepositoryVersionEntity, Long> {
+  /**
+   * Constructor.
+   */
+  public ExtensionRepositoryVersionDAO() {
+    super(ExtensionRepositoryVersionEntity.class);
+  }
+
+  /**
+   * Retrieves repository version by name.
+   *
+   * @param displayName display name
+   * @return null if there is no suitable repository version
+   */
+  @RequiresSession
+  public ExtensionRepositoryVersionEntity findByDisplayName(String displayName) {
+    // TODO, this assumes that the display name is unique, but neither the code nor the DB schema enforces this.
+    final TypedQuery<ExtensionRepositoryVersionEntity> query = entityManagerProvider.get().createNamedQuery("extensionRepositoryVersionByDisplayName", ExtensionRepositoryVersionEntity.class);
+    query.setParameter("displayname", displayName);
+    return daoUtils.selectSingle(query);
+  }
+
+  /**
+   * Retrieves repository version by extension.
+   *
+   * @param extensionId
+   *          extensionId
+   * @param version
+   *          version
+   * @return null if there is no suitable repository version
+   */
+  @RequiresSession
+  public ExtensionRepositoryVersionEntity findByExtensionAndVersion(ExtensionId extensionId,
+      String version) {
+    return findByExtensionNameAndVersion(extensionId.getExtensionName(), version);
+  }
+
+  /**
+   * Retrieves repository version by extension.
+   *
+   * @param extensionEntity Extension entity
+   * @param version version
+   * @return null if there is no suitable repository version
+   */
+  @RequiresSession
+  public ExtensionRepositoryVersionEntity findByExtensionAndVersion(ExtensionEntity extensionEntity,
+      String version) {
+    return findByExtensionNameAndVersion(extensionEntity.getExtensionName(), version);
+  }
+
+  /**
+   * Retrieves repository version, which is unique in this extension.
+   *
+   * @param extensionName Extension name such as HDP, HDPWIN, BIGTOP
+   * @param version version
+   * @return null if there is no suitable repository version
+   */
+  @RequiresSession
+  public ExtensionRepositoryVersionEntity findByExtensionNameAndVersion(String extensionName, String version) {
+    // TODO, need to make a unique composite key in DB using the extension_repo_version's extension_id and version.
+    // Ideally, 1.0-1234 foo should be unique in all HDP extensions.
+    // The composite key is slightly more relaxed since it would only prevent 2.3.0-1234 multiple times in the HDP 2.3 extension.
+    // There's already business logic to prevent creating 2.3-1234 in the wrong extension such as HDP 2.2.
+    final TypedQuery<ExtensionRepositoryVersionEntity> query = entityManagerProvider.get().createNamedQuery("repositoryVersionByExtensionNameAndVersion", ExtensionRepositoryVersionEntity.class);
+    query.setParameter("extensionName", extensionName);
+    query.setParameter("version", version);
+    return daoUtils.selectSingle(query);
+  }
+
+  /**
+   * Retrieves repository version by extension.
+   *
+   * @param extensionId extension id
+   *          extension with major version
+   * @return null if there is no suitable repository version
+   */
+  @RequiresSession
+  public List<ExtensionRepositoryVersionEntity> findByExtension(ExtensionId extensionId) {
+    final TypedQuery<ExtensionRepositoryVersionEntity> query = entityManagerProvider.get().createNamedQuery("repositoryVersionByExtension", ExtensionRepositoryVersionEntity.class);
+    query.setParameter("extensionName", extensionId.getExtensionName());
+    query.setParameter("extensionVersion", extensionId.getExtensionVersion());
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Creates entity.
+   *
+   * @param entity entity to create
+   */
+  @Override
+  @Transactional
+  public void create(ExtensionRepositoryVersionEntity entity){
+    super.create(entity);
+  }
+
+  /**
+   * Validates and creates an object.
+   * The version must be unique within this extension name.
+   * @param extensionEntity Extension entity.
+   * @param version Extension version, e.g., 1.0, 1.0.0.0
+   * @param displayName Unique display name
+   * @param operatingSystems JSON structure of repository URLs for each OS
+   * @return Returns the object created if successful, and throws an exception otherwise.
+   * @throws AmbariException
+   */
+  @Transactional
+  public ExtensionRepositoryVersionEntity create(ExtensionEntity extensionEntity,
+      String version, String displayName, String operatingSystems) throws AmbariException {
+
+    if (extensionEntity == null || version == null || version.isEmpty()
+        || displayName == null || displayName.isEmpty()) {
+      throw new AmbariException("At least one of the required properties is null or empty");
+    }
+
+    ExtensionRepositoryVersionEntity existingByDisplayName = findByDisplayName(displayName);
+
+    if (existingByDisplayName != null) {
+      throw new AmbariException("Repository version with display name '" + displayName + "' already exists");
+    }
+
+    ExtensionRepositoryVersionEntity existingVersionInExtension = findByExtensionNameAndVersion(extensionEntity.getExtensionName(), version);
+
+    if (existingVersionInExtension != null) {
+      throw new AmbariException(MessageFormat.format("Repository Version for version {0} already exists, in extension {1}-{2}",
+          version, existingVersionInExtension.getExtension().getExtensionName(), existingVersionInExtension.getExtension().getExtensionVersion()));
+    }
+
+    ExtensionRepositoryVersionEntity newEntity = new ExtensionRepositoryVersionEntity(
+        extensionEntity, version, displayName, operatingSystems);
+    this.create(newEntity);
+    return newEntity;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentDesiredStateDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentDesiredStateDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentDesiredStateDAO.java
index 46da9da..100a7d7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentDesiredStateDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentDesiredStateDAO.java
@@ -25,6 +25,7 @@ import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 
 import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ExtensionEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntityPK;
 import org.apache.ambari.server.orm.entities.HostEntity;
@@ -78,6 +79,20 @@ public class HostComponentDesiredStateDAO {
     return daoUtils.selectSingle(query);
   }
 
+  /**
+   * Retrieve the Host Component Desired States for the given extension.
+   *
+   * @param extension ExtensionEntity
+   * @return Return all of the Host Component States that match the criteria.
+   */
+  @RequiresSession
+  public List<HostComponentDesiredStateEntity> findByExtension(ExtensionEntity extension) {
+    final TypedQuery<HostComponentDesiredStateEntity> query = entityManagerProvider.get().createNamedQuery("HostComponentDesiredStateEntity.findByExtension", HostComponentDesiredStateEntity.class);
+    query.setParameter("extensionId", extension.getExtensionId());
+
+    return daoUtils.selectList(query);
+  }
+
   @Transactional
   public void refresh(HostComponentDesiredStateEntity hostComponentDesiredStateEntity) {
     entityManagerProvider.get().refresh(hostComponentDesiredStateEntity);

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
index 6389ef2..d7e2783 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostComponentStateDAO.java
@@ -26,6 +26,7 @@ import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 
 import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ExtensionEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.state.UpgradeState;
@@ -75,6 +76,20 @@ public class HostComponentStateDAO {
   }
 
   /**
+   * Retrieve all of the Host Component States for the given extension.
+   *
+   * @param extension ExtensionEntity
+   * @return Return all of the Host Component States that match the criteria.
+   */
+  @RequiresSession
+  public List<HostComponentStateEntity> findByExtension(ExtensionEntity extension) {
+    final TypedQuery<HostComponentStateEntity> query = entityManagerProvider.get().createNamedQuery("HostComponentStateEntity.findByExtension", HostComponentStateEntity.class);
+    query.setParameter("extensionId", extension.getExtensionId());
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
    * Retrieve all of the Host Component States for the given service.
    *
    * @param serviceName Service Name

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostExtensionVersionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostExtensionVersionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostExtensionVersionDAO.java
new file mode 100644
index 0000000..368cda5
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostExtensionVersionDAO.java
@@ -0,0 +1,229 @@
+/**
+ * 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.orm.dao;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.TypedQuery;
+
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.HostExtensionVersionEntity;
+import org.apache.ambari.server.state.RepositoryVersionState;
+import org.apache.ambari.server.state.ExtensionId;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+
+/**
+ * The {@link org.apache.ambari.server.orm.dao.HostExtensionVersionDAO} class manages the {@link org.apache.ambari.server.orm.entities.HostExtensionVersionEntity}
+ * instances associated with a host. Each host can have multiple extension versions in {@link org.apache.ambari.server.state.RepositoryVersionState#INSTALLED}
+ * which are installed, exactly one extension version that is either {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT} or
+ * {@link org.apache.ambari.server.state.RepositoryVersionState#UPGRADING}.
+ */
+@Singleton
+public class HostExtensionVersionDAO {
+  @Inject
+  Provider<EntityManager> entityManagerProvider;
+  @Inject
+  DaoUtils daoUtils;
+
+  /**
+   * Get the object with the given id.
+   *
+   * @param id Primary key id
+   * @return Return the object with the given primary key
+   */
+  @RequiresSession
+  public HostExtensionVersionEntity findByPK(long id) {
+    return entityManagerProvider.get().find(HostExtensionVersionEntity.class, id);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name, extension name,
+   * and extension version.
+   *
+   * @param clusterName
+   *          Cluster name
+   * @param extensionId
+   *          Extension (e.g., EXT-1.0)
+   * @param version
+   *          Extension version (e.g., 1.0.0.1-995)
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostExtensionVersionEntity> findByClusterExtensionAndVersion(
+      String clusterName, ExtensionId extensionId, String version) {
+    final TypedQuery<HostExtensionVersionEntity> query = entityManagerProvider.get().createNamedQuery("hostExtensionVersionByClusterAndExtensionAndVersion", HostExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("extensionName", extensionId.getExtensionName());
+    query.setParameter("extensionVersion", extensionId.getExtensionVersion());
+    query.setParameter("version", version);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given host name across all clusters.
+   *
+   * @param hostName FQDN of host
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostExtensionVersionEntity> findByHost(String hostName) {
+    final TypedQuery<HostExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostExtensionVersionByHostname", HostExtensionVersionEntity.class);
+    query.setParameter("hostName", hostName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name and host name.
+   *
+   * @param clusterName Cluster name
+   * @param hostName FQDN of host
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostExtensionVersionEntity> findByClusterAndHost(String  clusterName, String hostName) {
+    final TypedQuery<HostExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostExtensionVersionByClusterAndHostname", HostExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("hostName", hostName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve all of the host versions for the given cluster name, host name, and state.
+   *
+   * @param clusterName Cluster name
+   * @param hostName FQDN of host
+   * @param state repository version state
+   * @return Return all of the host versions that match the criteria.
+   */
+  @RequiresSession
+  public List<HostExtensionVersionEntity> findByClusterHostAndState(String  clusterName, String hostName, RepositoryVersionState state) {
+    final TypedQuery<HostExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostExtensionVersionByClusterHostnameAndState", HostExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("hostName", hostName);
+    query.setParameter("state", state);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Retrieve the single host version whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, of which there should be exactly one at all times
+   * for the given host.
+   *
+   * @param clusterName Cluster name
+   * @param hostName Host name
+   * @return Returns the single host version for this host whose state is {@link org.apache.ambari.server.state.RepositoryVersionState#CURRENT}, or {@code null} otherwise.
+   */
+  @RequiresSession
+  public HostExtensionVersionEntity findByHostAndStateCurrent(String clusterName, String hostName) {
+    try {
+      List<?> results = findByClusterHostAndState(clusterName, hostName, RepositoryVersionState.CURRENT);
+      if (results.isEmpty()) {
+        return null;
+      } else {
+        if (results.size() == 1) {
+          return (HostExtensionVersionEntity) results.get(0);
+        }
+      }
+      throw new NonUniqueResultException();
+    } catch (NoResultException ignored) {
+      return null;
+    }
+  }
+
+  /**
+   * Retrieve the single host version for the given cluster, extension name, extension
+   * version, and host name.
+   *
+   * @param clusterName
+   *          Cluster name
+   * @param extensionId
+   *          Extension ID (e.g., EXT-1.0)
+   * @param version
+   *          Extension version (e.g., 1.0.0.1-995)
+   * @param hostName
+   *          FQDN of host
+   * @return Returns the single host version that matches the criteria.
+   */
+  @RequiresSession
+  public HostExtensionVersionEntity findByClusterExtensionVersionAndHost(String clusterName,
+      ExtensionId extensionId, String version, String hostName) {
+
+    final TypedQuery<HostExtensionVersionEntity> query = entityManagerProvider.get()
+        .createNamedQuery("hostExtensionVersionByClusterExtensionVersionAndHostname", HostExtensionVersionEntity.class);
+    query.setParameter("clusterName", clusterName);
+    query.setParameter("extensionName", extensionId.getExtensionName());
+    query.setParameter("extensionVersion", extensionId.getExtensionVersion());
+    query.setParameter("version", version);
+    query.setParameter("hostName", hostName);
+
+    return daoUtils.selectSingle(query);
+  }
+
+  @RequiresSession
+  public List<HostExtensionVersionEntity> findAll() {
+    return daoUtils.selectAll(entityManagerProvider.get(), HostExtensionVersionEntity.class);
+  }
+
+  @Transactional
+  public void refresh(HostExtensionVersionEntity hostExtensionVersionEntity) {
+    entityManagerProvider.get().refresh(hostExtensionVersionEntity);
+  }
+
+  @Transactional
+  public void create(HostExtensionVersionEntity hostExtensionVersionEntity) {
+    entityManagerProvider.get().persist(hostExtensionVersionEntity);
+  }
+
+  @Transactional
+  public HostExtensionVersionEntity merge(HostExtensionVersionEntity hostExtensionVersionEntity) {
+    return entityManagerProvider.get().merge(hostExtensionVersionEntity);
+  }
+
+  @Transactional
+  public void remove(HostExtensionVersionEntity hostExtensionVersionEntity) {
+    entityManagerProvider.get().remove(merge(hostExtensionVersionEntity));
+  }
+
+  @Transactional
+  public void removeByHostName(String hostName) {
+    Collection<HostExtensionVersionEntity> hostExtensionVersions = this.findByHost(hostName);
+    for (HostExtensionVersionEntity hostExtensionVersion : hostExtensionVersions) {
+      this.remove(hostExtensionVersion);
+    }
+  }
+
+  @Transactional
+  public void removeByPK(long id) {
+    remove(findByPK(id));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
index c3c3e9e..5fb7c22 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
@@ -28,9 +28,9 @@ import javax.persistence.Table;
 /**
  * Entity that maps to a cluster config mapping.
  */
+@IdClass(ClusterConfigMappingEntityPK.class)
 @Table(name = "clusterconfigmapping")
 @Entity
-@IdClass(ClusterConfigMappingEntityPK.class)
 public class ClusterConfigMappingEntity {
 
   @Id

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterExtensionVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterExtensionVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterExtensionVersionEntity.java
new file mode 100644
index 0000000..68b6e22
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterExtensionVersionEntity.java
@@ -0,0 +1,223 @@
+/**
+ * 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.orm.entities;
+
+import static org.apache.commons.lang.StringUtils.defaultString;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+
+import org.apache.ambari.server.state.RepositoryVersionState;
+
+@Table(name = "cluster_extension_version")
+@Entity
+@TableGenerator(name = "cluster_extension_version_id_generator",
+    table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
+    , pkColumnValue = "cluster_extension_version_id_seq"
+    , initialValue = 0
+)
+@NamedQueries({
+    @NamedQuery(name = "clusterExtensionVersionByClusterAndExtensionAndVersion", query =
+        "SELECT clusterExtensionVersion FROM ClusterExtensionVersionEntity clusterExtensionVersion JOIN clusterExtensionVersion.clusterEntity cluster " +
+        "WHERE cluster.clusterName=:clusterName AND clusterExtensionVersion.repositoryVersion.extension.extensionName=:extensionName AND clusterExtensionVersion.repositoryVersion.extension.extensionVersion=:extensionVersion " +
+        "AND clusterExtensionVersion.repositoryVersion.version=:version"),
+    @NamedQuery(name = "clusterExtensionVersionByClusterAndExtensionName", query =
+        "SELECT clusterExtensionVersion FROM ClusterExtensionVersionEntity clusterExtensionVersion JOIN clusterExtensionVersion.clusterEntity cluster " +
+        "WHERE cluster.clusterName=:clusterName AND clusterExtensionVersion.repositoryVersion.extension.extensionName=:extensionName"),
+    @NamedQuery(name = "clusterExtensionVersionByClusterAndExtensionNameAndState", query =
+        "SELECT clusterExtensionVersion FROM ClusterExtensionVersionEntity clusterExtensionVersion JOIN clusterExtensionVersion.clusterEntity cluster " +
+        "WHERE cluster.clusterName=:clusterName AND clusterExtensionVersion.repositoryVersion.extension.extensionName=:extensionName AND clusterExtensionVersion.state=:state"),
+    @NamedQuery(name = "clusterExtensionVersionByCluster", query =
+        "SELECT clusterExtensionVersion FROM ClusterExtensionVersionEntity clusterExtensionVersion JOIN clusterExtensionVersion.clusterEntity cluster " +
+        "WHERE cluster.clusterName=:clusterName"),
+    @NamedQuery(name = "clusterExtensionVersionByExtensionVersion", query = "SELECT clusterExtensionVersion FROM ClusterExtensionVersionEntity clusterExtensionVersion " +
+        "WHERE clusterExtensionVersion.repositoryVersion.extension.extensionName=:extensionName " +
+        "AND clusterExtensionVersion.repositoryVersion.extension.extensionVersion=:extensionVersion AND clusterExtensionVersion.repositoryVersion.version=:version"),
+})
+public class ClusterExtensionVersionEntity {
+
+  @Id
+  @Column(name = "id", nullable = false, insertable = true, updatable = false)
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "cluster_extension_version_id_generator")
+  private Long id;
+
+  @Column(name = "cluster_id", nullable = false, insertable = false, updatable = false)
+  private Long clusterId;
+
+  @ManyToOne
+  @JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false)
+  private ClusterEntity clusterEntity;
+
+  @ManyToOne
+  @JoinColumn(name = "repo_version_id", referencedColumnName = "repo_version_id", nullable = false)
+  private ExtensionRepositoryVersionEntity repositoryVersion;
+
+  @Column(name = "state", nullable = false, insertable = true, updatable = true)
+  @Enumerated(value = EnumType.STRING)
+  private RepositoryVersionState state = RepositoryVersionState.CURRENT;
+
+  @Basic
+  @Column(name = "start_time", nullable = false, insertable = true, updatable = true)
+  private Long startTime = System.currentTimeMillis();
+
+  @Basic
+  @Column(name = "end_time", insertable = true, updatable = true)
+  private Long endTime;
+
+  @Basic
+  @Column(name = "user_name", insertable = true, updatable = true)
+  private String userName = "";
+
+  /**
+   * Empty constructor primarily used by unit tests.
+   */
+  public ClusterExtensionVersionEntity() {
+  }
+
+  /**
+   * Full constructor that doesn't have the endTime
+   * @param cluster Cluster entity
+   * @param repositoryVersion repository version
+   * @param state Cluster version state
+   * @param startTime Time the cluster version reached its first state
+   * @param userName User who performed the action
+   */
+  public ClusterExtensionVersionEntity(ClusterEntity cluster, ExtensionRepositoryVersionEntity repositoryVersion, RepositoryVersionState state, long startTime, String userName) {
+    clusterId = cluster.getClusterId();
+    this.repositoryVersion = repositoryVersion;
+    clusterEntity = cluster;
+    this.state = state;
+    this.startTime = startTime;
+    this.userName = userName;
+  }
+
+  /**
+   * Full constructor that does have the endTime
+   * @param cluster Cluster entity
+   * @param repositoryVersion repository version
+   * @param state Cluster version state
+   * @param startTime Time the cluster version reached its first state
+   * @param endTime Time the cluster version finalized its state
+   * @param userName User who performed the action
+   */
+  public ClusterExtensionVersionEntity(ClusterEntity cluster, ExtensionRepositoryVersionEntity repositoryVersion, RepositoryVersionState state, long startTime, long endTime, String userName) {
+    this(cluster, repositoryVersion, state, startTime, userName);
+    this.endTime = endTime;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public Long getClusterId() {
+    return clusterId;
+  }
+
+  public void setClusterId(Long clusterId) {
+    this.clusterId = clusterId;
+  }
+
+  public ClusterEntity getClusterEntity() {
+    return clusterEntity;
+  }
+
+  public void setClusterEntity(ClusterEntity clusterEntity) {
+    this.clusterEntity = clusterEntity;
+  }
+
+  public RepositoryVersionState getState() {
+    return state;
+  }
+
+  public void setState(RepositoryVersionState state) {
+    this.state = state;
+  }
+
+  public Long getStartTime() { return startTime; }
+
+  public void setStartTime(Long startTime) { this.startTime = startTime; }
+
+  public Long getEndTime() { return endTime; }
+
+  public void setEndTime(Long endTime) { this.endTime = endTime; }
+
+  public String getUserName() { return defaultString(userName); }
+
+  public void setUserName(String userName) { this.userName = userName; }
+
+  public void setRepositoryVersion(ExtensionRepositoryVersionEntity repositoryVersion) {
+    this.repositoryVersion = repositoryVersion;
+  }
+
+  public ExtensionRepositoryVersionEntity getRepositoryVersion() {
+    return repositoryVersion;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    ClusterExtensionVersionEntity that = (ClusterExtensionVersionEntity) o;
+
+    if (id != that.id
+        || clusterId != that.clusterId
+        || !repositoryVersion.equals(that.repositoryVersion)
+        || !state.equals(that.state)
+        || !startTime.equals(that.startTime)
+        || !endTime.equals(that.endTime)
+        || !userName.equals(that.userName)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = id !=null ? id.intValue() : 0;
+    result = 31 * result + (clusterId != null ? clusterId.hashCode() : 0);
+    result = 31 * result + (repositoryVersion != null ? repositoryVersion.hashCode() : 0);
+    result = 31 * result + (state != null ? state.hashCode() : 0);
+    result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
+    result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
+    result = 31 * result + (userName != null ? userName.hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java
new file mode 100644
index 0000000..b8ca66b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionEntity.java
@@ -0,0 +1,152 @@
+/**
+ * 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.orm.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import javax.persistence.UniqueConstraint;
+
+/**
+ * The {@link ExtensionEntity} class is used to model an extension to the stack.
+ */
+@Entity
+@Table(name = "extension", uniqueConstraints = @UniqueConstraint(columnNames = {
+    "extension_name", "extension_version" }))
+@TableGenerator(name = "extension_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value", pkColumnValue = "extension_id_seq", initialValue = 0)
+@NamedQueries({
+    @NamedQuery(name = "ExtensionEntity.findAll", query = "SELECT extension FROM ExtensionEntity extension"),
+    @NamedQuery(name = "ExtensionEntity.findByNameAndVersion", query = "SELECT extension FROM ExtensionEntity extension WHERE extension.extensionName = :extensionName AND extension.extensionVersion = :extensionVersion") })
+public class ExtensionEntity {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "extension_id_generator")
+  @Column(name = "extension_id", nullable = false, updatable = false)
+  private Long extensionId;
+
+  @Column(name = "extension_name", length = 255, nullable = false)
+  private String extensionName;
+
+  @Column(name = "extension_version", length = 255, nullable = false)
+  private String extensionVersion;
+
+  /**
+   * Constructor.
+   */
+  public ExtensionEntity() {
+  }
+
+  /**
+   * Gets the unique identifier for this extension.
+   *
+   * @return the ID.
+   */
+  public Long getExtensionId() {
+    return extensionId;
+  }
+
+  /**
+   * Gets the name of the extension.
+   *
+   * @return the name of the extension (never {@code null}).
+   */
+  public String getExtensionName() {
+    return extensionName;
+  }
+
+  /**
+   * Sets the name of the extension.
+   *
+   * @param extensionName
+   *          the extension name (not {@code null}).
+   */
+  public void setExtensionName(String extensionName) {
+    this.extensionName = extensionName;
+  }
+
+  /**
+   * Gets the version of the extension.
+   *
+   * @return the extension version (never {@code null}).
+   */
+  public String getExtensionVersion() {
+    return extensionVersion;
+  }
+
+  /**
+   * Sets the version of the extension.
+   *
+   * @param extensionVersion
+   *          the extension version (not {@code null}).
+   */
+  public void setExtensionVersion(String extensionVersion) {
+    this.extensionVersion = extensionVersion;
+  }
+
+  /**
+   *
+   */
+  @Override
+  public boolean equals(Object object) {
+    if (this == object) {
+      return true;
+    }
+
+    if (object == null || getClass() != object.getClass()) {
+      return false;
+    }
+
+    ExtensionEntity that = (ExtensionEntity) object;
+
+    if (extensionId != null ? !extensionId.equals(that.extensionId) : that.extensionId != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   *
+   */
+  @Override
+  public int hashCode() {
+    int result = null != extensionId ? extensionId.hashCode() : 0;
+    return result;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder buffer = new StringBuilder();
+    buffer.append(getClass().getSimpleName());
+    buffer.append("{");
+    buffer.append("id=").append(extensionId);
+    buffer.append(", name=").append(extensionName);
+    buffer.append(", version=").append(extensionVersion);
+    buffer.append("}");
+    return buffer.toString();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java
new file mode 100644
index 0000000..7965cd2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionLinkEntity.java
@@ -0,0 +1,135 @@
+/**
+ * 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.orm.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import javax.persistence.UniqueConstraint;
+
+/**
+ * The {@link ExtensionLinkEntity} class is used to model the extensions linked to the stack.
+ */
+@Table(name = "extensionlink", uniqueConstraints = @UniqueConstraint(columnNames = {
+		"stack_id", "extension_id" }))
+@TableGenerator(name = "link_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value", pkColumnValue = "link_id_seq", initialValue = 0)
+@NamedQueries({
+    @NamedQuery(name = "ExtensionLinkEntity.findAll", query = "SELECT link FROM ExtensionLinkEntity link"),
+    @NamedQuery(name = "ExtensionLinkEntity.findByStackAndExtension", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.stack.stackName = :stackName AND link.stack.stackVersion = :stackVersion AND link.extension.extensionName = :extensionName AND link.extension.extensionVersion = :extensionVersion"),
+    @NamedQuery(name = "ExtensionLinkEntity.findByStack", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.stack.stackName = :stackName AND link.stack.stackVersion = :stackVersion"),
+    @NamedQuery(name = "ExtensionLinkEntity.findByExtension", query = "SELECT link FROM ExtensionLinkEntity link WHERE link.extension.extensionName = :extensionName AND link.extension.extensionVersion = :extensionVersion") })
+@Entity
+public class ExtensionLinkEntity {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "link_id_generator")
+  @Column(name = "link_id", nullable = false, updatable = false)
+  private Long linkId;
+
+  @OneToOne
+  @JoinColumn(name = "stack_id", unique = false, nullable = false, insertable = true, updatable = false)
+  private StackEntity stack;
+
+  @OneToOne
+  @JoinColumn(name = "extension_id", unique = false, nullable = false, insertable = true, updatable = false)
+  private ExtensionEntity extension;
+
+  /**
+   * Constructor.
+   */
+  public ExtensionLinkEntity() {
+  }
+
+  public Long getLinkId() {
+    return linkId;
+  }
+
+  public void setLinkId(Long linkId) {
+    this.linkId = linkId;
+  }
+
+  public StackEntity getStack() {
+    return stack;
+  }
+
+  public void setStack(StackEntity stack) {
+    this.stack = stack;
+  }
+
+  public ExtensionEntity getExtension() {
+    return extension;
+  }
+
+  public void setExtension(ExtensionEntity extension) {
+    this.extension = extension;
+  }
+
+  /**
+   *
+   */
+  @Override
+  public boolean equals(Object object) {
+    if (this == object) {
+      return true;
+    }
+
+    if (object == null || getClass() != object.getClass()) {
+      return false;
+    }
+
+    ExtensionLinkEntity that = (ExtensionLinkEntity) object;
+
+    if (linkId != null ? !linkId.equals(that.linkId) : that.linkId != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   *
+   */
+  @Override
+  public int hashCode() {
+    int result = (null != linkId) ? linkId.hashCode() : 0;
+    return result;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    StringBuilder buffer = new StringBuilder();
+    buffer.append(getClass().getSimpleName());
+    buffer.append("{");
+    buffer.append("linkId=").append(linkId);
+    buffer.append(", stackId=").append(stack.getStackId());
+    buffer.append(", extensionId=").append(extension.getExtensionId());
+    buffer.append("}");
+    return buffer.toString();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionRepositoryVersionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionRepositoryVersionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionRepositoryVersionEntity.java
new file mode 100644
index 0000000..cc24495
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ExtensionRepositoryVersionEntity.java
@@ -0,0 +1,251 @@
+/**
+ * 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.orm.entities;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import javax.persistence.UniqueConstraint;
+
+import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.state.ExtensionId;
+import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+@Entity
+@Table(name = "extension_repo_version", uniqueConstraints = {
+    @UniqueConstraint(columnNames = {"display_name"}),
+    @UniqueConstraint(columnNames = {"extension", "version"})
+})
+@TableGenerator(name = "extension_repository_version_id_generator",
+    table = "ambari_sequences",
+    pkColumnName = "sequence_name",
+    valueColumnName = "sequence_value",
+    pkColumnValue = "extension_repo_version_id_seq",
+    initialValue = 0
+    )
+@NamedQueries({
+    @NamedQuery(name = "extensionRepositoryVersionByDisplayName", query = "SELECT repoversion FROM ExtensionRepositoryVersionEntity repoversion WHERE repoversion.displayName=:displayname"),
+    @NamedQuery(name = "repositoryVersionByExtension", query = "SELECT repoversion FROM ExtensionRepositoryVersionEntity repoversion WHERE repoversion.extension.extensionName=:extensionName AND repoversion.extension.extensionVersion=:extensionVersion"),
+        @NamedQuery(name = "repositoryVersionByExtensionNameAndVersion", query = "SELECT repoversion FROM ExtensionRepositoryVersionEntity repoversion WHERE repoversion.extension.extensionName=:extensionName AND repoversion.version=:version")
+})
+@StaticallyInject
+public class ExtensionRepositoryVersionEntity {
+
+  private static Logger LOG = LoggerFactory.getLogger(ExtensionRepositoryVersionEntity.class);
+
+  @Inject
+  private static Provider<RepositoryVersionHelper> repositoryVersionHelperProvider;
+
+  @Id
+  @Column(name = "repo_version_id")
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "extension_repository_version_id_generator")
+  private Long id;
+
+  /**
+   * Unidirectional one-to-one association to {@link ExtensionEntity}
+   */
+  @OneToOne
+  @JoinColumn(name = "extension_id", unique = false, nullable = false, insertable = true, updatable = true)
+  private ExtensionEntity extension;
+
+  @Column(name = "version")
+  private String version;
+
+  @Column(name = "display_name")
+  private String displayName;
+
+  @Column(name = "upgrade_package")
+  private String upgradePackage;
+
+  @Lob
+  @Column(name = "repositories")
+  private String operatingSystems;
+
+  // ----- ExtensionRepositoryVersionEntity -------------------------------------------------------
+
+  public ExtensionRepositoryVersionEntity() {
+
+  }
+
+  public ExtensionRepositoryVersionEntity(ExtensionEntity extension, String version,
+      String displayName, String operatingSystems) {
+    this.extension = extension;
+    this.version = version;
+    this.displayName = displayName;
+    this.upgradePackage = null;
+    this.operatingSystems = operatingSystems;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  /**
+   * Gets the repository version's extension.
+   *
+   * @return the extension.
+   */
+  public ExtensionEntity getExtension() {
+    return extension;
+  }
+
+  /**
+   * Sets the repository version's extension.
+   *
+   * @param extension
+   *          the extension to set for the repo version (not {@code null}).
+   */
+  public void setExtension(ExtensionEntity extension) {
+    this.extension = extension;
+  }
+
+  public String getVersion() {
+    return version;
+  }
+
+  public void setVersion(String version) {
+    this.version = version;
+  }
+
+  public String getDisplayName() {
+    return displayName;
+  }
+
+  public void setDisplayName(String displayName) {
+    this.displayName = displayName;
+  }
+
+  public String getUpgradePackage() {
+    return upgradePackage;
+  }
+
+  public void setUpgradePackage(String upgradePackage) {
+    this.upgradePackage = upgradePackage;
+  }
+
+  public String getOperatingSystemsJson() {
+    return operatingSystems;
+  }
+
+  public void setOperatingSystems(String repositories) {
+    operatingSystems = repositories;
+  }
+
+  /**
+   * Getter which hides json nature of operating systems and returns them as entities.
+   *
+   * @return empty list if stored json is invalid
+   */
+  public List<OperatingSystemEntity> getOperatingSystems() {
+    if (StringUtils.isNotBlank(operatingSystems)) {
+      try {
+        return repositoryVersionHelperProvider.get().parseOperatingSystems(operatingSystems);
+      } catch (Exception ex) {
+        // Should never happen as we validate json before storing it to DB
+        LOG.error("Could not parse operating systems json stored in database:" + operatingSystems, ex);
+      }
+    }
+    return Collections.emptyList();
+  }
+
+  public String getExtensionName() {
+    return getExtensionId().getExtensionName();
+  }
+
+  public String getExtensionVersion() {
+    return getExtensionId().getExtensionVersion();
+  }
+
+  public ExtensionId getExtensionId() {
+    if (null == extension) {
+      return null;
+    }
+
+    return new ExtensionId(extension.getExtensionName(), extension.getExtensionVersion());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    ExtensionRepositoryVersionEntity that = (ExtensionRepositoryVersionEntity) o;
+
+    if (id != null ? !id.equals(that.id) : that.id != null) {
+      return false;
+    }
+    if (extension != null ? !extension.equals(that.extension) : that.extension != null) {
+      return false;
+    }
+    if (version != null ? !version.equals(that.version) : that.version != null) {
+      return false;
+    }
+    if (displayName != null ? !displayName.equals(that.displayName) : that.displayName != null) {
+      return false;
+    }
+    if (upgradePackage != null ? !upgradePackage.equals(that.upgradePackage) : that.upgradePackage != null) {
+      return false;
+    }
+    if (operatingSystems != null ? !operatingSystems.equals(that.operatingSystems) : that.operatingSystems != null) {
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = id != null ? id.hashCode() : 0;
+    result = 31 * result + (extension != null ? extension.hashCode() : 0);
+    result = 31 * result + (version != null ? version.hashCode() : 0);
+    result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
+    result = 31 * result + (upgradePackage != null ? upgradePackage.hashCode() : 0);
+    result = 31 * result + (operatingSystems != null ? operatingSystems.hashCode() : 0);
+    return result;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentDesiredStateEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentDesiredStateEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentDesiredStateEntity.java
index b57a467..9ddf0d1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentDesiredStateEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostComponentDesiredStateEntity.java
@@ -47,6 +47,9 @@ import org.apache.ambari.server.state.State;
     @NamedQuery(name = "HostComponentDesiredStateEntity.findByHost", query =
         "SELECT hcds from HostComponentDesiredStateEntity hcds WHERE hcds.hostEntity.hostName=:hostName"),
 
+    @NamedQuery(name = "HostComponentDesiredStateEntity.findByExtension", query =
+        "SELECT hcds from HostComponentDesiredStateEntity hcds WHERE hcds.desiredExtension.extensionId=:extensionId"),
+
     @NamedQuery(name = "HostComponentDesiredStateEntity.findByService", query =
         "SELECT hcds from HostComponentDesiredStateEntity hcds WHERE hcds.serviceName=:serviceName"),
 
@@ -91,6 +94,13 @@ public class HostComponentDesiredStateEntity {
   @JoinColumn(name = "desired_stack_id", unique = false, nullable = false)
   private StackEntity desiredStack;
 
+  /**
+   * Unidirectional one-to-one association to {@link ExtensionEntity}
+   */
+  @OneToOne
+  @JoinColumn(name = "desired_extension_id", unique = false, nullable = true, insertable = true, updatable = true)
+  private ExtensionEntity desiredExtension;
+
   @Enumerated(value = EnumType.STRING)
   @Column(name = "admin_state", nullable = true, insertable = true, updatable = true)
   private HostComponentAdminState adminState;
@@ -165,6 +175,14 @@ public class HostComponentDesiredStateEntity {
     this.desiredStack = desiredStack;
   }
 
+  public ExtensionEntity getDesiredExtension() {
+    return desiredExtension;
+  }
+
+  public void setDesiredExtension(ExtensionEntity desiredExtension) {
+    this.desiredExtension = desiredExtension;
+  }
+
   public HostComponentAdminState getAdminState() {
     return adminState;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/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 f1af9b0..76168af 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
@@ -56,6 +56,9 @@ import org.apache.ambari.server.state.UpgradeState;
         name = "HostComponentStateEntity.findByHost",
         query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.hostEntity.hostName=:hostName"),
     @NamedQuery(
+        name = "HostComponentStateEntity.findByExtension",
+        query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.currentExtension.extensionId=:extensionId"),
+    @NamedQuery(
         name = "HostComponentStateEntity.findByService",
         query = "SELECT hcs from HostComponentStateEntity hcs WHERE hcs.serviceName=:serviceName"),
     @NamedQuery(
@@ -108,6 +111,13 @@ public class HostComponentStateEntity {
   @JoinColumn(name = "current_stack_id", unique = false, nullable = false, insertable = true, updatable = true)
   private StackEntity currentStack;
 
+  /**
+   * Unidirectional one-to-one association to {@link ExtensionEntity}
+   */
+  @OneToOne
+  @JoinColumn(name = "current_extension_id", unique = false, nullable = true, insertable = true, updatable = true)
+  private ExtensionEntity currentExtension;
+
   @ManyToOne
   @JoinColumns({
       @JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false),
@@ -187,6 +197,14 @@ public class HostComponentStateEntity {
     this.currentStack = currentStack;
   }
 
+  public ExtensionEntity getCurrentExtension() {
+    return currentExtension;
+  }
+
+  public void setCurrentExtension(ExtensionEntity currentExtension) {
+    this.currentExtension = currentExtension;
+  }
+
   public String getVersion() {
     return version;
   }
@@ -225,6 +243,11 @@ public class HostComponentStateEntity {
       return false;
     }
 
+    if (currentExtension != null ? !currentExtension.equals(that.currentExtension)
+        : that.currentExtension != null) {
+      return false;
+    }
+
     if (currentState != null ? !currentState.equals(that.currentState)
         : that.currentState != null) {
       return false;
@@ -259,6 +282,7 @@ public class HostComponentStateEntity {
     result = 31 * result + (currentState != null ? currentState.hashCode() : 0);
     result = 31 * result + (upgradeState != null ? upgradeState.hashCode() : 0);
     result = 31 * result + (currentStack != null ? currentStack.hashCode() : 0);
+    result = 31 * result + (currentExtension != null ? currentExtension.hashCode() : 0);
     result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
     result = 31 * result + (version != null ? version.hashCode() : 0);
     return result;

http://git-wip-us.apache.org/repos/asf/ambari/blob/647929db/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
index 2f94e67..9b3d6ac 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostEntity.java
@@ -50,7 +50,7 @@ import javax.persistence.TableGenerator;
     , initialValue = 0
 )
 @NamedQueries({
-    @NamedQuery(name = "HostEntity.findByHostName", query = "SELECT host FROM HostEntity host WHERE host.hostName = :hostName"),
+    @NamedQuery(name = "HostEntity.findByHostName", query = "SELECT host FROM HostEntity host WHERE host.hostName = :hostName")
 })
 public class HostEntity implements Comparable<HostEntity> {
 
@@ -132,6 +132,9 @@ public class HostEntity implements Comparable<HostEntity> {
   @OneToMany(mappedBy = "hostEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
   private Collection<HostVersionEntity> hostVersionEntities;
 
+  @OneToMany(mappedBy = "hostEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
+  private Collection<HostExtensionVersionEntity> hostExtensionVersionEntities;
+
   @ManyToMany
   @JoinTable(name = "ClusterHostMapping",
       joinColumns = {@JoinColumn(name = "host_id", referencedColumnName = "host_id")},
@@ -285,7 +288,7 @@ public class HostEntity implements Comparable<HostEntity> {
 
     HostEntity that = (HostEntity) o;
 
-    return getHostId() == that.getHostId() && hostName.equals(that.hostName);
+    return getHostId().equals(that.getHostId()) && hostName.equals(that.hostName);
   }
 
   /**
@@ -399,4 +402,12 @@ public class HostEntity implements Comparable<HostEntity> {
   public void setHostVersionEntities(Collection<HostVersionEntity> hostVersionEntities) {
     this.hostVersionEntities = hostVersionEntities;
   }
+
+  public Collection<HostExtensionVersionEntity> getHostExtensionVersionEntities() {
+    return hostExtensionVersionEntities;
+  }
+
+  public void setHostExtensionVersionEntities(Collection<HostExtensionVersionEntity> hostExtensionVersionEntities) {
+    this.hostExtensionVersionEntities = hostExtensionVersionEntities;
+  }
 }


Mime
View raw message