You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2014/02/03 16:40:42 UTC

[2/2] git commit: AMBARI-4467. Create new /blueprints REST endpoint.

AMBARI-4467.  Create new /blueprints REST endpoint.


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

Branch: refs/heads/trunk
Commit: 5f464316237c2a1661602eaa230f197221aae271
Parents: 6372db8
Author: John Speidel <js...@hortonworks.com>
Authored: Wed Jan 29 20:57:27 2014 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Mon Feb 3 09:36:57 2014 -0500

----------------------------------------------------------------------
 .../resources/BlueprintResourceDefinition.java  |  45 +++
 .../resources/ResourceInstanceFactoryImpl.java  |   4 +
 .../server/api/services/BlueprintService.java   | 138 +++++++
 .../ambari/server/controller/AmbariServer.java  |   3 +
 .../AbstractControllerResourceProvider.java     |   8 +-
 .../internal/BlueprintResourceProvider.java     | 370 +++++++++++++++++++
 .../internal/DefaultProviderModule.java         |   2 +
 .../ambari/server/controller/spi/Resource.java  |   4 +-
 .../ambari/server/orm/dao/BlueprintDAO.java     | 109 ++++++
 .../server/orm/entities/BlueprintEntity.java    | 128 +++++++
 .../orm/entities/HostGroupComponentEntity.java  | 129 +++++++
 .../entities/HostGroupComponentEntityPK.java    | 114 ++++++
 .../server/orm/entities/HostGroupEntity.java    | 149 ++++++++
 .../server/orm/entities/HostGroupEntityPK.java  |  88 +++++
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   5 +
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   5 +
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |  10 +
 .../src/main/resources/META-INF/persistence.xml |   3 +
 .../src/main/resources/key_properties.json      |   5 +-
 .../src/main/resources/properties.json          |  16 +-
 .../upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql    |   8 +
 .../upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql   |   9 +-
 .../ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql   |  12 +
 .../BlueprintResourceDefinitionTest.java        |  53 +++
 .../api/services/BlueprintServiceTest.java      | 104 ++++++
 .../internal/BlueprintResourceProviderTest.java | 290 +++++++++++++++
 .../ambari/server/orm/dao/BlueprintDAOTest.java | 152 ++++++++
 .../orm/entities/BlueprintEntityTest.java       |  62 ++++
 .../entities/HostGroupComponentEntityTest.java  |  58 +++
 .../orm/entities/HostGroupEntityTest.java       |  69 ++++
 30 files changed, 2141 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinition.java
new file mode 100644
index 0000000..7e81a8f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinition.java
@@ -0,0 +1,45 @@
+/**
+ * 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.controller.spi.Resource;
+
+
+/**
+ * Blueprint resource definition.
+ */
+public class BlueprintResourceDefinition extends BaseResourceDefinition {
+  /**
+   * Constructor.
+   *
+   */
+  public BlueprintResourceDefinition() {
+    super(Resource.Type.Blueprint);
+  }
+
+  @Override
+  public String getPluralName() {
+    return "blueprints";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "blueprint";
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/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 7dcaccb..aeb46e1 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
@@ -204,6 +204,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new ViewInstanceResourceDefinition(subResourceDefinitions);
         break;
 
+      case Blueprint:
+        resourceDefinition = new BlueprintResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/api/services/BlueprintService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BlueprintService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BlueprintService.java
new file mode 100644
index 0000000..2d13ca7
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BlueprintService.java
@@ -0,0 +1,138 @@
+/**
+ * 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.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.Collections;
+
+/**
+ * Service responsible for handling REST requests for the /blueprints endpoint.
+ * This blueprint resource is a blueprint template meaning that it doesn't contain
+ * any cluster specific information.  Updates are not permitted as blueprints are
+ * immutable.
+ */
+@Path("/blueprints/")
+public class BlueprintService extends BaseService {
+
+  /**
+   * Handles: GET  /blueprints
+   * Get all blueprints.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   * @return blueprint collection resource representation
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getBlueprints(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createBlueprintResource(null));
+  }
+
+  /**
+   * Handles: GET /blueprints/{blueprintID}
+   * Get a specific blueprint.
+   *
+   * @param headers        http headers
+   * @param ui             uri info
+   * @param blueprintName  blueprint id
+   * @return blueprint instance representation
+   */
+  @GET
+  @Path("{blueprintName}")
+  @Produces("text/plain")
+  public Response getBlueprint(@Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("blueprintName") String blueprintName) {
+
+    return handleRequest(headers, null, ui, Request.Type.GET, createBlueprintResource(blueprintName));
+  }
+
+  /**
+   * Handles: POST /blueprints/{blueprintID}
+   * Create a specific blueprint.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param blueprintName blueprint id
+   * @return information regarding the created blueprint
+   */
+  @POST
+  @Path("{blueprintName}")
+  @Produces("text/plain")
+  public Response createBlueprint(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                                  @PathParam("blueprintName") String blueprintName) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST, createBlueprintResource(blueprintName));
+  }
+
+  /**
+   * Handles: DELETE /blueprints/{blueprintID}
+   * Delete a specific blueprint.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @param blueprintName blueprint name
+   * @return information regarding the deleted blueprint
+   */
+  @DELETE
+  @Path("{blueprintName}")
+  @Produces("text/plain")
+  public Response deleteBlueprint(@Context HttpHeaders headers, @Context UriInfo ui,
+                                @PathParam("blueprintName") String blueprintName) {
+
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createBlueprintResource(blueprintName));
+  }
+
+  /**
+   * Handles: DELETE /blueprints
+   * Delete a set of blueprints that match a predicate.
+   *
+   * @param headers       http headers
+   * @param ui            uri info
+   * @return information regarding the deleted blueprint
+   */
+  @DELETE
+  @Produces("text/plain")
+  public Response deleteBlueprints(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createBlueprintResource(null));
+  }
+
+  /**
+   * Create a blueprint resource instance.
+   *
+   * @param blueprintName blueprint name
+   *
+   * @return a blueprint resource instance
+   */
+  ResourceInstance createBlueprintResource(String blueprintName) {
+    return createResource(Resource.Type.Blueprint,
+        Collections.singletonMap(Resource.Type.Blueprint, blueprintName));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index 75b6cb1..faaf0ee 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -42,10 +42,12 @@ import org.apache.ambari.server.bootstrap.BootStrapImpl;
 import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.AbstractControllerResourceProvider;
+import org.apache.ambari.server.controller.internal.BlueprintResourceProvider;
 import org.apache.ambari.server.controller.internal.StackDefinedPropertyProvider;
 import org.apache.ambari.server.controller.nagios.NagiosPropertyProvider;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.PersistenceType;
+import org.apache.ambari.server.orm.dao.BlueprintDAO;
 import org.apache.ambari.server.orm.dao.MetainfoDAO;
 import org.apache.ambari.server.orm.entities.MetainfoEntity;
 import org.apache.ambari.server.resources.ResourceManager;
@@ -478,6 +480,7 @@ public class AmbariServer {
     StackDefinedPropertyProvider.init(injector);
     NagiosPropertyProvider.init(injector);
     AbstractControllerResourceProvider.init(injector.getInstance(ResourceProviderFactory.class));
+    BlueprintResourceProvider.init(injector.getInstance(BlueprintDAO.class));
   }
 
   public static void main(String[] args) throws Exception {

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/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 c66ae65..880b13e 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
@@ -141,10 +141,10 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
   }
 
   /**
-   * Extracting given query_paramater value from the predicate
-   * @param queryParameterId
-   * @param predicate
-   * @return
+   * Extracting given query_parameter value from the predicate
+   * @param queryParameterId  query parameter id
+   * @param predicate         predicate
+   * @return the query parameter
    */
   protected static Object getQueryParameterValue(String queryParameterId, Predicate predicate) {
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java
new file mode 100644
index 0000000..832f788
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java
@@ -0,0 +1,370 @@
+/**
+ * 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 com.google.inject.Inject;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.DuplicateResourceException;
+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.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.BlueprintEntity;
+import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
+import org.apache.ambari.server.orm.entities.HostGroupEntity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resource Provider for Blueprint resources.
+ */
+public class BlueprintResourceProvider extends AbstractResourceProvider {
+
+  // ----- Property ID constants ---------------------------------------------
+
+  // Blueprints
+  protected static final String BLUEPRINT_NAME_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Blueprints", "blueprint_name");
+  protected static final String STACK_NAME_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Blueprints", "stack_name");
+  protected static final String STACK_VERSION_PROPERTY_ID =
+      PropertyHelper.getPropertyId("Blueprints", "stack_version");
+
+  // Host Groups
+  protected static final String HOST_GROUP_PROPERTY_ID = "host_groups";
+  protected static final String HOST_GROUP_NAME_PROPERTY_ID = "name";
+  protected static final String HOST_GROUP_CARDINALITY_PROPERTY_ID = "cardinality";
+
+  // Host Group Components
+  protected static final String COMPONENT_PROPERTY_ID ="components";
+  protected static final String COMPONENT_NAME_PROPERTY_ID ="name";
+
+  // Primary Key Fields
+  private static Set<String> pkPropertyIds =
+      new HashSet<String>(Arrays.asList(new String[]{
+          BLUEPRINT_NAME_PROPERTY_ID}));
+
+  /**
+   * Blueprint data access object.
+   */
+  private static BlueprintDAO dao;
+
+
+  // ----- Constructors ----------------------------------------------------
+
+  /**
+   * Create a  new resource provider for the given management controller.
+   *
+   * @param propertyIds     the property ids
+   * @param keyPropertyIds  the key property ids
+   */
+  BlueprintResourceProvider(Set<String> propertyIds, Map<Resource.Type, String> keyPropertyIds) {
+    super(propertyIds, keyPropertyIds);
+  }
+
+  /**
+   * Static initialization of DAO.
+   *
+   * @param blueprintDAO  blueprint data access object
+   */
+  @Inject
+  public static void init(BlueprintDAO blueprintDAO) {
+    dao = blueprintDAO;
+  }
+
+
+  // ----- ResourceProvider ------------------------------------------------
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return pkPropertyIds;
+  }
+
+  @Override
+  public RequestStatus createResources(Request request)
+      throws SystemException, UnsupportedPropertyException,
+             ResourceAlreadyExistsException, NoSuchParentResourceException {
+
+    for (Map<String, Object> properties : request.getProperties()) {
+      createResources(getCreateCommand(properties));
+    }
+    notifyCreate(Resource.Type.Blueprint, request);
+
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+             NoSuchResourceException, NoSuchParentResourceException {
+
+    List<BlueprintEntity> results        = null;
+    boolean               applyPredicate = false;
+
+    if (predicate != null) {
+      Set<Map<String, Object>> requestProps = getPropertyMaps(predicate);
+      if (requestProps.size() == 1 ) {
+        String name = (String) requestProps.iterator().next().get(
+            BLUEPRINT_NAME_PROPERTY_ID);
+
+        if (name != null) {
+          BlueprintEntity entity = dao.findByName(name);
+          results = entity == null ? Collections.<BlueprintEntity>emptyList() :
+              Collections.singletonList(entity);
+        }
+      }
+    }
+
+    if (results == null) {
+      applyPredicate = true;
+      results = dao.findAll();
+    }
+
+    Set<Resource> resources  = new HashSet<Resource>();
+    for (BlueprintEntity entity : results) {
+      Resource resource = toResource(entity, getRequestPropertyIds(request, predicate));
+      if (predicate == null || ! applyPredicate || predicate.evaluate(resource)) {
+        resources.add(resource);
+      }
+    }
+
+    if (predicate != null && resources.isEmpty()) {
+      throw new NoSuchResourceException(
+          "The requested resource doesn't exist: Blueprint not found, " + predicate);
+    }
+
+    return resources;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+             NoSuchResourceException, NoSuchParentResourceException {
+
+    // no-op, blueprints are immutable
+    //todo: meaningful error message
+    return null;
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException,
+             NoSuchResourceException, NoSuchParentResourceException {
+
+    //TODO (jspeidel): Revisit concurrency control
+    Set<Resource> setResources = getResources(
+        new RequestImpl(null, null, null, null), predicate);
+
+    for (final Resource resource : setResources) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Deleting Blueprint, name=" +
+            resource.getPropertyValue(BLUEPRINT_NAME_PROPERTY_ID));
+      }
+      modifyResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          dao.remove(toEntity(resource));
+          return null;
+        }
+      });
+    }
+
+    notifyDelete(Resource.Type.Blueprint, predicate);
+    return getRequestStatus(null);
+  }
+
+
+  // ----- Instance Methods ------------------------------------------------
+
+  /**
+   * Create a resource instance from a blueprint entity.
+   *
+   * @param entity        blueprint entity
+   * @param requestedIds  requested id's
+   *
+   * @return a new resource instance for the given blueprint entity
+   */
+  protected Resource toResource(BlueprintEntity entity, Set<String> requestedIds) {
+    Resource resource = new ResourceImpl(Resource.Type.Blueprint);
+    setResourceProperty(resource, BLUEPRINT_NAME_PROPERTY_ID, entity.getBlueprintName(), requestedIds);
+    setResourceProperty(resource, STACK_NAME_PROPERTY_ID, entity.getStackName(), requestedIds);
+    setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, entity.getStackVersion(), requestedIds);
+
+    List<Map<String, Object>> listGroupProps = new ArrayList<Map<String, Object>>();
+    Collection<HostGroupEntity> hostGroups = entity.getHostGroups();
+    for (HostGroupEntity hostGroup : hostGroups) {
+      Map<String, Object> mapGroupProps = new HashMap<String, Object>();
+      mapGroupProps.put(HOST_GROUP_NAME_PROPERTY_ID, hostGroup.getName());
+      listGroupProps.add(mapGroupProps);
+      mapGroupProps.put(HOST_GROUP_CARDINALITY_PROPERTY_ID, hostGroup.getCardinality());
+
+      List<Map<String, String>> listComponentProps = new ArrayList<Map<String, String>>();
+      Collection<HostGroupComponentEntity> components = hostGroup.getComponents();
+      for (HostGroupComponentEntity component : components) {
+        Map<String, String> mapComponentProps = new HashMap<String, String>();
+        mapComponentProps.put(COMPONENT_NAME_PROPERTY_ID, component.getName());
+        listComponentProps.add(mapComponentProps);
+      }
+      mapGroupProps.put(COMPONENT_PROPERTY_ID, listComponentProps);
+    }
+
+    setResourceProperty(resource, HOST_GROUP_PROPERTY_ID, listGroupProps, requestedIds);
+
+    return resource;
+  }
+
+  /**
+   * Convert a resource to a blueprint entity.
+   *
+   * @param resource the resource to convert
+   * @return  a new blueprint entity
+   */
+  protected BlueprintEntity toEntity(Resource resource) {
+    BlueprintEntity entity = new BlueprintEntity();
+    entity.setBlueprintName((String) resource.getPropertyValue(BLUEPRINT_NAME_PROPERTY_ID));
+    entity.setStackName((String) resource.getPropertyValue(STACK_NAME_PROPERTY_ID));
+    entity.setStackVersion((String) resource.getPropertyValue(STACK_VERSION_PROPERTY_ID));
+
+    Collection<HostGroupEntity> blueprintHostGroups = new ArrayList<HostGroupEntity>();
+    entity.setHostGroups(blueprintHostGroups);
+
+    Collection<Map<String, Object>> hostGroupProps = (Collection<Map<String, Object>>)
+        resource.getPropertyValue(HOST_GROUP_PROPERTY_ID);
+
+    for (Map<String, Object> properties : hostGroupProps) {
+      HostGroupEntity group = new HostGroupEntity();
+      group.setName((String) properties.get(BlueprintResourceProvider.HOST_GROUP_NAME_PROPERTY_ID));
+      group.setBlueprintEntity(entity);
+      group.setBlueprintName(entity.getBlueprintName());
+      group.setCardinality((String) properties.get(HOST_GROUP_CARDINALITY_PROPERTY_ID));
+
+      Collection<HostGroupComponentEntity> hostGroupComponents = new ArrayList<HostGroupComponentEntity>();
+      group.setComponents(hostGroupComponents);
+
+      List<Map<String, String>> listComponents = (List<Map<String, String>>)
+          properties.get(BlueprintResourceProvider.COMPONENT_PROPERTY_ID);
+
+      for (Map<String, String> componentProperties : listComponents) {
+        HostGroupComponentEntity component = new HostGroupComponentEntity();
+        component.setName(componentProperties.get(COMPONENT_NAME_PROPERTY_ID));
+        component.setBlueprintName(entity.getBlueprintName());
+        component.setHostGroupEntity(group);
+        component.setHostGroupName((String) properties.get(HOST_GROUP_NAME_PROPERTY_ID));
+
+        hostGroupComponents.add(component);
+      }
+      blueprintHostGroups.add(group);
+    }
+
+    return entity;
+  }
+
+  /**
+   * Convert a map of properties to a blueprint entity.
+   *
+   * @param properties  property map
+   * @return new blueprint entity
+   */
+  protected BlueprintEntity toEntity(Map<String, Object> properties) {
+    String name = (String) properties.get(BLUEPRINT_NAME_PROPERTY_ID);
+    if (name == null || name.isEmpty()) {
+      throw new IllegalArgumentException("Blueprint name must be provided");
+    }
+
+    BlueprintEntity blueprint = new BlueprintEntity();
+    blueprint.setBlueprintName(name);
+    blueprint.setStackName((String) properties.get(STACK_NAME_PROPERTY_ID));
+    blueprint.setStackVersion((String) properties.get(STACK_VERSION_PROPERTY_ID));
+
+    Collection<HostGroupEntity> blueprintHostGroups = new ArrayList<HostGroupEntity>();
+    blueprint.setHostGroups(blueprintHostGroups);
+
+    HashSet<HashMap<String, Object>> setHostGroups =
+        (HashSet<HashMap<String, Object>>) properties.get(HOST_GROUP_PROPERTY_ID);
+
+    for (HashMap<String, Object> hostGroupProperties : setHostGroups) {
+      HostGroupEntity group = new HostGroupEntity();
+      group.setName((String) hostGroupProperties.get(HOST_GROUP_NAME_PROPERTY_ID));
+      group.setBlueprintEntity(blueprint);
+      group.setBlueprintName(name);
+      group.setCardinality((String) hostGroupProperties.get(HOST_GROUP_CARDINALITY_PROPERTY_ID));
+
+      Collection<HostGroupComponentEntity> components = new ArrayList<HostGroupComponentEntity>();
+      group.setComponents(components);
+
+      HashSet<HashMap<String, String>> setComponents =
+          (HashSet<HashMap<String, String>>) hostGroupProperties.get(COMPONENT_PROPERTY_ID);
+      for (HashMap<String, String> componentProperties : setComponents) {
+        HostGroupComponentEntity component = new HostGroupComponentEntity();
+        component.setName(componentProperties.get(COMPONENT_NAME_PROPERTY_ID));
+        component.setBlueprintName(name);
+        component.setHostGroupEntity(group);
+        component.setHostGroupName((String) hostGroupProperties.get(HOST_GROUP_NAME_PROPERTY_ID));
+
+        components.add(component);
+      }
+      blueprintHostGroups.add(group);
+    }
+
+    return blueprint;
+  }
+
+  /**
+   * Create a create command with all properties set.
+   *
+   * @param properties  properties to be applied to blueprint
+   *
+   * @return a new create command
+   */
+  private Command<Void> getCreateCommand(final Map<String, Object> properties) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        BlueprintEntity blueprint = toEntity(properties);
+
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Creating Blueprint, name=" + blueprint.getBlueprintName());
+        }
+
+        if (dao.findByName(blueprint.getBlueprintName()) != null) {
+          throw new DuplicateResourceException(
+              "Attempted to create a Blueprint which already exists, blueprint_name=" +
+              blueprint.getBlueprintName());
+        }
+        dao.create(blueprint);
+        return null;
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
index e8765e4..4f25638 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
@@ -65,6 +65,8 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new ViewResourceProvider();
       case ViewInstance:
         return new ViewInstanceResourceProvider();
+      case Blueprint:
+        return new BlueprintResourceProvider(propertyIds, keyPropertyIds);
       default:
         return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,
             keyPropertyIds, managementController);

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/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 52f0cdf..56f9d49 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
@@ -103,7 +103,8 @@ public interface Resource {
     RootServiceComponent,
     RootServiceHostComponent,
     View,
-    ViewInstance;
+    ViewInstance,
+    Blueprint;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -168,6 +169,7 @@ public interface Resource {
     public static final Type RootServiceHostComponent = InternalType.RootServiceHostComponent.getType();
     public static final Type View = InternalType.View.getType();
     public static final Type ViewInstance = InternalType.ViewInstance.getType();
+    public static final Type Blueprint = InternalType.Blueprint.getType();
 
     /**
      * The type name.

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/BlueprintDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/BlueprintDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/BlueprintDAO.java
new file mode 100644
index 0000000..3d5fe78
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/BlueprintDAO.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.dao;
+
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.orm.entities.BlueprintEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.List;
+
+
+/**
+ * Blueprint Data Access Object.
+ */
+@Singleton
+public class BlueprintDAO {
+
+  /**
+   * JPA entity manager
+   */
+  @Inject
+  Provider<EntityManager> entityManagerProvider;
+
+  /**
+   * Find a blueprint with a given name.
+   *
+   * @param blueprint_name name of blueprint to find
+   *
+   * @return  a matching blueprint or null
+   */
+  public BlueprintEntity findByName(String blueprint_name) {
+    return entityManagerProvider.get().find(BlueprintEntity.class, blueprint_name);
+  }
+
+  /**
+   * Find all blueprints.
+   *
+   * @return all blueprints or an empty List
+   */
+  public List<BlueprintEntity> findAll() {
+    TypedQuery<BlueprintEntity> query = entityManagerProvider.get().
+        createNamedQuery("allBlueprints", BlueprintEntity.class);
+
+    return query.getResultList();
+  }
+
+  /**
+   * Refresh the state of the instance from the database,
+   * overwriting changes made to the entity, if any.
+   *
+   * @param blueprintEntity  entity to refresh
+   */
+  @Transactional
+  public void refresh(BlueprintEntity blueprintEntity) {
+    entityManagerProvider.get().refresh(blueprintEntity);
+  }
+
+  /**
+   * Make an instance managed and persistent.
+   *
+   * @param blueprintEntity  entity to persist
+   */
+  @Transactional
+  public void create(BlueprintEntity blueprintEntity) {
+    entityManagerProvider.get().persist(blueprintEntity);
+  }
+
+  /**
+   * Merge the state of the given entity into the current persistence context.
+   *
+   * @param blueprintEntity  entity to merge
+   * @return the merged entity
+   */
+  @Transactional
+  public BlueprintEntity merge(BlueprintEntity blueprintEntity) {
+    return entityManagerProvider.get().merge(blueprintEntity);
+  }
+
+  /**
+   * Remove the entity instance.
+   *
+   * @param blueprintEntity  entity to remove
+   */
+  @Transactional
+  public void remove(BlueprintEntity blueprintEntity) {
+    entityManagerProvider.get().remove(merge(blueprintEntity));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java
new file mode 100644
index 0000000..9e89c86
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.entities;
+
+import javax.persistence.Basic;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.Collection;
+
+/**
+ * Entity representing a Blueprint.
+ */
+@Table(name = "blueprint")
+@NamedQuery(name = "allBlueprints",
+    query = "SELECT blueprint FROM BlueprintEntity blueprint")
+@Entity
+public class BlueprintEntity {
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = true,
+      updatable = false, unique = true, length = 100)
+  private String blueprintName;
+
+  @Column(name = "stack_name", nullable = false, insertable = true, updatable = false)
+  @Basic
+  private String stackName;
+
+  @Column(name = "stack_version", nullable = false, insertable = true, updatable = false)
+  @Basic
+  private String stackVersion;
+
+  @OneToMany(cascade = CascadeType.ALL, mappedBy = "blueprint")
+  private Collection<HostGroupEntity> hostGroups;
+
+
+  /**
+   * Get the blueprint name.
+   *
+   * @return blueprint name
+   */
+  public String getBlueprintName() {
+    return blueprintName;
+  }
+
+  /**
+   * Set the blueprint name
+   *
+   * @param blueprintName  the blueprint name
+   */
+  public void setBlueprintName(String blueprintName) {
+    this.blueprintName = blueprintName;
+  }
+
+  /**
+   * Get the stack name.
+   *
+   * @return the stack name
+   */
+  public String getStackName() {
+    return stackName;
+  }
+
+  /**
+   * Set the stack name.
+   *
+   * @param stackName  the stack name
+   */
+  public void setStackName(String stackName) {
+    this.stackName = stackName;
+  }
+
+  /**
+   * Get the stack version.
+   *
+   * @return the stack version
+   */
+  public String getStackVersion() {
+    return stackVersion;
+  }
+
+  /**
+   * Set the stack version.
+   *
+   * @param stackVersion the stack version
+   */
+  public void setStackVersion(String stackVersion) {
+    this.stackVersion = stackVersion;
+  }
+
+  /**
+   * Get the collection of associated host groups.
+   *
+   * @return collection of host groups
+   */
+  public Collection<HostGroupEntity> getHostGroups() {
+    return hostGroups;
+  }
+
+  /**
+   * Set the host group collection.
+   *
+   * @param hostGroups  collection of associated host groups
+   */
+  public void setHostGroups(Collection<HostGroupEntity> hostGroups) {
+    this.hostGroups = hostGroups;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntity.java
new file mode 100644
index 0000000..984c549
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntity.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinColumns;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+/**
+ * Represents a Host Group Component which is embedded in a Blueprint.
+ */
+@IdClass(HostGroupComponentEntityPK.class)
+@Table(name = "hostgroup_component")
+@Entity
+public class HostGroupComponentEntity {
+
+  @Id
+  @Column(name = "hostgroup_name", nullable = false, insertable = false, updatable = false)
+  private String hostGroupName;
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = false, updatable = false)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "name", nullable = false, insertable = true, updatable = false)
+  private String name;
+
+  @ManyToOne
+  @JoinColumns({
+      @JoinColumn(name = "hostgroup_name", referencedColumnName = "name", nullable = false),
+      @JoinColumn(name = "blueprint_name", referencedColumnName = "blueprint_name", nullable = false)
+  })
+  private HostGroupEntity hostGroup;
+
+
+  /**
+   * Get the name of the host group component.
+   *
+   * @return component name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Set the name of the host group component.
+   *
+   * @param name component name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Get the host group entity.
+   *
+   * @return host group entity
+   */
+  public HostGroupEntity getHostGroupEntity() {
+    return hostGroup;
+  }
+
+  /**
+   * Set the host group entity.
+   *
+   * @param entity  host group entity
+   */
+  public void setHostGroupEntity(HostGroupEntity entity) {
+    this.hostGroup = entity;
+  }
+
+  /**
+   * Get the name of the associated host group.
+   *
+   * @return host group name
+   */
+  public String getHostGroupName() {
+    return hostGroupName;
+  }
+
+  /**
+   * Set the name of the associated host group.
+   *
+   * @param hostGroupName host group name
+   */
+  public void setHostGroupName(String hostGroupName) {
+    this.hostGroupName = hostGroupName;
+  }
+
+  /**
+   * 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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntityPK.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntityPK.java
new file mode 100644
index 0000000..fb9011b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupComponentEntityPK.java
@@ -0,0 +1,114 @@
+/**
+ * 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 HostGroupComponentEntity.
+ */
+public class HostGroupComponentEntityPK {
+
+  @Id
+  @Column(name = "hostgroup_name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String hostGroupName;
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String name;
+
+  /**
+   * Get the name of the associated host group.
+   *
+   * @return host group name
+   */
+  public String getHostGroupName() {
+    return hostGroupName;
+  }
+
+  /**
+   * Set the name of the associated host group.
+   *
+   * @param hostGroupName  host group name
+   */
+  public void setHostGroupName(String hostGroupName) {
+    this.hostGroupName = hostGroupName;
+  }
+
+  /**
+   * 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 name of the host group component.
+   *
+   * @return component name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Set the name of the host group component.
+   *
+   * @param name  component name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    HostGroupComponentEntityPK that = (HostGroupComponentEntityPK) o;
+
+    return this.hostGroupName.equals(that.hostGroupName) &&
+        this.name.equals(that.name) &&
+        this.blueprintName.equals(that.blueprintName);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = hostGroupName.hashCode();
+    result = 31 * result + blueprintName.hashCode();
+    result = 31 * result + name.hashCode();
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java
new file mode 100644
index 0000000..fbc6ef4
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.orm.entities;
+
+import javax.persistence.Basic;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import java.util.Collection;
+
+/**
+ * Represents a Host Group which is embedded in a Blueprint.
+ */
+@javax.persistence.IdClass(HostGroupEntityPK.class)
+@Table(name = "hostgroup")
+@Entity
+public class HostGroupEntity {
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = false, updatable = false)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "name", nullable = false, insertable = true, updatable = false)
+  private String name;
+
+  @Column
+  @Basic
+  private String cardinality;
+
+  @OneToMany(cascade = CascadeType.ALL, mappedBy = "hostGroup")
+  private Collection<HostGroupComponentEntity> components;
+
+  @ManyToOne
+  @JoinColumn(name = "blueprint_name", referencedColumnName = "blueprint_name", nullable = false)
+  private BlueprintEntity blueprint;
+
+
+  /**
+   * Get the host group name.
+   *
+   * @return host group name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Set the host group name.
+   *
+   * @param name  host group name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  /**
+   * 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 collection of associated component entities.
+   *
+   * @return collection of components
+   */
+  public Collection<HostGroupComponentEntity> getComponents() {
+    return components;
+  }
+
+  /**
+   * Set thge collection of associated component entities.
+   *
+   * @param components  collection of components
+   */
+  public void setComponents(Collection<HostGroupComponentEntity> components) {
+    this.components = components;
+  }
+
+  /**
+   * Get the cardinality for this host group.
+   *
+   * @return cardinality
+   */
+  public String getCardinality() {
+    return cardinality;
+  }
+
+  /**
+   * Set the cardinality value for this host group.
+   *
+   * @param cardinality cardinality value
+   */
+  public void setCardinality(String cardinality) {
+    this.cardinality = cardinality;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntityPK.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntityPK.java
new file mode 100644
index 0000000..b5aba5f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntityPK.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 HostGroupEntity.
+ */
+public class HostGroupEntityPK {
+
+  @Id
+  @Column(name = "blueprint_name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String blueprintName;
+
+  @Id
+  @Column(name = "name", nullable = false, insertable = true, updatable = false, length = 100)
+  private String name;
+
+  /**
+   * 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 name of the host group.
+   *
+   * @return host group name
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Set the name of the host group.
+   *
+   * @param name  host group name
+   */
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    HostGroupEntityPK that = (HostGroupEntityPK) o;
+
+    return this.blueprintName.equals(that.blueprintName) &&
+           this.name.equals(that.name);
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * blueprintName.hashCode() + name.hashCode();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 8b8e285..c9b4bdb 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -55,6 +55,9 @@ CREATE TABLE configgroup (group_id BIGINT, cluster_id BIGINT NOT NULL, group_nam
 CREATE TABLE configgrouphostmapping (config_group_id BIGINT NOT NULL, host_name VARCHAR(255) NOT NULL, PRIMARY KEY(config_group_id, host_name));
 CREATE TABLE requestschedule (schedule_id bigint, cluster_id BIGINT NOT NULL, description varchar(255), status varchar(255), batch_separation_seconds smallint, batch_toleration_limit smallint, create_user varchar(255), create_timestamp bigint, update_user varchar(255), update_timestamp bigint, minutes varchar(10), hours varchar(10), days_of_month varchar(10), month varchar(10), day_of_week varchar(10), yearToSchedule varchar(10), startTime varchar(50), endTime varchar(50), last_execution_status varchar(255), PRIMARY KEY(schedule_id));
 CREATE TABLE requestschedulebatchrequest (schedule_id bigint, batch_id bigint, request_id bigint, request_type varchar(255), request_uri varchar(1024), request_body LONGBLOB, request_status varchar(255), return_code smallint, return_message varchar(2000), PRIMARY KEY(schedule_id, batch_id));
+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));
 
 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);
@@ -88,6 +91,8 @@ ALTER TABLE configgroup ADD CONSTRAINT FK_configgroup_cluster_id FOREIGN KEY (cl
 ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_configgroup_id FOREIGN KEY (config_group_id) REFERENCES configgroup (group_id);
 ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_host_name FOREIGN KEY (host_name) REFERENCES hosts (host_name);
 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);
 
 
 INSERT INTO ambari_sequences(sequence_name, value) values ('cluster_id_seq', 1);

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 3f1a080..ed2305b 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -45,6 +45,9 @@ CREATE TABLE confgroupclusterconfigmapping (config_group_id NUMBER(19) NOT NULL,
 CREATE TABLE configgrouphostmapping (config_group_id NUMBER(19) NOT NULL, host_name VARCHAR2(255) NOT NULL, PRIMARY KEY(config_group_id, host_name));
 CREATE TABLE requestschedule (schedule_id NUMBER(19), cluster_id NUMBER(19) NOT NULL, description VARCHAR2(255), status VARCHAR2(255), batch_separation_seconds smallint, batch_toleration_limit smallint, create_user VARCHAR2(255), create_timestamp NUMBER(19), update_user VARCHAR2(255), update_timestamp NUMBER(19), minutes VARCHAR2(10), hours VARCHAR2(10), days_of_month VARCHAR2(10), month VARCHAR2(10), day_of_week VARCHAR2(10), yearToSchedule VARCHAR2(10), startTime VARCHAR2(50), endTime VARCHAR2(50), last_execution_status VARCHAR2(255), PRIMARY KEY(schedule_id));
 CREATE TABLE requestschedulebatchrequest (schedule_id NUMBER(19), batch_id NUMBER(19), request_id NUMBER(19), request_type VARCHAR2(255), request_uri VARCHAR2(1024), request_body BLOB, request_status VARCHAR2(255), return_code smallint, return_message VARCHAR2(2000), PRIMARY KEY(schedule_id, batch_id));
+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));
 
 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);
@@ -76,6 +79,8 @@ ALTER TABLE confgroupclusterconfigmapping ADD CONSTRAINT FK_cgccm_gid FOREIGN KE
 ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_cghm_cgid FOREIGN KEY (config_group_id) REFERENCES configgroup (group_id);
 ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_cghm_hname FOREIGN KEY (host_name) REFERENCES hosts (host_name);
 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);
 
 INSERT INTO ambari_sequences(sequence_name, value) values ('host_role_command_id_seq', 0);
 INSERT INTO ambari_sequences(sequence_name, value) values ('user_id_seq', 1);

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 78dce48..6bc6f24 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -116,6 +116,13 @@ GRANT ALL PRIVILEGES ON TABLE ambari.requestschedule TO :username;
 CREATE TABLE ambari.requestschedulebatchrequest (schedule_id bigint, batch_id bigint, request_id bigint, request_type varchar(255), request_uri varchar(1024), request_body BYTEA, request_status varchar(255), return_code smallint, return_message varchar(20000), PRIMARY KEY(schedule_id, batch_id));
 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));
+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;
+
 --------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);
 ALTER TABLE ambari.clusterservices ADD CONSTRAINT FK_clusterservices_cluster_id FOREIGN KEY (cluster_id) REFERENCES ambari.clusters (cluster_id);
@@ -148,6 +155,9 @@ ALTER TABLE ambari.confgroupclusterconfigmapping ADD CONSTRAINT FK_confgroupclus
 ALTER TABLE ambari.configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_configgroup_id FOREIGN KEY (config_group_id) REFERENCES ambari.configgroup (group_id);
 ALTER TABLE ambari.configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts (host_name);
 ALTER TABLE ambari.requestschedulebatchrequest ADD CONSTRAINT FK_requestschedulebatchrequest_schedule_id FOREIGN KEY (schedule_id) REFERENCES ambari.requestschedule (schedule_id);
+ALTER TABLE ambari.hostgroup ADD FOREIGN KEY (blueprint_name) REFERENCES ambari.blueprint(blueprint_name);
+ALTER TABLE ambari.hostgroup_component ADD FOREIGN KEY (blueprint_name, hostgroup_name) REFERENCES ambari.hostgroup(blueprint_name, name);
+
 
 ---------inserting some data-----------
 BEGIN;

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/META-INF/persistence.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml
index 3b07bd7..c85d79d 100644
--- a/ambari-server/src/main/resources/META-INF/persistence.xml
+++ b/ambari-server/src/main/resources/META-INF/persistence.xml
@@ -41,6 +41,9 @@
     <class>org.apache.ambari.server.orm.entities.ActionEntity</class>
     <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.HostGroupEntity</class>
+    <class>org.apache.ambari.server.orm.entities.HostGroupComponentEntity</class>
 
     <properties>
       <!--<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/ambari" />-->

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/key_properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/key_properties.json b/ambari-server/src/main/resources/key_properties.json
index dc7e23a..b3715cb 100644
--- a/ambari-server/src/main/resources/key_properties.json
+++ b/ambari-server/src/main/resources/key_properties.json
@@ -106,5 +106,8 @@
   "RequestSchedule" : {
     "Cluster": "RequestSchedule/cluster_name",
     "RequestSchedule": "RequestSchedule/id"
-  }
+  },
+  "Blueprint": {
+    "Blueprint": "Blueprints/blueprint_name"
+    }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index 4a824d3..2cd5e35 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -285,16 +285,16 @@
         "TaskAttempt/output_bytes",
         "TaskAttempt/status",
         "TaskAttempt/locality"
-     ],
+    ],
      "RootService":[
         "RootService/service_name"
-     ],
+    ],
     "RootServiceComponent":[
         "RootServiceComponents/service_name",
         "RootServiceComponents/component_name",
         "RootServiceComponents/properties",
         "RootServiceComponents/component_version"
-     ],
+    ],
     "RootServiceHostComponent":[
         "RootServiceHostComponents/service_name",
         "RootServiceHostComponents/component_name",
@@ -302,5 +302,13 @@
         "RootServiceHostComponents/component_state",
         "RootServiceHostComponents/component_version",
         "RootServiceHostComponents/properties"
-     ]
+     ],
+    "Blueprint":[
+        "Blueprints/blueprint_name",
+        "Blueprints/stack_name",
+        "Blueprints/stack_version",
+        "host_groups",
+        "host_groups/components",
+        "host_groups/cardinality"
+    ]
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql
index 509d1cf..c12aa82 100644
--- a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql
+++ b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-MySQL-UPGRADE.sql
@@ -83,3 +83,11 @@ create index idx_qrtz_ft_tg on QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
 ALTER TABLE hostcomponentdesiredstate ADD passive_state VARCHAR(32) NOT NULL DEFAULT 'ACTIVE';
 ALTER TABLE servicedesiredstate ADD passive_state VARCHAR(32) NOT NULL DEFAULT 'ACTIVE';
 ALTER TABLE hoststate ADD passive_state VARCHAR(512);
+
+-- blueprint related tables
+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));
+
+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);

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql
index a74f2a2..b82f4e8 100644
--- a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql
+++ b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Oracle-UPGRADE.sql
@@ -45,7 +45,6 @@ CREATE TABLE configgroup (group_id NUMBER(19), cluster_id NUMBER(19) NOT NULL, g
 CREATE TABLE confgroupclusterconfigmapping (config_group_id NUMBER(19) NOT NULL, cluster_id NUMBER(19) NOT NULL, config_type VARCHAR2(255) NOT NULL, version_tag VARCHAR2(255) NOT NULL, user_name VARCHAR2(255) DEFAULT '_db', create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY(config_group_id, cluster_id, config_type));
 CREATE TABLE configgrouphostmapping (config_group_id NUMBER(19) NOT NULL, host_name VARCHAR2(255) NOT NULL, PRIMARY KEY(config_group_id, host_name));
 
-
 ALTER TABLE configgroup ADD CONSTRAINT FK_configgroup_cluster_id FOREIGN KEY (cluster_id) REFERENCES clusters (cluster_id);
 ALTER TABLE confgroupclusterconfigmapping ADD CONSTRAINT FK_confg FOREIGN KEY (version_tag, config_type, cluster_id) REFERENCES clusterconfig (version_tag, type_name, cluster_id);
 ALTER TABLE confgroupclusterconfigmapping ADD CONSTRAINT FK_cgccm_gid FOREIGN KEY (config_group_id) REFERENCES configgroup (group_id);
@@ -55,6 +54,14 @@ ALTER TABLE configgrouphostmapping ADD CONSTRAINT FK_cghm_hname FOREIGN KEY (hos
 -- Don't set not null constraint
 -- ALTER TABLE stage MODIFY (cluster_host_info NOT NULL);
 
+-- blueprint related tables
+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));
+
+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);
+
 -- Abort all tasks in progress due to format change
 UPDATE host_role_command SET status = 'ABORTED' WHERE status IN ('PENDING', 'QUEUED', 'IN_PROGRESS');
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql
index 674d08c..01bf43b 100644
--- a/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql
+++ b/ambari-server/src/main/resources/upgrade/ddl/Ambari-DDL-Postgres-UPGRADE-1.3.0.sql
@@ -132,6 +132,18 @@ ALTER TABLE ambari.confgroupclusterconfigmapping ADD CONSTRAINT FK_confgroupclus
 ALTER TABLE ambari.configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_configgroup_id FOREIGN KEY (config_group_id) REFERENCES ambari.configgroup (group_id);
 ALTER TABLE ambari.configgrouphostmapping ADD CONSTRAINT FK_configgrouphostmapping_host_name FOREIGN KEY (host_name) REFERENCES ambari.hosts (host_name);
 
+-- create blueprint tables
+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));
+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
+-- add fk constraints to blueprint tables
+ALTER TABLE ambari.hostgroup ADD FOREIGN KEY (blueprint_name) REFERENCES ambari.blueprint(blueprint_name);
+ALTER TABLE ambari.hostgroup_component ADD FOREIGN KEY (blueprint_name, hostgroup_name) REFERENCES ambari.hostgroup(blueprint_name, name);
+
+
 -- add decommission state
 ALTER TABLE ambari.hostcomponentdesiredstate ADD COLUMN admin_state VARCHAR(32);
 ALTER TABLE ambari.hostcomponentdesiredstate ADD COLUMN passive_state VARCHAR(32) NOT NULL DEFAULT 'ACTIVE'

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinitionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinitionTest.java
new file mode 100644
index 0000000..a4531c3
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BlueprintResourceDefinitionTest.java
@@ -0,0 +1,53 @@
+/**
+ * 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.controller.spi.Resource;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * BlueprintResourceDefinition tests.
+ */
+public class BlueprintResourceDefinitionTest {
+
+  @Test
+  public void testGetType() throws Exception {
+    BlueprintResourceDefinition definition = new BlueprintResourceDefinition();
+    Assert.assertEquals(Resource.Type.Blueprint, definition.getType());
+  }
+
+  @Test
+  public void testGetPluralName() throws Exception {
+    BlueprintResourceDefinition definition = new BlueprintResourceDefinition();
+    Assert.assertEquals("blueprints", definition.getPluralName());
+  }
+
+  @Test
+  public void testGetSingularName() throws Exception {
+    BlueprintResourceDefinition definition = new BlueprintResourceDefinition();
+    Assert.assertEquals("blueprint", definition.getSingularName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() {
+    BlueprintResourceDefinition definition = new BlueprintResourceDefinition();
+    Assert.assertTrue(definition.getSubResourceDefinitions().isEmpty());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5f464316/ambari-server/src/test/java/org/apache/ambari/server/api/services/BlueprintServiceTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/BlueprintServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/BlueprintServiceTest.java
new file mode 100644
index 0000000..b109e96
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/BlueprintServiceTest.java
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for BlueprintService.
+ */
+public class BlueprintServiceTest extends BaseServiceTest {
+
+
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getBlueprint
+    BlueprintService BlueprintService = new TestBlueprintService("blueprintName");
+    Method m = BlueprintService.getClass().getMethod("getBlueprint", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "blueprintName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, BlueprintService, m, args, null));
+
+    //getBlueprints
+    BlueprintService = new TestBlueprintService(null);
+    m = BlueprintService.getClass().getMethod("getBlueprints", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, BlueprintService, m, args, null));
+
+    //createBlueprint
+    BlueprintService = new TestBlueprintService("blueprintName");
+    m = BlueprintService.getClass().getMethod("createBlueprint", String.class, HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo(), "blueprintName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, BlueprintService, m, args, "body"));
+
+    //deleteBlueprint
+    BlueprintService = new TestBlueprintService("blueprintName");
+    m = BlueprintService.getClass().getMethod("deleteBlueprint", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "blueprintName"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, BlueprintService, m, args, null));
+
+    //deleteBlueprints
+    BlueprintService = new TestBlueprintService(null);
+    m = BlueprintService.getClass().getMethod("deleteBlueprints", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, BlueprintService, m, args, null));
+
+    return listInvocations;
+  }
+
+
+  private class TestBlueprintService extends BlueprintService {
+    private String m_blueprintId;
+
+    private TestBlueprintService(String blueprintId) {
+      m_blueprintId = blueprintId;
+    }
+
+    @Override
+    ResourceInstance createBlueprintResource(String blueprintName) {
+      assertEquals(m_blueprintId, blueprintName);
+      return getTestResource();
+    }
+
+    @Override
+    RequestFactory getRequestFactory() {
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
+    }
+
+    @Override
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
+    }
+  }
+}
\ No newline at end of file