Bläddra i källkod

AMBARI-6660. Implement service config versions. (mpapirkovskyy)

Myroslav Papirkovskyy 10 år sedan
förälder
incheckning
ce40200289
66 ändrade filer med 1942 tillägg och 389 borttagningar
  1. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
  2. 4 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  3. 80 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceConfigVersionResourceDefinition.java
  4. 6 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigurationService.java
  5. 57 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceConfigVersionService.java
  6. 17 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
  7. 123 31
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
  8. 10 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java
  9. 10 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterResponse.java
  10. 9 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationRequest.java
  11. 29 17
      ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationResponse.java
  12. 89 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionRequest.java
  13. 104 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java
  14. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  15. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
  16. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
  17. 64 8
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
  18. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
  19. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
  20. 3 2
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
  21. 168 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java
  22. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  23. 21 2
      ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java
  24. 116 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceConfigDAO.java
  25. 51 11
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
  26. 0 83
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntityPK.java
  27. 11 8
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
  28. 10 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
  29. 102 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigApplicationEntity.java
  30. 146 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ServiceConfigEntity.java
  31. 33 5
      ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
  32. 20 2
      ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
  33. 3 8
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
  34. 43 12
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
  35. 52 0
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigVersionHelper.java
  36. 23 12
      ambari-server/src/main/java/org/apache/ambari/server/state/DesiredConfig.java
  37. 334 54
      ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
  38. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java
  39. 9 13
      ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
  40. 7 7
      ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
  41. 3 3
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
  42. 3 12
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
  43. 13 1
      ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
  44. 13 1
      ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
  45. 20 2
      ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
  46. 24 3
      ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
  47. 2 0
      ambari-server/src/main/resources/META-INF/persistence.xml
  48. 4 0
      ambari-server/src/main/resources/key_properties.json
  49. 11 1
      ambari-server/src/main/resources/properties.json
  50. 4 4
      ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
  51. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
  52. 2 1
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java
  53. 24 22
      ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
  54. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
  55. 1 5
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProviderTest.java
  56. 7 5
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProviderTest.java
  57. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/JMXHostProviderTest.java
  58. 2 8
      ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
  59. 5 6
      ambari-server/src/test/java/org/apache/ambari/server/state/ConfigHelperTest.java
  60. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/state/DesiredConfigTest.java
  61. 11 10
      ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
  62. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
  63. 3 3
      ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
  64. 4 4
      ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
  65. 2 7
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog150Test.java
  66. 4 4
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalogTest.java

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java

@@ -62,6 +62,7 @@ public class ClusterResourceDefinition extends BaseResourceDefinition {
     setChildren.add(new SubResourceDefinition(Resource.Type.Service));
     setChildren.add(new SubResourceDefinition(Resource.Type.Host));
     setChildren.add(new SubResourceDefinition(Resource.Type.Configuration));
+    setChildren.add(new SubResourceDefinition(Resource.Type.ServiceConfigVersion));
     setChildren.add(new SubResourceDefinition(Resource.Type.Request));
     setChildren.add(new SubResourceDefinition(Resource.Type.Workflow));
     setChildren.add(new SubResourceDefinition(Resource.Type.ConfigGroup));

+ 4 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -109,6 +109,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new ConfigurationResourceDefinition();
         break;
 
+      case ServiceConfigVersion:
+        resourceDefinition = new ServiceConfigVersionResourceDefinition();
+        break;
+
       case Task:
         resourceDefinition = new TaskResourceDefinition();
         break;

+ 80 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ServiceConfigVersionResourceDefinition.java

@@ -0,0 +1,80 @@
+/*
+ * 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.api.resources;
+
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ServiceConfigVersionResourceDefinition extends BaseResourceDefinition {
+  /**
+   * Constructor.
+   *
+   */
+  public ServiceConfigVersionResourceDefinition() {
+    super(Resource.Type.ServiceConfigVersion);
+  }
+
+  @Override
+  public List<PostProcessor> getPostProcessors() {
+    List<PostProcessor> listProcessors = super.getPostProcessors();
+    listProcessors.add(new HrefProcessor());
+
+    return listProcessors;
+  }
+
+  @Override
+  public String getPluralName() {
+    return "serviceconfigversions";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "serviceconfigversion";
+  }
+
+  private class HrefProcessor extends BaseHrefPostProcessor {
+
+    @Override
+    public void process(Request request, TreeNode<Resource> resultNode, String href) {
+      if (resultNode.getObject().getType() == Resource.Type.ServiceConfigVersion) {
+
+        if (! href.endsWith("/")) {
+          href += '/';
+        }
+
+        String clustersToken = "/clusters";
+        int idx = href.indexOf(clustersToken) + clustersToken.length() + 1;
+        idx = href.indexOf("/", idx) + 1;
+
+        String serviceName = (String) resultNode.getObject().getPropertyValue("service_name");
+        Long version = (Long) resultNode.getObject().getPropertyValue("serviceconfigversion");
+        href = href.substring(0, idx) + "cenfigurations/serviceconfigversions?service_name=" + serviceName + "&serviceconfigversion=" + version;
+
+        resultNode.setProperty("href", href);
+      } else {
+        super.process(request, resultNode, href);
+      }
+
+    }
+  }
+}

+ 6 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigurationService.java

@@ -20,6 +20,7 @@ package org.apache.ambari.server.api.services;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
@@ -50,6 +51,11 @@ public class ConfigurationService extends BaseService {
     m_clusterName = clusterName;
   }
 
+  @Path("serviceconfigversions")
+  public ServiceConfigVersionService getServiceConfigVersionService() {
+    return new ServiceConfigVersionService(m_clusterName);
+  }
+
   /**
    * Handles URL: /clusters/{clusterId}/configurations
    * Get all services for a cluster.

+ 57 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/ServiceConfigVersionService.java

@@ -0,0 +1,57 @@
+/*
+ * 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.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ServiceConfigVersionService extends BaseService {
+  /**
+   * Parent cluster name.
+   */
+  private String m_clusterName;
+
+  public ServiceConfigVersionService(String m_clusterName) {
+    this.m_clusterName = m_clusterName;
+  }
+
+  @GET
+  @Produces("text/plain")
+  public Response getServiceConfigVersions(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, body, ui, Request.Type.GET, createServiceConfigResource(m_clusterName));
+  }
+
+
+  ResourceInstance createServiceConfigResource(String clusterName) {
+    Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Cluster, clusterName);
+    mapIds.put(Resource.Type.ServiceConfigVersion, null);
+
+    return createResource(Resource.Type.ServiceConfigVersion, mapIds);
+  }
+}

+ 17 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java

@@ -75,7 +75,7 @@ public interface AmbariManagementController {
    *
    * @throws AmbariException when the configuration cannot be created.
    */
-  public void createConfiguration(ConfigurationRequest request)
+  public ConfigurationResponse createConfiguration(ConfigurationRequest request)
       throws AmbariException;
 
   /**
@@ -157,6 +157,15 @@ public interface AmbariManagementController {
   public Set<TaskStatusResponse> getTaskStatus(Set<TaskStatusRequest> requests)
       throws AmbariException;
 
+  /**
+   * Get service config version history
+   * @param requests service config version requests
+   * @return service config versions
+   * @throws AmbariException
+   */
+  Set<ServiceConfigVersionResponse> getServiceConfigVersions(Set<ServiceConfigVersionRequest> requests)
+      throws AmbariException;
+
   /**
    * Gets the users identified by the given request objects.
    *
@@ -635,6 +644,13 @@ public interface AmbariManagementController {
    */
   public ExecutionScheduleManager getExecutionScheduleManager();
 
+  /**
+   * Get cached clusterUpdateResults, used only for service config versions currently
+   * @param clusterRequest
+   * @return
+   */
+  ClusterResponse getClusterUpdateResults(ClusterRequest clusterRequest);
+
   /**
    * Get JobTracker hostname
    */

+ 123 - 31
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java

@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.controller;
 
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
 import com.google.gson.Gson;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -28,18 +30,11 @@ import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.ClusterNotFoundException;
@@ -136,6 +131,7 @@ import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
 import org.apache.http.client.utils.URIBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -214,6 +210,9 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   final private String serverDB;
   final private String mysqljdbcUrl;
 
+  private Cache<ClusterRequest, ClusterResponse> clusterUpdateCache =
+      CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
+
   @Inject
   private AmbariCustomCommandExecutionHelper customCommandExecutionHelper;
   @Inject
@@ -599,14 +598,13 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
 
   @Override
-  public synchronized void createConfiguration(
+  public synchronized ConfigurationResponse createConfiguration(
       ConfigurationRequest request) throws AmbariException {
     if (null == request.getClusterName() || request.getClusterName().isEmpty()
         || null == request.getType() || request.getType().isEmpty()
-        || null == request.getVersionTag() || request.getVersionTag().isEmpty()
         || null == request.getProperties()) {
       throw new IllegalArgumentException("Invalid Arguments,"
-          + " clustername, config type, config version and configs should not"
+          + " clustername, config type and configs should not"
           + " be null or empty");
     }
 
@@ -632,11 +630,17 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
     Config config = configFactory.createNew (cluster, request.getType(),
         request.getProperties(), propertiesAttributes);
-    config.setVersionTag(request.getVersionTag());
+
+    if (!StringUtils.isEmpty(request.getVersionTag())) {
+      config.setTag(request.getVersionTag());
+    }
 
     config.persist();
 
     cluster.addConfig(config);
+
+    return new ConfigurationResponse(cluster.getClusterName(), config.getType(), config.getTag(), config.getVersion(),
+        config.getProperties(), config.getPropertiesAttributes());
   }
 
   @Override
@@ -767,20 +771,22 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           + ", stackInfo=" + request.getStackVersion());
     }
 
+    Cluster singleCluster = null;
     if (request.getClusterName() != null) {
-      Cluster c = clusters.getCluster(request.getClusterName());
-      ClusterResponse cr = c.convertToResponse();
-      cr.setDesiredConfigs(c.getDesiredConfigs());
-      response.add(cr);
-      return response;
+      singleCluster = clusters.getCluster(request.getClusterName());
     } else if (request.getClusterId() != null) {
-      Cluster c = clusters.getClusterById(request.getClusterId());
-      ClusterResponse cr = c.convertToResponse();
-      cr.setDesiredConfigs(c.getDesiredConfigs());
+      singleCluster = clusters.getClusterById(request.getClusterId());
+    }
+
+    if (singleCluster != null) {
+      ClusterResponse cr = singleCluster.convertToResponse();
+      cr.setDesiredConfigs(singleCluster.getDesiredConfigs());
+      cr.setDesiredServiceConfigVersions(singleCluster.getActiveServiceConfigVersions());
       response.add(cr);
       return response;
     }
 
+
     Map<String, Cluster> allClusters = clusters.getClusters();
     for (Cluster c : allClusters.values()) {
       if (request.getStackVersion() != null) {
@@ -1017,7 +1023,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           request.getVersionTag());
       if (null != config) {
         ConfigurationResponse response = new ConfigurationResponse(
-            cluster.getClusterName(), config.getType(), config.getVersionTag(),
+            cluster.getClusterName(), config.getType(), config.getTag(), config.getVersion(),
             config.getProperties(), config.getPropertiesAttributes());
         responses.add(response);
       }
@@ -1031,7 +1037,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           for (Entry<String, Config> entry : configs.entrySet()) {
             ConfigurationResponse response = new ConfigurationResponse(
                 cluster.getClusterName(), request.getType(),
-                entry.getValue().getVersionTag(), new HashMap<String, String>(),
+                entry.getValue().getTag(), entry.getValue().getVersion(), new HashMap<String, String>(),
                 new HashMap<String, Map<String,String>>());
             responses.add(response);
           }
@@ -1042,7 +1048,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
         for (Config config : all) {
           ConfigurationResponse response = new ConfigurationResponse(
-             cluster.getClusterName(), config.getType(), config.getVersionTag(),
+             cluster.getClusterName(), config.getType(), config.getTag(), config.getVersion(),
              new HashMap<String, String>(), new HashMap<String, Map<String,String>>());
 
           responses.add(response);
@@ -1084,6 +1090,9 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         + ", request=" + request);
 
     final Cluster cluster = clusters.getCluster(request.getClusterName());
+    //save data to return configurations created
+    ConfigurationResponse configurationResponse = null;
+    ServiceConfigVersionResponse serviceConfigVersionResponse = null;
 
     // set or create configuration mapping (and optionally create the map of properties)
     if (null != request.getDesiredConfig()) {
@@ -1104,21 +1113,21 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
               request.getClusterName()));
 
           cr.setClusterName(cluster.getClusterName());
-          createConfiguration(cr);
+          configurationResponse = createConfiguration(cr);
         }
       }
 
       Config baseConfig = cluster.getConfig(cr.getType(), cr.getVersionTag());
       if (null != baseConfig) {
         String authName = getAuthName();
-
-        if (cluster.addDesiredConfig(authName, baseConfig)) {
+        serviceConfigVersionResponse = cluster.addDesiredConfig(authName, baseConfig);
+        if (serviceConfigVersionResponse != null) {
           Logger logger = LoggerFactory.getLogger("configchange");
           logger.info("cluster '" + request.getClusterName() + "' "
               + "changed by: '" + authName + "'; "
               + "type='" + baseConfig.getType() + "' "
-              + "tag='" + baseConfig.getVersionTag() + "'"
-              + (null == oldConfig ? "" : " from='"+ oldConfig.getVersionTag() + "'"));
+              + "tag='" + baseConfig.getTag() + "'"
+              + (null == oldConfig ? "" : " from='"+ oldConfig.getTag() + "'"));
         }
       }
     }
@@ -1177,9 +1186,53 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       cluster.setProvisioningState(provisioningState);
     }
 
+    if (null != request.getServiceConfigVersionRequest()) {
+      ServiceConfigVersionRequest serviceConfigVersionRequest = request.getServiceConfigVersionRequest();
+      if (StringUtils.isEmpty(serviceConfigVersionRequest.getServiceName()) ||
+          null == serviceConfigVersionRequest.getVersion()) {
+        String msg = "Service name and version should be specified in service config version";
+        LOG.error(msg);
+        throw new IllegalArgumentException(msg);
+      }
+
+      cluster.setServiceConfigVersion(serviceConfigVersionRequest.getServiceName(),
+          serviceConfigVersionRequest.getVersion(), getAuthName());
+    }
+
+    if (serviceConfigVersionResponse != null) {
+      if (configurationResponse != null) {
+        serviceConfigVersionResponse.setConfigurations(Collections.singletonList(configurationResponse));
+      }
+
+      ClusterResponse clusterResponse =
+          new ClusterResponse(cluster.getClusterId(), cluster.getClusterName(), null, null, null, null, null);
+
+      clusterResponse.setDesiredServiceConfigVersions(
+          Collections.singletonMap(serviceConfigVersionResponse.getServiceName(), serviceConfigVersionResponse));
+
+      //workaround to be able to retrieve update results in resource provider
+      //as this method only expected to return request response
+      saveClusterUpdate(request, clusterResponse);
+    }
+
     return null;
   }
 
+  /**
+   * Save cluster update results to retrieve later
+   * @param clusterRequest
+   * @param clusterResponse
+   */
+  public void saveClusterUpdate(ClusterRequest clusterRequest, ClusterResponse clusterResponse) {
+    clusterUpdateCache.put(clusterRequest, clusterResponse);
+  }
+
+
+  @Override
+  public ClusterResponse getClusterUpdateResults(ClusterRequest clusterRequest) {
+    return clusterUpdateCache.getIfPresent(clusterRequest);
+  }
+
   public String getJobTrackerHost(Cluster cluster) {
     try {
       Service svc = cluster.getService("MAPREDUCE");
@@ -2573,6 +2626,45 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     return response;
   }
 
+  @Override
+  public Set<ServiceConfigVersionResponse> getServiceConfigVersions(Set<ServiceConfigVersionRequest> requests)
+      throws AmbariException {
+    Set<ServiceConfigVersionResponse> responses = new LinkedHashSet<ServiceConfigVersionResponse>();
+
+    for (ServiceConfigVersionRequest request : requests) {
+      responses.addAll(getServiceConfigVersions(request));
+    }
+
+    return responses;
+  }
+
+  private Set<ServiceConfigVersionResponse> getServiceConfigVersions(ServiceConfigVersionRequest request)
+      throws AmbariException {
+    if (request.getClusterName() == null) {
+      throw new IllegalArgumentException("Invalid arguments, cluster name"
+          + " should not be null");
+    }
+
+    Cluster cluster = clusters.getCluster(request.getClusterName());
+
+    Set<ServiceConfigVersionResponse> result = new LinkedHashSet<ServiceConfigVersionResponse>();
+
+    for (ServiceConfigVersionResponse response : cluster.getServiceConfigVersions()) {
+      if (request.getServiceName() != null && !StringUtils.equals(request.getServiceName(), response.getServiceName())) {
+        continue;
+      }
+      if (request.getVersion() != null && NumberUtils.compare(request.getVersion(), response.getVersion()) != 0) {
+        continue;
+      }
+      if (request.getUserName() != null && !StringUtils.equals(request.getUserName(), response.getUserName())) {
+        continue;
+      }
+      result.add(response);
+    }
+
+    return result;
+  }
+
   @Override
   public Set<UserResponse> getUsers(Set<UserRequest> requests)
       throws AmbariException {

+ 10 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java

@@ -37,6 +37,8 @@ public class ClusterRequest {
   
   private ConfigurationRequest config = null;
 
+  private ServiceConfigVersionRequest serviceConfigVersionRequest = null;
+
   public ClusterRequest(Long clusterId, String clusterName, 
       String stackVersion, Set<String> hostNames) {
     this(clusterId, clusterName, null, stackVersion, hostNames);
@@ -163,6 +165,13 @@ public class ClusterRequest {
     sb.append("] }");
     return sb.toString();
   }
-  
 
+
+  public ServiceConfigVersionRequest getServiceConfigVersionRequest() {
+    return serviceConfigVersionRequest;
+  }
+
+  public void setServiceConfigVersionRequest(ServiceConfigVersionRequest serviceConfigVersionRequest) {
+    this.serviceConfigVersionRequest = serviceConfigVersionRequest;
+  }
 }

+ 10 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterResponse.java

@@ -36,6 +36,8 @@ public class ClusterResponse {
   private final String desiredStackVersion;
 
   private Map<String, DesiredConfig> desiredConfigs;
+
+  private Map<String, ServiceConfigVersionResponse> desiredServiceConfigVersions;
   
   private String provisioningState;
 
@@ -177,4 +179,12 @@ public class ClusterResponse {
   public ClusterHealthReport getClusterHealthReport() {
     return clusterHealthReport;
   }
+
+  public Map<String, ServiceConfigVersionResponse> getDesiredServiceConfigVersions() {
+    return desiredServiceConfigVersions;
+  }
+
+  public void setDesiredServiceConfigVersions(Map<String, ServiceConfigVersionResponse> desiredServiceConfigVersions) {
+    this.desiredServiceConfigVersions = desiredServiceConfigVersions;
+  }
 }

+ 9 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationRequest.java

@@ -30,6 +30,7 @@ public class ConfigurationRequest {
   private String clusterName;
   private String type;
   private String tag;
+  private Long version;
   private Map<String, String> configs;
   private boolean selected = true;
   private Map<String, Map<String, String>> configsAttributes;
@@ -137,4 +138,12 @@ public class ConfigurationRequest {
       Map<String, Map<String, String>> configsAttributes) {
     this.configsAttributes = configsAttributes;
   }
+
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
 }

+ 29 - 17
ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationResponse.java

@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.controller;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -32,12 +33,17 @@ public class ConfigurationResponse {
 
   private String versionTag;
 
+  private Long version;
+
+  private List<Long> serviceConfigVersions;
+
   private Map<String, String> configs;
 
   private Map<String, Map<String, String>> configAttributes;
 
   public ConfigurationResponse(String clusterName,
                                String type, String versionTag,
+                               Long version,
                                Map<String, String> configs,
                                Map<String, Map<String, String>> configAttributes) {
     super();
@@ -45,6 +51,7 @@ public class ConfigurationResponse {
     this.configs = configs;
     this.type = type;
     this.versionTag = versionTag;
+    this.version = version;
     this.configs = configs;
     this.configAttributes = configAttributes;
   }
@@ -100,26 +107,24 @@ public class ConfigurationResponse {
     return clusterName;
   }
 
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
 
-    ConfigurationResponse that =
-        (ConfigurationResponse) o;
-
-    if (clusterName != null ?
-        !clusterName.equals(that.clusterName) : that.clusterName != null) {
-      return false;
-    }
-    if (type != null ?
-        !type.equals(that.type) : that.type != null) {
-      return false;
-    }
-    if (versionTag != null ?
-        !versionTag.equals(that.versionTag) : that.versionTag != null){
-      return false;
-    }
+    ConfigurationResponse that = (ConfigurationResponse) o;
+
+    if (clusterName != null ? !clusterName.equals(that.clusterName) : that.clusterName != null) return false;
+    if (type != null ? !type.equals(that.type) : that.type != null) return false;
+    if (version != null ? !version.equals(that.version) : that.version != null) return false;
 
     return true;
   }
@@ -127,9 +132,16 @@ public class ConfigurationResponse {
   @Override
   public int hashCode() {
     int result = clusterName != null ? clusterName.hashCode() : 0;
-    result = 71 * result + (type != null ? type.hashCode() : 0);
-    result = 71 * result + (versionTag != null ? versionTag.hashCode():0);
+    result = 31 * result + (type != null ? type.hashCode() : 0);
+    result = 31 * result + (version != null ? version.hashCode() : 0);
     return result;
   }
 
+  public List<Long> getServiceConfigVersions() {
+    return serviceConfigVersions;
+  }
+
+  public void setServiceConfigVersions(List<Long> serviceConfigVersions) {
+    this.serviceConfigVersions = serviceConfigVersions;
+  }
 }

+ 89 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionRequest.java

@@ -0,0 +1,89 @@
+/*
+ * 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.controller;
+
+public class ServiceConfigVersionRequest {
+  private String clusterName;
+  private String serviceName;
+  private Long version;
+  private Long createTime;
+  private Long applyTime;
+  private String userName;
+
+  public ServiceConfigVersionRequest() {
+  }
+
+  public ServiceConfigVersionRequest(String clusterName, String serviceName, Long version, Long createTime, Long applyTime, String userName) {
+    this.clusterName = clusterName;
+    this.serviceName = serviceName;
+    this.version = version;
+    this.createTime = createTime;
+    this.applyTime = applyTime;
+    this.userName = userName;
+  }
+
+  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 getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(Long createTime) {
+    this.createTime = createTime;
+  }
+
+  public Long getApplyTime() {
+    return applyTime;
+  }
+
+  public void setApplyTime(Long applyTime) {
+    this.applyTime = applyTime;
+  }
+
+  public String getUserName() {
+    return userName;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public void setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+  }
+
+}

+ 104 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java

@@ -0,0 +1,104 @@
+/*
+ * 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.controller;
+
+
+import org.apache.ambari.server.state.Config;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.List;
+
+public class ServiceConfigVersionResponse {
+  private String clusterName;
+  private String serviceName;
+  private Long version;
+  private Long createTime;
+  private Long applyTime;
+  private String userName;
+  private List<ConfigurationResponse> configurations;
+
+  @JsonProperty("service_name")
+  public String getServiceName() {
+    return serviceName;
+  }
+
+  public void setServiceName(String serviceName) {
+    this.serviceName = serviceName;
+  }
+
+  @JsonProperty("serviceconfigversion")
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
+  @JsonProperty("createtime")
+  @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+  public Long getCreateTime() {
+    return createTime;
+  }
+
+  public void setCreateTime(Long createTime) {
+    this.createTime = createTime;
+  }
+
+  @JsonProperty("appliedtime")
+  @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+  public Long getApplyTime() {
+    return applyTime;
+  }
+
+  public void setApplyTime(Long applyTime) {
+    this.applyTime = applyTime;
+  }
+
+  @JsonProperty("user")
+  @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+  public String getUserName() {
+    return userName;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  @JsonProperty("cluster_name")
+  @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public void setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+  }
+
+  @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+  public List<ConfigurationResponse> getConfigurations() {
+    return configurations;
+  }
+
+  public void setConfigurations(List<ConfigurationResponse> configurations) {
+    this.configurations = configurations;
+  }
+}
+

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java

@@ -104,6 +104,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return resourceProviderFactory.getHostComponentResourceProvider(propertyIds, keyPropertyIds, managementController);
       case Configuration:
         return new ConfigurationResourceProvider(propertyIds, keyPropertyIds, managementController);
+      case ServiceConfigVersion:
+        return new ServiceConfigVersionResourceProvider(propertyIds, keyPropertyIds, managementController);
       case Action:
         return new ActionResourceProvider(propertyIds, keyPropertyIds, managementController);
       case Request:

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java

@@ -623,7 +623,7 @@ public abstract class AbstractProviderModule implements ProviderModule, Resource
         if (configs != null) {
           DesiredConfig config = (DesiredConfig) configs.get(configType);
           if (config != null) {
-            versionTag = config.getVersion();
+            versionTag = config.getTag();
           }
         }
       }

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java

@@ -32,6 +32,7 @@ import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.ParentObjectNotFoundException;
 import org.apache.ambari.server.controller.ConfigurationRequest;
 import org.apache.ambari.server.controller.RequestStatusResponse;
+import org.apache.ambari.server.controller.ServiceConfigVersionRequest;
 import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.server.controller.utilities.PredicateHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;

+ 64 - 8
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java

@@ -29,12 +29,7 @@ import java.util.Set;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.PersistKeyValueService;
-import org.apache.ambari.server.controller.AmbariManagementController;
-import org.apache.ambari.server.controller.ClusterRequest;
-import org.apache.ambari.server.controller.ClusterResponse;
-import org.apache.ambari.server.controller.ConfigGroupRequest;
-import org.apache.ambari.server.controller.ConfigurationRequest;
-import org.apache.ambari.server.controller.RequestStatusResponse;
+import org.apache.ambari.server.controller.*;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -66,6 +61,7 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
   protected static final String CLUSTER_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "version");  
   protected static final String CLUSTER_PROVISIONING_STATE_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "provisioning_state");
   protected static final String CLUSTER_DESIRED_CONFIGS_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "desired_configs");
+  protected static final String CLUSTER_DESIRED_SERVICE_CONFIG_VERSIONS_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "desired_serviceconfigversions");
   protected static final String CLUSTER_TOTAL_HOSTS_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "total_hosts");
   protected static final String CLUSTER_HEALTH_REPORT_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "health_report");
   protected static final String BLUEPRINT_PROPERTY_ID = PropertyHelper.getPropertyId(null, "blueprint");
@@ -175,6 +171,7 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
       setResourceProperty(resource, CLUSTER_NAME_PROPERTY_ID, response.getClusterName(), requestedIds);
       setResourceProperty(resource, CLUSTER_PROVISIONING_STATE_PROPERTY_ID, response.getProvisioningState(), requestedIds);
       setResourceProperty(resource, CLUSTER_DESIRED_CONFIGS_PROPERTY_ID, response.getDesiredConfigs(), requestedIds);
+      setResourceProperty(resource, CLUSTER_DESIRED_SERVICE_CONFIG_VERSIONS_PROPERTY_ID, response.getDesiredServiceConfigVersions(), requestedIds);
       setResourceProperty(resource, CLUSTER_TOTAL_HOSTS_PROPERTY_ID, response.getTotalHosts(), requestedIds);
       setResourceProperty(resource, CLUSTER_HEALTH_REPORT_PROPERTY_ID, response.getClusterHealthReport(), requestedIds);
       
@@ -211,7 +208,35 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
       }
     });
     notifyUpdate(Resource.Type.Cluster, request, predicate);
-    return getRequestStatus(response);
+
+    Set<Resource> associatedResources = null;
+    for (ClusterRequest clusterRequest : requests) {
+      ClusterResponse updateResults = getManagementController().getClusterUpdateResults(clusterRequest);
+      if (updateResults != null) {
+        Map<String, ServiceConfigVersionResponse> serviceConfigVersions = updateResults.getDesiredServiceConfigVersions();
+        if (serviceConfigVersions != null) {
+          associatedResources = new HashSet<Resource>();
+          for (Map.Entry<String, ServiceConfigVersionResponse> stringServiceConfigVersionResponseEntry : serviceConfigVersions.entrySet()) {
+            Resource resource = new ResourceImpl(Resource.Type.ServiceConfigVersion);
+            ServiceConfigVersionResponse serviceConfigVersionResponse = stringServiceConfigVersionResponseEntry.getValue();
+            resource.setProperty(ServiceConfigVersionResourceProvider.SERVICE_CONFIG_VERSION_SERVICE_NAME_PROPERTY_ID,
+                serviceConfigVersionResponse.getServiceName());
+            resource.setProperty(ServiceConfigVersionResourceProvider.SERVICE_CONFIG_VERSION_PROPERTY_ID,
+                serviceConfigVersionResponse.getVersion());
+            if (serviceConfigVersionResponse.getConfigurations() != null) {
+              resource.setProperty(
+                  ServiceConfigVersionResourceProvider.SERVICE_CONFIG_VERSION_CONFIGURATIONS_PROPERTY_ID,
+                  serviceConfigVersionResponse.getConfigurations());
+            }
+            associatedResources.add(resource);
+          }
+
+        }
+      }
+    }
+
+
+    return getRequestStatus(response, associatedResources);
   }
 
   @Override
@@ -272,12 +297,43 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
 
     ConfigurationRequest configRequest = getConfigurationRequest("Clusters", properties);
 
+    ServiceConfigVersionRequest serviceConfigVersionRequest = getServiceConfigVersionRequest("Clusters", properties);
+
     if (null != configRequest)
       cr.setDesiredConfig(configRequest);
 
+    if (serviceConfigVersionRequest != null) {
+      cr.setServiceConfigVersionRequest(serviceConfigVersionRequest);
+    }
+
     return cr;
   }
 
+  /**
+   * Helper method for creating rollback request
+   */
+  protected ServiceConfigVersionRequest getServiceConfigVersionRequest(String parentCategory, Map<String, Object> properties) {
+    ServiceConfigVersionRequest serviceConfigVersionRequest = null;
+
+    for (Map.Entry<String, Object> entry : properties.entrySet()) {
+      String absCategory = PropertyHelper.getPropertyCategory(entry.getKey());
+      String propName = PropertyHelper.getPropertyName(entry.getKey());
+
+      if (absCategory.startsWith(parentCategory + "/desired_serviceconfigversions")) {
+        serviceConfigVersionRequest =
+            (serviceConfigVersionRequest ==null ) ? new ServiceConfigVersionRequest() : serviceConfigVersionRequest;
+
+        if (propName.equals("service_name"))
+          serviceConfigVersionRequest.setServiceName(entry.getValue().toString());
+        else if (propName.equals("serviceconfigversion"))
+          serviceConfigVersionRequest.setVersion(Long.valueOf(entry.getValue().toString()));
+
+      }
+    }
+
+    return serviceConfigVersionRequest;
+  }
+
   /**
    * Determine if the request is a create using a blueprint.
    *
@@ -975,7 +1031,7 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
         String type = entry.getKey();
         String service = stack.getServiceForConfigType(type);
         Config config = new ConfigImpl(type);
-        config.setVersionTag(entity.getName());
+        config.setTag(entity.getName());
         config.setProperties(entry.getValue());
         Map<String, Config> serviceConfigs = groupConfigs.get(service);
         if (serviceConfigs == null) {

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java

@@ -621,7 +621,7 @@ public class ConfigGroupResourceProvider extends
           }
 
           Config config = new ConfigImpl(type);
-          config.setVersionTag(tag);
+          config.setTag(tag);
           config.setProperties(configProperties);
           config.setPropertiesAttributes(configAttributes);
 

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java

@@ -58,6 +58,8 @@ public class ConfigurationResourceProvider extends
     PropertyHelper.getPropertyId(null, "type");
   public static final String CONFIGURATION_CONFIG_TAG_PROPERTY_ID   =
     PropertyHelper.getPropertyId(null, "tag");
+  public static final String CONFIGURATION_CONFIG_VERSION_PROPERTY_ID   =
+    PropertyHelper.getPropertyId(null, "version");
 
 
   /**
@@ -159,6 +161,7 @@ public class ConfigurationResourceProvider extends
       resource.setProperty(CONFIGURATION_CLUSTER_NAME_PROPERTY_ID, response.getClusterName());
       resource.setProperty(CONFIGURATION_CONFIG_TYPE_PROPERTY_ID, response.getType());
       resource.setProperty(CONFIGURATION_CONFIG_TAG_PROPERTY_ID, response.getVersionTag());
+      resource.setProperty(CONFIGURATION_CONFIG_VERSION_PROPERTY_ID, response.getVersion());
 
       if (null != response.getConfigs() && response.getConfigs().size() > 0) {
         Map<String, String> configs = response.getConfigs();

+ 3 - 2
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java

@@ -616,8 +616,9 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
                   + "host '" + h.getHostName() + "' "
                   + "changed by: '" + authName + "'; "
                   + "type='" + baseConfig.getType() + "' "
-                  + "tag='" + baseConfig.getVersionTag() + "'"
-                  + (null == oldConfig ? "" : ", from='" + oldConfig.getVersion() + "'"));
+                  + "version='" + baseConfig.getVersion() + "'"
+                  + "tag='" + baseConfig.getTag() + "'"
+                  + (null == oldConfig ? "" : ", from='" + oldConfig.getTag() + "'"));
             }
           }
 

+ 168 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java

@@ -0,0 +1,168 @@
+/*
+ * 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.controller.internal;
+
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.ConfigurationResponse;
+import org.apache.ambari.server.controller.ServiceConfigVersionRequest;
+import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
+import org.apache.ambari.server.controller.spi.*;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.state.Config;
+
+import java.util.*;
+
+public class ServiceConfigVersionResourceProvider extends
+    AbstractControllerResourceProvider {
+  public static final String SERVICE_CONFIG_VERSION_CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(null, "cluster_name");
+  public static final String SERVICE_CONFIG_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId(null, "serviceconfigversion");
+  public static final String SERVICE_CONFIG_VERSION_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(null, "service_name");
+  public static final String SERVICE_CONFIG_VERSION_CREATE_TIME_PROPERTY_ID = PropertyHelper.getPropertyId(null, "createtime");
+  public static final String SERVICE_CONFIG_VERSION_APPLY_TIME_PROPERTY_ID = PropertyHelper.getPropertyId(null, "appliedtime");
+  public static final String SERVICE_CONFIG_VERSION_USER_PROPERTY_ID = PropertyHelper.getPropertyId(null, "user");
+  public static final String SERVICE_CONFIG_VERSION_CONFIGURATIONS_PROPERTY_ID = PropertyHelper.getPropertyId(null, "configurations");
+
+  /**
+   * The primary key property ids for the service config version resource type.
+   */
+  private static Set<String> pkPropertyIds =
+      new HashSet<String>(Arrays.asList(new String[]{
+          SERVICE_CONFIG_VERSION_CLUSTER_NAME_PROPERTY_ID,
+          SERVICE_CONFIG_VERSION_SERVICE_NAME_PROPERTY_ID}));
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Constructor
+   *
+   * @param propertyIds           the property ids supported by this provider
+   * @param keyPropertyIds        the key properties for this provider
+   * @param managementController  the associated management controller
+   */
+  ServiceConfigVersionResourceProvider(Set<String> propertyIds,
+                                Map<Resource.Type, String> keyPropertyIds,
+                                AmbariManagementController managementController) {
+
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return pkPropertyIds;
+  }
+
+  @Override
+  public RequestStatus createResources(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Cannot explicitly create service config version");
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    final Set<ServiceConfigVersionRequest> requests = new HashSet<ServiceConfigVersionRequest>();
+    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
+      requests.add(createRequest(properties));
+    }
+
+    Set<ServiceConfigVersionResponse> responses = getResources(new Command<Set<ServiceConfigVersionResponse>>() {
+      @Override
+      public Set<ServiceConfigVersionResponse> invoke() throws AmbariException {
+        return getManagementController().getServiceConfigVersions(requests);
+      }
+    });
+
+    Set<Resource> resources = new HashSet<Resource>();
+    for (ServiceConfigVersionResponse response : responses) {
+      Resource resource = new ResourceImpl(Resource.Type.ServiceConfigVersion);
+      resource.setProperty(SERVICE_CONFIG_VERSION_CLUSTER_NAME_PROPERTY_ID, response.getClusterName());
+      resource.setProperty(SERVICE_CONFIG_VERSION_SERVICE_NAME_PROPERTY_ID, response.getServiceName());
+      resource.setProperty(SERVICE_CONFIG_VERSION_USER_PROPERTY_ID, response.getUserName());
+      resource.setProperty(SERVICE_CONFIG_VERSION_PROPERTY_ID, response.getVersion());
+      resource.setProperty(SERVICE_CONFIG_VERSION_APPLY_TIME_PROPERTY_ID, response.getApplyTime());
+      resource.setProperty(SERVICE_CONFIG_VERSION_CREATE_TIME_PROPERTY_ID, response.getCreateTime());
+      resource.setProperty(SERVICE_CONFIG_VERSION_CONFIGURATIONS_PROPERTY_ID,
+          convertToSubResources(response.getClusterName(), response.getConfigurations()));
+
+      resources.add(resource);
+    }
+    return resources;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Cannot update service config version");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Cannot delete service config version");
+  }
+
+  @Override
+  public Set<String> checkPropertyIds(Set<String> propertyIds) {
+    propertyIds = super.checkPropertyIds(propertyIds);
+
+    if (propertyIds.isEmpty()) {
+      return propertyIds;
+    }
+    Set<String> unsupportedProperties = new HashSet<String>();
+
+    for (String propertyId : propertyIds) {
+
+      if (!propertyId.equals("cluster_name") && !propertyId.equals("serviceconfigversion") &&
+          !propertyId.equals("service_name") && !propertyId.equals("createtime") &&
+          !propertyId.equals("appliedtime") && !propertyId.equals("user")) {
+
+        unsupportedProperties.add(propertyId);
+
+      }
+    }
+    return unsupportedProperties;
+  }
+
+
+  private ServiceConfigVersionRequest createRequest(Map<String, Object> properties) {
+    String clusterName = (String) properties.get(SERVICE_CONFIG_VERSION_CLUSTER_NAME_PROPERTY_ID);
+    String serviceName = (String) properties.get(SERVICE_CONFIG_VERSION_SERVICE_NAME_PROPERTY_ID);
+    String user = (String) properties.get(SERVICE_CONFIG_VERSION_USER_PROPERTY_ID);
+    Object versionObject = properties.get(SERVICE_CONFIG_VERSION_PROPERTY_ID);
+    Long version = versionObject == null ? null : Long.valueOf(versionObject.toString());
+
+    return new ServiceConfigVersionRequest(clusterName, serviceName, version, null, null, user);
+  }
+
+  private List<Map<String, Object>> convertToSubResources(final String clusterName, List<ConfigurationResponse> configs) {
+    List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
+    for (final ConfigurationResponse config : configs) {
+      Map<String, Object> configMap = new LinkedHashMap<String, Object>();
+      configMap.put("Config", new HashMap<String, String>(){{put("cluster_name", clusterName);}});
+      configMap.put("type", config.getType());
+      configMap.put("tag", config.getVersionTag());
+      configMap.put("version", config.getVersion());
+      configMap.put("properties", config.getConfigs());
+      configMap.put("properties_attributes", config.getConfigAttributes());
+      result.add(configMap);
+    }
+
+    return result;
+  }
+}

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -80,6 +80,7 @@ public interface Resource {
     Component,
     HostComponent,
     Configuration,
+    ServiceConfigVersion,
     ConfigGroup,
     Action,
     Request,
@@ -157,6 +158,7 @@ public interface Resource {
     public static final Type Component = InternalType.Component.getType();
     public static final Type HostComponent = InternalType.HostComponent.getType();
     public static final Type Configuration = InternalType.Configuration.getType();
+    public static final Type ServiceConfigVersion = InternalType.ServiceConfigVersion.getType();
     public static final Type ConfigGroup = InternalType.ConfigGroup.getType();
     public static final Type Action = InternalType.Action.getType();
     public static final Type Request = InternalType.Request.getType();

+ 21 - 2
ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java

@@ -23,11 +23,14 @@ import java.util.List;
 import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
 
 import com.google.inject.Singleton;
+import org.apache.ambari.server.controller.ivory.Cluster;
 import org.apache.ambari.server.orm.RequiresSession;
 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 com.google.inject.Inject;
@@ -39,6 +42,8 @@ public class ClusterDAO {
 
   @Inject
   Provider<EntityManager> entityManagerProvider;
+  @Inject
+  DaoUtils daoUtils;
 
   /**
    * Looks for Cluster by ID
@@ -72,11 +77,25 @@ public class ClusterDAO {
   }
 
   @RequiresSession
-  public ClusterConfigEntity findConfig(ClusterConfigEntityPK configEntityPK) {
+  public ClusterConfigEntity findConfig(Long configEntityPK) {
     return entityManagerProvider.get().find(ClusterConfigEntity.class,
       configEntityPK);
   }
 
+  @RequiresSession
+  public ClusterConfigEntity findConfig(Long clusterId, String type, String tag) {
+    CriteriaBuilder cb = entityManagerProvider.get().getCriteriaBuilder();
+    CriteriaQuery<ClusterConfigEntity> cq = cb.createQuery(ClusterConfigEntity.class);
+    Root<ClusterConfigEntity> config = cq.from(ClusterConfigEntity.class);
+    cq.where(cb.and(
+        cb.equal(config.get("clusterId"), clusterId)),
+        cb.equal(config.get("type"), type),
+        cb.equal(config.get("tag"), tag)
+    );
+    TypedQuery<ClusterConfigEntity> query = entityManagerProvider.get().createQuery(cq);
+    return daoUtils.selectOne(query);
+  }
+
   /**
    * Create Cluster entity in Database
    * @param clusterEntity entity to create

+ 116 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ServiceConfigDAO.java

@@ -0,0 +1,116 @@
+/*
+ * 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 com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.RequiresSession;
+import org.apache.ambari.server.orm.entities.ServiceConfigApplicationEntity;
+import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Singleton
+@RequiresSession
+public class ServiceConfigDAO {
+  @Inject
+  Provider<EntityManager> entityManagerProvider;
+  @Inject
+  DaoUtils daoUtils;
+
+
+  public ServiceConfigEntity find(Long serviceConfigId) {
+    return entityManagerProvider.get().find(ServiceConfigEntity.class, serviceConfigId);
+  }
+
+  public ServiceConfigEntity findByServiceAndVersion(String serviceName, Long version) {
+    TypedQuery<ServiceConfigEntity> query = entityManagerProvider.get().
+        createQuery("SELECT scv FROM ServiceConfigEntity scv " +
+            "WHERE scv.serviceName=?1 AND scv.version=?2", ServiceConfigEntity.class);
+    return daoUtils.selectOne(query, serviceName, version);
+  }
+
+  public Map<String, Long> findMaxVersions(Long clusterId) {
+    Map<String, Long> maxVersions = new HashMap<String, Long>();
+
+    TypedQuery<String> query = entityManagerProvider.get().createQuery("SELECT DISTINCT scv.serviceName FROM ServiceConfigEntity scv WHERE scv.clusterId = ?1", String.class);
+    List<String> serviceNames = daoUtils.selectList(query, clusterId);
+
+    for (String serviceName : serviceNames) {
+      maxVersions.put(serviceName, findMaxVersion(clusterId, serviceName).getVersion());
+    }
+
+    return maxVersions;
+  }
+
+  public List<Long> getServiceConfigVersionsByConfig(Long clusterId, String configType, Long configVersion) {
+    TypedQuery<Long> query = entityManagerProvider.get().createQuery("SELECT scv.version " +
+        "FROM ServiceConfigEntity scv JOIN scv.clusterConfigEntities cc " +
+        "WHERE cc.clusterId=?1 AND cc.type = ?2 AND cc.version = ?3", Long.class);
+    return daoUtils.selectList(query, clusterId, configType, configVersion);
+  }
+
+  public ServiceConfigApplicationEntity getLastApplication(Long clusterId, String serviceName) {
+    TypedQuery<ServiceConfigApplicationEntity> query = entityManagerProvider.get().
+        createQuery("SELECT sca FROM ServiceConfigEntity scv JOIN scv.serviceConfigApplicationEntities sca " +
+                "WHERE scv.clusterId = ?1 AND scv.serviceName = ?2 ORDER BY sca.applyTimestamp DESC",
+            ServiceConfigApplicationEntity.class);
+    return daoUtils.selectOne(query, clusterId, serviceName);
+  }
+
+  public ServiceConfigEntity findMaxVersion(Long clusterId, String serviceName) {
+    TypedQuery<ServiceConfigEntity> query = entityManagerProvider.get().createQuery("SELECT scv FROM ServiceConfigEntity scv " +
+      "WHERE scv.clusterId=?1 AND scv.serviceName=?2 AND scv.version = (" +
+      "SELECT max(scv2.version) FROM ServiceConfigEntity scv2 " +
+      "WHERE scv2.clusterId=?1 AND scv2.serviceName=?2)", ServiceConfigEntity.class);
+
+    return daoUtils.selectSingle(query, clusterId, serviceName);
+  }
+
+  public List<ServiceConfigApplicationEntity> getServiceConfigApplications(Long clusterId) {
+    TypedQuery<ServiceConfigApplicationEntity> query = entityManagerProvider.get().createQuery(
+        "SELECT apply FROM ServiceConfigApplicationEntity apply JOIN apply.serviceConfigEntity scv " +
+            "WHERE scv.clusterId=?1 ORDER BY apply.applyTimestamp DESC", ServiceConfigApplicationEntity.class);
+
+    return daoUtils.selectList(query, clusterId);
+  }
+
+  @Transactional
+  public void create(ServiceConfigEntity serviceConfigEntity) {
+    entityManagerProvider.get().persist(serviceConfigEntity);
+  }
+
+  @Transactional
+  public ServiceConfigEntity merge(ServiceConfigEntity serviceConfigEntity) {
+    return entityManagerProvider.get().merge(serviceConfigEntity);
+  }
+
+  @Transactional
+  public void remove(ServiceConfigEntity serviceConfigEntity) {
+    entityManagerProvider.get().remove(merge(serviceConfigEntity));
+  }
+
+
+}

+ 51 - 11
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java

@@ -21,20 +21,32 @@ package org.apache.ambari.server.orm.entities;
 import javax.persistence.*;
 import java.util.Collection;
 
-@IdClass(ClusterConfigEntityPK.class)
-@Table(name = "clusterconfig")
 @Entity
+@Table(name = "clusterconfig",
+  uniqueConstraints = {@UniqueConstraint(name = "UQ_config_type_tag", columnNames = {"cluster_id", "type_name", "version_tag"}),
+    @UniqueConstraint(name = "UQ_config_type_version", columnNames = {"cluster_id", "type_name", "version"})})
+@TableGenerator(name = "config_id_generator",
+  table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "value"
+  , pkColumnValue = "config_id_seq"
+  , initialValue = 1
+  , allocationSize = 1
+)
 public class ClusterConfigEntity {
 
   @Id
+  @Column(name = "config_id")
+  @GeneratedValue(strategy = GenerationType.TABLE, generator = "config_id_generator")
+  private Long configId;
+
   @Column(name = "cluster_id", nullable = false, insertable = false, updatable = false, length = 10)
   private Long clusterId;
 
-  @Id
   @Column(name = "type_name")
   private String type;
 
-  @Id
+  @Column(name = "version")
+  private Long version;
+
   @Column(name = "version_tag")
   private String tag;
 
@@ -58,6 +70,17 @@ public class ClusterConfigEntity {
   @OneToMany(mappedBy = "clusterConfigEntity")
   private Collection<ConfigGroupConfigMappingEntity> configGroupConfigMappingEntities;
 
+  @ManyToMany(mappedBy = "clusterConfigEntities")
+  private Collection<ServiceConfigEntity> serviceConfigEntities;
+
+  public Long getConfigId() {
+    return configId;
+  }
+
+  public void setConfigId(Long configId) {
+    this.configId = configId;
+  }
+
   public Long getClusterId() {
     return clusterId;
   }
@@ -65,19 +88,27 @@ public class ClusterConfigEntity {
   public void setClusterId(Long clusterId) {
     this.clusterId = clusterId;
   }
-  
+
   public String getType() {
     return type;
   }
-  
+
   public void setType(String typeName) {
     type = typeName;
   }
-  
+
+  public Long getVersion() {
+    return version;
+  }
+
+  public void setVersion(Long version) {
+    this.version = version;
+  }
+
   public String getTag() {
     return tag;
   }
-  
+
   public void setTag(String versionTag) {
     tag = versionTag;
   }
@@ -89,11 +120,11 @@ public class ClusterConfigEntity {
   public void setData(String data) {
     this.configJson = data;
   }
-  
+
   public long getTimestamp() {
     return timestamp;
   }
-  
+
   public void setTimestamp(long stamp) {
     timestamp = stamp;
   }
@@ -117,7 +148,7 @@ public class ClusterConfigEntity {
     if (configJson != null ? !configJson.equals(that.configJson) : that.configJson != null)
       return false;
     if (configAttributesJson != null ? !configAttributesJson
-        .equals(that.configAttributesJson) : that.configAttributesJson != null)
+      .equals(that.configAttributesJson) : that.configAttributesJson != null)
       return false;
 
     return true;
@@ -146,4 +177,13 @@ public class ClusterConfigEntity {
   public void setConfigGroupConfigMappingEntities(Collection<ConfigGroupConfigMappingEntity> configGroupConfigMappingEntities) {
     this.configGroupConfigMappingEntities = configGroupConfigMappingEntities;
   }
+
+
+  public Collection<ServiceConfigEntity> getServiceConfigEntities() {
+    return serviceConfigEntities;
+  }
+
+  public void setServiceConfigEntities(Collection<ServiceConfigEntity> serviceConfigEntities) {
+    this.serviceConfigEntities = serviceConfigEntities;
+  }
 }

+ 0 - 83
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntityPK.java

@@ -1,83 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ambari.server.orm.entities;
-
-import javax.persistence.Column;
-import javax.persistence.Id;
-import java.io.Serializable;
-
-@SuppressWarnings("serial")
-public class ClusterConfigEntityPK implements Serializable {
-  private Long clusterId;
-
-  @Id
-  @Column(name = "cluster_id", nullable = false, insertable = true, updatable = true, length = 10)
-  public Long getClusterId() {
-    return clusterId;
-  }
-
-  public void setClusterId(Long clusterId) {
-    this.clusterId = clusterId;
-  }
-
-  private String type;
-  @Id
-  @Column(name = "type_name", nullable = false, insertable = true, updatable = false)
-  public String getType() {
-    return type;
-  }
-
-  public void setType(String typeName) {
-    type = typeName;
-  }
-
-  private String tag;
-  @Id
-  @Column(name="version_tag", nullable = false, insertable = true, updatable = false)
-  public String getTag() {
-    return tag;
-  }
-
-  public void setTag(String configTag) {
-    tag = configTag;
-  }
-
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-
-    ClusterConfigEntityPK that = (ClusterConfigEntityPK) o;
-
-    if (clusterId != null ? !clusterId.equals(that.clusterId) : that.clusterId != null) return false;
-    if (type != null ? !type.equals(that.type) : that.type != null) return false;
-    if (tag != null ? !tag.equals(that.tag) : that.tag != null) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = clusterId !=null ? clusterId.intValue() : 0;
-    result = 31 * result + (type != null ? type.hashCode() : 0);
-    result = 31 * result + (tag != null ? tag.hashCode() : 0);
-    return result;
-  }
-}

+ 11 - 8
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java

@@ -19,18 +19,21 @@ 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.IdClass;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
+import javax.persistence.TableGenerator;
 
 /**
  * Entity that maps to a cluster config mapping.
  */
-@IdClass(ClusterConfigMappingEntityPK.class)
 @Table(name = "clusterconfigmapping")
 @Entity
+@IdClass(ClusterConfigMappingEntityPK.class)
 public class ClusterConfigMappingEntity {
 
   @Id
@@ -46,7 +49,7 @@ public class ClusterConfigMappingEntity {
   private Long createTimestamp;
 
   @Column(name = "version_tag", insertable = true, updatable = false, nullable = false)
-  private String versionTag;
+  private String tag;
 
   @Column(name = "selected", insertable = true, updatable = true, nullable = false)
   private int selectedInd = 0;
@@ -81,12 +84,12 @@ public class ClusterConfigMappingEntity {
     createTimestamp = timestamp;
   }
   
-  public String getVersion() {
-    return versionTag;
+  public String getTag() {
+    return tag;
   }
   
-  public void setVersion(String version) {
-    versionTag = version;
+  public void setTag(String version) {
+    tag = version;
   }
  
   public int isSelected() {
@@ -118,6 +121,6 @@ public class ClusterConfigMappingEntity {
   public void setClusterEntity(ClusterEntity entity) {
     clusterEntity = entity;
   }
-  
-  
+
+
 }

+ 10 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java

@@ -93,6 +93,9 @@ public class ClusterEntity {
   @OneToMany(mappedBy = "clusterEntity", cascade = CascadeType.ALL)
   private Collection<RequestScheduleEntity> requestScheduleEntities;
 
+  @OneToMany(mappedBy = "clusterEntity", cascade = CascadeType.REMOVE)
+  private Collection<ServiceConfigEntity> serviceConfigEntities;
+
   public Long getClusterId() {
     return clusterId;
   }
@@ -231,4 +234,11 @@ public class ClusterEntity {
     this.requestScheduleEntities = requestScheduleEntities;
   }
 
+  public Collection<ServiceConfigEntity> getServiceConfigEntities() {
+    return serviceConfigEntities;
+  }
+
+  public void setServiceConfigEntities(Collection<ServiceConfigEntity> serviceConfigEntities) {
+    this.serviceConfigEntities = serviceConfigEntities;
+  }
 }

+ 102 - 0
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;
+  }
+}

+ 146 - 0
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;
+  }
+}

+ 33 - 5
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.
@@ -297,6 +318,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
    *

+ 20 - 2
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
@@ -70,6 +82,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

+ 3 - 8
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);
     }

+ 43 - 12
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
@@ -124,6 +139,14 @@ public class ConfigImpl implements Config {
     this.properties.putAll(properties);
   }
 
+  @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) {
@@ -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()));

+ 52 - 0
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();
+  }
+}

+ 23 - 12
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) {

+ 334 - 54
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) {

+ 2 - 0
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);
 

+ 9 - 13
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);
       }
 

+ 7 - 7
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 {
   
 }
 
-  
+  

+ 3 - 3
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);
                 }
               }

+ 3 - 12
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) {

+ 13 - 1
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'

+ 13 - 1
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}');
 

+ 20 - 2
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'

+ 24 - 3
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'

+ 2 - 0
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" />-->

+ 4 - 0
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"
   },

+ 11 - 1
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",

+ 4 - 4
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);

+ 2 - 2
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);
 

+ 2 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/resources/ClusterResourceDefinitionTest.java

@@ -50,7 +50,7 @@ public class ClusterResourceDefinitionTest {
     ResourceDefinition resource = new ClusterResourceDefinition();
     Set<SubResourceDefinition> subResources = resource.getSubResourceDefinitions();
 
-    assertEquals(7, subResources.size());
+    assertEquals(8, subResources.size());
     assertTrue(includesType(subResources, Resource.Type.Service));
     assertTrue(includesType(subResources, Resource.Type.Host));
     assertTrue(includesType(subResources, Resource.Type.Configuration));
@@ -58,6 +58,7 @@ public class ClusterResourceDefinitionTest {
     assertTrue(includesType(subResources, Resource.Type.Workflow));
     assertTrue(includesType(subResources, Resource.Type.ConfigGroup));
     assertTrue(includesType(subResources, Resource.Type.AlertDefinition));
+    assertTrue(includesType(subResources, Resource.Type.ServiceConfigVersion));
   }
 
   @Test

+ 24 - 22
ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java

@@ -1583,7 +1583,7 @@ public class AmbariManagementControllerTest {
     Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String,String>>();
 
     Config c1 = new ConfigImpl(cluster, "hdfs-site", properties, propertiesAttributes, injector);
-    c1.setVersionTag("v1");
+    c1.setTag("v1");
     cluster.addConfig(c1);
     c1.persist();
     configs.put(c1.getType(), c1);
@@ -1631,9 +1631,9 @@ public class AmbariManagementControllerTest {
     Map<String, String> mapRequestProps = new HashMap<String, String>();
     mapRequestProps.put("context", "Called from a test");
 
-    c1.setVersionTag("v1");
-    c2.setVersionTag("v1");
-    c3.setVersionTag("v1");
+    c1.setTag("v1");
+    c2.setTag("v1");
+    c3.setTag("v1");
 
     cluster.addConfig(c1);
     cluster.addConfig(c2);
@@ -3871,13 +3871,13 @@ public class AmbariManagementControllerTest {
         new HashMap<String, String>() {{
           put("key1", "value1");
         }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
         new HashMap<String, String>() {{
           put("key1", "value1");
         }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
 
     cluster.addConfig(config1);
     cluster.addConfig(config2);
@@ -4002,13 +4002,13 @@ public class AmbariManagementControllerTest {
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
 
     cluster.addConfig(config1);
     cluster.addConfig(config2);
@@ -4203,13 +4203,15 @@ public class AmbariManagementControllerTest {
         new HashMap<String, String>() {{
           put("key1", "value1");
         }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
         new HashMap<String, String>() {{
           put("key1", "value1");
         }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
+    config1.persist();
+    config2.persist();
 
     cluster.addConfig(config1);
     cluster.addConfig(config2);
@@ -4465,12 +4467,12 @@ public class AmbariManagementControllerTest {
     ConfigFactory cf = injector.getInstance(ConfigFactory.class);
     Config config1 = cf.createNew(cluster, "global",
         new HashMap<String, String>(){{ put("key1", "value1"); }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
     config1.setPropertiesAttributes(new HashMap<String, Map<String, String>>(){{ put("attr1", new HashMap<String, String>()); }});
 
     Config config2 = cf.createNew(cluster, "core-site",
         new HashMap<String, String>(){{ put("key1", "value1"); }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
     config2.setPropertiesAttributes(new HashMap<String, Map<String, String>>(){{ put("attr2", new HashMap<String, String>()); }});
 
     cluster.addConfig(config1);
@@ -6259,13 +6261,13 @@ public class AmbariManagementControllerTest {
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
 
     cluster.addConfig(config1);
     cluster.addConfig(config2);
@@ -6355,13 +6357,13 @@ public class AmbariManagementControllerTest {
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
       }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version1");
+    config2.setTag("version1");
 
     cluster.addConfig(config1);
     cluster.addConfig(config2);
@@ -6723,7 +6725,7 @@ public class AmbariManagementControllerTest {
     Cluster cluster = clusters.getCluster(clusterName);
     final Config config = new ConfigImpl("core-site");
     config.setProperties(configs);
-    config.setVersionTag("version122");
+    config.setTag("version122");
     Long groupId = createConfigGroup(cluster, "g1", "t1",
       new ArrayList<String>() {{ add("h1"); }},
       new ArrayList<Config>() {{ add(config); }});
@@ -6736,7 +6738,7 @@ public class AmbariManagementControllerTest {
 
     final Config config2 = new ConfigImpl("mapred-site");
     config2.setProperties(configs);
-    config2.setVersionTag("version122");
+    config2.setTag("version122");
     groupId = createConfigGroup(cluster, "g2", "t2",
       new ArrayList<String>() {{ add("h1"); }},
       new ArrayList<Config>() {{ add(config2); }});
@@ -6878,7 +6880,7 @@ public class AmbariManagementControllerTest {
 
     final Config config = new ConfigImpl("hdfs-site");
     config.setProperties(configs);
-    config.setVersionTag("version122");
+    config.setTag("version122");
     Long groupId = createConfigGroup(clusters.getCluster(clusterName), "g1", "t1",
         new ArrayList<String>() {{
           add("h1");
@@ -6984,7 +6986,7 @@ public class AmbariManagementControllerTest {
 
     final Config config = new ConfigImpl("hdfs-site");
     config.setProperties(configs);
-    config.setVersionTag("version122");
+    config.setTag("version122");
     Long groupId = createConfigGroup(clusters.getCluster(clusterName), "g1", "t1",
       new ArrayList<String>() {{ add("h1"); add("h2"); }},
       new ArrayList<Config>() {{ add(config); }});
@@ -7589,7 +7591,7 @@ public class AmbariManagementControllerTest {
     }
   }
 
-  private void resetCluster(Cluster cluster, StackId currentStackId) {
+  private void resetCluster(Cluster cluster, StackId currentStackId) throws AmbariException{
     cluster.setDesiredStackVersion(currentStackId);
     for (Service service : cluster.getServices().values()) {
       service.setDesiredStackVersion(currentStackId);
@@ -10053,7 +10055,7 @@ public class AmbariManagementControllerTest {
         new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>(), new HashMap<String, Map<String,String>>()));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     config = cluster.getDesiredConfigByType("typeA");
-    Assert.assertEquals("v2", config.getVersionTag());
+    Assert.assertEquals("v2", config.getTag());
     Assert.assertNotNull(config);
     Assert.assertEquals(Integer.valueOf(0), Integer.valueOf(config.getProperties().size()));
     

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java

@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import static org.easymock.EasyMock.anyObject;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createMockBuilder;
@@ -2576,6 +2577,8 @@ public class ClusterResourceProviderTest {
         AbstractResourceProviderTest.Matcher.getClusterRequestSet(103L, null, null, "HDP-0.1", null), eq(mapRequestProps))).
         andReturn(response).once();
 
+    expect(managementController.getClusterUpdateResults(anyObject(ClusterRequest.class))).andReturn(null).anyTimes();
+
     // replay
     replay(managementController, response);
 
@@ -2632,6 +2635,8 @@ public class ClusterResourceProviderTest {
     expect(managementController.getClusters(EasyMock.<Set<ClusterRequest>>anyObject())).andReturn(nameResponse).times(2);
     expect(managementController.updateClusters(Collections.singleton(EasyMock.anyObject(ClusterRequest.class)),
         eq(mapRequestProps))).andReturn(response).times(1);
+    expect(managementController.getClusterUpdateResults(anyObject(ClusterRequest.class))).andReturn(null).anyTimes();
+
 
     // replay
     replay(managementController, response);

+ 1 - 5
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProviderTest.java

@@ -17,9 +17,6 @@
  */
 package org.apache.ambari.server.controller.internal;
 
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.ConfigGroupNotFoundException;
-import org.apache.ambari.server.ObjectNotFoundException;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.ConfigGroupRequest;
 import org.apache.ambari.server.controller.ConfigGroupResponse;
@@ -60,7 +57,6 @@ import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
-import static org.hamcrest.CoreMatchers.instanceOf;
 
 public class ConfigGroupResourceProviderTest {
 
@@ -153,7 +149,7 @@ public class ConfigGroupResourceProviderTest {
       configGroup, response);
 
     assertEquals("version100", captureConfigs.getValue().get("core-site")
-      .getVersionTag());
+      .getTag());
     assertTrue(captureHosts.getValue().containsKey("h1"));
     assertTrue(captureHosts.getValue().containsKey("h2"));
   }

+ 7 - 5
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProviderTest.java

@@ -51,6 +51,7 @@ public class ConfigurationResourceProviderTest {
 
     managementController.createConfiguration(AbstractResourceProviderTest.Matcher.getConfigurationRequest(
         "Cluster100", "type", "tag", new HashMap<String, String>(), null));
+    expectLastCall().andReturn(null);
 
     // replay
     replay(managementController, response);
@@ -99,6 +100,7 @@ public class ConfigurationResourceProviderTest {
             });
           }
         }));
+    expectLastCall().andReturn(null);
 
     // replay
     replay(managementController, response);
@@ -136,13 +138,13 @@ public class ConfigurationResourceProviderTest {
     AmbariManagementController managementController = createMock(AmbariManagementController.class);
 
     Set<ConfigurationResponse> allResponse = new HashSet<ConfigurationResponse>();
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null, null));
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null, null));
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag3", null, null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", 1L, null, null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", 2L, null, null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag3", 3L, null, null));
 
     Set<ConfigurationResponse> orResponse = new HashSet<ConfigurationResponse>();
-    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null, null));
-    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null, null));
+    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", 1L, null, null));
+    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", 2L, null, null));
 
     Capture<Set<ConfigurationRequest>> configRequestCapture1 = new Capture<Set<ConfigurationRequest>>();
     Capture<Set<ConfigurationRequest>> configRequestCapture2 = new Capture<Set<ConfigurationRequest>>();

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/JMXHostProviderTest.java

@@ -273,7 +273,7 @@ public class JMXHostProviderTest {
     controller.updateClusters(Collections.singleton(crReq), null);
     Cluster cluster = clusters.getCluster(clusterName);
     Assert.assertEquals("versionN", cluster.getDesiredConfigByType("hdfs-site")
-      .getVersionTag());
+      .getTag());
 
     ConfigurationRequest cr2 = new ConfigurationRequest(clusterName,
       "yarn-site", "versionN", yarnConfigs, null);
@@ -281,7 +281,7 @@ public class JMXHostProviderTest {
     controller.updateClusters(Collections.singleton(crReq), null);
 
     Assert.assertEquals("versionN", cluster.getDesiredConfigByType("yarn-site")
-      .getVersionTag());
+      .getTag());
     Assert.assertEquals("localhost:${ambari.dfs.datanode.http.port}", cluster.getDesiredConfigByType
       ("hdfs-site").getProperties().get(NAMENODE_PORT_V1));
   }

+ 2 - 8
ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java

@@ -17,8 +17,6 @@
  */
 package org.apache.ambari.server.state;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.persist.PersistService;
@@ -40,14 +38,10 @@ import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 public class ConfigGroupTest {
@@ -106,7 +100,7 @@ public class ConfigGroupTest {
     attributes.put("a", "true");
     propertiesAttributes.put("final", attributes);
     Config config = configFactory.createNew(cluster, "hdfs-site", properties, propertiesAttributes);
-    config.setVersionTag("testversion");
+    config.setTag("testversion");
 
     Host host = clusters.getHost("h1");
 
@@ -172,7 +166,7 @@ public class ConfigGroupTest {
     Config config = new ConfigImpl("test-site");
     config.setProperties(properties);
     config.setPropertiesAttributes(propertiesAttributes);
-    config.setVersionTag("version100");
+    config.setTag("version100");
 
     configGroup.addConfiguration(config);
     Assert.assertEquals(2, configGroup.getConfigurations().values().size());

+ 5 - 6
ambari-server/src/test/java/org/apache/ambari/server/state/ConfigHelperTest.java

@@ -31,7 +31,6 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.configgroup.ConfigGroup;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
-import org.apache.ambari.server.state.host.HostImpl;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -157,7 +156,7 @@ public class ConfigHelperTest {
   @Test
   public void testEffectiveTagsForHost() throws Exception {
     final Config config = new ConfigImpl("core-site");
-    config.setVersionTag("version122");
+    config.setTag("version122");
 
     Map<String, String> properties = new HashMap<String, String>();
     properties.put("a", "b");
@@ -184,7 +183,7 @@ public class ConfigHelperTest {
   @Test
   public void testEffectivePropertiesWithOverrides() throws Exception {
     final Config config1 = new ConfigImpl("core-site");
-    config1.setVersionTag("version122");
+    config1.setTag("version122");
 
     Map<String, String> properties = new HashMap<String, String>();
     properties.put("a", "b");
@@ -192,7 +191,7 @@ public class ConfigHelperTest {
     config1.setProperties(properties);
 
     final Config config2 = new ConfigImpl("global");
-    config2.setVersionTag("version122");
+    config2.setTag("version122");
     Map<String, String> properties2 = new HashMap<String, String>();
     properties2.put("namenode_heapsize", "1111");
     config2.setProperties(properties2);
@@ -222,7 +221,7 @@ public class ConfigHelperTest {
   @Test
   public void testEffectivePropertiesAttributesWithOverrides() throws Exception {
     final Config config1 = new ConfigImpl("core-site");
-    config1.setVersionTag("version122");
+    config1.setTag("version122");
 
     Map<String, String> attributes = new HashMap<String, String>();
     attributes.put("fs.trash.interval", "11");
@@ -232,7 +231,7 @@ public class ConfigHelperTest {
     config1.setPropertiesAttributes(config1Attributes);
 
     final Config config2 = new ConfigImpl("global");
-    config2.setVersionTag("version122");
+    config2.setTag("version122");
     attributes = new HashMap<String, String>();
     attributes.put("namenode_heapsize", "z");
     attributes.put("c", "q");

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/state/DesiredConfigTest.java

@@ -32,11 +32,11 @@ public class DesiredConfigTest {
   public void testDesiredConfig() throws Exception {
     DesiredConfig dc = new DesiredConfig();
     dc.setServiceName("service");
-    dc.setVersion("global");
+    dc.setTag("global");
     dc.setUser("_test");
 
     Assert.assertEquals("Expected service 'service'", "service", dc.getServiceName());
-    Assert.assertEquals("Expected version 'global'", "global", dc.getVersion());
+    Assert.assertEquals("Expected version 'global'", "global", dc.getTag());
     Assert.assertEquals("Expected no host overrides", 0, dc.getHostOverrides().size());
     Assert.assertEquals("Expected user '_test'", "_test", dc.getUser());
     

+ 11 - 10
ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java

@@ -314,15 +314,15 @@ public class ClusterTest {
     c2PropAttributes.get("final").put("x", "true");
     Config config1 = configFactory.createNew(c1, "global",
         new HashMap<String, String>() {{ put("a", "b"); }}, c1PropAttributes);
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
     
     Config config2 = configFactory.createNew(c1, "global",
         new HashMap<String, String>() {{ put("x", "y"); }}, c2PropAttributes);
-    config2.setVersionTag("version2");
+    config2.setTag("version2");
     
     Config config3 = configFactory.createNew(c1, "core-site",
         new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
-    config3.setVersionTag("version2");
+    config3.setTag("version2");
     
     c1.addConfig(config1);
     c1.addConfig(config2);
@@ -338,7 +338,7 @@ public class ClusterTest {
     
     c1.addDesiredConfig("_test", config2);
     res = c1.getDesiredConfigByType("global");
-    Assert.assertEquals("Expected version tag to be 'version2'", "version2", res.getVersionTag());
+    Assert.assertEquals("Expected version tag to be 'version2'", "version2", res.getTag());
     Assert.assertEquals("true", res.getPropertiesAttributes().get("final").get("x"));
   }
   
@@ -346,15 +346,15 @@ public class ClusterTest {
   public void testDesiredConfigs() throws Exception {
     Config config1 = configFactory.createNew(c1, "global",
         new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("version1");
+    config1.setTag("version1");
     
     Config config2 = configFactory.createNew(c1, "global",
         new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("version2");
+    config2.setTag("version2");
     
     Config config3 = configFactory.createNew(c1, "core-site",
         new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
-    config3.setVersionTag("version2");
+    config3.setTag("version2");
     
     c1.addConfig(config1);
     c1.addConfig(config2);
@@ -375,8 +375,8 @@ public class ClusterTest {
     Assert.assertFalse("Expect desired config not contain 'mapred-site'", desiredConfigs.containsKey("mapred-site"));
     Assert.assertTrue("Expect desired config contain " + config1.getType(), desiredConfigs.containsKey("global"));
     Assert.assertTrue("Expect desired config contain " + config3.getType(), desiredConfigs.containsKey("core-site"));
-    Assert.assertEquals("Expect desired config for global should be " + config1.getVersionTag(),
-        config1.getVersionTag(), desiredConfigs.get(config1.getType()).getVersion());
+    Assert.assertEquals("Expect desired config for global should be " + config1.getTag(),
+        config1.getTag(), desiredConfigs.get(config1.getType()).getTag());
     Assert.assertEquals("_test1", desiredConfigs.get(config1.getType()).getUser());
     Assert.assertEquals("_test3", desiredConfigs.get(config3.getType()).getUser());
     DesiredConfig dc = desiredConfigs.get(config1.getType());
@@ -401,6 +401,7 @@ public class ClusterTest {
   
   public ClusterEntity createDummyData() {
     ClusterEntity clusterEntity = new ClusterEntity();
+    clusterEntity.setClusterId(1L);
     clusterEntity.setClusterName("test_cluster1");
     clusterEntity.setClusterInfo("test_cluster_info1");
 
@@ -545,7 +546,7 @@ public class ClusterTest {
     Config config = configFactory.createNew(c1, "hdfs-site", new HashMap<String, String>(){{
       put("test", "test");
     }}, propAttributes);
-    config.setVersionTag("1");
+    config.setTag("1");
 
     host1.addDesiredConfig(c1.getClusterId(), true, "test", config);
 

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java

@@ -319,14 +319,14 @@ public class ClustersTest {
         new HashMap<String, String>() {{
           put("prop1", "val1");
         }}, new HashMap<String, Map<String,String>>());
-    config1.setVersionTag("1");
+    config1.setTag("1");
     config1.persist();
     
     Config config2 = injector.getInstance(ConfigFactory.class).createNew(cluster, "t1",
         new HashMap<String, String>() {{
           put("prop2", "val2");
         }}, new HashMap<String, Map<String,String>>());
-    config2.setVersionTag("2");
+    config2.setTag("2");
     config2.persist();
     
     // cluster desired config

+ 3 - 3
ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java

@@ -395,7 +395,7 @@ public class HostTest {
     }
     
     
-    config.setVersionTag("v1");
+    config.setTag("v1");
     host.addDesiredConfig(c1.getClusterId(), true, "_test", config);
     
     Map<String, DesiredConfig> map = host.getDesiredConfigs(c1.getClusterId());
@@ -404,12 +404,12 @@ public class HostTest {
     
     config = configFactory.createNew(c1, "global",
         new HashMap<String,String>() {{ put("c", "d"); }}, new HashMap<String, Map<String,String>>());
-    config.setVersionTag("v2");
+    config.setTag("v2");
     host.addDesiredConfig(c1.getClusterId(), true, "_test1", config);
     
     map = host.getDesiredConfigs(c1.getClusterId());
     Assert.assertTrue("Expect desired config to contain global", map.containsKey("global"));
-    Assert.assertEquals("Expect version to be 'v2'", "v2", map.get("global").getVersion());
+    Assert.assertEquals("Expect version to be 'v2'", "v2", map.get("global").getTag());
     Assert.assertEquals("Expect user to be '_test1'", "_test1", map.get("global").getUser());
     
     host.addDesiredConfig(c1.getClusterId(), false, "_test2", config);

+ 4 - 4
ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java

@@ -187,7 +187,7 @@ public class ServiceComponentHostTest {
     if (c.getConfig("time", String.valueOf(timestamp)) == null) {
       Config config = configFactory.createNew (c, "time",
           new HashMap<String, String>(), new HashMap<String, Map<String,String>>());
-      config.setVersionTag(String.valueOf(timestamp));
+      config.setTag(String.valueOf(timestamp));
       c.addConfig(config);
       config.persist();
     }
@@ -802,7 +802,7 @@ public class ServiceComponentHostTest {
     final Config c = configFactory.createNew(cluster, "hdfs-site",
         new HashMap<String, String>() {{ put("dfs.journalnode.http-address", "http://goo"); }}, 
         new HashMap<String, Map<String,String>>());
-    c.setVersionTag("version3");
+    c.setTag("version3");
     c.persist();
     cluster.addConfig(c);
     //host.addDesiredConfig(cluster.getClusterId(), true, "user", c);
@@ -869,7 +869,7 @@ public class ServiceComponentHostTest {
     final Config c1 = configFactory.createNew(cluster, "core-site",
       new HashMap<String, String>() {{ put("fs.trash.interval", "400"); }}, 
       new HashMap<String, Map<String,String>>());
-    c1.setVersionTag("version2");
+    c1.setTag("version2");
     c1.persist();
     cluster.addConfig(c1);
     configGroup = configGroupFactory.createNew(cluster, "g2",
@@ -1038,7 +1038,7 @@ public class ServiceComponentHostTest {
    */
   private void makeConfig(Cluster cluster, String type, String tag, Map<String, String> values, Map<String, Map<String, String>> attributes) {
     Config config = configFactory.createNew(cluster, type, values, attributes);
-    config.setVersionTag(tag);
+    config.setTag(tag);
     config.persist();
     cluster.addConfig(config);
     cluster.addDesiredConfig("user", config);

+ 2 - 7
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog150Test.java

@@ -32,7 +32,6 @@ import org.apache.ambari.server.orm.dao.HostComponentDesiredStateDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.KeyValueDAO;
 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;
@@ -263,11 +262,7 @@ public class UpgradeCatalog150Test {
 
     Long clusterId = clusterEntity.getClusterId();
 
-    ClusterConfigEntityPK configEntityPK = new ClusterConfigEntityPK();
-    configEntityPK.setClusterId(clusterId);
-    configEntityPK.setType("hdfs-log4j");
-    configEntityPK.setTag("version1");
-    ClusterConfigEntity configEntity = clusterDAO.findConfig(configEntityPK);
+    ClusterConfigEntity configEntity = clusterDAO.findConfig(clusterId, "hdfs-log4j", "version1");
     Assert.assertNull(configEntity);
 
     for (ClusterConfigMappingEntity ccme : clusterEntity.getConfigMappingEntities()) {
@@ -279,7 +274,7 @@ public class UpgradeCatalog150Test {
     UpgradeCatalog150 upgradeCatalog150 = injector.getInstance(UpgradeCatalog150.class);
     upgradeCatalog150.addMissingLog4jConfigs();
 
-    configEntity = clusterDAO.findConfig(configEntityPK);
+    configEntity = clusterDAO.findConfig(clusterId, "hdfs-log4j", "version1");
     Assert.assertNotNull(configEntity);
 
     //Get updated cluster

+ 4 - 4
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalogTest.java

@@ -161,7 +161,7 @@ public class UpgradeCatalogTest {
     testCatalog.updateConfigurationProperties("global",
       Collections.singletonMap("x", "y"), false, false);
     config = cluster.getDesiredConfigByType("global");
-    String version = config.getVersionTag();
+    String version = config.getTag();
     Assert.assertNotNull(config);
     Assert.assertNotSame("version1", version);
     Assert.assertTrue(config.getProperties().containsKey("x"));
@@ -172,17 +172,17 @@ public class UpgradeCatalogTest {
       Collections.singletonMap("x", "z"), true, false);
     config = cluster.getDesiredConfigByType("global");
     Assert.assertNotNull(config);
-    Assert.assertNotSame(version, config.getVersionTag());
+    Assert.assertNotSame(version, config.getTag());
     Assert.assertTrue(config.getProperties().containsKey("x"));
     Assert.assertEquals("z", config.getProperties().get("x"));
-    version = config.getVersionTag();
+    version = config.getTag();
 
     // Retain original
     testCatalog.updateConfigurationProperties("global",
       Collections.singletonMap("x", "y"), false, false);
     config = cluster.getDesiredConfigByType("global");
     Assert.assertNotNull(config);
-    Assert.assertSame(version, config.getVersionTag());
+    Assert.assertSame(version, config.getTag());
     Assert.assertTrue(config.getProperties().containsKey("x"));
     Assert.assertEquals("z", config.getProperties().get("x"));
   }