You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mp...@apache.org on 2014/07/30 23:37:28 UTC

[2/3] AMBARI-6660. Implement service config versions. (mpapirkovskyy)

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigApplicationEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigApplicationEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigApplicationEntity.java
new file mode 100644
index 0000000..cae46e3
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigApplicationEntity.java
@@ -0,0 +1,102 @@
+/*
+ * 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.Basic;
+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.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+
+@Entity
+@Table(name = "serviceconfigapplication")
+@TableGenerator(name = "service_config_application_id_generator",
+  table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "value"
+  , pkColumnValue = "service_config_application_id_seq"
+  , initialValue = 1
+  , allocationSize = 1
+)
+public class ServiceConfigApplicationEntity {
+  @Id
+  @Column(name = "apply_id")
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "service_config_application_id_generator")
+  private Long applyId;
+
+  @Basic
+  @Column(name = "service_config_id", updatable = false, insertable = false, nullable = false)
+  private Long serviceConfigId;
+
+  @Basic
+  @Column(name = "apply_timestamp")
+  private Long applyTimestamp;
+
+  @Basic
+  @Column(name = "user_name")
+  private String user;
+
+  @ManyToOne
+  @JoinColumn(name = "service_config_id", referencedColumnName = "service_config_id")
+  private ServiceConfigEntity serviceConfigEntity;
+
+
+  public Long getApplyId() {
+    return applyId;
+  }
+
+  public void setApplyId(Long applyId) {
+    this.applyId = applyId;
+  }
+
+  public Long getServiceConfigId() {
+    return serviceConfigId;
+  }
+
+  public void setServiceConfigId(Long serviceConfigId) {
+    this.serviceConfigId = serviceConfigId;
+  }
+
+  public Long getApplyTimestamp() {
+    return applyTimestamp;
+  }
+
+  public void setApplyTimestamp(Long applyTimestamp) {
+    this.applyTimestamp = applyTimestamp;
+  }
+
+  public ServiceConfigEntity getServiceConfigEntity() {
+    return serviceConfigEntity;
+  }
+
+  public void setServiceConfigEntity(ServiceConfigEntity serviceConfigEntity) {
+    this.serviceConfigEntity = serviceConfigEntity;
+  }
+
+  public String getUser() {
+    return user;
+  }
+
+  public void setUser(String user) {
+    this.user = user;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigEntity.java
new file mode 100644
index 0000000..2727388
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigEntity.java
@@ -0,0 +1,146 @@
+/*
+ * 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.Basic;
+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.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.TableGenerator;
+import java.util.List;
+
+@Entity
+@Table(name = "serviceconfig")
+@TableGenerator(name = "service_config_id_generator",
+  table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "value"
+  , pkColumnValue = "service_config_id_seq"
+  , initialValue = 1
+  , allocationSize = 1
+)
+public class ServiceConfigEntity {
+  @Id
+  @Column(name = "service_config_id")
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "service_config_id_generator")
+  private Long serviceConfigId;
+
+  @Basic
+  @Column(name = "cluster_id", insertable = false, updatable = false, nullable = false)
+  private Long clusterId;
+
+  @Basic
+  @Column(name = "service_name", nullable = false)
+  private String serviceName;
+
+  @Basic
+  @Column(name = "version", nullable = false)
+  private Long version;
+
+  @Basic
+  @Column(name = "create_timestamp", nullable = false)
+  private Long createTimestamp = System.currentTimeMillis();
+
+  @OneToMany(mappedBy = "serviceConfigEntity", cascade = CascadeType.ALL)
+  private List<ServiceConfigApplicationEntity> serviceConfigApplicationEntities;
+
+  @ManyToMany
+  @JoinTable(
+    name = "serviceconfigmapping",
+    joinColumns = {@JoinColumn(name = "service_config_id", referencedColumnName = "service_config_id")},
+    inverseJoinColumns = {@JoinColumn(name = "config_id", referencedColumnName = "config_id")}
+  )
+  private List<ClusterConfigEntity> clusterConfigEntities;
+
+  @ManyToOne
+  @JoinColumn(name = "cluster_id", referencedColumnName = "cluster_id", nullable = false)
+  private ClusterEntity clusterEntity;
+
+  public Long getServiceConfigId() {
+    return serviceConfigId;
+  }
+
+  public void setServiceConfigId(Long serviceConfigId) {
+    this.serviceConfigId = serviceConfigId;
+  }
+
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
+  public Long getCreateTimestamp() {
+    return createTimestamp;
+  }
+
+  public void setCreateTimestamp(Long create_timestamp) {
+    this.createTimestamp = create_timestamp;
+  }
+
+
+  public List<ServiceConfigApplicationEntity> getServiceConfigApplicationEntities() {
+    return serviceConfigApplicationEntities;
+  }
+
+  public void setServiceConfigApplicationEntities(List<ServiceConfigApplicationEntity> serviceConfigApplicationEntities) {
+    this.serviceConfigApplicationEntities = serviceConfigApplicationEntities;
+  }
+
+  public List<ClusterConfigEntity> getClusterConfigEntities() {
+    return clusterConfigEntities;
+  }
+
+  public void setClusterConfigEntities(List<ClusterConfigEntity> clusterConfigEntities) {
+    this.clusterConfigEntities = clusterConfigEntities;
+  }
+
+  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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 97290ff..1d74038 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -24,12 +24,11 @@ import java.util.Map;
 import java.util.concurrent.locks.ReadWriteLock;
 
 import com.google.common.collect.ListMultimap;
-import com.google.inject.persist.Transactional;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
-import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.scheduler.RequestExecution;
+import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 
 public interface Cluster {
 
@@ -91,7 +90,7 @@ public interface Cluster {
    * Set desired stack version
    * @param stackVersion
    */
-  public void setDesiredStackVersion(StackId stackVersion);
+  public void setDesiredStackVersion(StackId stackVersion) throws AmbariException;
 
   /**
    * Get current stack version
@@ -157,11 +156,33 @@ public interface Cluster {
    * Adds and sets a DESIRED configuration to be applied to a cluster.  There
    * can be only one selected config per type.
    * @param user the user making the change for audit purposes
-   * @param config  the {@link Config} object to set as desired
+   * @param config  the {@link org.apache.ambari.server.state.Config} object to set as desired
    * @return <code>true</code> if the config was added, or <code>false</code>
    * if the config is already set as the current
    */
-  public boolean addDesiredConfig(String user, Config config);
+  public ServiceConfigVersionResponse addDesiredConfig(String user, Config config);
+
+  /**
+   * Apply specified service config version (rollback)
+   * @param serviceName service name
+   * @param version service config version
+   * @param user the user making the change for audit purposes
+   * @return true if service config version applied
+   * @throws AmbariException
+   */
+  boolean setServiceConfigVersion(String serviceName, Long version, String user) throws AmbariException;
+
+  /**
+   * Get currently active service config versions for stack services
+   * @return
+   */
+  Map<String, ServiceConfigVersionResponse> getActiveServiceConfigVersions();
+
+  /**
+   * Get service config version history
+   * @return
+   */
+  List<ServiceConfigVersionResponse> getServiceConfigVersions();
 
   /**
    * Gets the desired (and selected) config by type.
@@ -298,6 +319,13 @@ public interface Cluster {
   public void deleteRequestExecution(Long id) throws AmbariException;
 
   /**
+   * Get next version of specified config type
+   * @param type config type
+   * @return next version of config
+   */
+  Long getNextConfigVersion(String type);
+
+  /**
    * Bulk handle service component host events
    *
    * @param eventMap serviceName - event mapping

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
index 194a3ad..bdfe1bd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
@@ -34,7 +34,13 @@ public interface Config {
   /**
    * @return Version Tag this config instance is mapped to
    */
-  public String getVersionTag();
+  public String getTag();
+
+  /**
+   *
+   * @return version of config by type
+   */
+  public Long getVersion();
 
   /**
    * @return Properties that define this config instance
@@ -50,7 +56,13 @@ public interface Config {
    * Change the version tag
    * @param versionTag
    */
-  public void setVersionTag(String versionTag);
+  public void setTag(String versionTag);
+
+  /**
+   * Set config version
+   * @param version
+   */
+  public void setVersion(Long version);
 
   /**
    * Replace properties with new provided set
@@ -71,6 +83,12 @@ public interface Config {
   public void updateProperties(Map<String, String> properties);
 
   /**
+   * Ger service config versions containing this config
+   * @return
+   */
+  List<Long> getServiceConfigVersions();
+
+  /**
    * Delete certain properties
    * @param properties Property keys to be deleted
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
index 3afc918..81c3c38 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
@@ -45,7 +45,6 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.ConfigurationRequest;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
-import org.apache.ambari.server.orm.entities.ClusterConfigEntityPK;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 /**
@@ -110,7 +109,7 @@ public class ConfigHelper {
     
     for (Entry<String, DesiredConfig> clusterEntry : clusterDesired.entrySet()) {
       String type = clusterEntry.getKey();
-      String tag = clusterEntry.getValue().getVersion();
+      String tag = clusterEntry.getValue().getTag();
 
       // 1) start with cluster config
       Config config = cluster.getConfig(type, tag);
@@ -120,7 +119,7 @@ public class ConfigHelper {
 
       Map<String, String> tags = new LinkedHashMap<String, String>();
 
-      tags.put(CLUSTER_DEFAULT_TAG, config.getVersionTag());
+      tags.put(CLUSTER_DEFAULT_TAG, config.getTag());
 
       // AMBARI-3672. Only consider Config groups for override tags
       // tags -> (configGroupId, versionTag)
@@ -374,12 +373,8 @@ public class ConfigHelper {
     Set<String> globalVersions = cluster.getConfigsByType(type).keySet();
     
     for(String version:globalVersions) {
-      ClusterConfigEntityPK clusterConfigEntityPK = new ClusterConfigEntityPK();
-      clusterConfigEntityPK.setClusterId(cluster.getClusterId());
-      clusterConfigEntityPK.setTag(version);
-      clusterConfigEntityPK.setType(type);
       ClusterConfigEntity clusterConfigEntity = clusterDAO.findConfig
-        (clusterConfigEntityPK);
+        (cluster.getClusterId(), type, version);
       
       clusterDAO.removeConfig(clusterConfigEntity);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index a7feb0b..1972739 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -18,12 +18,10 @@
 
 package org.apache.ambari.server.state;
 
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import org.apache.ambari.server.orm.dao.ClusterDAO;
+import org.apache.ambari.server.orm.dao.ServiceConfigDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 
@@ -33,12 +31,15 @@ import com.google.inject.Injector;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
 
 public class ConfigImpl implements Config {
+  public static final String GENERATED_TAG_PREFIX = "generatedTag_";
 
   private Cluster cluster;
   private String type;
-  private String versionTag;
+  private String tag;
+  private Long version;
   private Map<String, String> properties;
   private Map<String, Map<String, String>> propertiesAttributes;
   private ClusterConfigEntity entity;
@@ -47,6 +48,9 @@ public class ConfigImpl implements Config {
   private ClusterDAO clusterDAO;
   @Inject
   private Gson gson;
+  @Inject
+  private ServiceConfigDAO serviceConfigDAO;
+  
 
   @AssistedInject
   public ConfigImpl(@Assisted Cluster cluster, @Assisted String type, @Assisted Map<String, String> properties, 
@@ -63,7 +67,8 @@ public class ConfigImpl implements Config {
   public ConfigImpl(@Assisted Cluster cluster, @Assisted ClusterConfigEntity entity, Injector injector) {
     this.cluster = cluster;
     this.type = entity.getType();
-    this.versionTag = entity.getTag();
+    this.tag = entity.getTag();
+    this.version = entity.getVersion();
     this.entity = entity;
     injector.injectMembers(this);
   }
@@ -81,8 +86,13 @@ public class ConfigImpl implements Config {
   }
 
   @Override
-  public synchronized String getVersionTag() {
-    return versionTag;
+  public synchronized String getTag() {
+    return tag;
+  }
+
+  @Override
+  public synchronized Long getVersion() {
+    return version;
   }
 
   @Override
@@ -105,8 +115,13 @@ public class ConfigImpl implements Config {
   }
 
   @Override
-  public synchronized void setVersionTag(String versionTag) {
-    this.versionTag = versionTag;
+  public synchronized void setTag(String tag) {
+    this.tag = tag;
+  }
+
+  @Override
+  public synchronized void setVersion(Long version) {
+    this.version = version;
   }
 
   @Override
@@ -125,6 +140,14 @@ public class ConfigImpl implements Config {
   }
 
   @Override
+  public synchronized List<Long> getServiceConfigVersions() {
+    if (cluster == null || type == null || version == null) {
+      return Collections.emptyList();
+    }
+    return serviceConfigDAO.getServiceConfigVersionsByConfig(cluster.getClusterId(), type, version);
+  }
+
+  @Override
   public synchronized void deleteProperties(List<String> properties) {
     for (String key : properties) {
       this.properties.remove(key);
@@ -136,12 +159,20 @@ public class ConfigImpl implements Config {
   public synchronized void persist() {
     
     ClusterEntity clusterEntity = clusterDAO.findById(cluster.getClusterId());
+
+    if (this.version == null) {
+      this.version = cluster.getNextConfigVersion(type);
+    }
+    if (this.tag == null) {
+      tag = GENERATED_TAG_PREFIX + version;
+    }
     
     ClusterConfigEntity entity = new ClusterConfigEntity();
     entity.setClusterEntity(clusterEntity);
-    entity.setClusterId(Long.valueOf(cluster.getClusterId()));
+    entity.setClusterId(cluster.getClusterId());
     entity.setType(type);
-    entity.setTag(getVersionTag());
+    entity.setVersion(version);
+    entity.setTag(getTag());
     entity.setTimestamp(new Date().getTime());
     
     entity.setData(gson.toJson(getProperties()));

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigVersionHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigVersionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigVersionHelper.java
new file mode 100644
index 0000000..a7e9b5a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigVersionHelper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.state;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Utility class to manage config versions for cluster
+ */
+public class ConfigVersionHelper {
+
+  ConcurrentMap<String, AtomicLong> versionCounters = new ConcurrentHashMap<String, AtomicLong>();
+
+  public ConfigVersionHelper(Map<String, Long> configTypeLastVersions) {
+    for (Map.Entry<String, Long> entry : configTypeLastVersions.entrySet()) {
+      String type = entry.getKey();
+      Long version = entry.getValue();
+      versionCounters.put(type, new AtomicLong(version));
+    }
+  }
+
+  public long getNextVersion(String key) {
+    AtomicLong version = versionCounters.get(key);
+    if (version == null) {
+      version = new AtomicLong();
+      AtomicLong tmp = versionCounters.putIfAbsent(key, version);
+      if (tmp != null) {
+        version = tmp;
+      }
+    }
+    return version.incrementAndGet();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java
index 38ad201..0635284 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java
@@ -30,26 +30,27 @@ import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
  */
 public class DesiredConfig {
 
-  private String versionTag;
+  private String tag;
   private String serviceName;
   private String user;
+  private Long version;
   private List<HostOverride> hostOverrides = new ArrayList<HostOverride>();
 
   /**
-   * Sets the version tag
-   * @param version the version tag
+   * Sets the tag
+   * @param tag the tag
    */
-  public void setVersion(String version) {
-    versionTag = version;
+  public void setTag(String tag) {
+    this.tag = tag;
   }
 
   /**
-   * Gets the version tag
-   * @return the version tag
+   * Gets the tag
+   * @return the tag
    */
   @JsonProperty("tag")
-  public String getVersion() {
-    return versionTag;
+  public String getTag() {
+    return tag;
   }
 
   /**
@@ -104,10 +105,20 @@ public class DesiredConfig {
   public List<HostOverride> getHostOverrides() {
     return hostOverrides;
   }
-  
+
+  @JsonProperty("version")
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
   /**
    * Used to represent an override on a host.
    */
+  //TODO include changes for config versions
   public static class HostOverride {
     private String hostName;
     private String versionOverrideTag;
@@ -130,7 +141,7 @@ public class DesiredConfig {
     }
 
     /**
-     * @return the override version tag
+     * @return the override tag tag
      */
     @JsonProperty("tag")
     public String getVersionTag() {
@@ -143,7 +154,7 @@ public class DesiredConfig {
   public String toString() {
     StringBuilder sb = new StringBuilder();
     sb.append("{");
-    sb.append("version=").append(versionTag);
+    sb.append("tag=").append(tag);
     if (null != serviceName)
       sb.append(", service=").append(serviceName);
     if (null != hostOverrides && hostOverrides.size() > 0) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index d2f4871..99141a3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -36,17 +36,25 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import javax.persistence.RollbackException;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.ObjectNotFoundException;
+import org.apache.ambari.server.ParentObjectNotFoundException;
 import org.apache.ambari.server.ServiceComponentHostNotFoundException;
 import org.apache.ambari.server.ServiceNotFoundException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.ClusterResponse;
+import org.apache.ambari.server.controller.ConfigurationResponse;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
+import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.cache.ConfigGroupHostMapping;
 import org.apache.ambari.server.orm.cache.HostConfigMapping;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.dao.ClusterStateDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
 import org.apache.ambari.server.orm.dao.HostConfigMappingDAO;
+import org.apache.ambari.server.orm.dao.ServiceConfigDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
@@ -54,29 +62,16 @@ import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
 import org.apache.ambari.server.orm.entities.ClusterStateEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupEntity;
 import org.apache.ambari.server.orm.entities.RequestScheduleEntity;
-import org.apache.ambari.server.state.Alert;
-import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.ClusterHealthReport;
-import org.apache.ambari.server.state.Clusters;
-import org.apache.ambari.server.state.Config;
-import org.apache.ambari.server.state.ConfigFactory;
-import org.apache.ambari.server.state.ConfigHelper;
-import org.apache.ambari.server.state.DesiredConfig;
-import org.apache.ambari.server.state.Host;
-import org.apache.ambari.server.state.HostHealthStatus;
-import org.apache.ambari.server.state.MaintenanceState;
-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.ServiceComponentHostEvent;
-import org.apache.ambari.server.state.ServiceFactory;
-import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.orm.entities.ServiceConfigApplicationEntity;
+import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
+import org.apache.ambari.server.state.*;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.scheduler.RequestExecution;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
+import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
+import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -134,7 +129,9 @@ public class ClusterImpl implements Cluster {
 
   private ClusterEntity clusterEntity;
   
-  private Set<Alert> clusterAlerts = new HashSet<Alert>(); 
+  private Set<Alert> clusterAlerts = new HashSet<Alert>();
+
+  private final ConfigVersionHelper configVersionHelper;
 
   @Inject
   private ClusterDAO clusterDAO;
@@ -158,12 +155,18 @@ public class ClusterImpl implements Cluster {
   private ConfigHelper configHelper;
   @Inject
   private MaintenanceStateHelper maintenanceStateHelper;
+  @Inject
+  private AmbariMetaInfo ambariMetaInfo;
+  @Inject
+  private ServiceConfigDAO serviceConfigDAO;
 
   private volatile boolean svcHostsLoaded = false;
 
+  private volatile Multimap<String, String> serviceConfigTypes;
+
   @Inject
   public ClusterImpl(@Assisted ClusterEntity clusterEntity,
-                     Injector injector) {
+                     Injector injector) throws AmbariException {
     injector.injectMembers(this);
     this.clusterEntity = clusterEntity;
 
@@ -186,14 +189,59 @@ public class ClusterImpl implements Cluster {
         allConfigs.get(entity.getType()).put(entity.getTag(), config);
       }
     }
+
+    if (desiredStackVersion != null && !StringUtils.isEmpty(desiredStackVersion.getStackName()) && !
+      StringUtils.isEmpty(desiredStackVersion.getStackVersion())) {
+      loadServiceConfigTypes();
+    }
+
+    configVersionHelper = new ConfigVersionHelper(getConfigLastVersions());
   }
 
+
   @Override
   public ReadWriteLock getClusterGlobalLock() {
     return clusterGlobalLock;
   }
 
 
+  public void loadServiceConfigTypes() throws AmbariException {
+    try {
+      serviceConfigTypes = collectServiceConfigTypesMapping();
+    } catch (AmbariException e) {
+      LOG.error("Cannot load stack info:", e);
+      throw e;
+    }
+    LOG.info("Service config types loaded: {}", serviceConfigTypes);
+  }
+
+  /**
+   * Construct config type to service name mapping
+   * @throws AmbariException when stack or its part not found
+   */
+  private Multimap<String, String> collectServiceConfigTypesMapping() throws AmbariException {
+    Multimap<String, String> serviceConfigTypes = HashMultimap.create();
+
+    Map<String, ServiceInfo> serviceInfoMap = null;
+    try {
+      serviceInfoMap = ambariMetaInfo.getServices(desiredStackVersion.getStackName(), desiredStackVersion.getStackVersion());
+    } catch (ParentObjectNotFoundException e) {
+      LOG.error("Service config versioning disabled due to exception: ", e);
+      return serviceConfigTypes;
+    }
+    for (String serviceName : serviceInfoMap.keySet()) {
+      //collect config types for service
+      Set<PropertyInfo> properties = ambariMetaInfo.getProperties(desiredStackVersion.getStackName(), desiredStackVersion.getStackVersion(), serviceName);
+      for (PropertyInfo property : properties) {
+        int extIndex = property.getFilename().indexOf(AmbariMetaInfo.SERVICE_CONFIG_FILE_NAME_POSTFIX);
+        String configType = property.getFilename().substring(0, extIndex);
+        serviceConfigTypes.put(serviceName, configType);
+      }
+    }
+
+    return serviceConfigTypes;
+  }
+
   /**
    * Make sure we load all the service host components.
    * We need this for live status checks.
@@ -888,7 +936,7 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
-  public void setDesiredStackVersion(StackId stackVersion) {
+  public void setDesiredStackVersion(StackId stackVersion) throws AmbariException {
     clusterGlobalLock.readLock().lock();
     try {
       readWriteLock.writeLock().lock();
@@ -903,6 +951,7 @@ public class ClusterImpl implements Cluster {
         this.desiredStackVersion = stackVersion;
         clusterEntity.setDesiredStackVersion(gson.toJson(stackVersion));
         clusterDAO.merge(clusterEntity);
+        loadServiceConfigTypes();
       } finally {
         readWriteLock.writeLock().unlock();
       }
@@ -1053,16 +1102,14 @@ public class ClusterImpl implements Cluster {
       readWriteLock.writeLock().lock();
       try {
         if (config.getType() == null
-          || config.getType().isEmpty()
-          || config.getVersionTag() == null
-          || config.getVersionTag().isEmpty()) {
-          // TODO throw error
+          || config.getType().isEmpty()) {
+          throw new IllegalArgumentException("Config type cannot be empty");
         }
         if (!allConfigs.containsKey(config.getType())) {
           allConfigs.put(config.getType(), new HashMap<String, Config>());
         }
 
-        allConfigs.get(config.getType()).put(config.getVersionTag(), config);
+        allConfigs.get(config.getType()).put(config.getTag(), config);
       } finally {
         readWriteLock.writeLock().unlock();
       }
@@ -1277,8 +1324,7 @@ public class ClusterImpl implements Cluster {
   }
 
   @Override
-  @Transactional
-  public boolean addDesiredConfig(String user, Config config) {
+  public ServiceConfigVersionResponse addDesiredConfig(String user, Config config) {
     if (null == user)
       throw new NullPointerException("User must be specified.");
 
@@ -1289,39 +1335,21 @@ public class ClusterImpl implements Cluster {
         Config currentDesired = getDesiredConfigByType(config.getType());
 
         // do not set if it is already the current
-        if (null != currentDesired && currentDesired.getVersionTag().equals(config.getVersionTag())) {
-          return false;
-        }
-
-        Collection<ClusterConfigMappingEntity> entities = clusterEntity.getConfigMappingEntities();
-
-        for (ClusterConfigMappingEntity e : entities) {
-          if (e.isSelected() > 0 && e.getType().equals(config.getType())) {
-            e.setSelected(0);
-          }
+        if (null != currentDesired && currentDesired.getTag().equals(config.getTag())) {
+          return null;
         }
 
-        ClusterConfigMappingEntity entity = new ClusterConfigMappingEntity();
-        entity.setClusterEntity(clusterEntity);
-        entity.setClusterId(clusterEntity.getClusterId());
-        entity.setCreateTimestamp(Long.valueOf(System.currentTimeMillis()));
-        entity.setSelected(1);
-        entity.setUser(user);
-        entity.setType(config.getType());
-        entity.setVersion(config.getVersionTag());
-        entities.add(entity);
+        ServiceConfigVersionResponse serviceConfigVersionResponse =
+            applyConfig(config.getType(), config.getTag(), user);
 
-        clusterDAO.merge(clusterEntity);
         configHelper.invalidateStaleConfigsCache();
-        return true;
+        return serviceConfigVersionResponse;
       } finally {
         readWriteLock.writeLock().unlock();
       }
     } finally {
       clusterGlobalLock.readLock().unlock();
     }
-
-
   }
 
   @Override
@@ -1337,8 +1365,9 @@ public class ClusterImpl implements Cluster {
           if (e.isSelected() > 0) {
             DesiredConfig c = new DesiredConfig();
             c.setServiceName(null);
-            c.setVersion(e.getVersion());
+            c.setTag(e.getTag());
             c.setUser(e.getUser());
+            c.setVersion(allConfigs.get(e.getType()).get(e.getTag()).getVersion());
 
             map.put(e.getType(), c);
             types.add(e.getType());
@@ -1366,8 +1395,233 @@ public class ClusterImpl implements Cluster {
     } finally {
       clusterGlobalLock.readLock().unlock();
     }
+  }
+
+  @Override
+  public boolean setServiceConfigVersion(String serviceName, Long version, String user) throws AmbariException {
+    if (null == user)
+      throw new NullPointerException("User must be specified.");
+
+    clusterGlobalLock.writeLock().lock();
+    try {
+      readWriteLock.writeLock().lock();
+      try {
+        applyServiceConfigVersion(serviceName, version, user);
+
+        return true;
+      } finally {
+        readWriteLock.writeLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  @Override
+  public Map<String, ServiceConfigVersionResponse> getActiveServiceConfigVersions() {
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.readLock().lock();
+      try {
+        Map<String, ServiceConfigVersionResponse> result = new HashMap<String, ServiceConfigVersionResponse>();
+        for (String serviceName : serviceConfigTypes.keySet()) {
+          ServiceConfigVersionResponse activeServiceConfigVersion = getActiveServiceConfigVersion(serviceName);
+          if (activeServiceConfigVersion != null) {
+            result.put(serviceName, activeServiceConfigVersion);
+          }
+        }
+        return result;
+      } finally {
+        readWriteLock.readLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  @Override
+  @RequiresSession
+  public List<ServiceConfigVersionResponse> getServiceConfigVersions() {
+    clusterGlobalLock.readLock().lock();
+    try {
+      readWriteLock.readLock().lock();
+      try {
+        List<ServiceConfigVersionResponse> serviceConfigVersionResponses = new ArrayList<ServiceConfigVersionResponse>();
+        for (ServiceConfigApplicationEntity applicationEntity : serviceConfigDAO.getServiceConfigApplications(getClusterId())) {
+          ServiceConfigVersionResponse serviceConfigVersionResponse = new ServiceConfigVersionResponse();
+
+          ServiceConfigEntity serviceConfigEntity = applicationEntity.getServiceConfigEntity();
+
+
+          serviceConfigVersionResponse.setClusterName(getClusterName());
+          serviceConfigVersionResponse.setServiceName(serviceConfigEntity.getServiceName());
+          serviceConfigVersionResponse.setVersion(serviceConfigEntity.getVersion());
+          serviceConfigVersionResponse.setCreateTime(serviceConfigEntity.getCreateTimestamp());
+          serviceConfigVersionResponse.setApplyTime(applicationEntity.getApplyTimestamp());
+          serviceConfigVersionResponse.setUserName(applicationEntity.getUser());
+          serviceConfigVersionResponse.setConfigurations(new ArrayList<ConfigurationResponse>());
+
+          List<ClusterConfigEntity> clusterConfigEntities = serviceConfigEntity.getClusterConfigEntities();
+          for (ClusterConfigEntity clusterConfigEntity : clusterConfigEntities) {
+            Config config = allConfigs.get(clusterConfigEntity.getType()).get(clusterConfigEntity.getTag());
+            serviceConfigVersionResponse.getConfigurations().add(new ConfigurationResponse(getClusterName(),
+                config.getType(), config.getTag(), config.getVersion(), config.getProperties(),
+                config.getPropertiesAttributes()));
+          }
+
+          serviceConfigVersionResponses.add(serviceConfigVersionResponse);
+        }
+
+        return serviceConfigVersionResponses;
+      } finally {
+        readWriteLock.readLock().unlock();
+      }
+    } finally {
+      clusterGlobalLock.readLock().unlock();
+    }
+  }
+
+  @RequiresSession
+  ServiceConfigVersionResponse getActiveServiceConfigVersion(String serviceName) {
+    ServiceConfigApplicationEntity lastApplication = serviceConfigDAO.getLastApplication(getClusterId(), serviceName);
+    if (lastApplication == null) {
+      LOG.warn("No active service config version found for service {}", serviceName);
+      return null;
+    }
+    ServiceConfigVersionResponse serviceConfigVersionResponse = new ServiceConfigVersionResponse();
 
+    ServiceConfigEntity serviceConfigEntity = lastApplication.getServiceConfigEntity();
 
+    serviceConfigVersionResponse.setClusterName(getClusterName());
+    serviceConfigVersionResponse.setServiceName(serviceConfigEntity.getServiceName());
+    serviceConfigVersionResponse.setVersion(serviceConfigEntity.getVersion());
+    serviceConfigVersionResponse.setCreateTime(serviceConfigEntity.getCreateTimestamp());
+    serviceConfigVersionResponse.setApplyTime(lastApplication.getApplyTimestamp());
+    serviceConfigVersionResponse.setUserName(lastApplication.getUser());
+    return serviceConfigVersionResponse;
+  }
+
+  @Transactional
+  void applyServiceConfigVersion(String serviceName, Long serviceConfigVersion, String user) throws AmbariException {
+    ServiceConfigEntity serviceConfigEntity = serviceConfigDAO.findByServiceAndVersion(serviceName, serviceConfigVersion);
+    if (serviceConfigEntity == null) {
+      throw new ObjectNotFoundException("Service config version with serviceName={} and version={} not found");
+    }
+
+    //disable all configs related to service
+    Collection<String> configTypes = serviceConfigTypes.get(serviceName);
+    for (ClusterConfigMappingEntity entity : clusterEntity.getConfigMappingEntities()) {
+      if (configTypes.contains(entity.getType()) && entity.isSelected() > 0) {
+        entity.setSelected(0);
+      }
+    }
+    clusterDAO.merge(clusterEntity);
+
+    for (ClusterConfigEntity configEntity : serviceConfigEntity.getClusterConfigEntities()) {
+      selectConfig(configEntity.getType(), configEntity.getTag(), user);
+    }
+
+    ServiceConfigApplicationEntity applicationEntity = new ServiceConfigApplicationEntity();
+    applicationEntity.setApplyTimestamp(System.currentTimeMillis());
+    applicationEntity.setServiceConfigEntity(serviceConfigEntity);
+    applicationEntity.setUser(user);
+
+    serviceConfigEntity.getServiceConfigApplicationEntities().add(applicationEntity);
+
+    serviceConfigDAO.merge(serviceConfigEntity);
+  }
+
+  @Transactional
+  void selectConfig(String type, String tag, String user) {
+    Collection<ClusterConfigMappingEntity> entities = clusterEntity.getConfigMappingEntities();
+
+    //disable previous config
+    for (ClusterConfigMappingEntity e : entities) {
+      if (e.isSelected() > 0 && e.getType().equals(type)) {
+        e.setSelected(0);
+      }
+    }
+
+    ClusterConfigMappingEntity entity = new ClusterConfigMappingEntity();
+    entity.setClusterEntity(clusterEntity);
+    entity.setClusterId(clusterEntity.getClusterId());
+    entity.setCreateTimestamp(System.currentTimeMillis());
+    entity.setSelected(1);
+    entity.setUser(user);
+    entity.setType(type);
+    entity.setTag(tag);
+    entities.add(entity);
+
+    clusterDAO.merge(clusterEntity);
+
+  }
+
+  @Transactional
+  ServiceConfigVersionResponse applyConfig(String type, String tag, String user) {
+
+    selectConfig(type, tag, user);
+
+    String serviceName = null;
+    //find service name for config type
+    for (Entry<String, String> entry : serviceConfigTypes.entries()) {
+      if (StringUtils.equals(entry.getValue(), type)) {
+        serviceName = entry.getKey();
+        break;
+      }
+    }
+
+    if (serviceName == null) {
+      LOG.error("No service found for config type '{}', service config version not created");
+      return null;
+    } else {
+      return createServiceConfigVersion(serviceName, user);
+    }
+
+  }
+
+  private ServiceConfigVersionResponse createServiceConfigVersion(String serviceName, String user) {
+    //create next service config version
+    ServiceConfigEntity serviceConfigEntity = new ServiceConfigEntity();
+    serviceConfigEntity.setServiceName(serviceName);
+    serviceConfigEntity.setClusterEntity(clusterEntity);
+    serviceConfigEntity.setVersion(configVersionHelper.getNextVersion(serviceName));
+
+    //set first default application
+    serviceConfigEntity.setServiceConfigApplicationEntities(new ArrayList<ServiceConfigApplicationEntity>());
+    ServiceConfigApplicationEntity serviceConfigApplicationEntity = new ServiceConfigApplicationEntity();
+    serviceConfigApplicationEntity.setApplyTimestamp(serviceConfigEntity.getCreateTimestamp());
+    serviceConfigApplicationEntity.setServiceConfigEntity(serviceConfigEntity);
+    serviceConfigApplicationEntity.setUser(user);
+    serviceConfigEntity.getServiceConfigApplicationEntities().add(serviceConfigApplicationEntity);
+
+    List<ClusterConfigEntity> configEntities = new ArrayList<ClusterConfigEntity>();
+    serviceConfigEntity.setClusterConfigEntities(configEntities);
+
+    //add configs from this service
+    Collection<String> configTypes = serviceConfigTypes.get(serviceName);
+    for (ClusterConfigMappingEntity mappingEntity : clusterEntity.getConfigMappingEntities()) {
+      if (mappingEntity.isSelected() > 0 && configTypes.contains(mappingEntity.getType())) {
+        ClusterConfigEntity configEntity =
+          clusterDAO.findConfig(getClusterId(), mappingEntity.getType(), mappingEntity.getTag());
+        if (configEntity != null) {
+          configEntities.add(configEntity);
+        } else {
+          LOG.error("Desired cluster config type={}, tag={} is not present in database," +
+            " unable to add to service config version");
+        }
+      }
+    }
+
+    serviceConfigDAO.create(serviceConfigEntity);
+
+    ServiceConfigVersionResponse response = new ServiceConfigVersionResponse();
+    response.setUserName(user);
+    response.setClusterName(getClusterName());
+    response.setVersion(serviceConfigEntity.getVersion());
+    response.setServiceName(serviceConfigEntity.getServiceName());
+    response.setCreateTime(serviceConfigEntity.getCreateTimestamp());
+    response.setApplyTime(serviceConfigApplicationEntity.getApplyTimestamp());
+    return response;
   }
 
   @Override
@@ -1378,7 +1632,7 @@ public class ClusterImpl implements Cluster {
       try {
         for (ClusterConfigMappingEntity e : clusterEntity.getConfigMappingEntities()) {
           if (e.isSelected() > 0 && e.getType().equals(configType)) {
-            return getConfig(e.getType(), e.getVersion());
+            return getConfig(e.getType(), e.getTag());
           }
         }
 
@@ -1410,7 +1664,7 @@ public class ClusterImpl implements Cluster {
 
     for (HostConfigMapping mappingEntity : mappingEntities) {
       DesiredConfig desiredConfig = new DesiredConfig();
-      desiredConfig.setVersion(mappingEntity.getVersion());
+      desiredConfig.setTag(mappingEntity.getVersion());
       desiredConfig.setServiceName(mappingEntity.getServiceName());
       desiredConfig.setUser(mappingEntity.getUser());
 
@@ -1433,6 +1687,32 @@ public class ClusterImpl implements Cluster {
     return getHostsDesiredConfigs(hostnames);
   }
 
+  @Override
+  public Long getNextConfigVersion(String type) {
+    return configVersionHelper.getNextVersion(type);
+  }
+
+  private Map<String, Long> getConfigLastVersions() {
+    Map<String, Long> maxVersions = new HashMap<String, Long>();
+    //config versions
+    for (Entry<String, Map<String, Config>> mapEntry : allConfigs.entrySet()) {
+      String type = mapEntry.getKey();
+      Long lastVersion = 0L;
+      for (Entry<String, Config> configEntry : mapEntry.getValue().entrySet()) {
+        Long version = configEntry.getValue().getVersion();
+        if (version > lastVersion) {
+          lastVersion = version;
+        }
+      }
+      maxVersions.put(type, lastVersion);
+    }
+
+    //service config versions
+    maxVersions.putAll(serviceConfigDAO.findMaxVersions(getClusterId()));
+
+    return maxVersions;
+  }
+
   @Transactional
   @Override
   public List<ServiceComponentHostEvent> processServiceComponentHostEvents(ListMultimap<String, ServiceComponentHostEvent> eventMap) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
index f858264..023d530 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
@@ -615,6 +615,8 @@ public class ClustersImpl implements Clusters {
 
       hostClusterMap.get(hostname).remove(cluster);
       clusterHostMap.get(clusterName).remove(host);
+      host.refresh();
+      cluster.refresh();
 
       deleteConfigGroupHostMapping(hostname);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
index 9a3e85b..fa06c07 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
@@ -26,16 +26,13 @@ import com.google.inject.persist.Transactional;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.DuplicateResourceException;
 import org.apache.ambari.server.controller.ConfigGroupResponse;
-import org.apache.ambari.server.controller.internal.ConfigGroupResourceProvider;
 import org.apache.ambari.server.controller.internal.ConfigurationResourceProvider;
-import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.orm.dao.ClusterDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupConfigMappingDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupDAO;
 import org.apache.ambari.server.orm.dao.ConfigGroupHostMappingDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
-import org.apache.ambari.server.orm.entities.ClusterConfigEntityPK;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupEntity;
@@ -369,12 +366,8 @@ public class ConfigGroupImpl implements ConfigGroup {
 
     if (configurations != null && !configurations.isEmpty()) {
       for (Config config : configurations.values()) {
-        ClusterConfigEntityPK clusterConfigEntityPK = new ClusterConfigEntityPK();
-        clusterConfigEntityPK.setClusterId(cluster.getClusterId());
-        clusterConfigEntityPK.setTag(config.getVersionTag());
-        clusterConfigEntityPK.setType(config.getType());
         ClusterConfigEntity clusterConfigEntity = clusterDAO.findConfig
-          (clusterConfigEntityPK);
+          (cluster.getClusterId(), config.getType(), config.getTag());
 
         if (clusterConfigEntity == null) {
           // Create configuration
@@ -382,13 +375,16 @@ public class ConfigGroupImpl implements ConfigGroup {
           clusterConfigEntity.setClusterId(clusterEntity.getClusterId());
           clusterConfigEntity.setClusterEntity(clusterEntity);
           clusterConfigEntity.setType(config.getType());
-          clusterConfigEntity.setTag(config.getVersionTag());
+          clusterConfigEntity.setTag(config.getTag());
+          clusterConfigEntity.setVersion(cluster.getNextConfigVersion(config.getType()));
           clusterConfigEntity.setData(gson.toJson(config.getProperties()));
           if (null != config.getPropertiesAttributes()) {
             clusterConfigEntity.setAttributes(gson.toJson(config.getPropertiesAttributes()));
           }
           clusterConfigEntity.setTimestamp(System.currentTimeMillis());
 
+          //TODO why not use config.persist() here?
+
           // TODO: Is locking necessary and functional ?
           cluster.getClusterGlobalLock().writeLock().lock();
           try {
@@ -471,10 +467,10 @@ public class ConfigGroupImpl implements ConfigGroup {
     try {
       if (configurations != null && !configurations.isEmpty()) {
         for (Config c : configurations.values()) {
-          if (c.getType().equals(config.getType()) && c.getVersionTag().equals
-            (config.getVersionTag())) {
+          if (c.getType().equals(config.getType()) && c.getTag().equals
+            (config.getTag())) {
             throw new DuplicateResourceException("Config " + config.getType() +
-              " with tag " + config.getVersionTag() + " is already associated " +
+              " with tag " + config.getTag() + " is already associated " +
               "with Config Group " + configGroupEntity.getGroupName());
           }
         }
@@ -504,7 +500,7 @@ public class ConfigGroupImpl implements ConfigGroup {
         configMap.put(ConfigurationResourceProvider
           .CONFIGURATION_CONFIG_TYPE_PROPERTY_ID, config.getType());
         configMap.put(ConfigurationResourceProvider
-          .CONFIGURATION_CONFIG_TAG_PROPERTY_ID, config.getVersionTag());
+          .CONFIGURATION_CONFIG_TAG_PROPERTY_ID, config.getTag());
         configObjMap.add(configMap);
       }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
index c1aa492..619859c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
@@ -1150,7 +1150,7 @@ public class HostImpl implements Host {
       throw new NullPointerException("User must be specified.");
     
     HostConfigMapping exist = getDesiredConfigEntity(clusterId, config.getType());
-    if (null != exist && exist.getVersion().equals(config.getVersionTag())) {
+    if (null != exist && exist.getVersion().equals(config.getTag())) {
       if (!selected) {
         exist.setSelected(0);
         hostConfigMappingDAO.merge(exist);
@@ -1175,7 +1175,7 @@ public class HostImpl implements Host {
       hostConfigMapping.setSelected(1);
       hostConfigMapping.setUser(user);
       hostConfigMapping.setType(config.getType());
-      hostConfigMapping.setVersion(config.getVersionTag());
+      hostConfigMapping.setVersion(config.getTag());
       
       hostConfigMappingDAO.create(hostConfigMapping);
     }
@@ -1196,7 +1196,7 @@ public class HostImpl implements Host {
         clusterId, hostEntity.getHostName())) {
       
       DesiredConfig dc = new DesiredConfig();
-      dc.setVersion(e.getVersion());
+      dc.setTag(e.getVersion());
       dc.setServiceName(e.getServiceName());
       dc.setUser(e.getUser());
       map.put(e.getType(), dc);
@@ -1217,7 +1217,7 @@ public class HostImpl implements Host {
     for (Map.Entry<String, DesiredConfig> desiredConfigEntry :
         cluster.getDesiredConfigs().entrySet()) {
       HostConfig hostConfig = new HostConfig();
-      hostConfig.setDefaultVersionTag(desiredConfigEntry.getValue().getVersion());
+      hostConfig.setDefaultVersionTag(desiredConfigEntry.getValue().getTag());
       hostConfigMap.put(desiredConfigEntry.getKey(), hostConfig);
     }
 
@@ -1235,11 +1235,11 @@ public class HostImpl implements Host {
             hostConfig = new HostConfig();
             hostConfigMap.put(configType, hostConfig);
             hostConfig.setDefaultVersionTag(cluster.getDesiredConfigByType
-              (configType).getVersionTag());
+              (configType).getTag());
           }
           Config config = configEntry.getValue();
           hostConfig.getConfigGroupOverrides().put(configGroup.getId(),
-            config.getVersionTag());
+            config.getTag());
         }
       }
     }
@@ -1306,4 +1306,4 @@ public class HostImpl implements Host {
   
 }
 
-  
\ No newline at end of file
+  

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
index 1178bab..e697fbd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
@@ -231,12 +231,12 @@ public abstract class AbstractUpgradeCatalog implements UpgradeCatalog {
               if (baseConfig != null) {
                 String authName = "ambari-upgrade";
 
-                if (cluster.addDesiredConfig(authName, baseConfig)) {
-                  String oldConfigString = (oldConfig != null) ? " from='" + oldConfig.getVersionTag() + "'" : "";
+                if (cluster.addDesiredConfig(authName, baseConfig) != null) {
+                  String oldConfigString = (oldConfig != null) ? " from='" + oldConfig.getTag() + "'" : "";
                   LOG.info("cluster '" + cluster.getClusterName() + "' "
                     + "changed by: '" + authName + "'; "
                     + "type='" + baseConfig.getType() + "' "
-                    + "tag='" + baseConfig.getVersionTag() + "'"
+                    + "tag='" + baseConfig.getTag() + "'"
                     + oldConfigString);
                 }
               }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
index c7de486..451cd6d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
@@ -36,7 +36,6 @@ import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.orm.dao.KeyValueDAO;
 import org.apache.ambari.server.orm.dao.ServiceComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
-import org.apache.ambari.server.orm.entities.ClusterConfigEntityPK;
 import org.apache.ambari.server.orm.entities.ClusterConfigMappingEntity;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ClusterServiceEntity;
@@ -694,11 +693,7 @@ public class UpgradeCatalog150 extends AbstractUpgradeCatalog {
         if (configTypes != null) {
           for (String configType : configTypes) {
             if (configType.contains(log4jConfigTypeContains)) {
-              ClusterConfigEntityPK configEntityPK = new ClusterConfigEntityPK();
-              configEntityPK.setClusterId(clusterId);
-              configEntityPK.setType(configType);
-              configEntityPK.setTag(defaultVersionTag);
-              ClusterConfigEntity configEntity = clusterDAO.findConfig(configEntityPK);
+              ClusterConfigEntity configEntity = clusterDAO.findConfig(clusterId, configType, defaultVersionTag);
               
               if (configEntity == null) {
                 String filename = configType + ".xml";
@@ -732,7 +727,7 @@ public class UpgradeCatalog150 extends AbstractUpgradeCatalog {
                     Long.valueOf(System.currentTimeMillis()));
                   clusterConfigMappingEntity.setSelected(1);
                   clusterConfigMappingEntity.setUser(defaultUser);
-                  clusterConfigMappingEntity.setVersion(configEntity.getTag());
+                  clusterConfigMappingEntity.setTag(configEntity.getTag());
                   entities.add(clusterConfigMappingEntity);
                   clusterDAO.merge(clusterEntity);
                 }
@@ -762,11 +757,7 @@ public class UpgradeCatalog150 extends AbstractUpgradeCatalog {
         List<ClusterEntity> clusterEntities = clusterDAO.findAll();
         for (ClusterEntity clusterEntity : clusterEntities) {
           Long clusterId = clusterEntity.getClusterId();
-          ClusterConfigEntityPK configEntityPK = new ClusterConfigEntityPK();
-          configEntityPK.setClusterId(clusterId);
-          configEntityPK.setType("hdfs-exclude-file");
-          configEntityPK.setTag(value.trim());
-          ClusterConfigEntity configEntity = clusterDAO.findConfig(configEntityPK);
+          ClusterConfigEntity configEntity = clusterDAO.findConfig(clusterId, "hdfs-exclude-file", value.trim());
           if (configEntity != null) {
             String configData = configEntity.getData();
             if (configData != null) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/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 cf51a49..3777b75 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -27,7 +27,10 @@ delimiter ;
 # USE @schema;
 
 CREATE TABLE clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
-CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data LONGTEXT NOT NULL, config_attributes LONGTEXT, create_timestamp BIGINT NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
+CREATE TABLE clusterconfig (config_id BIGINT NOT NULL, version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data LONGTEXT NOT NULL, config_attributes LONGTEXT, create_timestamp BIGINT NOT NULL, PRIMARY KEY (config_id));
+CREATE TABLE serviceconfig (service_config_id BIGINT NOT NULL, cluster_id BIGINT NOT NULL, service_name VARCHAR(255) NOT NULL, version BIGINT NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (service_config_id));
+CREATE TABLE serviceconfigmapping (service_config_id BIGINT NOT NULL, config_id BIGINT NOT NULL, PRIMARY KEY(service_config_id, config_id));
+CREATE TABLE serviceconfigapplication (apply_id BIGINT NOT NULL, service_config_id BIGINT NOT NULL, apply_timestamp BIGINT NOT NULL, user_name VARCHAR(255) NOT NULL DEFAULT '_db',  PRIMARY KEY(apply_id));
 CREATE TABLE clusterservices (service_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, service_enabled INTEGER NOT NULL, PRIMARY KEY (service_name, cluster_id));
 CREATE TABLE clusterstate (cluster_id BIGINT NOT NULL, current_cluster_state VARCHAR(255) NOT NULL, current_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 CREATE TABLE hostcomponentdesiredstate (cluster_id BIGINT NOT NULL, component_name VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, desired_state VARCHAR(255) NOT NULL, host_name VARCHAR(255) NOT NULL, service_name VARCHAR(255) NOT NULL, admin_state VARCHAR(32), maintenance_state VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', restart_required TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (cluster_id, component_name, host_name, service_name));
@@ -137,6 +140,12 @@ ALTER TABLE viewinstance ADD CONSTRAINT FK_viewinstance_resource_id FOREIGN KEY
 ALTER TABLE adminprivilege ADD CONSTRAINT FK_privilege_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE users ADD CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE groups ADD CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_tag UNIQUE (cluster_id, type_name, version_tag);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_version UNIQUE (cluster_id, type_name, version);
+ALTER TABLE serviceconfig ADD CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_name, version);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_config FOREIGN KEY (config_id) REFERENCES clusterconfig(config_id);
+ALTER TABLE serviceconfigapplication ADD CONSTRAINT FK_scva_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
 
 
 INSERT INTO ambari_sequences(sequence_name, value) values ('cluster_id_seq', 1);
@@ -156,6 +165,9 @@ INSERT INTO ambari_sequences(sequence_name, value) values ('principal_type_id_se
 INSERT INTO ambari_sequences(sequence_name, value) values ('principal_id_seq', 2);
 INSERT INTO ambari_sequences(sequence_name, value) values ('permission_id_seq', 5);
 INSERT INTO ambari_sequences(sequence_name, value) values ('privilege_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('config_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('service_config_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('service_config_application_id_seq', 1);
 
 insert into adminresourcetype (resource_type_id, resource_type_name)
   select 1, 'AMBARI'

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/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 c05affd..1db44bc 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -17,7 +17,10 @@
 --
 
 CREATE TABLE clusters (cluster_id NUMBER(19) NOT NULL, cluster_info VARCHAR2(255) NULL, cluster_name VARCHAR2(100) NOT NULL UNIQUE, provisioning_state VARCHAR2(255) DEFAULT 'INIT' NOT NULL, desired_cluster_state VARCHAR2(255) NULL, desired_stack_version VARCHAR2(255) NULL, PRIMARY KEY (cluster_id));
-CREATE TABLE clusterconfig (version_tag VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, config_data CLOB NOT NULL, config_attributes CLOB, create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
+CREATE TABLE clusterconfig (config_id NUMBER(19) NOT NULL, version_tag VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, config_data CLOB NOT NULL, config_attributes CLOB, create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY (config_id));
+CREATE TABLE serviceconfig (service_config_id NUMBER(19) NOT NULL, cluster_id NUMBER(19) NOT NULL, service_name VARCHAR(255) NOT NULL, version NUMBER(19) NOT NULL, create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY (service_config_id));
+CREATE TABLE serviceconfigmapping (service_config_id BIGINT NOT NULL, config_id BIGINT NOT NULL, PRIMARY KEY(service_config_id, config_id));
+CREATE TABLE serviceconfigapplication (apply_id NUMBER(19) NOT NULL, service_config_id NUMBER(19) NOT NULL, apply_timestamp NUMBER(19) NOT NULL, user_name VARCHAR(255) NOT NULL DEFAULT '_db',  PRIMARY KEY(apply_id));
 CREATE TABLE clusterservices (service_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, service_enabled NUMBER(10) NOT NULL, PRIMARY KEY (service_name, cluster_id));
 CREATE TABLE clusterstate (cluster_id NUMBER(19) NOT NULL, current_cluster_state VARCHAR2(255) NULL, current_stack_version VARCHAR2(255) NULL, PRIMARY KEY (cluster_id));
 CREATE TABLE hostcomponentdesiredstate (cluster_id NUMBER(19) NOT NULL, component_name VARCHAR2(255) NOT NULL, desired_stack_version VARCHAR2(255) NULL, desired_state VARCHAR2(255) NOT NULL, host_name VARCHAR2(255) NOT NULL, service_name VARCHAR2(255) NOT NULL, admin_state VARCHAR2(32) NULL, maintenance_state VARCHAR2(32) NOT NULL, restart_required NUMBER(1) DEFAULT 0 NOT NULL, PRIMARY KEY (cluster_id, component_name, host_name, service_name));
@@ -127,6 +130,12 @@ ALTER TABLE viewinstance ADD CONSTRAINT FK_viewinstance_resource_id FOREIGN KEY
 ALTER TABLE adminprivilege ADD CONSTRAINT FK_privilege_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE users ADD CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE groups ADD CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_tag UNIQUE (cluster_id, type_name, version_tag);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_version UNIQUE (cluster_id, type_name, version);
+ALTER TABLE serviceconfig ADD CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_name, version);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_config FOREIGN KEY (config_id) REFERENCES clusterconfig(config_id);
+ALTER TABLE serviceconfigapplication ADD CONSTRAINT FK_scva_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
 
 INSERT INTO ambari_sequences(sequence_name, value) values ('host_role_command_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, value) values ('user_id_seq', 1);
@@ -145,6 +154,9 @@ INSERT INTO ambari_sequences(sequence_name, value) values ('principal_type_id_se
 INSERT INTO ambari_sequences(sequence_name, value) values ('principal_id_seq', 2);
 INSERT INTO ambari_sequences(sequence_name, value) values ('permission_id_seq', 5);
 INSERT INTO ambari_sequences(sequence_name, value) values ('privilege_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('config_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('service_config_id_seq', 1);
+INSERT INTO ambari_sequences(sequence_name, value) values ('service_config_application_id_seq', 1);
 
 INSERT INTO metainfo("metainfo_key", "metainfo_value") values ('version', '${ambariVersion}');
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/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 82335e3..1c5629e 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -19,10 +19,16 @@
 ------create tables ang grant privileges to db user---------
 CREATE TABLE clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 
-CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000), create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
+CREATE TABLE clusterconfig (config_id BIGINT NOT NULL, version_tag VARCHAR(255) NOT NULL, version BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000), create_timestamp BIGINT NOT NULL, PRIMARY KEY (config_id));
 
 CREATE TABLE clusterconfigmapping (cluster_id BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, version_tag VARCHAR(255) NOT NULL, create_timestamp BIGINT NOT NULL, selected INTEGER NOT NULL DEFAULT 0, user_name VARCHAR(255) NOT NULL DEFAULT '_db', PRIMARY KEY (cluster_id, type_name, create_timestamp));
 
+CREATE TABLE serviceconfig (service_config_id BIGINT NOT NULL, cluster_id BIGINT NOT NULL, service_name VARCHAR(255) NOT NULL, version BIGINT NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (service_config_id));
+
+CREATE TABLE serviceconfigmapping (service_config_id BIGINT NOT NULL, config_id BIGINT NOT NULL, PRIMARY KEY(service_config_id, config_id));
+
+CREATE TABLE serviceconfigapplication (apply_id BIGINT NOT NULL, service_config_id BIGINT NOT NULL, apply_timestamp BIGINT NOT NULL, user_name VARCHAR(255) NOT NULL DEFAULT '_db',  PRIMARY KEY(apply_id));
+
 CREATE TABLE clusterservices (service_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, service_enabled INTEGER NOT NULL, PRIMARY KEY (service_name, cluster_id));
 
 CREATE TABLE clusterstate (cluster_id BIGINT NOT NULL, current_cluster_state VARCHAR(255) NOT NULL, current_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
@@ -160,6 +166,12 @@ ALTER TABLE viewinstance ADD CONSTRAINT FK_viewinstance_resource_id FOREIGN KEY
 ALTER TABLE adminprivilege ADD CONSTRAINT FK_privilege_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE users ADD CONSTRAINT FK_users_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
 ALTER TABLE groups ADD CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_tag UNIQUE (cluster_id, type_name, version_tag);
+ALTER TABLE clusterconfig ADD CONSTRAINT UQ_config_type_version UNIQUE (cluster_id, type_name, version);
+ALTER TABLE serviceconfig ADD CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_name, version);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
+ALTER TABLE serviceconfigmapping ADD CONSTRAINT FK_scvm_config FOREIGN KEY (config_id) REFERENCES clusterconfig(config_id);
+ALTER TABLE serviceconfigapplication ADD CONSTRAINT FK_scva_scv FOREIGN KEY (service_config_id) REFERENCES serviceconfig(service_config_id);
 
 -- Alerting Framework
 CREATE TABLE alert_definition (
@@ -307,7 +319,13 @@ BEGIN;
   union all
   select 'alert_notice_id_seq', 0
   union all
-  select 'alert_current_id_seq', 0;
+  select 'alert_current_id_seq', 0
+  union all
+  select 'config_id_seq', 1
+  union all
+  select 'service_config_id_seq', 1
+  union  all
+  select 'service_config_application_id_seq', 1;
 
   INSERT INTO adminresourcetype (resource_type_id, resource_type_name)
   SELECT 1, 'AMBARI'

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/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 45b83c5..c2c29b1 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
@@ -31,12 +31,21 @@ ALTER ROLE :username SET search_path TO 'ambari';
 CREATE TABLE ambari.clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusters TO :username;
 
-CREATE TABLE ambari.clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000), create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
+CREATE TABLE ambari.clusterconfig (config_id BIGINT NOT NULL, version_tag VARCHAR(255) NOT NULL, version BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000), create_timestamp BIGINT NOT NULL, PRIMARY KEY (config_id));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusterconfig TO :username;
 
 CREATE TABLE ambari.clusterconfigmapping (cluster_id BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, version_tag VARCHAR(255) NOT NULL, create_timestamp BIGINT NOT NULL, selected INTEGER NOT NULL DEFAULT 0, user_name VARCHAR(255) NOT NULL DEFAULT '_db', PRIMARY KEY (cluster_id, type_name, create_timestamp));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusterconfigmapping TO :username;
 
+CREATE TABLE ambari.serviceconfig (service_config_id BIGINT NOT NULL, cluster_id BIGINT NOT NULL, service_name VARCHAR(255) NOT NULL, version BIGINT NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (service_config_id));
+GRANT ALL PRIVILEGES ON TABLE ambari.serviceconfig TO :username;
+
+CREATE TABLE ambari.serviceconfigmapping (service_config_id BIGINT NOT NULL, config_id BIGINT NOT NULL, PRIMARY KEY(service_config_id, config_id));
+GRANT ALL PRIVILEGES ON TABLE ambari.serviceconfigmapping TO :username;
+
+CREATE TABLE ambari.serviceconfigapplication (apply_id BIGINT NOT NULL, service_config_id BIGINT NOT NULL, apply_timestamp BIGINT NOT NULL, user_name VARCHAR(255) NOT NULL DEFAULT '_db',  PRIMARY KEY(apply_id));
+GRANT ALL PRIVILEGES ON TABLE ambari.serviceconfigapplication TO :username;
+
 CREATE TABLE ambari.clusterservices (service_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, service_enabled INTEGER NOT NULL, PRIMARY KEY (service_name, cluster_id));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusterservices TO :username;
 
@@ -212,6 +221,12 @@ ALTER TABLE ambari.viewinstance ADD CONSTRAINT FK_viewinst_view_name FOREIGN KEY
 ALTER TABLE ambari.viewinstanceproperty ADD CONSTRAINT FK_viewinstprop_view_name FOREIGN KEY (view_name, view_instance_name) REFERENCES ambari.viewinstance(view_name, name);
 ALTER TABLE ambari.viewinstancedata ADD CONSTRAINT FK_viewinstdata_view_name FOREIGN KEY (view_instance_id, view_name, view_instance_name) REFERENCES ambari.viewinstance(view_instance_id, view_name, name);
 ALTER TABLE ambari.viewentity ADD CONSTRAINT FK_viewentity_view_name FOREIGN KEY (view_name, view_instance_name) REFERENCES ambari.viewinstance(view_name, name);
+ALTER TABLE ambari.clusterconfig ADD CONSTRAINT UQ_config_type_tag UNIQUE (cluster_id, type_name, version_tag);
+ALTER TABLE ambari.clusterconfig ADD CONSTRAINT UQ_config_type_version UNIQUE (cluster_id, type_name, version);
+ALTER TABLE ambari.serviceconfig ADD CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_name, version);
+ALTER TABLE ambari.serviceconfigmapping ADD CONSTRAINT FK_scvm_scv FOREIGN KEY (service_config_id) REFERENCES ambari.serviceconfig(service_config_id);
+ALTER TABLE ambari.serviceconfigmapping ADD CONSTRAINT FK_scvm_config FOREIGN KEY (config_id) REFERENCES ambari.clusterconfig(config_id);
+ALTER TABLE ambari.serviceconfigapplication ADD CONSTRAINT FK_scva_scv FOREIGN KEY (service_config_id) REFERENCES ambari.serviceconfig(service_config_id);
 ALTER TABLE ambari.adminresource ADD CONSTRAINT FK_resource_resource_type_id FOREIGN KEY (resource_type_id) REFERENCES ambari.adminresourcetype(resource_type_id);
 ALTER TABLE ambari.adminprincipal ADD CONSTRAINT FK_principal_principal_type_id FOREIGN KEY (principal_type_id) REFERENCES ambari.adminprincipaltype(principal_type_id);
 ALTER TABLE ambari.adminpermission ADD CONSTRAINT FK_permission_resource_type_id FOREIGN KEY (resource_type_id) REFERENCES ambari.adminresourcetype(resource_type_id);
@@ -379,8 +394,14 @@ INSERT INTO ambari.ambari_sequences (sequence_name, "value")
   union all
   select 'alert_notice_id_seq', 0
   union all
-  select 'alert_current_id_seq', 0;
-  
+  select 'alert_current_id_seq', 0
+  union all
+  select 'config_id_seq', 1
+  union all
+  select 'service_config_id_seq', 1
+  union  all
+  select 'service_config_application_id_seq', 1;
+
 
 INSERT INTO ambari.adminresourcetype (resource_type_id, resource_type_name)
   SELECT 1, 'AMBARI'

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml
index ea79c7a..3fb5ef9 100644
--- a/ambari-server/src/main/resources/META-INF/persistence.xml
+++ b/ambari-server/src/main/resources/META-INF/persistence.xml
@@ -68,6 +68,8 @@
     <class>org.apache.ambari.server.orm.entities.PrincipalEntity</class>
     <class>org.apache.ambari.server.orm.entities.PermissionEntity</class>
     <class>org.apache.ambari.server.orm.entities.PrivilegeEntity</class>
+    <class>org.apache.ambari.server.orm.entities.ServiceConfigEntity</class>
+    <class>org.apache.ambari.server.orm.entities.ServiceConfigApplicationEntity</class>
 
     <properties>
       <!--<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/ambari" />-->

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/resources/key_properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/key_properties.json b/ambari-server/src/main/resources/key_properties.json
index 4e9c379..3c63ccb 100644
--- a/ambari-server/src/main/resources/key_properties.json
+++ b/ambari-server/src/main/resources/key_properties.json
@@ -26,6 +26,10 @@
     "Cluster": "Config/cluster_name",
     "Configuration": "Config/type"
   },
+  "ServiceConfigVersion" : {
+    "Cluster" : "cluster_name",
+    "ServiceConfigVersion" : "service_name"
+  },
   "Action": {
     "Action": "Actions/action_name"
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index 237f646..697a44c 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -6,6 +6,7 @@
         "Clusters/version",
         "Clusters/state",
         "Clusters/desired_configs",
+        "Clusters/desired_serviceconfigversions",
         "Clusters/total_hosts",
         "Clusters/health_report",
         "_"
@@ -81,7 +82,16 @@
     "Configuration":[
         "Config/tag",
         "Config/type",
-        "Config/cluster_name"
+        "Config/cluster_name",
+        "Config/version"
+    ],
+    "ServiceConfigVersion":[
+        "ServiceConfigVersion/cluster_name",
+        "ServiceConfigVersion/service_name",
+        "ServiceConfigVersion/serviceconfigversion",
+        "ServiceConfigVersion/createtime",
+        "ServiceConfigVersion/appliedtime",
+        "ServiceConfigVersion/user"
     ],
     "ConfigGroup": [
         "ConfigGroup/id",

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
index bcac30a..b19d1c0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
@@ -129,22 +129,22 @@ public class ExecutionCommandWrapperTest {
     
     //Cluster level global config
     Config globalConfig = configFactory.createNew(cluster1, GLOBAL_CONFIG, GLOBAL_CLUSTER, CONFIG_ATTRIBUTES);
-    globalConfig.setVersionTag(CLUSTER_VERSION_TAG);
+    globalConfig.setTag(CLUSTER_VERSION_TAG);
     cluster1.addConfig(globalConfig);
 
     //Cluster level service config
     Config serviceSiteConfigCluster = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_CLUSTER, CONFIG_ATTRIBUTES);
-    serviceSiteConfigCluster.setVersionTag(CLUSTER_VERSION_TAG);
+    serviceSiteConfigCluster.setTag(CLUSTER_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigCluster);
     
     //Service level service config
     Config serviceSiteConfigService = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_SERVICE, CONFIG_ATTRIBUTES);
-    serviceSiteConfigService.setVersionTag(SERVICE_VERSION_TAG);
+    serviceSiteConfigService.setTag(SERVICE_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigService);
     
     //Host level service config
     Config serviceSiteConfigHost = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_HOST, CONFIG_ATTRIBUTES);
-    serviceSiteConfigHost.setVersionTag(HOST_VERSION_TAG);
+    serviceSiteConfigHost.setTag(HOST_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigHost);
     
     ActionDBAccessor db = injector.getInstance(ActionDBAccessorImpl.class);

http://git-wip-us.apache.org/repos/asf/ambari/blob/ce402002/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
index e860c8e..8ec1ed8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
@@ -125,7 +125,7 @@ public class TestHeartbeatMonitor {
     ConfigFactory configFactory = injector.getInstance(ConfigFactory.class);
     Config config = configFactory.createNew(cluster, "hadoop-env",
         new HashMap<String,String>() {{ put("a", "b"); }}, new HashMap<String, Map<String,String>>());
-    config.setVersionTag("version1");
+    config.setTag("version1");
     cluster.addConfig(config);
     cluster.addDesiredConfig("_test", config);
     
@@ -216,7 +216,7 @@ public class TestHeartbeatMonitor {
       new HashMap<String, String>() {{
         put("a", "b");
       }}, new HashMap<String, Map<String,String>>());
-    config.setVersionTag("version1");
+    config.setTag("version1");
     cluster.addConfig(config);
     cluster.addDesiredConfig("_test", config);