ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From swa...@apache.org
Subject [2/2] git commit: AMBARI-3606. Add ConfigGroup resource provider to support API calls. (swagle)
Date Fri, 01 Nov 2013 18:53:35 GMT
AMBARI-3606. Add ConfigGroup resource provider to support API calls. (swagle)


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

Branch: refs/heads/trunk
Commit: aa8e98f467ec64c3227023b3c3dadd61ca98f355
Parents: 12ac556
Author: Siddharth Wagle <swagle@hortonworks.com>
Authored: Fri Nov 1 11:53:17 2013 -0700
Committer: Siddharth Wagle <swagle@hortonworks.com>
Committed: Fri Nov 1 11:53:17 2013 -0700

----------------------------------------------------------------------
 .../ambari/server/api/handlers/ReadHandler.java |   1 +
 .../resources/ClusterResourceDefinition.java    |   1 +
 .../ConfigGroupResourceDefinition.java          | 100 +++
 .../resources/ResourceInstanceFactoryImpl.java  |   4 +
 .../server/api/services/ClusterService.java     |   8 +
 .../server/api/services/ConfigGroupService.java | 157 +++++
 .../services/serializers/JsonSerializer.java    |   1 -
 .../controller/AmbariManagementController.java  |  10 +-
 .../AmbariManagementControllerImpl.java         |  17 +-
 .../server/controller/ConfigGroupRequest.java   | 102 ++++
 .../server/controller/ConfigGroupResponse.java  | 100 +++
 .../server/controller/ControllerModule.java     |   5 +
 .../AbstractControllerResourceProvider.java     |   2 +
 .../internal/AbstractResourceProvider.java      |   1 -
 .../internal/ClusterControllerImpl.java         |  67 +-
 .../internal/ConfigGroupResourceProvider.java   | 609 +++++++++++++++++++
 .../internal/ConfigurationResourceProvider.java |   9 +-
 .../DefaultResourcePredicateEvaluator.java      |  32 +
 .../controller/internal/RequestStatusImpl.java  |   1 +
 .../controller/internal/ResourceImpl.java       |   2 +
 .../ambari/server/controller/spi/Resource.java  |   1 +
 .../spi/ResourcePredicateEvaluator.java         |  30 +
 .../ambari/server/orm/dao/ClusterDAO.java       |   7 +
 .../orm/dao/ConfigGroupConfigMappingDAO.java    |  10 +
 .../orm/dao/ConfigGroupHostMappingDAO.java      |  11 +
 .../server/orm/entities/ClusterEntity.java      |  11 +-
 .../org/apache/ambari/server/state/Cluster.java |  30 +
 .../apache/ambari/server/state/Clusters.java    |   2 +
 .../apache/ambari/server/state/ConfigImpl.java  |   9 +-
 .../server/state/cluster/ClusterImpl.java       | 133 ++++
 .../server/state/cluster/ClustersImpl.java      |   4 +
 .../server/state/configgroup/ConfigGroup.java   | 150 +++++
 .../state/configgroup/ConfigGroupFactory.java   |  39 ++
 .../state/configgroup/ConfigGroupImpl.java      | 513 ++++++++++++++++
 .../Ambari-DDL-Postgres-REMOTE-CREATE.sql       |   4 +-
 .../src/main/resources/key_properties.json      |   4 +
 .../src/main/resources/properties.json          |   9 +
 .../api/services/ConfigGroupServiceTest.java    | 115 ++++
 .../ConfigGroupResourceProviderTest.java        | 446 ++++++++++++++
 .../ambari/server/state/ConfigGroupTest.java    | 214 +++++++
 40 files changed, 2944 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
index e4c0064..ffdc1dc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java
@@ -78,6 +78,7 @@ public class ReadHandler implements RequestHandler {
     } catch (IllegalArgumentException e) {
       result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST,
           "Invalid Request: " + e.getMessage()));
+      LOG.error("Bad request: ", e);
     }  catch (RuntimeException e) {
       if (LOG.isErrorEnabled()) {
         LOG.error("Caught a runtime exception executing a query", e);

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
index eacc6b9..4b0e8e1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
@@ -54,6 +54,7 @@ public class ClusterResourceDefinition extends BaseResourceDefinition {
     setChildren.add(new SubResourceDefinition(Resource.Type.Configuration));
     setChildren.add(new SubResourceDefinition(Resource.Type.Request));
     setChildren.add(new SubResourceDefinition(Resource.Type.Workflow));
+    setChildren.add(new SubResourceDefinition(Resource.Type.ConfigGroup));
 
     return setChildren;
   }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigGroupResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigGroupResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigGroupResourceDefinition.java
new file mode 100644
index 0000000..d7bb57d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigGroupResourceDefinition.java
@@ -0,0 +1,100 @@
+/**
+ * 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 org.apache.ambari.server.controller.spi.Schema;
+import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ConfigGroupResourceDefinition extends BaseResourceDefinition {
+  /**
+   * Constructor.
+   */
+  public ConfigGroupResourceDefinition() {
+    super(Resource.Type.ConfigGroup);
+  }
+
+  @Override
+  public String getPluralName() {
+    return "config_groups";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "config_group";
+  }
+
+  @Override
+  public List<PostProcessor> getPostProcessors() {
+    List<PostProcessor> listProcessors = super.getPostProcessors();
+    listProcessors.add(new ConfigGroupHrefProcessor());
+
+    return listProcessors;
+  }
+
+  private class ConfigGroupHrefProcessor extends BaseHrefPostProcessor {
+
+    @Override
+    public void process(Request request, TreeNode<Resource> resultNode, String href) {
+      if (resultNode.getObject().getType() == Resource.Type.ConfigGroup) {
+
+        Resource r = resultNode.getObject();
+        Schema schema = ClusterControllerHelper.getClusterController().getSchema(r.getType());
+        Object clusterId = r.getPropertyValue(schema.getKeyPropertyId
+          (Resource.Type.Cluster));
+
+        Map<String, Object> configGroup = r.getPropertiesMap().get("ConfigGroup");
+        String partialUrl = href.substring(0, href.indexOf("/clusters/")
+          + "/clusters/".length()) + clusterId;
+
+        for (Map.Entry<String, Object> entry : configGroup.entrySet()) {
+          if (entry.getKey().contains("hosts") && entry.getValue() != null) {
+            Set<Map<String, Object>> hostSet = (Set<Map<String, Object>>) entry.getValue();
+
+            for (Map<String, Object> hostMap : hostSet) {
+              String idx = partialUrl + "/hosts/" + hostMap.get("host_name");
+              hostMap.put("href", idx);
+            }
+
+          } else if (entry.getKey().contains("desired_configs") && entry
+            .getValue() != null) {
+
+            Set<Map<String, Object>> configSet = (Set<Map<String,
+              Object>>) entry.getValue();
+
+            for (Map<String, Object> configMap : configSet) {
+              String idx = partialUrl + "/configurations?"
+                + "type=" + configMap.get("type")
+                + "&tag=" + configMap.get("tag");
+              configMap.put("href", idx);
+            }
+          }
+
+        }
+      } else {
+        super.process(request, resultNode, href);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index f3a53be..e71f1fc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -143,6 +143,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new RootServiceHostComponentResourceDefinition();
         break;
 
+      case ConfigGroup:
+        resourceDefinition = new ConfigGroupResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
index 8451b85..4da4385 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
@@ -192,6 +192,14 @@ public class ClusterService extends BaseService {
   }
 
   /**
+   * Gets the config group service
+   */
+  @Path("{clusterName}/config_groups")
+  public ConfigGroupService getConfigGroupService(@PathParam("clusterName") String clusterName) {
+    return new ConfigGroupService(clusterName);
+  }
+
+  /**
    * Create a cluster resource instance.
    *
    * @param clusterName cluster name

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigGroupService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigGroupService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigGroupService.java
new file mode 100644
index 0000000..4fb9a88
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigGroupService.java
@@ -0,0 +1,157 @@
+/**
+ * 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.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+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;
+
+/**
+ * Service responsible for management of Config Groups
+ */
+public class ConfigGroupService extends BaseService {
+  /**
+   * Parent cluster name.
+   */
+  private String m_clusterName;
+
+  /**
+   * Constructor
+   * @param m_clusterName
+   */
+  public ConfigGroupService(String m_clusterName) {
+    this.m_clusterName = m_clusterName;
+  }
+
+  /**
+   * Handles URL: /clusters/{clusterId}/config_groups
+   * Get all the config groups for a cluster.
+   *
+   * @param headers
+   * @param ui
+   * @return
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getConfigGroups(@Context HttpHeaders headers,
+                                  @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET,
+      createConfigGroupResource(m_clusterName, null));
+  }
+
+  /**
+   * Handles URL: /clusters/{clusterId}/config_groups/{groupId}
+   * Get details on a config group.
+   *
+   * @return
+   */
+  @GET
+  @Path("{groupId}")
+  @Produces("text/plain")
+  public Response getConfigGroup(@Context HttpHeaders headers,
+          @Context UriInfo ui, @PathParam("groupId") String groupId) {
+    return handleRequest(headers, null, ui, Request.Type.GET,
+        createConfigGroupResource(m_clusterName, groupId));
+  }
+
+  /**
+   * Handles POST /clusters/{clusterId}/config_groups
+   * Create a new config group
+   *
+   * @param body
+   * @param headers
+   * @param ui
+   * @return
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createConfigGroup(String body, @Context HttpHeaders headers,
+                                    @Context UriInfo ui) {
+    return handleRequest(headers, body, ui, Request.Type.POST,
+      createConfigGroupResource(m_clusterName, null));
+  }
+
+  /**
+   * Handles PUT /clusters/{clusterId}/config_groups/{groupId}
+   * Update a config group
+   *
+   * @param body
+   * @param headers
+   * @param ui
+   * @param groupId
+   * @return
+   */
+  @PUT
+  @Path("{groupId}")
+  @Produces("text/plain")
+  public Response updateConfigGroup(String body, @Context HttpHeaders
+    headers, @Context UriInfo ui, @PathParam("groupId") String groupId) {
+    return handleRequest(headers, body, ui, Request.Type.PUT,
+      createConfigGroupResource(m_clusterName, groupId));
+  }
+
+  /**
+   * Handles DELETE /clusters/{clusterId}/config_groups/{groupId}
+   * Delete a config group
+   *
+   * @param headers
+   * @param ui
+   * @param groupId
+   * @return
+   */
+  @DELETE
+  @Path("{groupId}")
+  @Produces("text/plain")
+  public Response deleteConfigGroup(@Context HttpHeaders headers,
+                                    @Context UriInfo ui,
+                                    @PathParam("groupId") String groupId) {
+    return handleRequest(headers, null, ui, Request.Type.DELETE,
+      createConfigGroupResource(m_clusterName, groupId));
+  }
+
+
+  /**
+   * Create a request resource instance.
+   *
+   * @param clusterName  cluster name
+   * @param groupId    config group id
+   *
+   * @return a request resource instance
+   */
+  ResourceInstance createConfigGroupResource(String clusterName,
+                                             String groupId) {
+    Map<Resource.Type,String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Cluster, clusterName);
+    mapIds.put(Resource.Type.ConfigGroup, groupId);
+
+    return createResource(Resource.Type.ConfigGroup, mapIds);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
index bb5eddb..038ac90 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
@@ -101,7 +101,6 @@ public class JsonSerializer implements ResultSerializer {
   }
 
   private void processNode(TreeNode<Resource> node) throws IOException {
-
     if (isObject(node)) {
       m_generator.writeStartObject();
       writeHref(node);

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
index 32f25ea..40389c2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
@@ -23,12 +23,14 @@ import org.apache.ambari.server.actionmanager.ActionManager;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.ConfigFactory;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentFactory;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.ServiceFactory;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 
 import java.util.Collection;
 import java.util.List;
@@ -486,5 +488,11 @@ public interface AmbariManagementController {
                                             Map<String, Map<State, List<ServiceComponentHost>>> changedHosts,
                                             Collection<ServiceComponentHost> ignoredHosts,
                                             boolean runSmokeTest, boolean reconfigureClients) throws AmbariException;
-  }
+
+  public ConfigGroupFactory getConfigGroupFactory();
+
+  public ConfigFactory getConfigFactory();
+
+  public String getAuthName();
+}
   

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 78c3c90..0c8119b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -83,6 +83,7 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostInstallEvent;
 import org.apache.ambari.server.state.svccomphost.ServiceComponentHostMaintenanceEvent;
@@ -151,6 +152,8 @@ public class AmbariManagementControllerImpl implements
   private Configuration configs;
   @Inject
   private AbstractRootServiceResponseFactory rootServiceResponseFactory;
+  @Inject
+  private ConfigGroupFactory configGroupFactory;
 
   final private String masterHostname;
   final private Integer masterPort;
@@ -1262,7 +1265,7 @@ public class AmbariManagementControllerImpl implements
 
     // HACK HACK HACK if the service has configs that are NOT included
     // in cluster-level, then use them anyway.  THIS IS GENERALLY A BAD
-    // IDEA, but is included for backward compatability.  Do not check host
+    // IDEA, but is included for backward compatibility.  Do not check host
     // overrides, because that wasn't in the version where this code would
     // be the case.
     Service service = cluster.getService(serviceName);
@@ -3195,7 +3198,7 @@ public class AmbariManagementControllerImpl implements
   /**
    * @return the authenticated user's name
    */
-  private String getAuthName() {
+  public String getAuthName() {
     return AuthorizationHelper.getAuthenticatedName(configs.getAnonymousAuditName());
   }
 
@@ -3292,6 +3295,16 @@ public class AmbariManagementControllerImpl implements
   }
 
   @Override
+  public ConfigGroupFactory getConfigGroupFactory() {
+    return configGroupFactory;
+  }
+
+  @Override
+  public ConfigFactory getConfigFactory() {
+    return configFactory;
+  }
+
+  @Override
   public ActionManager getActionManager() {
     return actionManager;
   }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupRequest.java
new file mode 100644
index 0000000..4c0d3a2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupRequest.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.controller;
+
+import org.apache.ambari.server.state.Config;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ConfigGroupRequest {
+  private Long id;
+  private String clusterName;
+  private String groupName;
+  private String tag;
+  private String description;
+  private Set<String> hosts;
+  private Map<String, Config> configs;
+
+  public ConfigGroupRequest(Long id, String clusterName, String groupName,
+                            String tag, String description, Set<String> hosts,
+                            Map<String, Config> configs) {
+    this.id = id;
+    this.clusterName = clusterName;
+    this.groupName = groupName;
+    this.tag = tag;
+    this.description = description;
+    this.hosts = hosts;
+    this.configs = configs;
+  }
+
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public void setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+  }
+
+  public String getGroupName() {
+    return groupName;
+  }
+
+  public void setGroupName(String groupName) {
+    this.groupName = groupName;
+  }
+
+  public String getTag() {
+    return tag;
+  }
+
+  public void setTag(String tag) {
+    this.tag = tag;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public Set<String> getHosts() {
+    return hosts;
+  }
+
+  public void setHosts(Set<String> hosts) {
+    this.hosts = hosts;
+  }
+
+  public Map<String, Config> getConfigs() {
+    return configs;
+  }
+
+  public void setConfigs(Map<String, Config> configs) {
+    this.configs = configs;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupResponse.java
new file mode 100644
index 0000000..a9f0687
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigGroupResponse.java
@@ -0,0 +1,100 @@
+/**
+ * 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 java.util.Map;
+import java.util.Set;
+
+public class ConfigGroupResponse {
+  private Long id;
+  private String clusterName;
+  private String groupName;
+  private String tag;
+  private String description;
+  private Set<Map<String, Object>> hosts;
+  private Set<Map<String, Object>> configVersions;
+
+  public ConfigGroupResponse(Long id, String clusterName,
+          String groupName, String tag, String description,
+          Set<Map<String, Object>> hosts,
+          Set<Map<String, Object>> configVersions) {
+    this.id = id;
+    this.clusterName = clusterName;
+    this.groupName = groupName;
+    this.tag = tag;
+    this.description = description;
+    this.hosts = hosts;
+    this.configVersions = configVersions;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(Long id) {
+    this.id = id;
+  }
+
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  public void setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+  }
+
+  public String getGroupName() {
+    return groupName;
+  }
+
+  public void setGroupName(String groupName) {
+    this.groupName = groupName;
+  }
+
+  public String getTag() {
+    return tag;
+  }
+
+  public void setTag(String tag) {
+    this.tag = tag;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public Set<Map<String, Object>> getHosts() {
+    return hosts;
+  }
+
+  public void setHosts(Set<Map<String, Object>> hosts) {
+    this.hosts = hosts;
+  }
+
+  public Set<Map<String, Object>> getConfigurations() {
+    return configVersions;
+  }
+
+  public void setConfigurations(Set<Map<String, Object>> configurations) {
+    this.configVersions = configurations;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index 597fbc5..f35e277 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -18,6 +18,9 @@
 
 package org.apache.ambari.server.controller;
 
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
+import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
+import org.apache.ambari.server.state.configgroup.ConfigGroupImpl;
 import org.apache.ambari.server.state.svccomphost.HBaseMasterPortScanner;
 import com.google.gson.Gson;
 import com.google.inject.Scopes;
@@ -165,6 +168,8 @@ public class ControllerModule extends AbstractModule {
         ServiceComponentHostFactory.class));
     install(new FactoryModuleBuilder().implement(
         Config.class, ConfigImpl.class).build(ConfigFactory.class));
+    install(new FactoryModuleBuilder().implement(
+      ConfigGroup.class, ConfigGroupImpl.class).build(ConfigGroupFactory.class));
     install(new FactoryModuleBuilder().build(StageFactory.class));
     bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
   }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
index 1a5a38b..464040e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
@@ -125,6 +125,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return new RootServiceComponentResourceProvider(propertyIds, keyPropertyIds, managementController);
       case RootServiceHostComponent:
         return new RootServiceHostComponentResourceProvider(propertyIds, keyPropertyIds, managementController);
+      case ConfigGroup:
+        return new ConfigGroupResourceProvider(propertyIds, keyPropertyIds, managementController);
       default:
         throw new IllegalArgumentException("Unknown type " + type);
     }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
index 0c8ddc6..cec3d1d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
@@ -340,7 +340,6 @@ public abstract class AbstractResourceProvider extends BaseProvider implements R
     return config;
   }
 
-
   // ----- Inner interface ---------------------------------------------------
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
index 5bad486..34bc117 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterControllerImpl.java
@@ -75,6 +75,12 @@ public class ClusterControllerImpl implements ClusterController {
    */
   private final ResourceComparator comparator = new ResourceComparator();
 
+  /**
+   * Predicate evaluator
+   */
+  private static final DefaultResourcePredicateEvaluator
+    DEFAULT_RESOURCE_PREDICATE_EVALUATOR =
+    new DefaultResourcePredicateEvaluator();
 
   // ----- Constructors ------------------------------------------------------
 
@@ -105,6 +111,9 @@ public class ClusterControllerImpl implements ClusterController {
     ensurePropertyProviders(type);
     Set<Resource> resources;
 
+    ResourcePredicateEvaluator evaluator = provider instanceof ResourcePredicateEvaluator ?
+      (ResourcePredicateEvaluator) provider : DEFAULT_RESOURCE_PREDICATE_EVALUATOR;
+
     if (provider == null) {
       resources = Collections.emptySet();
     } else {
@@ -128,13 +137,17 @@ public class ClusterControllerImpl implements ClusterController {
       if (pageRequest != null) {
         switch (pageRequest.getStartingPoint()) {
           case Beginning:
-            return getPageFromOffset(pageRequest.getPageSize(), 0, sortedResources, predicate);
+            return getPageFromOffset(pageRequest.getPageSize(), 0,
+              sortedResources, predicate, evaluator);
           case End:
-            return getPageToOffset(pageRequest.getPageSize(), -1, sortedResources, predicate);
+            return getPageToOffset(pageRequest.getPageSize(), -1,
+              sortedResources, predicate, evaluator);
           case OffsetStart:
-            return getPageFromOffset(pageRequest.getPageSize(), pageRequest.getOffset(), sortedResources, predicate);
+            return getPageFromOffset(pageRequest.getPageSize(),
+              pageRequest.getOffset(), sortedResources, predicate, evaluator);
           case OffsetEnd:
-            return getPageToOffset(pageRequest.getPageSize(), pageRequest.getOffset(), sortedResources, predicate);
+            return getPageToOffset(pageRequest.getPageSize(),
+              pageRequest.getOffset(), sortedResources, predicate, evaluator);
           // TODO : need to support the following cases for pagination
 //          case PredicateStart:
 //          case PredicateEnd:
@@ -143,7 +156,8 @@ public class ClusterControllerImpl implements ClusterController {
       resources = sortedResources;
     }
 
-    return new PageResponseImpl(new ResourceIterable(resources, predicate), 0, null, null);
+    return new PageResponseImpl(new ResourceIterable(resources, predicate, evaluator),
+      0, null, null);
   }
 
   @Override
@@ -417,7 +431,10 @@ public class ClusterControllerImpl implements ClusterController {
    *
    * @return a page response containing a page of resources
    */
-  private PageResponse getPageFromOffset(int pageSize, int offset, NavigableSet<Resource> resources, Predicate predicate) {
+  private PageResponse getPageFromOffset(int pageSize, int offset,
+                                         NavigableSet<Resource> resources,
+                                         Predicate predicate,
+                                         ResourcePredicateEvaluator evaluator) {
 
     int                currentOffset = 0;
     Resource           previous      = null;
@@ -435,7 +452,8 @@ public class ClusterControllerImpl implements ClusterController {
       pageResources.add(iterator.next());
     }
 
-    return new PageResponseImpl(new ResourceIterable(pageResources, predicate),
+    return new PageResponseImpl(new ResourceIterable(pageResources,
+        predicate, evaluator),
         currentOffset,
         previous,
         iterator.hasNext() ? iterator.next() : null);
@@ -451,7 +469,10 @@ public class ClusterControllerImpl implements ClusterController {
    *
    * @return a page response containing a page of resources
    */
-  private PageResponse getPageToOffset(int pageSize, int offset, NavigableSet<Resource> resources, Predicate predicate) {
+  private PageResponse getPageToOffset(int pageSize, int offset,
+                                       NavigableSet<Resource> resources,
+                                       Predicate predicate,
+                                       ResourcePredicateEvaluator evaluator) {
 
     int                currentOffset = resources.size() - 1;
     Resource           next          = null;
@@ -472,7 +493,8 @@ public class ClusterControllerImpl implements ClusterController {
       --currentOffset;
     }
 
-    return new PageResponseImpl(new ResourceIterable(new LinkedHashSet<Resource>(pageResources), predicate),
+    return new PageResponseImpl(new ResourceIterable(new
+        LinkedHashSet<Resource>(pageResources), predicate, evaluator),
         currentOffset + 1,
         iterator.hasNext() ? iterator.next() : null,
         next);
@@ -501,6 +523,11 @@ public class ClusterControllerImpl implements ClusterController {
      */
     private final Predicate predicate;
 
+    /**
+     * The predicate evaluator.
+     */
+    private final ResourcePredicateEvaluator evaluator;
+
     // ----- Constructors ----------------------------------------------------
 
     /**
@@ -509,16 +536,18 @@ public class ClusterControllerImpl implements ClusterController {
      * @param resources  the set of resources to iterate over
      * @param predicate  the predicate used to filter the set of resources
      */
-    private ResourceIterable(Set<Resource> resources, Predicate predicate) {
+    private ResourceIterable(Set<Resource> resources, Predicate predicate,
+                             ResourcePredicateEvaluator evaluator) {
       this.resources = resources;
       this.predicate = predicate;
+      this.evaluator = evaluator;
     }
 
     // ----- Iterable --------------------------------------------------------
 
     @Override
     public Iterator<Resource> iterator() {
-      return new ResourceIterator(resources, predicate);
+      return new ResourceIterator(resources, predicate, evaluator);
     }
   }
 
@@ -542,6 +571,10 @@ public class ClusterControllerImpl implements ClusterController {
      */
     private Resource nextResource;
 
+    /**
+     * The predicate evaluator.
+     */
+    private final ResourcePredicateEvaluator evaluator;
 
     // ----- Constructors ----------------------------------------------------
 
@@ -551,9 +584,11 @@ public class ClusterControllerImpl implements ClusterController {
      * @param resources  the set of resources to iterate over
      * @param predicate  the predicate used to filter the set of resources
      */
-    private ResourceIterator(Set<Resource> resources, Predicate predicate) {
-      this.iterator     = resources.iterator();
-      this.predicate    = predicate;
+    private ResourceIterator(Set<Resource> resources, Predicate predicate,
+                             ResourcePredicateEvaluator evaluator) {
+      this.iterator = resources.iterator();
+      this.predicate = predicate;
+      this.evaluator = evaluator;
       this.nextResource = getNextResource();
     }
 
@@ -591,8 +626,8 @@ public class ClusterControllerImpl implements ClusterController {
     private Resource getNextResource() {
       while (iterator.hasNext()) {
         Resource next = iterator.next();
-        
-        if (predicate == null || predicate.evaluate(next)) {
+
+        if (predicate == null || evaluator.evaluate(predicate, next)) {
           return next;
         }
       }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
new file mode 100644
index 0000000..982a163
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigGroupResourceProvider.java
@@ -0,0 +1,609 @@
+/**
+ * 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.ClusterNotFoundException;
+import org.apache.ambari.server.DuplicateResourceException;
+import org.apache.ambari.server.HostNotFoundException;
+import org.apache.ambari.server.ParentObjectNotFoundException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.ConfigGroupRequest;
+import org.apache.ambari.server.controller.ConfigGroupResponse;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.ResourcePredicateEvaluator;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.entities.ConfigGroupEntity;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.ConfigImpl;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
+import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ConfigGroupResourceProvider extends
+  AbstractControllerResourceProvider implements ResourcePredicateEvaluator {
+  protected static final String CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID =
+    PropertyHelper.getPropertyId("ConfigGroup", "cluster_name");
+  protected static final String CONFIGGROUP_ID_PROPERTY_ID = PropertyHelper
+    .getPropertyId("ConfigGroup", "id");
+  protected static final String CONFIGGROUP_NAME_PROPERTY_ID = PropertyHelper
+    .getPropertyId("ConfigGroup", "group_name");
+  protected static final String CONFIGGROUP_TAG_PROPERTY_ID = PropertyHelper
+    .getPropertyId("ConfigGroup", "tag");
+  protected static final String CONFIGGROUP_DESC_PROPERTY_ID = PropertyHelper
+    .getPropertyId("ConfigGroup", "description");
+  protected static final String CONFIGGROUP_HOSTNAME_PROPERTY_ID =
+    PropertyHelper.getPropertyId(null, "host_name");
+  public static final String CONFIGGROUP_HOSTS_PROPERTY_ID = PropertyHelper
+    .getPropertyId("ConfigGroup", "hosts");
+  public static final String CONFIGGROUP_CONFIGS_PROPERTY_ID =
+    PropertyHelper.getPropertyId("ConfigGroup", "desired_configs");
+
+  private static Set<String> pkPropertyIds = new HashSet<String>(Arrays
+    .asList(new String[] { CONFIGGROUP_ID_PROPERTY_ID }));
+
+  /**
+   * Create a  new resource provider for the given management controller.
+   *
+   * @param propertyIds          the property ids
+   * @param keyPropertyIds       the key property ids
+   * @param managementController the management controller
+   */
+  protected ConfigGroupResourceProvider(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 {
+
+    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();
+    for (Map<String, Object> propertyMap : request.getProperties()) {
+      requests.add(getConfigGroupRequest(propertyMap));
+    }
+    Set<ConfigGroupResponse> responses =
+      createResources(new Command<Set<ConfigGroupResponse>>() {
+        @Override
+        public Set<ConfigGroupResponse> invoke() throws AmbariException {
+          return createConfigGroups(requests);
+        }
+      });
+
+    notifyCreate(Resource.Type.ConfigGroup, request);
+
+    Set<Resource> associatedResources = new HashSet<Resource>();
+    for (ConfigGroupResponse response : responses) {
+      Resource resource = new ResourceImpl(Resource.Type.ConfigGroup);
+      resource.setProperty(CONFIGGROUP_ID_PROPERTY_ID, response.getId());
+      associatedResources.add(resource);
+    }
+
+    return getRequestStatus(null, associatedResources);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) throws
+       SystemException, UnsupportedPropertyException, NoSuchResourceException,
+       NoSuchParentResourceException {
+
+    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();
+    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
+      requests.add(getConfigGroupRequest(propertyMap));
+    }
+
+    Set<ConfigGroupResponse> responses = getResources(new Command<Set<ConfigGroupResponse>>() {
+      @Override
+      public Set<ConfigGroupResponse> invoke() throws AmbariException {
+        return getConfigGroups(requests);
+      }
+    });
+
+    Set<String>   requestedIds = getRequestPropertyIds(request, predicate);
+    Set<Resource> resources    = new HashSet<Resource>();
+
+    for (ConfigGroupResponse response : responses) {
+      Resource resource = new ResourceImpl(Resource.Type.ConfigGroup);
+
+      setResourceProperty(resource, CONFIGGROUP_ID_PROPERTY_ID,
+        response.getId(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID,
+        response.getClusterName(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_NAME_PROPERTY_ID,
+        response.getGroupName(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_TAG_PROPERTY_ID,
+        response.getTag(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_DESC_PROPERTY_ID,
+        response.getDescription(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_HOSTS_PROPERTY_ID,
+        response.getHosts(), requestedIds);
+      setResourceProperty(resource, CONFIGGROUP_CONFIGS_PROPERTY_ID,
+        response.getConfigurations(), requestedIds);
+
+      resources.add(resource);
+    }
+
+    return resources;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate) throws
+       SystemException, UnsupportedPropertyException,
+       NoSuchResourceException, NoSuchParentResourceException {
+
+    final Set<ConfigGroupRequest> requests = new HashSet<ConfigGroupRequest>();
+
+    Iterator<Map<String,Object>> iterator = request.getProperties().iterator();
+    if (iterator.hasNext()) {
+      for (Map<String, Object> propertyMap : getPropertyMaps(iterator.next(), predicate)) {
+        requests.add(getConfigGroupRequest(propertyMap));
+      }
+
+      modifyResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          updateConfigGroups(requests);
+          return null;
+        }
+      });
+    }
+
+    notifyUpdate(Resource.Type.ConfigGroup, request, predicate);
+
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate) throws
+       SystemException, UnsupportedPropertyException, NoSuchResourceException,
+       NoSuchParentResourceException {
+
+    for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
+      final ConfigGroupRequest configGroupRequest = getConfigGroupRequest(propertyMap);
+
+      modifyResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          deleteConfigGroup(configGroupRequest);
+          return null;
+        }
+      });
+    }
+
+    notifyDelete(Resource.Type.ConfigGroup, predicate);
+
+    return getRequestStatus(null);
+  }
+
+  private synchronized  Set<ConfigGroupResponse> getConfigGroups
+    (Set<ConfigGroupRequest> requests) throws AmbariException {
+    Set<ConfigGroupResponse> responses = new HashSet<ConfigGroupResponse>();
+    if (requests != null) {
+      for (ConfigGroupRequest request : requests) {
+        LOG.debug("Received a Config group request with"
+          + ", clusterName = " + request.getClusterName()
+          + ", groupId = " + request.getId()
+          + ", groupName = " + request.getGroupName()
+          + ", tag = " + request.getTag());
+
+        if (request.getClusterName() == null) {
+          LOG.warn("Cluster name is a required field.");
+          continue;
+        }
+
+        Cluster cluster = getManagementController().getClusters().getCluster
+          (request.getClusterName());
+        Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();
+
+        // By group id
+        if (request.getId() != null) {
+          ConfigGroup configGroup = configGroupMap.get(request.getId());
+          if (configGroup != null) {
+            responses.add(configGroup.convertToResponse());
+          }
+          continue;
+        }
+        // By group name
+        if (request.getGroupName() != null) {
+          for (ConfigGroup configGroup : configGroupMap.values()) {
+            if (configGroup.getName().equals(request.getGroupName())) {
+              responses.add(configGroup.convertToResponse());
+            }
+          }
+          continue;
+        }
+        // By tag only
+        if (request.getTag() != null && request.getHosts().isEmpty()) {
+          for (ConfigGroup configGroup : configGroupMap.values()) {
+            if (configGroup.getTag().equals(request.getTag())) {
+              responses.add(configGroup.convertToResponse());
+            }
+          }
+          continue;
+        }
+        // By hostnames only
+        if (!request.getHosts().isEmpty() && request.getTag() == null) {
+          for (String hostname : request.getHosts()) {
+            Map<Long, ConfigGroup> groupMap = cluster
+              .getConfigGroupsByHostname(hostname);
+
+            if (!groupMap.isEmpty()) {
+              for (ConfigGroup configGroup : groupMap.values()) {
+                responses.add(configGroup.convertToResponse());
+              }
+            }
+          }
+          continue;
+        }
+        // By tag and hostnames
+        if (request.getTag() != null && !request.getHosts().isEmpty()) {
+          for (ConfigGroup configGroup : configGroupMap.values()) {
+            // Has tag
+            if (configGroup.getTag().equals(request.getTag())) {
+              // Has a match with hosts
+              Set<String> groupHosts = new HashSet<String>(configGroup
+                .getHosts().keySet());
+              groupHosts.retainAll(request.getHosts());
+              if (!groupHosts.isEmpty()) {
+                responses.add(configGroup.convertToResponse());
+              }
+            }
+          }
+          continue;
+        }
+        // Select all
+        for (ConfigGroup configGroup : configGroupMap.values()) {
+          responses.add(configGroup.convertToResponse());
+        }
+      }
+    }
+    return responses;
+  }
+
+  private void verifyHostList(Cluster cluster, Map<String, Host> hosts,
+                              ConfigGroupRequest request) throws AmbariException {
+
+    Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();
+
+    if (configGroupMap != null) {
+      for (ConfigGroup configGroup : configGroupMap.values()) {
+        if (configGroup.getTag().equals(request.getTag())
+            && !configGroup.getId().equals(request.getId())) {
+          // Check the new host list for duplicated with this group
+          for (Host host : hosts.values()) {
+            if (configGroup.getHosts().containsKey(host.getHostName())) {
+              throw new DuplicateResourceException("Host is already " +
+                "associated with a config group"
+                + ", clusterName = " + configGroup.getClusterName()
+                + ", configGroupName = " + configGroup.getName()
+                + ", tag = " + configGroup.getTag()
+                + ", hostname = " + host.getHostName());
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private synchronized void deleteConfigGroup(ConfigGroupRequest request)
+    throws AmbariException {
+    if (request.getId() == null) {
+      throw new AmbariException("Config group id is a required field.");
+    }
+
+    Clusters clusters = getManagementController().getClusters();
+
+    Cluster cluster;
+    try {
+      cluster = clusters.getCluster(request.getClusterName());
+    } catch (ClusterNotFoundException e) {
+      throw new ParentObjectNotFoundException(
+        "Attempted to add a service to a cluster which doesn't exist", e);
+    }
+
+    cluster.deleteConfigGroup(request.getId());
+  }
+
+  private void validateRequest(ConfigGroupRequest request) {
+    if (request.getClusterName() == null
+      || request.getClusterName().isEmpty()
+      || request.getGroupName() == null
+      || request.getGroupName().isEmpty()
+      || request.getTag() == null
+      || request.getTag().isEmpty()) {
+
+      LOG.debug("Received a config group request with cluster name = " +
+        request.getClusterName() + ", group name = " + request.getGroupName()
+        + ", tag = " + request.getTag());
+
+      throw new IllegalArgumentException("Cluster name, " +
+        "group name and tag need to be provided.");
+
+    }
+  }
+
+  private synchronized Set<ConfigGroupResponse> createConfigGroups
+    (Set<ConfigGroupRequest> requests) throws AmbariException {
+
+    if (requests.isEmpty()) {
+      LOG.warn("Received an empty requests set");
+      return null;
+    }
+
+    Set<ConfigGroupResponse> configGroupResponses = new
+      HashSet<ConfigGroupResponse>();
+
+    Clusters clusters = getManagementController().getClusters();
+    ConfigGroupFactory configGroupFactory = getManagementController()
+      .getConfigGroupFactory();
+
+    for (ConfigGroupRequest request : requests) {
+
+      Cluster cluster;
+      try {
+        cluster = clusters.getCluster(request.getClusterName());
+      } catch (ClusterNotFoundException e) {
+        throw new ParentObjectNotFoundException(
+          "Attempted to add a config group to a cluster which doesn't exist", e);
+      }
+
+      validateRequest(request);
+
+      Map<Long, ConfigGroup> configGroupMap = cluster.getConfigGroups();
+      if (configGroupMap != null) {
+        for (ConfigGroup configGroup : configGroupMap.values()) {
+          if (configGroup.getName().equals(request.getGroupName())) {
+            throw new DuplicateResourceException("Config group already " +
+              "exists with the same name, "
+              + "clusterName = " + request.getClusterName()
+              + ", groupName = " + request.getGroupName());
+          }
+        }
+      }
+
+      // Find hosts
+      Map<String, Host> hosts = new HashMap<String, Host>();
+      if (request.getHosts() != null && !request.getHosts().isEmpty()) {
+        for (String hostname : request.getHosts()) {
+          Host host = clusters.getHost(hostname);
+          if (host == null) {
+            throw new HostNotFoundException(hostname);
+          }
+          hosts.put(hostname, host);
+        }
+      }
+
+      verifyHostList(cluster, hosts, request);
+
+      ConfigGroup configGroup = configGroupFactory.createNew(cluster,
+        request.getGroupName(),
+        request.getTag(), request.getDescription(),
+        request.getConfigs(), hosts);
+
+      // Persist before add, since id is auto-generated
+      Logger logger = LoggerFactory.getLogger("configchange");
+      logger.info("Persisting new Config group, "
+        + ", clusterName = " + configGroup.getClusterName()
+        + ", id = " + configGroup.getId()
+        + ", tag = " + configGroup.getTag()
+        + ", user = " + getManagementController().getAuthName());
+
+      configGroup.persist();
+      cluster.addConfigGroup(configGroup);
+
+      ConfigGroupResponse response = new ConfigGroupResponse(configGroup
+        .getId(), configGroup.getClusterName(), configGroup.getName(),
+        configGroup.getTag(), configGroup.getDescription(), null, null);
+
+      configGroupResponses.add(response);
+    }
+
+    return configGroupResponses;
+  }
+
+  private synchronized void updateConfigGroups
+    (Set<ConfigGroupRequest> requests) throws AmbariException {
+
+    if (requests.isEmpty()) {
+      LOG.warn("Received an empty requests set");
+      return;
+    }
+
+    Clusters clusters = getManagementController().getClusters();
+
+    for (ConfigGroupRequest request : requests) {
+
+      Cluster cluster;
+      try {
+        cluster = clusters.getCluster(request.getClusterName());
+      } catch (ClusterNotFoundException e) {
+        throw new ParentObjectNotFoundException(
+          "Attempted to add a service to a cluster which doesn't exist", e);
+      }
+
+      if (request.getId() == null) {
+        throw new AmbariException("Config group Id is a required parameter.");
+      }
+
+      validateRequest(request);
+
+      // Find config group
+      ConfigGroup configGroup = cluster.getConfigGroups().get(request.getId());
+      if (configGroup == null) {
+        throw new AmbariException("Config group not found"
+                                 + ", clusterName = " + request.getClusterName()
+                                 + ", groupId = " + request.getId());
+      }
+
+      // Update hosts
+      Map<String, Host> hosts = new HashMap<String, Host>();
+      if (request.getHosts() != null && !request.getHosts().isEmpty()) {
+        for (String hostname : request.getHosts()) {
+          Host host = clusters.getHost(hostname);
+          if (host == null) {
+            throw new HostNotFoundException(hostname);
+          }
+          hosts.put(hostname, host);
+        }
+      }
+
+      verifyHostList(cluster, hosts, request);
+
+      configGroup.setHosts(hosts);
+
+      // Update Configs
+      configGroup.setConfigurations(request.getConfigs());
+
+      // Save
+      configGroup.setName(request.getGroupName());
+      configGroup.setDescription(request.getDescription());
+      configGroup.setTag(request.getTag());
+
+      Logger logger = LoggerFactory.getLogger("configchange");
+      logger.info("Persisting updated Config group, "
+        + ", clusterName = " + configGroup.getClusterName()
+        + ", id = " + configGroup.getId()
+        + ", tag = " + configGroup.getTag()
+        + ", user = " + getManagementController().getAuthName());
+
+      configGroup.persist();
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private ConfigGroupRequest getConfigGroupRequest(Map<String, Object> properties) {
+    Object groupIdObj = properties.get(CONFIGGROUP_ID_PROPERTY_ID);
+    Long groupId = null;
+    if (groupIdObj != null)  {
+      groupId = groupIdObj instanceof Long ? (Long) groupIdObj :
+        Long.parseLong((String) groupIdObj);
+    }
+
+    ConfigGroupRequest request = new ConfigGroupRequest(
+      groupId,
+      (String) properties.get(CONFIGGROUP_CLUSTER_NAME_PROPERTY_ID),
+      (String) properties.get(CONFIGGROUP_NAME_PROPERTY_ID),
+      (String) properties.get(CONFIGGROUP_TAG_PROPERTY_ID),
+      (String) properties.get(CONFIGGROUP_DESC_PROPERTY_ID),
+      null,
+      null);
+
+    Map<String, Config> configurations = new HashMap<String, Config>();
+    Set<String> hosts = new HashSet<String>();
+
+    Object hostObj = properties.get(CONFIGGROUP_HOSTS_PROPERTY_ID);
+    if (hostObj != null) {
+      if (hostObj instanceof HashSet<?>) {
+        try {
+          Set<Map<String, String>> hostsSet = (Set<Map<String, String>>) hostObj;
+          for (Map<String, String> hostMap : hostsSet) {
+            if (hostMap.containsKey(CONFIGGROUP_HOSTNAME_PROPERTY_ID)) {
+              String hostname = hostMap.get(CONFIGGROUP_HOSTNAME_PROPERTY_ID);
+              hosts.add(hostname);
+            }
+          }
+        } catch (Exception e) {
+          LOG.warn("Host json in unparseable format. " + hostObj, e);
+        }
+      } else {
+        if (hostObj instanceof String) {
+          hosts.add((String) hostObj);
+        }
+      }
+    }
+
+    Object configObj = properties.get(CONFIGGROUP_CONFIGS_PROPERTY_ID);
+    if (configObj != null && configObj instanceof HashSet<?>) {
+      try {
+        Set<Map<String, Object>> configSet = (Set<Map<String, Object>>) configObj;
+        for (Map<String, Object> configMap : configSet) {
+          String type = (String) configMap.get(ConfigurationResourceProvider
+            .CONFIGURATION_CONFIG_TYPE_PROPERTY_ID);
+          String tag = (String) configMap.get(ConfigurationResourceProvider
+            .CONFIGURATION_CONFIG_TAG_PROPERTY_ID);
+
+          Map<String, String> configProperties = new HashMap<String, String>();
+
+          for (Map.Entry<String, Object> entry : configMap.entrySet()) {
+            String propertyCategory = PropertyHelper.getPropertyCategory(entry.getKey());
+            if (propertyCategory != null
+                && propertyCategory.equals("properties")
+                  && null != entry.getValue()) {
+              configProperties.put(PropertyHelper.getPropertyName(entry.getKey()),
+                entry.getValue().toString());
+            }
+          }
+
+          Config config = new ConfigImpl(type);
+          config.setVersionTag(tag);
+          config.setProperties(configProperties);
+
+          configurations.put(config.getType(), config);
+        }
+      } catch (Exception e) {
+        LOG.warn("Config json in unparseable format. " + configObj, e);
+      }
+    }
+
+    request.setConfigs(configurations);
+    request.setHosts(hosts);
+
+    return request;
+  }
+
+  /**
+   * Bypassing predicate evaluation for the lack of a matcher for a
+   * non-scalar resource
+   *
+   * @param predicate  the predicate
+   * @param resource   the resource
+   *
+   * @return
+   */
+  @Override
+  public boolean evaluate(Predicate predicate, Resource resource) {
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
index f9f6564..c40ad68 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
@@ -43,15 +43,18 @@ import java.util.Map.Entry;
 /**
  * Resource provider for configuration resources.
  */
-class ConfigurationResourceProvider extends AbstractControllerResourceProvider {
+public class ConfigurationResourceProvider extends
+  AbstractControllerResourceProvider {
 
   // ----- Property ID constants ---------------------------------------------
 
   // Configurations (values are part of query strings and body post, so they don't have defined categories)
   protected static final String CONFIGURATION_CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Config", "cluster_name");
   // TODO : should these be Config/type and Config/tag to be consistent?
-  protected static final String CONFIGURATION_CONFIG_TYPE_PROPERTY_ID  = PropertyHelper.getPropertyId(null, "type");
-  protected static final String CONFIGURATION_CONFIG_TAG_PROPERTY_ID   = PropertyHelper.getPropertyId(null, "tag");
+  public static final String CONFIGURATION_CONFIG_TYPE_PROPERTY_ID  =
+    PropertyHelper.getPropertyId(null, "type");
+  public static final String CONFIGURATION_CONFIG_TAG_PROPERTY_ID   =
+    PropertyHelper.getPropertyId(null, "tag");
 
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultResourcePredicateEvaluator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultResourcePredicateEvaluator.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultResourcePredicateEvaluator.java
new file mode 100644
index 0000000..9b843aa
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultResourcePredicateEvaluator.java
@@ -0,0 +1,32 @@
+/**
+ * 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.controller.spi.ResourcePredicateEvaluator;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * The default predicate evaluator.
+ */
+public class DefaultResourcePredicateEvaluator implements ResourcePredicateEvaluator {
+  @Override
+  public boolean evaluate(Predicate predicate, Resource resource) {
+    return predicate.evaluate(resource);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestStatusImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestStatusImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestStatusImpl.java
index 977538a..b0fde8a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestStatusImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestStatusImpl.java
@@ -22,6 +22,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
index e77ae76..d7c1e78 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ResourceImpl.java
@@ -41,6 +41,8 @@ public class ResourceImpl implements Resource {
   private final Map<String, Map<String, Object>> propertiesMap = new TreeMap<String, Map<String, Object>>();
 
 
+  private volatile boolean skipPredicateCheck = false;
+
   // ----- Constructors ------------------------------------------------------
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index e45d59d..3389fca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -76,6 +76,7 @@ public interface Resource {
     Component,
     HostComponent,
     Configuration,
+    ConfigGroup,
     Action,
     Request,
     Task,

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/ResourcePredicateEvaluator.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/ResourcePredicateEvaluator.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/ResourcePredicateEvaluator.java
new file mode 100644
index 0000000..425ef45
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/ResourcePredicateEvaluator.java
@@ -0,0 +1,30 @@
+/**
+ * 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.spi;
+
+public interface ResourcePredicateEvaluator {
+  /**
+   * Evaluate the given predicate for the given resource.
+   *
+   * @param predicate  the predicate
+   * @param resource   the resource
+   *
+   * @return true if the predicate evaluates to true for the resource
+   */
+  public boolean evaluate(Predicate predicate, Resource resource);
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java
index 3c9c509..6060edc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java
@@ -26,6 +26,7 @@ import javax.persistence.TypedQuery;
 
 import com.google.inject.Singleton;
 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;
@@ -86,6 +87,12 @@ public class ClusterDAO {
     entityManagerProvider.get().persist(entity);
   }
 
+  @Transactional
+  public ClusterConfigEntity findConfig(ClusterConfigEntityPK configEntityPK) {
+    return entityManagerProvider.get().find(ClusterConfigEntity.class,
+      configEntityPK);
+  }
+
   /**
    * Retrieve entity data from DB
    * @param clusterEntity entity to refresh

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupConfigMappingDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupConfigMappingDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupConfigMappingDAO.java
index 1b91dec..15ae9ee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupConfigMappingDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupConfigMappingDAO.java
@@ -85,4 +85,14 @@ public class ConfigGroupConfigMappingDAO {
     entityManagerProvider.get().remove(findByPK
       (configGroupConfigMappingEntityPK));
   }
+
+  @Transactional
+  public void removeAllByGroup(Long groupId) {
+    TypedQuery<Long> query = entityManagerProvider.get().createQuery
+      ("DELETE FROM ConfigGroupConfigMappingEntity configs WHERE configs" +
+        ".configGroupId = ?1", Long.class);
+
+    daoUtils.executeUpdate(query, groupId);
+    entityManagerProvider.get().flush();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupHostMappingDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupHostMappingDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupHostMappingDAO.java
index cdcdaa9..9ca3fee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupHostMappingDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ConfigGroupHostMappingDAO.java
@@ -25,6 +25,7 @@ import org.apache.ambari.server.orm.entities.ConfigGroupHostMappingEntity;
 import org.apache.ambari.server.orm.entities.ConfigGroupHostMappingEntityPK;
 import javax.persistence.EntityManager;
 import javax.persistence.NoResultException;
+import javax.persistence.Query;
 import javax.persistence.TypedQuery;
 import java.util.List;
 
@@ -100,4 +101,14 @@ public class ConfigGroupHostMappingDAO {
     entityManagerProvider.get().remove(findByPK
       (configGroupHostMappingEntityPK));
   }
+
+  @Transactional
+  public void removeAllByGroup(Long groupId) {
+    TypedQuery<Long> query = entityManagerProvider.get().createQuery
+      ("DELETE FROM ConfigGroupHostMappingEntity confighosts WHERE " +
+        "confighosts.configGroupId = ?1", Long.class);
+
+    daoUtils.executeUpdate(query, groupId);
+    entityManagerProvider.get().flush();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
index 28ce1d4..0ea5614 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterEntity.java
@@ -82,6 +82,9 @@ public class ClusterEntity {
   @OneToMany(mappedBy = "clusterEntity", cascade = CascadeType.ALL)
   private Collection<ClusterConfigMappingEntity> configMappingEntities;
 
+  @OneToMany(mappedBy = "clusterEntity", cascade = CascadeType.ALL)
+  private Collection<ConfigGroupEntity> configGroupEntities;
+
   public Long getClusterId() {
     return clusterId;
   }
@@ -197,6 +200,12 @@ public class ClusterEntity {
   public void setConfigMappingEntities(Collection<ClusterConfigMappingEntity> entities) {
     configMappingEntities = entities;
   }
-  
 
+  public Collection<ConfigGroupEntity> getConfigGroupEntities() {
+    return configGroupEntities;
+  }
+
+  public void setConfigGroupEntities(Collection<ConfigGroupEntity> configGroupEntities) {
+    this.configGroupEntities = configGroupEntities;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index fc97032..93e4500 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -25,6 +25,7 @@ import java.util.concurrent.locks.ReadWriteLock;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.ClusterResponse;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
 
 public interface Cluster {
 
@@ -225,4 +226,33 @@ public interface Cluster {
    * @return
    */
   Map<String, Map<String, DesiredConfig>> getAllHostsDesiredConfigs();
+
+  /**
+   * Add a new config group to the set of Config groups associated with this
+   * cluster
+   * @param configGroup
+   * @throws AmbariException
+   */
+  public void addConfigGroup(ConfigGroup configGroup) throws AmbariException;
+
+  /**
+   * Get all config groups associated with this cluster
+   * @return
+   * @throws AmbariException
+   */
+  public Map<Long, ConfigGroup> getConfigGroups() throws AmbariException;
+
+  /**
+   * Delete ths config group identified by the config group id
+   * @param id
+   * @throws AmbariException
+   */
+  public void deleteConfigGroup(Long id) throws AmbariException;
+
+  /**
+   * Find all config groups associated with the give hostname
+   * @param hostname
+   * @return Map of config group id to config group
+   */
+  public Map<Long, ConfigGroup> getConfigGroupsByHostname(String hostname);
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
index 634f594..8cabaeb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
 
 /**
  * Single entity that tracks all clusters and hosts that are managed
@@ -173,4 +174,5 @@ public interface Clusters {
    */
   public void deleteHost(String hostname)
       throws AmbariException;
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/aa8e98f4/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index fe6dc0f..2766a60 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -65,7 +65,14 @@ public class ConfigImpl implements Config {
     this.entity = entity;
     injector.injectMembers(this);
   }
-  
+
+  /**
+   * Constructor for clients not using factory.
+   */
+  public ConfigImpl(String type) {
+    this.type = type;
+  }
+
   @Override
   public String getType() {
     return type;


Mime
View raw message