浏览代码

AMBARI-5144. Allow cluster scoped configuratio to be specified in a blueprint

John Speidel 11 年之前
父节点
当前提交
1531a7edcf
共有 17 个文件被更改,包括 614 次插入26 次删除
  1. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
  2. 68 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java
  3. 43 12
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
  4. 125 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntity.java
  5. 88 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityPK.java
  6. 21 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java
  7. 12 1
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java
  8. 2 0
      ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
  9. 2 0
      ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
  10. 2 0
      ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
  11. 1 0
      ambari-server/src/main/resources/META-INF/persistence.xml
  12. 2 1
      ambari-server/src/main/resources/properties.json
  13. 109 7
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BlueprintResourceProviderTest.java
  14. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
  15. 69 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityPKTest.java
  16. 59 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityTest.java
  17. 8 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java

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

@@ -505,7 +505,7 @@ public class AmbariServer {
     StackDefinedPropertyProvider.init(injector);
     NagiosPropertyProvider.init(injector);
     AbstractControllerResourceProvider.init(injector.getInstance(ResourceProviderFactory.class));
-    BlueprintResourceProvider.init(injector.getInstance(BlueprintDAO.class));
+    BlueprintResourceProvider.init(injector.getInstance(BlueprintDAO.class), injector.getInstance(Gson.class));
     ClusterResourceProvider.injectBlueprintDAO(injector.getInstance(BlueprintDAO.class));
   }
 

+ 68 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java

@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import com.google.gson.Gson;
 import com.google.inject.Inject;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.DuplicateResourceException;
@@ -32,6 +33,7 @@ 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.dao.BlueprintDAO;
+import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
 import org.apache.ambari.server.orm.entities.BlueprintEntity;
 import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
@@ -70,6 +72,9 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
   protected static final String COMPONENT_PROPERTY_ID ="components";
   protected static final String COMPONENT_NAME_PROPERTY_ID ="name";
 
+  // Configurations
+  protected static final String CONFIGURATION_PROPERTY_ID = "configurations";
+
   // Primary Key Fields
   private static Set<String> pkPropertyIds =
       new HashSet<String>(Arrays.asList(new String[]{
@@ -80,6 +85,8 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
    */
   private static BlueprintDAO dao;
 
+  private static Gson jsonSerializer;
+
 
   // ----- Constructors ----------------------------------------------------
 
@@ -94,13 +101,15 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
   }
 
   /**
-   * Static initialization of DAO.
+   * Static initialization.
    *
    * @param blueprintDAO  blueprint data access object
+   * @param gson          gson json serializer
    */
   @Inject
-  public static void init(BlueprintDAO blueprintDAO) {
-    dao = blueprintDAO;
+  public static void init(BlueprintDAO blueprintDAO, Gson gson) {
+    dao            = blueprintDAO;
+    jsonSerializer = gson;
   }
 
 
@@ -238,9 +247,20 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       }
       mapGroupProps.put(COMPONENT_PROPERTY_ID, listComponentProps);
     }
-
     setResourceProperty(resource, HOST_GROUP_PROPERTY_ID, listGroupProps, requestedIds);
 
+    List<Map<String, Object>> listConfigurations = new ArrayList<Map<String, Object>>();
+    Collection<BlueprintConfigEntity> configurations = entity.getConfigurations();
+    for (BlueprintConfigEntity config : configurations) {
+      Map<String, Object> mapConfigurations = new HashMap<String, Object>();
+      String type = config.getType();
+      Map<String, String> properties = jsonSerializer.<Map<String, String>>fromJson(
+          config.getConfigData(), Map.class);
+      mapConfigurations.put(type, properties);
+      listConfigurations.add(mapConfigurations);
+    }
+    setResourceProperty(resource, CONFIGURATION_PROPERTY_ID, listConfigurations, requestedIds);
+
     return resource;
   }
 
@@ -250,6 +270,7 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
    * @param resource the resource to convert
    * @return  a new blueprint entity
    */
+  @SuppressWarnings("unchecked")
   protected BlueprintEntity toEntity(Resource resource) {
     BlueprintEntity entity = new BlueprintEntity();
     entity.setBlueprintName((String) resource.getPropertyValue(BLUEPRINT_NAME_PROPERTY_ID));
@@ -287,6 +308,9 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       blueprintHostGroups.add(group);
     }
 
+    entity.setConfigurations(createConfigEntities(
+        (Collection<Map<String, String>>) resource.getPropertyValue(CONFIGURATION_PROPERTY_ID), entity));
+
     return entity;
   }
 
@@ -296,6 +320,7 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
    * @param properties  property map
    * @return new blueprint entity
    */
+  @SuppressWarnings("unchecked")
   protected BlueprintEntity toEntity(Map<String, Object> properties) {
     String name = (String) properties.get(BLUEPRINT_NAME_PROPERTY_ID);
     if (name == null || name.isEmpty()) {
@@ -337,9 +362,48 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       blueprintHostGroups.add(group);
     }
 
+    blueprint.setConfigurations(createConfigEntities(
+        (Collection<Map<String, String>>) properties.get(CONFIGURATION_PROPERTY_ID), blueprint));
+
     return blueprint;
   }
 
+  /**
+   * Create blueprint configuration entities from properties.
+   *
+   * @param setConfigurations  set of property maps
+   * @param blueprint          blueprint entity
+   *
+   * @return collection of blueprint config entities
+   */
+  private Collection<BlueprintConfigEntity> createConfigEntities(Collection<Map<String, String>> setConfigurations,
+                                                                 BlueprintEntity blueprint) {
+
+    Collection<BlueprintConfigEntity> configurations = new ArrayList<BlueprintConfigEntity>();
+    if (setConfigurations != null) {
+      for (Map<String, String> configuration : setConfigurations) {
+        BlueprintConfigEntity configEntity = new BlueprintConfigEntity();
+        configEntity.setBlueprintEntity(blueprint);
+        configEntity.setBlueprintName(blueprint.getBlueprintName());
+        Map<String, String> configData = new HashMap<String, String>();
+
+        for (Map.Entry<String, String> entry : configuration.entrySet()) {
+          String absolutePropName = entry.getKey();
+
+          int idx = absolutePropName.indexOf('/');
+          if (configEntity.getType() == null) {
+            configEntity.setType(absolutePropName.substring(0, idx));
+          }
+          configData.put(absolutePropName.substring(idx + 1), entry.getValue());
+        }
+        configEntity.setConfigData(jsonSerializer.toJson(configData));
+        configurations.add(configEntity);
+      }
+    }
+
+    return configurations;
+  }
+
   /**
    * Create a create command with all properties set.
    *

+ 43 - 12
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java

@@ -25,6 +25,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.gson.Gson;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.services.PersistKeyValueService;
@@ -52,6 +53,7 @@ import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.orm.dao.BlueprintDAO;
+import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
 import org.apache.ambari.server.orm.entities.BlueprintEntity;
 import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
@@ -313,7 +315,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
 
     Map<String, HostGroup> blueprintHostGroups = parseBlueprintHostGroups(blueprint, stack);
     applyRequestInfoToHostGroups(properties, blueprintHostGroups);
-    processConfigurations(stack, blueprintHostGroups);
+    processConfigurations(processBlueprintConfigurations(blueprint), stack, blueprintHostGroups);
 
     String clusterName = (String) properties.get(CLUSTER_NAME_PROPERTY_ID);
     createClusterResource(buildClusterResourceProperties(stack, clusterName));
@@ -486,9 +488,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   private void setConfigurationsOnCluster(String clusterName) throws SystemException {
     for (Map.Entry<String, Map<String, String>> entry : mapClusterConfigurations.entrySet()) {
       String type = entry.getKey();
-      if (type.endsWith(".xml")) {
-        type = type.substring(0, type.length() - 4);
-      }
+
       try {
         //todo: properly handle non system exceptions
         setConfigurationsOnCluster(clusterName, type, entry.getValue());
@@ -651,17 +651,36 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   }
 
   /**
-   * Process cluster configurations.  This includes obtaining the default configuration properties from the stack,
-   * overlaying configuration properties specified in the cluster create request and updating properties with
-   * topology specific information.
+   * Process configurations contained in blueprint.
+   *
+   * @param blueprint  blueprint entity
+   *
+   * @return configuration properties contained within in blueprint
+   */
+  private Map<String, Map<String, String>> processBlueprintConfigurations(BlueprintEntity blueprint) {
+    Map<String, Map<String, String>> mapConfigurations = new HashMap<String, Map<String, String>>();
+    Collection<BlueprintConfigEntity> configs = blueprint.getConfigurations();
+    Gson jsonSerializer = new Gson();
+    for (BlueprintConfigEntity config : configs) {
+      mapConfigurations.put(config.getType(), jsonSerializer.<Map<String, String>> fromJson(
+          config.getConfigData(), Map.class));
+    }
+    return mapConfigurations;
+  }
+
+  /**
+   * Process cluster configurations.  This includes obtaining the default configuration properties
+   * from the stack,overlaying configuration properties specified in the blueprint and cluster
+   * create request and updating properties with topology specific information.
    *
    * @param stack                associated stack
    * @param blueprintHostGroups  host groups contained in the blueprint
    */
-  // processing at cluster level only now
-  public void processConfigurations(Stack stack, Map<String, HostGroup> blueprintHostGroups)  {
-    Set<String> services = getServicesToDeploy(stack, blueprintHostGroups);
+  private void processConfigurations(Map<String, Map<String, String>> blueprintConfigurations,
+                                    Stack stack, Map<String,
+                                    HostGroup> blueprintHostGroups)  {
 
+    Set<String> services = getServicesToDeploy(stack, blueprintHostGroups);
     for (String service : services) {
       Collection<String> configTypes = stack.getConfigurationTypes(service);
       for (String type : configTypes) {
@@ -670,6 +689,18 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
           String propName = entry.getKey();
           String value    = entry.getValue();
 
+          //todo: move to Stack
+          //strip .xml from type
+          if (type.endsWith(".xml")) {
+            type = type.substring(0, type.length() - 4);
+          }
+          // overlay property if specified at cluster scope in blueprint
+          Map<String, String> blueprintTypeConfig = blueprintConfigurations.get(type);
+          if (blueprintTypeConfig != null && blueprintTypeConfig.containsKey(propName)) {
+            System.out.println("Overwriting property: " + propName + " for configuration " + type);
+            value = blueprintTypeConfig.get(propName);
+          }
+
           Map<String, String> typeProps = mapClusterConfigurations.get(type);
           if (typeProps == null) {
             typeProps = new HashMap<String, String>();
@@ -689,10 +720,10 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     }
     // AMBARI-4921
     //todo: hard-coding default values for required global config properties which are not in stack definition
-    Map<String, String> globalProperties = mapClusterConfigurations.get("global.xml");
+    Map<String, String> globalProperties = mapClusterConfigurations.get("global");
     if (globalProperties == null) {
       globalProperties = new HashMap<String, String>();
-      mapClusterConfigurations.put("global.xml", globalProperties);
+      mapClusterConfigurations.put("global", globalProperties);
     }
     globalProperties.put("user_group", "hadoop");
     globalProperties.put("smokeuser", "ambari-qa");

+ 125 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntity.java

@@ -0,0 +1,125 @@
+/**
+ * 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.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+/**
+ * Represents a blueprint configuration.
+ */
+@javax.persistence.IdClass(BlueprintConfigEntityPK.class)
+@Table(name = "blueprint_configuration")
+@Entity
+public class BlueprintConfigEntity {
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = false, updatable = false)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "type_name", nullable = false, insertable = true, updatable = false)
+  private String type;
+
+  @Column(name = "config_data", nullable = false, insertable = true, updatable = false)
+  @Basic
+  private String configData;
+
+  @ManyToOne
+  @JoinColumn(name = "blueprint_name", referencedColumnName = "blueprint_name", nullable = false)
+  private BlueprintEntity blueprint;
+
+
+  /**
+   * Get the configuration type.
+   *
+   * @return configuration type
+   */
+  public String getType() {
+    return type;
+  }
+
+  /**
+   * Set the configuration type.
+   *
+   * @param type  configuration type
+   */
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  /**
+   * Get the blueprint entity instance.
+   *
+   * @return blueprint entity
+   */
+  public BlueprintEntity getBlueprintEntity() {
+    return blueprint;
+  }
+
+  /**
+   * Set the blueprint entity instance.
+   *
+   * @param entity  blueprint entity
+   */
+  public void setBlueprintEntity(BlueprintEntity entity) {
+    this.blueprint = entity;
+  }
+
+  /**
+   * Get the name of the associated blueprint.
+   *
+   * @return blueprint name
+   */
+  public String getBlueprintName() {
+    return blueprintName;
+  }
+
+  /**
+   * Set the name of the associated blueprint.
+   * '
+   * @param blueprintName  blueprint name
+   */
+  public void setBlueprintName(String blueprintName) {
+    this.blueprintName = blueprintName;
+  }
+
+  /**
+   * Get the config data.
+   *
+   * @return config data in json format
+   */
+  public String getConfigData() {
+    return configData;
+  }
+
+  /**
+   * Set the config data.
+   *
+   * @param configData  all config data in json format
+   */
+  public void setConfigData(String configData) {
+    this.configData = configData;
+  }
+}

+ 88 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityPK.java

@@ -0,0 +1,88 @@
+/**
+ * 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;
+
+/**
+ * Composite primary key for BlueprintConfigEntity.
+ */
+public class BlueprintConfigEntityPK {
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "type_name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String type;
+
+  /**
+   * Get the name of the associated blueprint.
+   *
+   * @return blueprint name
+   */
+  public String getBlueprintName() {
+    return blueprintName;
+  }
+
+  /**
+   * Set the name of the associated blueprint.
+   *
+   * @param blueprintName  blueprint name
+   */
+  public void setBlueprintName(String blueprintName) {
+    this.blueprintName = blueprintName;
+  }
+
+  /**
+   * Get the configuration type.
+   *
+   * @return configuration type
+   */
+  public String getType() {
+    return type;
+  }
+
+  /**
+   * Set the configuration type.
+   *
+   * @param type  configuration type
+   */
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    BlueprintConfigEntityPK that = (BlueprintConfigEntityPK) o;
+
+    return this.blueprintName.equals(that.blueprintName) &&
+        this.type.equals(that.type);
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * blueprintName.hashCode() + type.hashCode();
+  }
+}

+ 21 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java

@@ -53,6 +53,9 @@ public class BlueprintEntity {
   @OneToMany(cascade = CascadeType.ALL, mappedBy = "blueprint")
   private Collection<HostGroupEntity> hostGroups;
 
+  @OneToMany(cascade = CascadeType.ALL, mappedBy = "blueprint")
+  private Collection<BlueprintConfigEntity> configurations;
+
 
   /**
    * Get the blueprint name.
@@ -125,4 +128,22 @@ public class BlueprintEntity {
   public void setHostGroups(Collection<HostGroupEntity> hostGroups) {
     this.hostGroups = hostGroups;
   }
+
+  /**
+   * Get the collection of associated configurations.
+   *
+   * @return collection of configurations
+   */
+  public Collection<BlueprintConfigEntity> getConfigurations() {
+    return configurations;
+  }
+
+  /**
+   * Set the configuration collection.
+   *
+   * @param configurations  collection of associated configurations
+   */
+  public void setConfigurations(Collection<BlueprintConfigEntity> configurations) {
+    this.configurations = configurations;
+  }
 }

+ 12 - 1
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog150.java

@@ -197,6 +197,14 @@ public class UpgradeCatalog150 extends AbstractUpgradeCatalog {
 
     dbAccessor.createTable("blueprint", columns, "blueprint_name");
 
+    // Blueprint Config
+    columns.clear();
+    columns.add(new DBColumnInfo("blueprint_name", String.class, 255, null, false));
+    columns.add(new DBColumnInfo("type_name", String.class, 255, null, false));
+    columns.add(new DBColumnInfo("config_data", String.class, 32000, null, false));
+
+    dbAccessor.createTable("blueprint_configuration", columns, "blueprint_name", "type_name");
+
     // HostGroup
     columns.clear();
     columns.add(new DBColumnInfo("blueprint_name", String.class, 255, null, false));
@@ -303,7 +311,10 @@ public class UpgradeCatalog150 extends AbstractUpgradeCatalog {
     dbAccessor.addFKConstraint("configgrouphostmapping", "FK_cghostm_host_name", "host_name", "hosts", "host_name", true);
     dbAccessor.addFKConstraint("clusterconfigmapping", "FK_clustercfgmap_cluster_id", "cluster_id", "clusters", "cluster_id", true);
     dbAccessor.addFKConstraint("requestresourcefilter", "FK_requestresourcefilter_req_id", "request_id", "request", "request_id", true);
-
+    dbAccessor.addFKConstraint("hostgroup", "FK_hostgroup_blueprint_name", "blueprint_name", "blueprint", "blueprint_name", true);
+    dbAccessor.addFKConstraint("hostgroup_component", "FK_component_blueprint_name", "blueprint_name", "hostgroup", "blueprint_name", true);
+    dbAccessor.addFKConstraint("hostgroup_component", "FK_component_hostgroup_name", "hostgroup_name", "hostgroup", "name", true);
+    dbAccessor.addFKConstraint("blueprint_configuration", "FK_configuration_blueprint_name", "blueprint_name", "blueprint", "blueprint_name", true);
   }
 
   private void moveRCATableInMySQL(String tableName, String dbName) throws SQLException {

+ 2 - 0
ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql

@@ -59,6 +59,7 @@ CREATE TABLE requestschedulebatchrequest (schedule_id bigint, batch_id bigint, r
 CREATE TABLE blueprint (blueprint_name VARCHAR(255) NOT NULL, stack_name VARCHAR(255) NOT NULL, stack_version VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name));
 CREATE TABLE hostgroup (blueprint_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, cardinality VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name, name));
 CREATE TABLE hostgroup_component (blueprint_name VARCHAR(255) NOT NULL, hostgroup_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name, hostgroup_name, name));
+CREATE TABLE blueprint_configuration (blueprint_name VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, config_data VARCHAR(32000) NOT NULL , PRIMARY KEY(blueprint_name, type_name));
 
 ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user);
 ALTER TABLE clusterconfig ADD CONSTRAINT FK_clusterconfig_cluster_id FOREIGN KEY (cluster_id) REFERENCES clusters (cluster_id);
@@ -94,6 +95,7 @@ ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_host
 ALTER TABLE requestschedulebatchrequest ADD CONSTRAINT FK_requestschedulebatchrequest_schedule_id FOREIGN KEY (schedule_id) REFERENCES ambari.requestschedule (schedule_id);
 ALTER TABLE hostgroup ADD FOREIGN KEY (blueprint_name) REFERENCES blueprint(blueprint_name);
 ALTER TABLE hostgroup_component ADD FOREIGN KEY (blueprint_name, hostgroup_name) REFERENCES hostgroup(blueprint_name, name);
+ALTER TABLE blueprint_configuration ADD FOREIGN KEY (blueprint_name) REFERENCES blueprint(blueprint_name);
 ALTER TABLE requestresourcefilter ADD CONSTRAINT FK_requestresourcefilter_req_id FOREIGN KEY (request_id) REFERENCES request (request_id);
 
 

+ 2 - 0
ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql

@@ -49,6 +49,7 @@ CREATE TABLE requestschedulebatchrequest (schedule_id NUMBER(19), batch_id NUMBE
 CREATE TABLE blueprint (blueprint_name VARCHAR2(255) NOT NULL, stack_name VARCHAR2(255) NOT NULL, stack_version VARCHAR2(255) NOT NULL, PRIMARY KEY(blueprint_name));
 CREATE TABLE hostgroup (blueprint_name VARCHAR2(255) NOT NULL, name VARCHAR2(255) NOT NULL, cardinality VARCHAR2(255) NOT NULL, PRIMARY KEY(blueprint_name, name));
 CREATE TABLE hostgroup_component (blueprint_name VARCHAR2(255) NOT NULL, hostgroup_name VARCHAR2(255) NOT NULL, name VARCHAR2(255) NOT NULL, PRIMARY KEY(blueprint_name, hostgroup_name, name));
+CREATE TABLE blueprint_configuration (blueprint_name VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, config_data VARCHAR2(32000) NOT NULL , PRIMARY KEY(blueprint_name, type_name));
 
 ALTER TABLE users ADD CONSTRAINT UNQ_users_0 UNIQUE (user_name, ldap_user);
 ALTER TABLE clusterconfig ADD CONSTRAINT FK_clusterconfig_cluster_id FOREIGN KEY (cluster_id) REFERENCES clusters (cluster_id);
@@ -82,6 +83,7 @@ ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_cghm_hname FOREIGN KEY (hos
 ALTER TABLE requestschedulebatchrequest ADD CONSTRAINT FK_rsbatchrequest_schedule_id FOREIGN KEY (schedule_id) REFERENCES requestschedule (schedule_id);
 ALTER TABLE hostgroup ADD FOREIGN KEY (blueprint_name) REFERENCES ambari.blueprint(blueprint_name);
 ALTER TABLE hostgroup_component ADD FOREIGN KEY (blueprint_name, hostgroup_name) REFERENCES ambari.hostgroup(blueprint_name, name);
+ALTER TABLE blueprint_configuration ADD FOREIGN KEY (blueprint_name) REFERENCES ambari.blueprint(blueprint_name);
 ALTER TABLE requestresourcefilter ADD CONSTRAINT FK_requestresourcefilter_req_id FOREIGN KEY (request_id) REFERENCES request (request_id);
 
 INSERT INTO ambari_sequences(sequence_name, value) values ('host_role_command_id_seq', 0);

+ 2 - 0
ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql

@@ -121,9 +121,11 @@ GRANT ALL PRIVILEGES ON TABLE ambari.requestschedulebatchrequest TO :username;
 CREATE TABLE ambari.blueprint (blueprint_name VARCHAR(255) NOT NULL, stack_name VARCHAR(255) NOT NULL, stack_version VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name));
 CREATE TABLE ambari.hostgroup (blueprint_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, cardinality VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name, name));
 CREATE TABLE ambari.hostgroup_component (blueprint_name VARCHAR(255) NOT NULL, hostgroup_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(blueprint_name, hostgroup_name, name));
+CREATE TABLE ambari.blueprint_configuration (blueprint_name varchar(255) NOT NULL, type_name varchar(255) NOT NULL, config_data varchar(32000) NOT NULL , PRIMARY KEY(blueprint_name, type_name));
 GRANT ALL PRIVILEGES ON TABLE ambari.blueprint TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_component TO :username;
+GRANT ALL PRIVILEGES ON TABLE ambari.blueprint_configuration TO :username;
 
 --------altering tables by creating foreign keys----------
 ALTER TABLE ambari.clusterconfig ADD CONSTRAINT FK_clusterconfig_cluster_id FOREIGN KEY (cluster_id) REFERENCES ambari.clusters (cluster_id);

+ 1 - 0
ambari-server/src/main/resources/META-INF/persistence.xml

@@ -41,6 +41,7 @@
     <class>org.apache.ambari.server.orm.entities.RequestScheduleEntity</class>
     <class>org.apache.ambari.server.orm.entities.RequestScheduleBatchRequestEntity</class>
     <class>org.apache.ambari.server.orm.entities.BlueprintEntity</class>
+    <class>org.apache.ambari.server.orm.entities.BlueprintConfigEntity</class>
     <class>org.apache.ambari.server.orm.entities.HostGroupEntity</class>
     <class>org.apache.ambari.server.orm.entities.HostGroupComponentEntity</class>
     <class>org.apache.ambari.server.orm.entities.RequestResourceFilterEntity</class>

+ 2 - 1
ambari-server/src/main/resources/properties.json

@@ -310,6 +310,7 @@
         "Blueprints/stack_version",
         "host_groups",
         "host_groups/components",
-        "host_groups/cardinality"
+        "host_groups/cardinality",
+        "configurations"
     ]
 }

+ 109 - 7
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BlueprintResourceProviderTest.java

@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import com.google.gson.Gson;
 import org.apache.ambari.server.controller.predicate.EqualsPredicate;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
@@ -30,12 +31,14 @@ 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.dao.BlueprintDAO;
+import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
 import org.apache.ambari.server.orm.entities.BlueprintEntity;
 import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
 import org.easymock.Capture;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -64,7 +67,6 @@ import static org.easymock.EasyMock.reset;
 import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.fail;
 
-
 /**
  * BlueprintResourceProvider unit tests.
  */
@@ -73,10 +75,11 @@ public class BlueprintResourceProviderTest {
   private static String BLUEPRINT_NAME = "test-blueprint";
 
   private final static BlueprintDAO dao = createStrictMock(BlueprintDAO.class);
+  private final static Gson gson = new Gson();
 
   @BeforeClass
   public static void initClass() {
-    BlueprintResourceProvider.init(dao);
+    BlueprintResourceProvider.init(dao, gson);
   }
 
   @Before
@@ -113,7 +116,42 @@ public class BlueprintResourceProviderTest {
     assertEquals(request, lastEvent.getRequest());
     assertNull(lastEvent.getPredicate());
 
-    validateEntity(entityCapture.getValue());
+    validateEntity(entityCapture.getValue(), false);
+
+    verify(dao, request);
+  }
+
+  @Test
+  public void testCreateResources_withConfiguration() throws ResourceAlreadyExistsException, SystemException,
+      UnsupportedPropertyException, NoSuchParentResourceException {
+
+    Set<Map<String, Object>> setProperties = getTestProperties();
+    setConfigurationProperties(setProperties);
+    Request request = createMock(Request.class);
+    Capture<BlueprintEntity> entityCapture = new Capture<BlueprintEntity>();
+
+    // set expectations
+    expect(request.getProperties()).andReturn(setProperties);
+    expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
+    dao.create(capture(entityCapture));
+
+    replay(dao, request);
+    // end expectations
+
+    ResourceProvider provider = createProvider();
+    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
+    ((ObservableResourceProvider)provider).addObserver(observer);
+
+    provider.createResources(request);
+
+    ResourceProviderEvent lastEvent = observer.getLastEvent();
+    assertNotNull(lastEvent);
+    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
+    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
+    assertEquals(request, lastEvent.getRequest());
+    assertNull(lastEvent.getPredicate());
+
+    validateEntity(entityCapture.getValue(), true);
 
     verify(dao, request);
   }
@@ -138,9 +176,35 @@ public class BlueprintResourceProviderTest {
     assertEquals(1, setResults.size());
 
     verify(dao);
-    validateResource(setResults.iterator().next());
+    validateResource(setResults.iterator().next(), false);
   }
 
+  @Test
+  public void testGetResourcesNoPredicate_withConfiguration() throws SystemException, UnsupportedPropertyException,
+      NoSuchParentResourceException, NoSuchResourceException {
+    Request request = createNiceMock(Request.class);
+
+    ResourceProvider provider = createProvider();
+    Set<Map<String, Object>> testProperties = getTestProperties();
+    setConfigurationProperties(testProperties);
+    BlueprintEntity entity = ((BlueprintResourceProvider) provider).toEntity(
+        testProperties.iterator().next());
+
+    List<BlueprintEntity> results = new ArrayList<BlueprintEntity>();
+    results.add(entity);
+
+    // set expectations
+    expect(dao.findAll()).andReturn(results);
+    replay(dao, request);
+
+    Set<Resource> setResults = provider.getResources(request, null);
+    assertEquals(1, setResults.size());
+
+    verify(dao);
+    validateResource(setResults.iterator().next(), true);
+  }
+
+
   @Test
   public void testDeleteResources() throws SystemException, UnsupportedPropertyException,
                                            NoSuchParentResourceException, NoSuchResourceException {
@@ -172,7 +236,7 @@ public class BlueprintResourceProviderTest {
 
     verify(dao);
 
-    validateEntity(entityCapture.getValue());
+    validateEntity(entityCapture.getValue(), false);
   }
 
   private Set<Map<String, Object>> getTestProperties() {
@@ -213,7 +277,17 @@ public class BlueprintResourceProviderTest {
     return Collections.singleton(mapProperties);
   }
 
-  private void validateEntity(BlueprintEntity entity) {
+  private void setConfigurationProperties(Set<Map<String, Object>> properties ) {
+    Map<String, String> mapConfigsProps = new HashMap<String, String>();
+    mapConfigsProps.put("core-site/fs.trash.interval", "480");
+    mapConfigsProps.put("core-site/ipc.client.idlethreshold", "8500");
+
+    // single entry in set which was created in getTestProperties
+    Map<String, Object> mapProperties = properties.iterator().next();
+    mapProperties.put("configurations", Collections.singleton(mapConfigsProps));
+  }
+
+  private void validateEntity(BlueprintEntity entity, boolean containsConfig) {
     assertEquals(BLUEPRINT_NAME, entity.getBlueprintName());
     assertEquals("test-stack-name", entity.getStackName());
     assertEquals("test-stack-version", entity.getStackVersion());
@@ -243,9 +317,24 @@ public class BlueprintResourceProviderTest {
         fail("Unexpected host group name");
       }
     }
+    Collection<BlueprintConfigEntity> configurations = entity.getConfigurations();
+    if (containsConfig) {
+      assertEquals(1, configurations.size());
+      BlueprintConfigEntity blueprintConfigEntity = configurations.iterator().next();
+      assertEquals(BLUEPRINT_NAME, blueprintConfigEntity.getBlueprintName());
+      assertSame(entity, blueprintConfigEntity.getBlueprintEntity());
+      assertEquals("core-site", blueprintConfigEntity.getType());
+      Map<String, String> properties = gson.<Map<String, String>>fromJson(
+          blueprintConfigEntity.getConfigData(), Map.class);
+      assertEquals(2, properties.size());
+      assertEquals("480", properties.get("fs.trash.interval"));
+      assertEquals("8500", properties.get("ipc.client.idlethreshold"));
+    } else {
+      assertEquals(0, configurations.size());
+    }
   }
 
-  private void validateResource(Resource resource) {
+  private void validateResource(Resource resource, boolean containsConfig) {
     assertEquals(BLUEPRINT_NAME, resource.getPropertyValue(BlueprintResourceProvider.BLUEPRINT_NAME_PROPERTY_ID));
     assertEquals("test-stack-name", resource.getPropertyValue(BlueprintResourceProvider.STACK_NAME_PROPERTY_ID));
     assertEquals("test-stack-version", resource.getPropertyValue(BlueprintResourceProvider.STACK_VERSION_PROPERTY_ID));
@@ -279,6 +368,19 @@ public class BlueprintResourceProviderTest {
         fail("Unexpected host group name");
       }
     }
+
+    if (containsConfig) {
+      Collection<Map<String, Object>> blueprintConfigurations = (Collection<Map<String, Object>>)
+          resource.getPropertyValue(BlueprintResourceProvider.CONFIGURATION_PROPERTY_ID);
+      assertEquals(1, blueprintConfigurations.size());
+
+      Map<String, Object> typeConfigs = blueprintConfigurations.iterator().next();
+      assertEquals(1, typeConfigs.size());
+      Map<String, String> properties = (Map<String, String>) typeConfigs.get("core-site");
+      assertEquals(2, properties.size());
+      assertEquals("480", properties.get("fs.trash.interval"));
+      assertEquals("8500", properties.get("ipc.client.idlethreshold"));
+    }
   }
 
   private BlueprintResourceProvider createProvider() {

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

@@ -54,6 +54,7 @@ import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.orm.dao.BlueprintDAO;
+import org.apache.ambari.server.orm.entities.BlueprintConfigEntity;
 import org.apache.ambari.server.orm.entities.BlueprintEntity;
 import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
@@ -236,6 +237,7 @@ public class ClusterResourceProviderTest {
     expect(blueprintDAO.findByName(blueprintName)).andReturn(blueprint);
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
+    expect(blueprint.getConfigurations()).andReturn(Collections.<BlueprintConfigEntity>emptyList());
 
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");

+ 69 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityPKTest.java

@@ -0,0 +1,69 @@
+/**
+ * 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 org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+/**
+ * BlueprintConfigEntityPK unit tests.
+ */
+public class BlueprintConfigEntityPKTest {
+  @Test
+  public void testSetGetBlueprintName() {
+    BlueprintConfigEntityPK pk = new BlueprintConfigEntityPK();
+    pk.setBlueprintName("foo");
+    assertEquals("foo", pk.getBlueprintName());
+  }
+
+  @Test
+  public void testSetGetType() {
+    BlueprintConfigEntityPK pk = new BlueprintConfigEntityPK();
+    pk.setType("foo");
+    assertEquals("foo", pk.getType());
+  }
+
+  @Test
+  public void testEquals() {
+    BlueprintConfigEntityPK pk = new BlueprintConfigEntityPK();
+    BlueprintConfigEntityPK pk2 = new BlueprintConfigEntityPK();
+
+
+    pk.setBlueprintName("foo");
+    pk.setType("core-site");
+    pk2.setBlueprintName("foo");
+    pk2.setType("core-site");
+    assertEquals(pk, pk2);
+    assertEquals(pk2, pk);
+
+    pk.setBlueprintName("foo2");
+    assertFalse(pk.equals(pk2));
+    assertFalse(pk2.equals(pk));
+
+    pk2.setBlueprintName("foo2");
+    assertEquals(pk, pk2);
+    assertEquals(pk2, pk);
+
+    pk.setType("other-type");
+    assertFalse(pk.equals(pk2));
+    assertFalse(pk2.equals(pk));
+  }
+}

+ 59 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintConfigEntityTest.java

@@ -0,0 +1,59 @@
+/**
+ * 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 org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+/**
+ * BlueprintConfigEntity unit tests.
+ */
+public class BlueprintConfigEntityTest {
+  @Test
+  public void testSetGetType() {
+    BlueprintConfigEntity entity = new BlueprintConfigEntity();
+    entity.setType("foo");
+    assertEquals("foo", entity.getType());
+  }
+
+  @Test
+  public void testSetGetBlueprintEntity() {
+    BlueprintEntity bp = new BlueprintEntity();
+
+    BlueprintConfigEntity entity = new BlueprintConfigEntity();
+    entity.setBlueprintEntity(bp);
+    assertSame(bp, entity.getBlueprintEntity());
+  }
+
+  @Test
+  public void testSetGetBlueprintName() {
+    BlueprintConfigEntity entity = new BlueprintConfigEntity();
+    entity.setBlueprintName("foo");
+    assertEquals("foo", entity.getBlueprintName());
+  }
+
+  @Test
+  public void testSetGetConfigData() {
+    BlueprintConfigEntity entity = new BlueprintConfigEntity();
+    entity.setConfigData("foo");
+    assertEquals("foo", entity.getConfigData());
+  }
+}

+ 8 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java

@@ -59,4 +59,12 @@ public class BlueprintEntityTest {
     assertSame(hostGroups, entity.getHostGroups());
   }
 
+  @Test
+  public void testSetGetConfigurations() {
+    BlueprintEntity entity = new BlueprintEntity();
+    Collection<BlueprintConfigEntity> configurations = Collections.emptyList();
+    entity.setConfigurations(configurations);
+    assertSame(configurations, entity.getConfigurations());
+  }
+
 }