Przeglądaj źródła

AMBARI-6015. Provide topology validation when creating blueprint.

John Speidel 11 lat temu
rodzic
commit
ac8641fb30
20 zmienionych plików z 1140 dodań i 607 usunięć
  1. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  2. 968 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java
  3. 27 75
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java
  4. 4 493
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
  5. 0 2
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
  6. 9 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java
  7. 8 0
      ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java
  8. 2 1
      ambari-server/src/main/resources/properties.json
  9. 0 10
      ambari-server/src/main/resources/stacks/HDP/1.3.2/services/HIVE/metainfo.xml
  10. 1 1
      ambari-server/src/main/resources/stacks/HDP/1.3.2/services/ZOOKEEPER/metainfo.xml
  11. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/FLUME/metainfo.xml
  12. 5 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/metainfo.xml
  13. 2 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml
  14. 9 1
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HDFS/metainfo.xml
  15. 1 10
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HIVE/metainfo.xml
  16. 1 1
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/ZOOKEEPER/metainfo.xml
  17. 2 0
      ambari-server/src/main/resources/stacks/HDP/2.1/services/FALCON/metainfo.xml
  18. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.1/services/TEZ/metainfo.xml
  19. 41 10
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BlueprintResourceProviderTest.java
  20. 56 3
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java

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

@@ -138,6 +138,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
         return new RequestScheduleResourceProvider(propertyIds, keyPropertyIds, managementController);
       case HostComponentProcess:
         return new HostComponentProcessResourceProvider(propertyIds, keyPropertyIds, managementController);
+      case Blueprint:
+        return new BlueprintResourceProvider(propertyIds, keyPropertyIds, managementController);
       default:
         throw new IllegalArgumentException("Unknown type " + type);
     }

+ 968 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java

@@ -0,0 +1,968 @@
+/**
+ * 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.gson.Gson;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.StackAccessException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.StackConfigurationRequest;
+import org.apache.ambari.server.controller.StackConfigurationResponse;
+import org.apache.ambari.server.controller.StackServiceComponentRequest;
+import org.apache.ambari.server.controller.StackServiceComponentResponse;
+import org.apache.ambari.server.controller.StackServiceRequest;
+import org.apache.ambari.server.controller.StackServiceResponse;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.SystemException;
+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.HostGroupConfigEntity;
+import org.apache.ambari.server.orm.entities.HostGroupEntity;
+import org.apache.ambari.server.state.AutoDeployInfo;
+import org.apache.ambari.server.state.DependencyInfo;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Base blueprint processing resource provider.
+ */
+public abstract class BaseBlueprintProcessor extends AbstractControllerResourceProvider {
+
+  /**
+   * Data access object used to obtain blueprint entities.
+   */
+  protected static BlueprintDAO blueprintDAO;
+
+  /**
+   * Stack related information.
+   */
+  protected static AmbariMetaInfo stackInfo;
+
+
+  protected BaseBlueprintProcessor(Set<String> propertyIds,
+                                   Map<Resource.Type, String> keyPropertyIds,
+                                   AmbariManagementController managementController) {
+
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+  /**
+   * Get host groups which contain a component.
+   *
+   * @param component   component name
+   * @param hostGroups  collection of host groups to check
+   *
+   * @return collection of host groups which contain the specified component
+   */
+  protected Collection<HostGroup> getHostGroupsForComponent(String component, Collection<HostGroup> hostGroups) {
+    Collection<HostGroup> resultGroups = new HashSet<HostGroup>();
+    for (HostGroup group : hostGroups ) {
+      if (group.getComponents().contains(component)) {
+        resultGroups.add(group);
+      }
+    }
+    return resultGroups;
+  }
+
+  /**
+   * Parse blueprint host groups.
+   *
+   * @param blueprint  associated blueprint
+   * @param stack      associated stack
+   *
+   * @return map of host group name to host group
+   */
+  protected Map<String, HostGroup> parseBlueprintHostGroups(BlueprintEntity blueprint, Stack stack) {
+    Map<String, HostGroup> mapHostGroups = new HashMap<String, HostGroup>();
+
+    for (HostGroupEntity hostGroup : blueprint.getHostGroups()) {
+      mapHostGroups.put(hostGroup.getName(), new HostGroup(hostGroup, stack));
+    }
+    return mapHostGroups;
+  }
+
+  /**
+   * Parse stack information.
+   *
+   * @param blueprint  associated blueprint
+   *
+   * @return stack instance
+   *
+   * @throws SystemException an unexpected exception occurred
+   */
+  protected Stack parseStack(BlueprintEntity blueprint) throws SystemException {
+    Stack stack;
+    try {
+      stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
+    } catch (StackAccessException e) {
+      throw new IllegalArgumentException("Invalid stack information provided for cluster.  " +
+          "stack name: " + blueprint.getStackName() +
+          " stack version: " + blueprint.getStackVersion());
+    } catch (AmbariException e) {
+      throw new SystemException("Unable to obtain stack information.", e);
+    }
+    return stack;
+  }
+
+  /**
+   * Validate blueprint topology.
+   * An exception is thrown in the case of validation failure.
+   * For missing components which are auto-deploy enabled, these are added to the topology which is reflected
+   * in the blueprint entity that is returned.
+   *
+   * @param blueprint  blueprint to validate
+   *
+   * @return blueprint entity which may have been updated as a result of auto-deployment of components.
+   *
+   * @throws AmbariException an unexpected error occurred
+   * @throws IllegalArgumentException when validation fails
+   */
+  protected BlueprintEntity validateTopology(BlueprintEntity blueprint) throws AmbariException {
+    Stack stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
+    Map<String, HostGroup> hostGroupMap = parseBlueprintHostGroups(blueprint, stack);
+    Collection<HostGroup> hostGroups = hostGroupMap.values();
+    Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies =
+        new HashMap<String, Map<String, Collection<DependencyInfo>>>();
+
+    Collection<String> services = getTopologyServices(hostGroups);
+    for (HostGroup group : hostGroups) {
+      Map<String, Collection<DependencyInfo>> missingGroupDependencies = group.validateTopology(hostGroups, services);
+      if (! missingGroupDependencies.isEmpty()) {
+        missingDependencies.put(group.getEntity().getName(), missingGroupDependencies);
+      }
+    }
+
+    Collection<String> cardinalityFailures = new HashSet<String>();
+    for (String service : services) {
+      for (String component : stack.getComponents(service)) {
+        Cardinality cardinality = stack.getCardinality(component);
+        AutoDeployInfo autoDeploy = stack.getAutoDeployInfo(component);
+        if (cardinality.isAll()) {
+          cardinalityFailures.addAll(verifyComponentInAllHostGroups(
+              blueprint, hostGroups, component, autoDeploy));
+        } else {
+          cardinalityFailures.addAll(verifyComponentCardinalityCount(
+              blueprint, hostGroups, component, cardinality, autoDeploy));
+        }
+      }
+    }
+
+    if (! missingDependencies.isEmpty() || ! cardinalityFailures.isEmpty()) {
+      generateInvalidTopologyException(missingDependencies, cardinalityFailures);
+    }
+
+    return blueprint;
+  }
+
+  /**
+   * Process cluster scoped configurations contained in blueprint.
+   *
+   * @param blueprint  blueprint entity
+   *
+   * @return cluster scoped properties contained within in blueprint
+   */
+  protected Map<String, Map<String, String>> processBlueprintConfigurations(
+      BlueprintEntity blueprint, Collection<Map<String, String>> configOverrides) {
+
+    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));
+    }
+    overrideExistingProperties(mapConfigurations, configOverrides);
+
+    return mapConfigurations;
+  }
+
+  /**
+   * Override existing properties or add new.
+   *
+   * @param existingProperties  current property values
+   * @param configOverrides     override properties
+   */
+  protected void overrideExistingProperties(Map<String, Map<String, String>> existingProperties,
+                                            Collection<Map<String, String>> configOverrides) {
+    if (configOverrides != null) {
+      for (Map<String, String> properties : configOverrides) {
+        String category = null;
+        int propertyOffset = -1;
+        for (Map.Entry<String, String> entry : properties.entrySet()) {
+          String absolutePropName = entry.getKey();
+          if (category == null) {
+            propertyOffset =  absolutePropName.indexOf('/');
+            category = absolutePropName.substring(0, propertyOffset);
+          }
+          Map<String, String> existingCategoryProperties = existingProperties.get(category);
+          if (existingCategoryProperties == null) {
+            existingCategoryProperties = new HashMap<String, String>();
+            existingProperties.put(category, existingCategoryProperties);
+          }
+          //override existing property or add new
+          existingCategoryProperties.put(absolutePropName.substring(propertyOffset + 1), entry.getValue());
+        }
+      }
+    }
+  }
+
+  /**
+   * Add a new component entity to a host group entity.
+   *
+   * @param blueprint  blueprint entity
+   * @param hostGroup  host group name
+   * @param component  name of component which is being added
+   */
+  protected void addComponentToBlueprint(BlueprintEntity blueprint, String hostGroup, String component) {
+    HostGroupComponentEntity componentEntity = new HostGroupComponentEntity();
+    componentEntity.setBlueprintName(blueprint.getBlueprintName());
+    componentEntity.setName(component);
+
+    for (HostGroupEntity hostGroupEntity : blueprint.getHostGroups()) {
+      if (hostGroupEntity.getName().equals(hostGroup))  {
+        componentEntity.setHostGroupEntity(hostGroupEntity);
+        componentEntity.setHostGroupName(hostGroupEntity.getName());
+        hostGroupEntity.addComponent(componentEntity);
+        break;
+      }
+    }
+  }
+
+  /**
+   * Obtain a blueprint entity based on name.
+   *
+   * @param blueprintName  name of blueprint to obtain
+   *
+   * @return blueprint entity for the given name
+   * @throws IllegalArgumentException no blueprint with the given name found
+   */
+  protected BlueprintEntity getExistingBlueprint(String blueprintName) {
+    BlueprintEntity blueprint = blueprintDAO.findByName(blueprintName);
+    if (blueprint == null) {
+      throw new IllegalArgumentException("Specified blueprint doesn't exist: " + blueprintName);
+    }
+    return blueprint;
+  }
+
+  /**
+   * Get all services provided in topology.
+   *
+   * @param hostGroups  all host groups in topology
+   *
+   * @return collections of all services provided by topology
+   */
+  protected Collection<String> getTopologyServices(Collection<HostGroup> hostGroups) {
+    Collection<String> services = new HashSet<String>();
+    for (HostGroup group : hostGroups) {
+      services.addAll(group.getServices());
+    }
+    return services;
+  }
+
+  /**
+   * Verify that a component meets cardinality requirements.  For components that are
+   * auto-install enabled, will add component to topology if needed.
+   *
+   * @param blueprint    blueprint instance
+   * @param hostGroups   collection of host groups
+   * @param component    component to validate
+   * @param cardinality  required cardinality
+   * @param autoDeploy   auto-deploy information for component
+   *
+   * @return collection of missing component information
+   */
+  private Collection<String> verifyComponentCardinalityCount(BlueprintEntity blueprint,
+                                                             Collection<HostGroup> hostGroups,
+                                                             String component,
+                                                             Cardinality cardinality,
+                                                             AutoDeployInfo autoDeploy) {
+
+    Collection<String> cardinalityFailures = new HashSet<String>();
+
+    int actualCount = getHostGroupsForComponent(component, hostGroups).size();
+    boolean autoDeployed = false;
+    if (! cardinality.isValidCount(actualCount)) {
+      if (autoDeploy != null && autoDeploy.isEnabled() && cardinality.cardinality.equals("1")) {
+        String coLocateName = autoDeploy.getCoLocate();
+        if (coLocateName != null && ! coLocateName.isEmpty()) {
+          Collection<HostGroup> coLocateHostGroups = getHostGroupsForComponent(
+              coLocateName.split("/")[1], hostGroups);
+          if (! coLocateHostGroups.isEmpty()) {
+            autoDeployed = true;
+            HostGroup group = coLocateHostGroups.iterator().next();
+            if (group.addComponent(component)) {
+              addComponentToBlueprint(blueprint, group.getEntity().getName(), component);
+            }
+          }
+        }
+      }
+      if (! autoDeployed) {
+        cardinalityFailures.add(component + "(actual=" + actualCount + ", required=" +
+            cardinality.cardinality + ")");
+      }
+    }
+    return cardinalityFailures;
+  }
+
+  /**
+   * Verify that a component is included in all host groups.
+   * For components that are auto-install enabled, will add component to topology if needed.
+   *
+   * @param blueprint   blueprint instance
+   * @param hostGroups  collection of host groups
+   * @param component   component to validate
+   * @param autoDeploy  auto-deploy information for component
+   *
+   * @return collection of missing component information
+   */
+  private Collection<String> verifyComponentInAllHostGroups(BlueprintEntity blueprint,
+                                                            Collection<HostGroup> hostGroups,
+                                                            String component,
+                                                            AutoDeployInfo autoDeploy) {
+
+    Collection<String> cardinalityFailures = new HashSet<String>();
+    int actualCount = getHostGroupsForComponent(component, hostGroups).size();
+    if (actualCount != hostGroups.size()) {
+      if (autoDeploy != null && autoDeploy.isEnabled()) {
+        for (HostGroup group : hostGroups) {
+          if (group.addComponent(component)) {
+            addComponentToBlueprint(blueprint, group.getEntity().getName(), component);
+          }
+        }
+      } else {
+        cardinalityFailures.add(component + "(actual=" + actualCount + ", required=ALL)");
+      }
+    }
+    return cardinalityFailures;
+  }
+
+  /**
+   * Generate an exception for topology validation failure.
+   *
+   * @param missingDependencies  missing dependency information
+   * @param cardinalityFailures  missing service component information
+   *
+   * @throws IllegalArgumentException  Always thrown and contains information regarding the topology validation failure
+   *                                   in the msg
+   */
+  private void generateInvalidTopologyException(Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies,
+                                                Collection<String> cardinalityFailures) {
+
+    String msg = "Cluster Topology validation failed.";
+    if (! cardinalityFailures.isEmpty()) {
+      msg += "  Invalid service component count: " + cardinalityFailures;
+    }
+    if (! missingDependencies.isEmpty()) {
+      msg += "  Unresolved component dependencies: " + missingDependencies;
+    }
+    msg += ".  To disable topology validation and create the blueprint, " +
+           "add the following to the end of the url: '?validate_topology=false'";
+    throw new IllegalArgumentException(msg);
+  }
+
+
+  // ----- Inner Classes -----------------------------------------------------
+
+  /**
+   * Encapsulates stack information.
+   */
+  protected class Stack {
+    /**
+     * Stack name
+     */
+    private String name;
+
+    /**
+     * Stack version
+     */
+    private String version;
+
+    /**
+     * Map of service name to components
+     */
+    private Map<String, Collection<String>> serviceComponents =
+        new HashMap<String, Collection<String>>();
+
+    /**
+     * Map of component to service
+     */
+    private Map<String, String> componentService = new HashMap<String, String>();
+
+    /**
+     * Map of component to dependencies
+     */
+    private Map<String, Collection<DependencyInfo>> dependencies =
+        new HashMap<String, Collection<DependencyInfo>>();
+
+    /**
+     * Map of dependency to conditional service
+     */
+    private Map<DependencyInfo, String> dependencyConditionalServiceMap =
+        new HashMap<DependencyInfo, String>();
+
+    /**
+     * Map of component to required cardinality
+     */
+    private Map<String, String> cardinalityRequirements = new HashMap<String, String>();
+
+    /**
+     * Map of component to auto-deploy information
+     */
+    private Map<String, AutoDeployInfo> componentAutoDeployInfo =
+        new HashMap<String, AutoDeployInfo>();
+
+    /**
+     * Map of service to config type properties
+     */
+    private Map<String, Map<String, Map<String, String>>> serviceConfigurations =
+        new HashMap<String, Map<String, Map<String, String>>>();
+
+    /**
+     * Constructor.
+     *
+     * @param name     stack name
+     * @param version  stack version
+     *
+     * @throws AmbariException an exception occurred getting stack information
+     *                         for the specified name and version
+     */
+    public Stack(String name, String version) throws AmbariException {
+      this.name = name;
+      this.version = version;
+
+      Set<StackServiceResponse> stackServices = getManagementController().getStackServices(
+          Collections.singleton(new StackServiceRequest(name, version, null)));
+
+      for (StackServiceResponse stackService : stackServices) {
+        String serviceName = stackService.getServiceName();
+        parseComponents(serviceName);
+        parseConfigurations(serviceName);
+        recordConditionalDependencies();
+      }
+    }
+
+    /**
+     * Obtain stack name.
+     *
+     * @return stack name
+     */
+    public String getName() {
+      return name;
+    }
+
+    /**
+     * Obtain stack version.
+     *
+     * @return stack version
+     */
+    public String getVersion() {
+      return version;
+    }
+
+    /**
+     * Get services contained in the stack.
+     *
+     * @return collection of all services for the stack
+     */
+    public Collection<String> getServices() {
+      return serviceComponents.keySet();
+    }
+
+    /**
+     * Get components contained in the stack for the specified service.
+     *
+     * @param service  service name
+     *
+     * @return collection of component names for the specified service
+     */
+    public Collection<String> getComponents(String service) {
+      return serviceComponents.get(service);
+    }
+
+    /**
+     * Get configuration types for the specified service.
+     *
+     * @param service  service name
+     *
+     * @return collection of configuration types for the specified service
+     */
+    public Collection<String> getConfigurationTypes(String service) {
+      return serviceConfigurations.get(service).keySet();
+    }
+
+    /**
+     * Get config properties for the specified service and configuration type.
+     *
+     * @param service  service name
+     * @param type     configuration type
+     *
+     * @return map of property names to values for the specified service and configuration type
+     */
+    public Map<String, String> getConfigurationProperties(String service, String type) {
+      return serviceConfigurations.get(service).get(type);
+    }
+
+    /**
+     * Get the service for the specified component.
+     *
+     * @param component  component name
+     *
+     * @return service name that contains tha specified component
+     */
+    public String getServiceForComponent(String component) {
+      return componentService.get(component);
+    }
+
+    /**
+     * Get the names of the services which contains the specified components.
+     *
+     * @param components collection of components
+     *
+     * @return collection of services which contain the specified components
+     */
+    public Collection<String> getServicesForComponents(Collection<String> components) {
+      Set<String> services = new HashSet<String>();
+      for (String component : components) {
+        services.add(getServiceForComponent(component));
+      }
+
+      return services;
+    }
+
+    /**
+     * Obtain the service name which corresponds to the specified configuration.
+     *
+     * @param config  configuration type
+     *
+     * @return name of service which corresponds to the specified configuration type
+     */
+    public String getServiceForConfigType(String config) {
+      for (Map.Entry<String, Map<String, Map<String, String>>> entry : serviceConfigurations.entrySet()) {
+        Map<String, Map<String, String>> typeMap = entry.getValue();
+        if (typeMap.containsKey(config)) {
+          return entry.getKey();
+        }
+      }
+      throw new IllegalArgumentException(
+          "Specified configuration type is not associated with any service: " + config);
+    }
+
+    /**
+     * Return the dependencies specified for the given component.
+     *
+     * @param component  component to get dependency information for
+     *
+     * @return collection of dependency information for the specified component
+     */
+    //todo: full dependency graph
+    public Collection<DependencyInfo> getDependenciesForComponent(String component) {
+      return dependencies.containsKey(component) ? dependencies.get(component) :
+          Collections.<DependencyInfo>emptySet();
+    }
+
+    /**
+     * Get the service, if any, that a component dependency is conditional on.
+     *
+     * @param dependency  dependency to get conditional service for
+     *
+     * @return conditional service for provided component or null if dependency
+     *         is not conditional on a service
+     */
+    public String getConditionalServiceForDependency(DependencyInfo dependency) {
+      return dependencyConditionalServiceMap.get(dependency);
+    }
+
+    /**
+     * Obtain the required cardinality for the specified component.
+     */
+    public Cardinality getCardinality(String component) {
+      return new Cardinality(cardinalityRequirements.get(component));
+    }
+
+    /**
+     * Obtain auto-deploy information for the specified component.
+     */
+    public AutoDeployInfo getAutoDeployInfo(String component) {
+      return componentAutoDeployInfo.get(component);
+    }
+
+    /**
+     * Parse components for the specified service from the stack definition.
+     *
+     * @param service  service name
+     *
+     * @throws AmbariException an exception occurred getting components from the stack definition
+     */
+    private void parseComponents(String service) throws AmbariException{
+      Collection<String> componentSet = new HashSet<String>();
+
+      Set<StackServiceComponentResponse> components = getManagementController().getStackComponents(
+          Collections.singleton(new StackServiceComponentRequest(name, version, service, null)));
+
+      // stack service components
+      for (StackServiceComponentResponse component : components) {
+        String componentName = component.getComponentName();
+        componentSet.add(componentName);
+        componentService.put(componentName, service);
+        String cardinality = component.getCardinality();
+        if (cardinality != null) {
+          cardinalityRequirements.put(componentName, cardinality);
+        }
+        AutoDeployInfo autoDeploy = component.getAutoDeploy();
+        if (autoDeploy != null) {
+          componentAutoDeployInfo.put(componentName, autoDeploy);
+        }
+
+        // populate component dependencies
+        Collection<DependencyInfo> componentDependencies = stackInfo.getComponentDependencies(
+            name, version, service, componentName);
+
+        if (componentDependencies != null && ! componentDependencies.isEmpty()) {
+          dependencies.put(componentName, componentDependencies);
+        }
+      }
+      this.serviceComponents.put(service, componentSet);
+    }
+
+    /**
+     * Parse configurations for the specified service from the stack definition.
+     *
+     * @param service  service name
+     *
+     * @throws AmbariException an exception occurred getting configurations from the stack definition
+     */
+    private void parseConfigurations(String service) throws AmbariException {
+      Map<String, Map<String, String>> mapServiceConfig = new HashMap<String, Map<String, String>>();
+
+      serviceConfigurations.put(service, mapServiceConfig);
+
+      Set<StackConfigurationResponse> serviceConfigs = getManagementController().getStackConfigurations(
+          Collections.singleton(new StackConfigurationRequest(name, version, service, null)));
+
+      for (StackConfigurationResponse config : serviceConfigs) {
+        String type = config.getType();
+        //strip .xml from type
+        if (type.endsWith(".xml")) {
+          type = type.substring(0, type.length() - 4);
+        }
+        Map<String, String> mapTypeConfig = mapServiceConfig.get(type);
+        if (mapTypeConfig == null) {
+          mapTypeConfig = new HashMap<String, String>();
+          mapServiceConfig.put(type, mapTypeConfig);
+        }
+        mapTypeConfig.put(config.getPropertyName(), config.getPropertyValue());
+      }
+    }
+
+    /**
+     * Register conditional dependencies.
+     */
+    //todo: This information should be specified in the stack definition.
+    private void recordConditionalDependencies() {
+      Collection<DependencyInfo> nagiosDependencies = getDependenciesForComponent("NAGIOS_SERVER");
+      for (DependencyInfo dependency : nagiosDependencies) {
+        if (dependency.getComponentName().equals("HCAT")) {
+          dependencyConditionalServiceMap.put(dependency, "HCATALOG");
+        } else if (dependency.getComponentName().equals("OOZIE_CLIENT")) {
+          dependencyConditionalServiceMap.put(dependency, "OOZIE");
+        }
+      }
+    }
+  }
+
+  /**
+   * Host group representation.
+   */
+  protected class HostGroup {
+    /**
+     * Host group entity
+     */
+    private HostGroupEntity hostGroup;
+
+    /**
+     * Components contained in the host group
+     */
+    private Collection<String> components = new HashSet<String>();
+
+    /**
+     * Hosts contained associated with the host group
+     */
+    private Collection<String> hosts = new HashSet<String>();
+
+    /**
+     * Map of service to components for the host group
+     */
+    private Map<String, Set<String>> componentsForService = new HashMap<String, Set<String>>();
+
+    /**
+     * Map of host group configurations.
+     * Type -> Map<Key, Val>
+     */
+    private Map<String, Map<String, String>> configurations =
+        new HashMap<String, Map<String, String>>();
+
+    /**
+     * Associated stack
+     */
+    private Stack stack;
+
+    /**
+     * Constructor.
+     *
+     * @param hostGroup  host group
+     * @param stack      stack
+     */
+    public HostGroup(HostGroupEntity hostGroup, Stack stack) {
+      this.hostGroup = hostGroup;
+      this.stack = stack;
+      parseComponents();
+      parseConfigurations();
+    }
+
+    /**
+     * Associate a host with the host group.
+     *
+     * @param fqdn  fully qualified domain name of the host being added
+     */
+    public void addHostInfo(String fqdn) {
+      this.hosts.add(fqdn);
+    }
+
+    /**
+     * Get associated host information.
+     *
+     * @return collection of hosts associated with the host group
+     */
+    public Collection<String> getHostInfo() {
+      return this.hosts;
+    }
+
+    /**
+     * Get the services which are deployed to this host group.
+     *
+     * @return collection of services which have components in this host group
+     */
+    public Collection<String> getServices() {
+      return componentsForService.keySet();
+    }
+
+    /**
+     * Get the components associated with the host group.
+     *
+     * @return  collection of component names for the host group
+     */
+    public Collection<String> getComponents() {
+      return this.components;
+    }
+
+
+    /**
+     * Add a component to the host group.
+     *
+     * @param component  component to add
+     *
+     * @return true if component was added; false if component already existed
+     */
+    public boolean addComponent(String component) {
+      boolean added = components.add(component);
+      if (added) {
+        String service = stack.getServiceForComponent(component);
+        if (service != null) {
+          // an example of a component without a service in the stack is AMBARI_SERVER
+          Set<String> serviceComponents = componentsForService.get(service);
+          if (serviceComponents == null) {
+            serviceComponents = new HashSet<String>();
+            componentsForService.put(service, serviceComponents);
+          }
+          serviceComponents.add(component);
+        }
+      }
+      return added;
+    }
+
+    /**
+     * Get the components for the specified service which are associated with the host group.
+     *
+     * @param service  service name
+     *
+     * @return set of component names
+     */
+    public Collection<String> getComponents(String service) {
+      return componentsForService.get(service);
+    }
+
+    /**
+     * Get the configurations associated with the host group.
+     *
+     * @return map of configuration type to a map of properties
+     */
+    public Map<String, Map<String, String>> getConfigurations() {
+      return configurations;
+    }
+
+    /**
+     * Get the associated entity.
+     *
+     * @return  associated host group entity
+     */
+    public HostGroupEntity getEntity() {
+      return hostGroup;
+    }
+
+    /**
+     * Validate host group topology. This includes ensuring that all component dependencies are satisfied.
+     *
+     * @param hostGroups  collection of all host groups
+     *
+     * @return map of component to missing dependencies
+     */
+    public Map<String, Collection<DependencyInfo>> validateTopology(Collection<HostGroup> hostGroups,
+                                                                    Collection<String> services) {
+
+      Map<String, Collection<DependencyInfo>> missingDependencies =
+          new HashMap<String, Collection<DependencyInfo>>();
+
+      for (String component : new HashSet<String>(components)) {
+        Collection<DependencyInfo> dependenciesForComponent = stack.getDependenciesForComponent(component);
+        for (DependencyInfo dependency : dependenciesForComponent) {
+          String conditionalService = stack.getConditionalServiceForDependency(dependency);
+          if (conditionalService != null && ! services.contains(conditionalService)) {
+            continue;
+          }
+
+          BlueprintEntity   entity          = hostGroup.getBlueprintEntity();
+          String            dependencyScope = dependency.getScope();
+          String            componentName   = dependency.getComponentName();
+          AutoDeployInfo    autoDeployInfo  = dependency.getAutoDeploy();
+          boolean           resolved        = false;
+
+          if (dependencyScope.equals("cluster")) {
+            Collection<String> missingDependencyInfo = verifyComponentCardinalityCount(entity, hostGroups,
+                componentName, new Cardinality("1"), autoDeployInfo);
+            resolved = missingDependencyInfo.isEmpty();
+          } else if (dependencyScope.equals("host")) {
+            if (components.contains(component) || (autoDeployInfo != null && autoDeployInfo.isEnabled())) {
+              resolved = true;
+              if (addComponent(componentName)) {
+                addComponentToBlueprint(hostGroup.getBlueprintEntity(), getEntity().getName(), componentName);
+              }
+            }
+          }
+
+          if (! resolved) {
+            Collection<DependencyInfo> missingCompDependencies = missingDependencies.get(component);
+            if (missingCompDependencies == null) {
+              missingCompDependencies = new HashSet<DependencyInfo>();
+              missingDependencies.put(component, missingCompDependencies);
+            }
+            missingCompDependencies.add(dependency);
+          }
+        }
+      }
+      return missingDependencies;
+    }
+
+    /**
+     * Parse component information.
+     */
+    private void parseComponents() {
+      for (HostGroupComponentEntity componentEntity : hostGroup.getComponents() ) {
+        addComponent(componentEntity.getName());
+      }
+    }
+
+    /**
+     * Parse host group configurations.
+     */
+    private void parseConfigurations() {
+      Gson jsonSerializer = new Gson();
+      for (HostGroupConfigEntity configEntity : hostGroup.getConfigurations()) {
+        String type = configEntity.getType();
+        Map<String, String> typeProperties = configurations.get(type);
+        if ( typeProperties == null) {
+          typeProperties = new HashMap<String, String>();
+          configurations.put(type, typeProperties);
+        }
+        configurations.put(type, jsonSerializer.<Map<String, String>>fromJson(
+            configEntity.getConfigData(), Map.class));
+      }
+    }
+  }
+
+  /**
+   * Component cardinality representation.
+   */
+  protected static class Cardinality {
+    String cardinality;
+    int min = 0;
+    int max = Integer.MAX_VALUE;
+    int exact = -1;
+    boolean isAll = false;
+
+    public Cardinality(String cardinality) {
+      this.cardinality = cardinality;
+      if (cardinality != null && ! cardinality.isEmpty()) {
+        if (cardinality.contains("+")) {
+          min = Integer.valueOf(cardinality.split("\\+")[0]);
+        } else if (cardinality.contains("-")) {
+          String[] toks = cardinality.split("-");
+          min = Integer.parseInt(toks[0]);
+          max = Integer.parseInt(toks[1]);
+        } else if (cardinality.equals("ALL")) {
+          isAll = true;
+        } else {
+          exact = Integer.parseInt(cardinality);
+        }
+      }
+    }
+
+    /**
+     * Determine if component is required for all host groups.
+     *
+     * @return true if cardinality is 'ALL', false otherwise
+     */
+    public boolean isAll() {
+      return isAll;
+    }
+
+    /**
+     * Determine if the given count satisfies the required cardinality.
+     *
+     * @param count  number of host groups containing component
+     *
+     * @return true id count satisfies the required cardinality, false otherwise
+     */
+    public boolean isValidCount(int count) {
+      if (isAll) {
+        return false;
+      } else if (exact != -1) {
+        return count == exact;
+      } else return count >= min && count <= max;
+    }
+  }
+}

+ 27 - 75
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java

@@ -23,6 +23,7 @@ import com.google.inject.Inject;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.DuplicateResourceException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -58,7 +59,7 @@ import java.util.Set;
 /**
  * Resource Provider for Blueprint resources.
  */
-public class BlueprintResourceProvider extends AbstractResourceProvider {
+public class BlueprintResourceProvider extends BaseBlueprintProcessor {
 
   // ----- Property ID constants ---------------------------------------------
 
@@ -87,21 +88,11 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       new HashSet<String>(Arrays.asList(new String[]{
           BLUEPRINT_NAME_PROPERTY_ID}));
 
-  /**
-   * Blueprint data access object.
-   */
-  private static BlueprintDAO dao;
-
   /**
    * Used to serialize to/from json.
    */
   private static Gson jsonSerializer;
 
-  /**
-   * Stack information.
-   */
-  private static AmbariMetaInfo stackInfo;
-
 
   // ----- Constructors ----------------------------------------------------
 
@@ -110,21 +101,25 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
    *
    * @param propertyIds     the property ids
    * @param keyPropertyIds  the key property ids
+   * @param controller      management controller
    */
-  BlueprintResourceProvider(Set<String> propertyIds, Map<Resource.Type, String> keyPropertyIds) {
-    super(propertyIds, keyPropertyIds);
+  BlueprintResourceProvider(Set<String> propertyIds,
+                            Map<Resource.Type, String> keyPropertyIds,
+                            AmbariManagementController controller) {
+
+    super(propertyIds, keyPropertyIds, controller);
   }
 
   /**
    * Static initialization.
    *
-   * @param blueprintDAO  blueprint data access object
-   * @param gson          gson json serializer
-   * @param metaInfo      stack related information
+   * @param dao       blueprint data access object
+   * @param gson      json serializer
+   * @param metaInfo  stack related information
    */
   @Inject
-  public static void init(BlueprintDAO blueprintDAO, Gson gson, AmbariMetaInfo metaInfo) {
-    dao            = blueprintDAO;
+  public static void init(BlueprintDAO dao, Gson gson, AmbariMetaInfo metaInfo) {
+    blueprintDAO   = dao;
     jsonSerializer = gson;
     stackInfo      = metaInfo;
   }
@@ -165,7 +160,7 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
             BLUEPRINT_NAME_PROPERTY_ID);
 
         if (name != null) {
-          BlueprintEntity entity = dao.findByName(name);
+          BlueprintEntity entity = blueprintDAO.findByName(name);
           results = entity == null ? Collections.<BlueprintEntity>emptyList() :
               Collections.singletonList(entity);
         }
@@ -174,7 +169,7 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
 
     if (results == null) {
       applyPredicate = true;
-      results = dao.findAll();
+      results = blueprintDAO.findAll();
     }
 
     Set<Resource> resources  = new HashSet<Resource>();
@@ -220,7 +215,7 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       modifyResources(new Command<Void>() {
         @Override
         public Void invoke() throws AmbariException {
-        dao.removeByName(blueprintName);
+        blueprintDAO.removeByName(blueprintName);
         return null;
         }
       });
@@ -272,57 +267,6 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
 
     return resource;
   }
-  /**
-   * Convert a resource to a blueprint entity.
-   *
-   * @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));
-    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);
-      createHostGroupConfigEntities((Collection<Map<String,
-          String>>) properties.get(CONFIGURATION_PROPERTY_ID), group);
-
-      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);
-    }
-
-    createBlueprintConfigEntities((Collection<Map<String,
-        String>>) resource.getPropertyValue(CONFIGURATION_PROPERTY_ID), entity);
-
-    return entity;
-  }
 
   /**
    * Convert a map of properties to a blueprint entity.
@@ -588,12 +532,11 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
       public Void invoke() throws AmbariException {
         BlueprintEntity blueprint = toBlueprintEntity(properties);
 
-        if (dao.findByName(blueprint.getBlueprintName()) != null) {
+        if (blueprintDAO.findByName(blueprint.getBlueprintName()) != null) {
           throw new DuplicateResourceException(
               "Attempted to create a Blueprint which already exists, blueprint_name=" +
               blueprint.getBlueprintName());
         }
-
         Map<String, Map<String, Collection<String>>> missingProperties = blueprint.validateConfigurations(
             stackInfo, PropertyInfo.PropertyType.DEFAULT);
 
@@ -602,10 +545,19 @@ public class BlueprintResourceProvider extends AbstractResourceProvider {
                                              missingProperties);
         }
 
+        String validateTopology = (String) properties.get("validate_topology");
+        if (validateTopology == null || ! validateTopology.equalsIgnoreCase("false")) {
+          validateTopology(blueprint);
+        }
+
         if (LOG.isDebugEnabled()) {
           LOG.debug("Creating Blueprint, name=" + blueprint.getBlueprintName());
         }
-        dao.create(blueprint);
+        try {
+          blueprintDAO.create(blueprint);
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
         return null;
       }
     };

+ 4 - 493
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java

@@ -27,7 +27,6 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.api.services.PersistKeyValueService;
 import org.apache.ambari.server.controller.AmbariManagementController;
@@ -36,12 +35,6 @@ import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ConfigGroupRequest;
 import org.apache.ambari.server.controller.ConfigurationRequest;
 import org.apache.ambari.server.controller.RequestStatusResponse;
-import org.apache.ambari.server.controller.StackConfigurationRequest;
-import org.apache.ambari.server.controller.StackConfigurationResponse;
-import org.apache.ambari.server.controller.StackServiceComponentRequest;
-import org.apache.ambari.server.controller.StackServiceComponentResponse;
-import org.apache.ambari.server.controller.StackServiceRequest;
-import org.apache.ambari.server.controller.StackServiceResponse;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -54,21 +47,16 @@ 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.HostGroupConfigEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigImpl;
 import org.apache.ambari.server.state.PropertyInfo;
 
-import com.google.gson.Gson;
-
 /**
  * Resource provider for cluster resources.
  */
-public class ClusterResourceProvider extends AbstractControllerResourceProvider {
+public class ClusterResourceProvider extends BaseBlueprintProcessor {
 
   // ----- Property ID constants ---------------------------------------------
 
@@ -85,17 +73,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   private static Set<String> pkPropertyIds =
       new HashSet<String>(Arrays.asList(new String[]{CLUSTER_ID_PROPERTY_ID}));
 
-  /**
-   * Data access object used to obtain blueprint entities.
-   */
-  private static BlueprintDAO blueprintDAO;
-
-  /**
-   * Stack related information.
-   */
-  private static AmbariMetaInfo stackInfo;
-
-  /**
+   /**
    * Maps properties to updaters which update the property when provisioning a cluster via a blueprint
    */
   private Map<String, PropertyUpdater> propertyUpdaters =
@@ -331,7 +309,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
         "' based on blueprint '" + blueprintName + "'.");
 
     //todo: build up a proper topology object
-    BlueprintEntity blueprint = getBlueprint(blueprintName);
+    BlueprintEntity blueprint = getExistingBlueprint(blueprintName);
     Stack stack = parseStack(blueprint);
 
     Map<String, HostGroup> blueprintHostGroups = parseBlueprintHostGroups(blueprint, stack);
@@ -454,52 +432,6 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     return foundProperty;
   }
 
-  /**
-   * Override existing properties or add new.
-   *
-   * @param existingProperties  property overrides
-   * @param configOverrides     current property values
-   */
-  private void overrideExistingProperties(Map<String, Map<String, String>> existingProperties,
-                                          Collection<Map<String, String>> configOverrides) {
-    if (configOverrides != null) {
-      for (Map<String, String> properties : configOverrides) {
-        String category = null;
-        int propertyOffset = -1;
-        for (Map.Entry<String, String> entry : properties.entrySet()) {
-          String absolutePropName = entry.getKey();
-          if (category == null) {
-            propertyOffset =  absolutePropName.indexOf('/');
-            category = absolutePropName.substring(0, propertyOffset);
-          }
-          Map<String, String> existingCategoryProperties = existingProperties.get(category);
-          if (existingCategoryProperties == null) {
-            existingCategoryProperties = new HashMap<String, String>();
-            existingProperties.put(category, existingCategoryProperties);
-          }
-          //override existing property or add new
-          existingCategoryProperties.put(absolutePropName.substring(propertyOffset + 1), entry.getValue());
-        }
-      }
-    }
-  }
-
-  /**
-   * Obtain a blueprint entity based on name.
-   *
-   * @param blueprintName  name of blueprint to obtain
-   *
-   * @return blueprint entity for the given name
-   * @throws IllegalArgumentException no blueprint with the given name found
-   */
-  private BlueprintEntity getBlueprint(String blueprintName) {
-    BlueprintEntity blueprint = blueprintDAO.findByName(blueprintName);
-    if (blueprint == null) {
-      throw new IllegalArgumentException("Specified blueprint doesn't exist: " + blueprintName);
-    }
-    return blueprint;
-  }
-
   /**
    * Create service and component resources.
    *
@@ -542,7 +474,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   private Map<String, Object> buildClusterResourceProperties(Stack stack, String clusterName) {
     Map<String, Object> clusterProperties = new HashMap<String, Object>();
     clusterProperties.put(CLUSTER_NAME_PROPERTY_ID, clusterName);
-    clusterProperties.put(CLUSTER_VERSION_PROPERTY_ID, stack.name + "-" + stack.version);
+    clusterProperties.put(CLUSTER_VERSION_PROPERTY_ID, stack.getName() + "-" + stack.getVersion());
     return clusterProperties;
   }
 
@@ -731,47 +663,6 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     validateHostMappings(blueprintHostGroups);
   }
 
-  /**
-   * Parse blueprint host groups.
-   *
-   * @param blueprint  associated blueprint
-   * @param stack      associated stack
-   *
-   * @return map of host group name to host group
-   */
-  private Map<String, HostGroup> parseBlueprintHostGroups(BlueprintEntity blueprint, Stack stack) {
-    Map<String, HostGroup> mapHostGroups = new HashMap<String, HostGroup>();
-
-    for (HostGroupEntity hostGroup : blueprint.getHostGroups()) {
-      mapHostGroups.put(hostGroup.getName(), new HostGroup(hostGroup, stack));
-    }
-    return mapHostGroups;
-  }
-
-  /**
-   * Parse stack information.
-   *
-   * @param blueprint  associated blueprint
-   *
-   * @return stack instance
-   *
-   * @throws SystemException an unexpected exception occurred
-   */
-  private Stack parseStack(BlueprintEntity blueprint) throws SystemException {
-    Stack stack;
-    try {
-      stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
-    } catch (StackAccessException e) {
-      throw new IllegalArgumentException("Invalid stack information provided for cluster.  " +
-          "stack name: " + blueprint.getStackName() +
-          " stack version: " + blueprint.getStackVersion());
-    } catch (AmbariException e) {
-      //todo: review all exception handling associated with cluster creation via blueprint
-      throw new SystemException("Unable to obtain stack information.", e);
-    }
-    return stack;
-  }
-
   /**
    * Create the cluster resource.
    *
@@ -809,28 +700,6 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     }
   }
 
-  /**
-   * 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,
-                                                                          Collection<Map<String, String>> configOverrides) {
-    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));
-    }
-    overrideExistingProperties(mapConfigurations, configOverrides);
-
-    return mapConfigurations;
-  }
-
   /**
    * Process cluster configurations.  This includes obtaining the default configuration properties
    * from the stack,overlaying configuration properties specified in the blueprint and cluster
@@ -1021,24 +890,6 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     propertyUpdaters.put("hbase_regionserver_heapsize", new MPropertyUpdater());
   }
 
-  /**
-   * Get host groups which contain a component.
-   *
-   * @param component   component name
-   * @param hostGroups  collection of host groups to check
-   *
-   * @return collection of host groups which contain the specified component
-   */
-  private Collection<HostGroup> getHostGroupsForComponent(String component, Collection<HostGroup> hostGroups) {
-    Collection<HostGroup> resultGroups = new HashSet<HostGroup>();
-    for (HostGroup group : hostGroups ) {
-      if (group.getComponents().contains(component)) {
-        resultGroups.add(group);
-      }
-    }
-    return resultGroups;
-  }
-
   /**
    * Register config groups for host group scoped configuration.
    * For each host group with configuration specified in the blueprint, a config group is created
@@ -1114,346 +965,6 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   }
 
 
-  // ----- Inner Classes -----------------------------------------------------
-
-  /**
-   * Encapsulates stack information.
-   */
-  private class Stack {
-    /**
-     * Stack name
-     */
-    private String name;
-
-    /**
-     * Stack version
-     */
-    private String version;
-
-    /**
-     * Map of service name to components
-     */
-    private Map<String, Collection<String>> serviceComponents = new HashMap<String, Collection<String>>();
-
-    /**
-     * Map of component to service
-     */
-    private Map<String, String> componentService = new HashMap<String, String>();
-
-    /**
-     * Map of service to config type properties
-     */
-    private Map<String, Map<String, Map<String, String>>> serviceConfigurations =
-        new HashMap<String, Map<String, Map<String, String>>>();
-
-    /**
-     * Constructor.
-     *
-     * @param name     stack name
-     * @param version  stack version
-     *
-     * @throws AmbariException an exception occurred getting stack information
-     *                         for the specified name and version
-     */
-    public Stack(String name, String version) throws AmbariException {
-      this.name = name;
-      this.version = version;
-
-      Set<StackServiceResponse> stackServices = getManagementController().getStackServices(
-          Collections.singleton(new StackServiceRequest(name, version, null)));
-
-      for (StackServiceResponse stackService : stackServices) {
-        String serviceName = stackService.getServiceName();
-        parseComponents(serviceName);
-        parseConfigurations(serviceName);
-      }
-    }
-
-    /**
-     * Get services contained in the stack.
-     *
-     * @return collection of all services for the stack
-     */
-    public Collection<String> getServices() {
-      return serviceComponents.keySet();
-    }
-
-    /**
-     * Get components contained in the stack for the specified service.
-     *
-     * @param service  service name
-     *
-     * @return collection of component names for the specified service
-     */
-    public Collection<String> getComponents(String service) {
-      return serviceComponents.get(service);
-    }
-
-    /**
-     * Get configuration types for the specified service.
-     *
-     * @param service  service name
-     *
-     * @return collection of configuration types for the specified service
-     */
-    public Collection<String> getConfigurationTypes(String service) {
-      return serviceConfigurations.get(service).keySet();
-    }
-
-    /**
-     * Get config properties for the specified service and configuration type.
-     *
-     * @param service  service name
-     * @param type     configuration type
-     *
-     * @return map of property names to values for the specified service and configuration type
-     */
-    public Map<String, String> getConfigurationProperties(String service, String type) {
-      return serviceConfigurations.get(service).get(type);
-    }
-
-    /**
-     * Get the service for the specified component.
-     *
-     * @param component  component name
-     *
-     * @return service name that contains tha specified component
-     */
-    public String getServiceForComponent(String component) {
-      return componentService.get(component);
-    }
-
-    /**
-     * Get the names of the services which contains the specified components.
-     *
-     * @param components collection of components
-     *
-     * @return collection of services which contain the specified components
-     */
-    public Collection<String> getServicesForComponents(Collection<String> components) {
-      Set<String> services = new HashSet<String>();
-      for (String component : components) {
-        services.add(getServiceForComponent(component));
-      }
-
-      return services;
-    }
-
-    /**
-     * Obtain the service name which corresponds to the specified configuration.
-     *
-     * @param config  configuration type
-     *
-     * @return name of service which corresponds to the specified configuration type
-     */
-    public String getServiceForConfigType(String config) {
-      for (Map.Entry<String, Map<String, Map<String, String>>> entry : serviceConfigurations.entrySet()) {
-        Map<String, Map<String, String>> typeMap = entry.getValue();
-        if (typeMap.containsKey(config)) {
-          return entry.getKey();
-        }
-      }
-      throw new IllegalArgumentException(
-          "Specified configuration type is not associated with any service: " + config);
-    }
-
-    /**
-     * Parse components for the specified service from the stack definition.
-     *
-     * @param service  service name
-     *
-     * @throws AmbariException an exception occurred getting components from the stack definition
-     */
-    private void parseComponents(String service) throws AmbariException{
-      Collection<String> componentSet = new HashSet<String>();
-
-      Set<StackServiceComponentResponse> components = getManagementController().getStackComponents(
-          Collections.singleton(new StackServiceComponentRequest(name, version, service, null)
-      ));
-
-      // stack service components
-      for (StackServiceComponentResponse component : components) {
-        String componentName = component.getComponentName();
-        componentSet.add(componentName);
-        componentService.put(componentName, service);
-      }
-      this.serviceComponents.put(service, componentSet);
-    }
-
-    /**
-     * Parse configurations for the specified service from the stack definition.
-     *
-     * @param service  service name
-     *
-     * @throws AmbariException an exception occurred getting configurations from the stack definition
-     */
-    private void parseConfigurations(String service) throws AmbariException {
-      Map<String, Map<String, String>> mapServiceConfig = new HashMap<String, Map<String, String>>();
-
-      serviceConfigurations.put(service, mapServiceConfig);
-
-      Set<StackConfigurationResponse> serviceConfigs =
-        getManagementController().getStackConfigurations(
-          Collections.singleton(new StackConfigurationRequest(name, version, service, null)
-        ));
-
-      for (StackConfigurationResponse config : serviceConfigs) {
-        String type = config.getType();
-        //strip .xml from type
-        if (type.endsWith(".xml")) {
-          type = type.substring(0, type.length() - 4);
-        }
-        Map<String, String> mapTypeConfig = mapServiceConfig.get(type);
-        if (mapTypeConfig == null) {
-          mapTypeConfig = new HashMap<String, String>();
-          mapServiceConfig.put(type, mapTypeConfig);
-        }
-
-        mapTypeConfig.put(config.getPropertyName(), config.getPropertyValue());
-      }
-    }
-  }
-
-  /**
-   * Host group representation.
-   */
-  protected class HostGroup {
-    /**
-     * Host group entity
-     */
-    private HostGroupEntity hostGroup;
-
-    /**
-     * Components contained in the host group
-     */
-    private Collection<String> components = new HashSet<String>();
-
-    /**
-     * Hosts contained associated with the host group
-     */
-    private Collection<String> hosts = new HashSet<String>();
-
-    /**
-     * Map of service to components for the host group
-     */
-    private Map<String, Set<String>> componentsForService = new HashMap<String, Set<String>>();
-
-    /**
-     * Map of host group configurations.
-     * Type -> Map<Key, Val>
-     */
-    private Map<String, Map<String, String>> configurations =
-        new HashMap<String, Map<String, String>>();
-
-    /**
-     * Associated stack
-     */
-    private Stack stack;
-
-    /**
-     * Constructor.
-     *
-     * @param hostGroup  host group
-     * @param stack      stack
-     */
-    public HostGroup(HostGroupEntity hostGroup, Stack stack) {
-      this.hostGroup = hostGroup;
-      this.stack = stack;
-      parseComponents();
-      parseConfigurations();
-    }
-
-    /**
-     * Associate a host with the host group.
-     *
-     * @param fqdn  fully qualified domain name of the host being added
-     */
-    public void addHostInfo(String fqdn) {
-      this.hosts.add(fqdn);
-    }
-
-    /**
-     * Get associated host information.
-     *
-     * @return collection of hosts associated with the host group
-     */
-    public Collection<String> getHostInfo() {
-      return this.hosts;
-    }
-
-    /**
-     * Get the components associated with the host group.
-     *
-     * @return  collection of component names for the host group
-     */
-    public Collection<String> getComponents() {
-      return this.components;
-    }
-
-    /**
-     * Get the components for the specified service which are associated with the host group.
-     *
-     * @param service  service name
-     *
-     * @return set of component names
-     */
-    public Collection<String> getComponents(String service) {
-      return componentsForService.get(service);
-    }
-
-    /**
-     * Get the configurations associated with the host group.
-     *
-     * @return map of configuration type to a map of properties
-     */
-    public Map<String, Map<String, String>> getConfigurations() {
-      return configurations;
-    }
-
-    /**
-     * Get the associated entity.
-     *
-     * @return  associated host group entity
-     */
-    public HostGroupEntity getEntity() {
-      return hostGroup;
-    }
-
-    /**
-     * Parse component information.
-     */
-    private void parseComponents() {
-      for (HostGroupComponentEntity componentEntity : hostGroup.getComponents() ) {
-        String name = componentEntity.getName();
-        components.add(name);
-        String service = stack.getServiceForComponent(name);
-        Set<String> serviceComponents = componentsForService.get(service);
-        if (serviceComponents == null) {
-          serviceComponents = new HashSet<String>();
-          componentsForService.put(service, serviceComponents);
-        }
-        serviceComponents.add(name);
-      }
-    }
-
-    /**
-     * Parse host group configurations.
-     */
-    private void parseConfigurations() {
-      Gson jsonSerializer = new Gson();
-      for (HostGroupConfigEntity configEntity : hostGroup.getConfigurations()) {
-        String type = configEntity.getType();
-        Map<String, String> typeProperties = configurations.get(type);
-        if ( typeProperties == null) {
-          typeProperties = new HashMap<String, String>();
-          configurations.put(type, typeProperties);
-        }
-        configurations.put(type, jsonSerializer.<Map<String, String>>fromJson(
-            configEntity.getConfigData(), Map.class));
-      }
-    }
-  }
-
   /**
    * Provides functionality to update a property value.
    */

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

@@ -67,8 +67,6 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new ViewVersionResourceProvider();
       case ViewInstance:
         return new ViewInstanceResourceProvider();
-      case Blueprint:
-        return new BlueprintResourceProvider(propertyIds, keyPropertyIds);
       case StackServiceComponentDependency:
         return new StackDependencyResourceProvider(propertyIds, keyPropertyIds);
       default:

+ 9 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/HostGroupEntity.java

@@ -132,6 +132,15 @@ public class HostGroupEntity {
     this.components = components;
   }
 
+  /**
+   * Add a component to the host group.
+   *
+   * @param component  component to add
+   */
+  public void addComponent(HostGroupComponentEntity component) {
+    this.components.add(component);
+  }
+
   /**
    * Get the collection of associated configuration entities.
    *

+ 8 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java

@@ -135,4 +135,12 @@ public class DependencyInfo {
   public String getServiceName() {
     return serviceName;
   }
+
+  @Override
+  public String toString() {
+    return "DependencyInfo[name=" + getName() +
+           ", scope=" + getScope() +
+           ", auto-deploy=" + m_autoDeploy.isEnabled() +
+           "]";
+  }
 }

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

@@ -334,7 +334,8 @@
         "host_groups",
         "host_groups/components",
         "host_groups/cardinality",
-        "configurations"
+        "configurations",
+        "validate_topology"
     ],
     "HostComponentProcess": [
       "HostComponentProcess/cluster_name",

+ 0 - 10
ambari-server/src/main/resources/stacks/HDP/1.3.2/services/HIVE/metainfo.xml

@@ -27,12 +27,7 @@
         <component>
           <name>HIVE_METASTORE</name>
           <category>MASTER</category>
-          <!-- may be 0 if specifying external metastore, how to specify this? -->
           <cardinality>1</cardinality>
-          <auto-deploy>
-            <enabled>true</enabled>
-            <co-locate>HIVE/HIVE_SERVER</co-locate>
-          </auto-deploy>
           <commandScript>
             <script>scripts/hive_metastore.py</script>
             <scriptType>PYTHON</scriptType>
@@ -70,12 +65,7 @@
         <component>
           <name>MYSQL_SERVER</name>
           <category>MASTER</category>
-          <!-- may be 0 if specifying external db, how to specify this? -->
           <cardinality>1</cardinality>
-          <auto-deploy>
-            <enabled>true</enabled>
-            <co-locate>HIVE/HIVE_SERVER</co-locate>
-          </auto-deploy>
           <commandScript>
             <script>scripts/mysql_server.py</script>
             <scriptType>PYTHON</scriptType>

+ 1 - 1
ambari-server/src/main/resources/stacks/HDP/1.3.2/services/ZOOKEEPER/metainfo.xml

@@ -27,7 +27,7 @@
         <component>
           <name>ZOOKEEPER_SERVER</name>
           <category>MASTER</category>
-          <cardinality>1</cardinality>
+          <cardinality>1+</cardinality>
           <commandScript>
             <script>scripts/zookeeper_server.py</script>
             <scriptType>PYTHON</scriptType>

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/FLUME/metainfo.xml

@@ -26,6 +26,7 @@
         <component>
           <name>FLUME_HANDLER</name>
           <category>SLAVE</category>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/flume_handler.py</script>
             <scriptType>PYTHON</scriptType>

+ 5 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/GANGLIA/metainfo.xml

@@ -26,6 +26,7 @@
         <component>
           <name>GANGLIA_SERVER</name>
           <category>MASTER</category>
+          <cardinality>1</cardinality>
           <commandScript>
             <script>scripts/ganglia_server.py</script>
             <scriptType>PYTHON</scriptType>
@@ -36,6 +37,10 @@
         <component>
           <name>GANGLIA_MONITOR</name>
           <category>SLAVE</category>
+          <cardinality>ALL</cardinality>
+          <auto-deploy>
+            <enabled>true</enabled>
+          </auto-deploy>
           <commandScript>
             <script>scripts/ganglia_monitor.py</script>
             <scriptType>PYTHON</scriptType>

+ 2 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HBASE/metainfo.xml

@@ -66,6 +66,7 @@
         <component>
           <name>HBASE_REGIONSERVER</name>
           <category>SLAVE</category>
+          <cardinality>1+</cardinality>
           <commandScript>
             <script>scripts/hbase_regionserver.py</script>
             <scriptType>PYTHON</scriptType>
@@ -75,6 +76,7 @@
         <component>
           <name>HBASE_CLIENT</name>
           <category>CLIENT</category>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/hbase_client.py</script>
             <scriptType>PYTHON</scriptType>

+ 9 - 1
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HDFS/metainfo.xml

@@ -27,6 +27,7 @@
         <component>
           <name>NAMENODE</name>
           <category>MASTER</category>
+          <cardinality>1</cardinality>
           <commandScript>
             <script>scripts/namenode.py</script>
             <scriptType>PYTHON</scriptType>
@@ -47,6 +48,7 @@
         <component>
           <name>DATANODE</name>
           <category>SLAVE</category>
+          <cardinality>1+</cardinality>
           <commandScript>
             <script>scripts/datanode.py</script>
             <scriptType>PYTHON</scriptType>
@@ -56,6 +58,8 @@
 
         <component>
           <name>SECONDARY_NAMENODE</name>
+          <!-- TODO:  cardinality is conditional on HA usage -->
+          <cardinality>1</cardinality>
           <category>MASTER</category>
           <commandScript>
             <script>scripts/snamenode.py</script>
@@ -67,6 +71,7 @@
         <component>
           <name>HDFS_CLIENT</name>
           <category>CLIENT</category>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/hdfs_client.py</script>
             <scriptType>PYTHON</scriptType>
@@ -76,7 +81,8 @@
 
         <component>
           <name>JOURNALNODE</name>
-          <category>MASTER</category>
+          <category>SLAVE</category>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/journalnode.py</script>
             <scriptType>PYTHON</scriptType>
@@ -87,6 +93,8 @@
         <component>
           <name>ZKFC</name>
           <category>SLAVE</category>
+          <!-- TODO: cardinality is conditional on HA topology -->
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/zkfc_slave.py</script>
             <scriptType>PYTHON</scriptType>

+ 1 - 10
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HIVE/metainfo.xml

@@ -27,12 +27,7 @@
         <component>
           <name>HIVE_METASTORE</name>
           <category>MASTER</category>
-          <!-- may be 0 if specifying external metastore, how to specify this? -->
           <cardinality>1</cardinality>
-          <auto-deploy>
-            <enabled>true</enabled>
-            <co-locate>HIVE/HIVE_SERVER</co-locate>
-          </auto-deploy>
           <commandScript>
             <script>scripts/hive_metastore.py</script>
             <scriptType>PYTHON</scriptType>
@@ -84,12 +79,7 @@
         <component>
           <name>MYSQL_SERVER</name>
           <category>MASTER</category>
-          <!-- may be 0 if specifying external db, how to specify this? -->
           <cardinality>1</cardinality>
-          <auto-deploy>
-            <enabled>true</enabled>
-            <co-locate>HIVE/HIVE_SERVER</co-locate>
-          </auto-deploy>
           <commandScript>
             <script>scripts/mysql_server.py</script>
             <scriptType>PYTHON</scriptType>
@@ -99,6 +89,7 @@
         <component>
           <name>HIVE_CLIENT</name>
           <category>CLIENT</category>
+          <cardinality>0</cardinality>
           <commandScript>
             <script>scripts/hive_client.py</script>
             <scriptType>PYTHON</scriptType>

+ 1 - 1
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/ZOOKEEPER/metainfo.xml

@@ -27,7 +27,7 @@
         <component>
           <name>ZOOKEEPER_SERVER</name>
           <category>MASTER</category>
-          <cardinality>1</cardinality>
+          <cardinality>1+</cardinality>
           <commandScript>
             <script>scripts/zookeeper_server.py</script>
             <scriptType>PYTHON</scriptType>

+ 2 - 0
ambari-server/src/main/resources/stacks/HDP/2.1/services/FALCON/metainfo.xml

@@ -26,6 +26,7 @@
         <component>
           <name>FALCON_CLIENT</name>
           <category>CLIENT</category>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/falcon_client.py</script>
             <scriptType>PYTHON</scriptType>
@@ -35,6 +36,7 @@
         <component>
           <name>FALCON_SERVER</name>
           <category>MASTER</category>
+          <cardinality>1</cardinality> 
           <commandScript>
             <script>scripts/falcon_server.py</script>
             <scriptType>PYTHON</scriptType>

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.1/services/TEZ/metainfo.xml

@@ -25,6 +25,7 @@
       <components>
         <component>
           <name>TEZ_CLIENT</name>
+          <cardinality>0+</cardinality>
           <category>CLIENT</category>
           <commandScript>
             <script>scripts/tez_client.py</script>

+ 41 - 10
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BlueprintResourceProviderTest.java

@@ -21,6 +21,9 @@ package org.apache.ambari.server.controller.internal;
 import com.google.gson.Gson;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.StackServiceRequest;
+import org.apache.ambari.server.controller.StackServiceResponse;
 import org.apache.ambari.server.controller.predicate.EqualsPredicate;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
@@ -99,7 +102,9 @@ public class BlueprintResourceProviderTest {
   public void testCreateResources() throws AmbariException, ResourceAlreadyExistsException, SystemException,
       UnsupportedPropertyException, NoSuchParentResourceException {
 
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
     Request request = createMock(Request.class);
+    Capture<Set<StackServiceRequest>> stackServiceRequestCapture = new Capture<Set<StackServiceRequest>>();
 
     Map<String, ServiceInfo> services = new HashMap<String, ServiceInfo>();
     ServiceInfo service = new ServiceInfo();
@@ -120,6 +125,8 @@ public class BlueprintResourceProviderTest {
     Capture<BlueprintEntity> entityCapture = new Capture<BlueprintEntity>();
 
     // set expectations
+    expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(
+        Collections.<StackServiceResponse>emptySet());
     expect(request.getProperties()).andReturn(setProperties);
     expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
     expect(metaInfo.getServices("test-stack-name", "test-stack-version")).andReturn(services).anyTimes();
@@ -133,10 +140,15 @@ public class BlueprintResourceProviderTest {
         Collections.<String, org.apache.ambari.server.state.PropertyInfo>emptyMap()).anyTimes();
     dao.create(capture(entityCapture));
 
-    replay(dao, metaInfo, request);
+    replay(dao, metaInfo, request, managementController);
     // end expectations
 
-    ResourceProvider provider = createProvider();
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.Blueprint,
+        PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        managementController);
+
     AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
     ((ObservableResourceProvider)provider).addObserver(observer);
 
@@ -151,7 +163,7 @@ public class BlueprintResourceProviderTest {
 
     validateEntity(entityCapture.getValue(), false);
 
-    verify(dao, metaInfo, request);
+    verify(dao, metaInfo, request, managementController);
   }
 
   @Test
@@ -160,6 +172,8 @@ public class BlueprintResourceProviderTest {
 
     Set<Map<String, Object>> setProperties = getTestProperties();
     setConfigurationProperties(setProperties);
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    Capture<Set<StackServiceRequest>> stackServiceRequestCapture = new Capture<Set<StackServiceRequest>>();
     Request request = createMock(Request.class);
 
     Map<String, ServiceInfo> services = new HashMap<String, ServiceInfo>();
@@ -178,6 +192,8 @@ public class BlueprintResourceProviderTest {
     Capture<BlueprintEntity> entityCapture = new Capture<BlueprintEntity>();
 
     // set expectations
+    expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(
+        Collections.<StackServiceResponse>emptySet());
     expect(request.getProperties()).andReturn(setProperties);
     expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
     expect(metaInfo.getServices("test-stack-name", "test-stack-version")).andReturn(services).anyTimes();
@@ -191,10 +207,15 @@ public class BlueprintResourceProviderTest {
         Collections.<String, org.apache.ambari.server.state.PropertyInfo>emptyMap()).anyTimes();
     dao.create(capture(entityCapture));
 
-    replay(dao, metaInfo, request);
+    replay(dao, metaInfo, request, managementController);
     // end expectations
 
-    ResourceProvider provider = createProvider();
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.Blueprint,
+        PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        managementController);
+
     AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
     ((ObservableResourceProvider)provider).addObserver(observer);
 
@@ -209,7 +230,7 @@ public class BlueprintResourceProviderTest {
 
     validateEntity(entityCapture.getValue(), true);
 
-    verify(dao, metaInfo, request);
+    verify(dao, metaInfo, request, managementController);
   }
 
   @Test
@@ -490,6 +511,8 @@ public class BlueprintResourceProviderTest {
       SystemException, UnsupportedPropertyException, NoSuchParentResourceException
   {
     Request request = createMock(Request.class);
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    Capture<Set<StackServiceRequest>> stackServiceRequestCapture = new Capture<Set<StackServiceRequest>>();
 
     Map<String, ServiceInfo> services = new HashMap<String, ServiceInfo>();
     ServiceInfo service = new ServiceInfo();
@@ -512,6 +535,8 @@ public class BlueprintResourceProviderTest {
     Capture<BlueprintEntity> entityCapture = new Capture<BlueprintEntity>();
 
     // set expectations
+    expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(
+        Collections.<StackServiceResponse>emptySet());
     expect(request.getProperties()).andReturn(setProperties);
     expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
     expect(metaInfo.getServices("test-stack-name", "test-stack-version")).andReturn(services).anyTimes();
@@ -524,10 +549,15 @@ public class BlueprintResourceProviderTest {
 
     dao.create(capture(entityCapture));
 
-    replay(dao, metaInfo, request);
+    replay(dao, metaInfo, request, managementController);
     // end expectations
 
-    ResourceProvider provider = createProvider();
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.Blueprint,
+        PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        managementController);
+
     AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
     ((ObservableResourceProvider)provider).addObserver(observer);
 
@@ -540,7 +570,7 @@ public class BlueprintResourceProviderTest {
     assertEquals(request, lastEvent.getRequest());
     assertNull(lastEvent.getPredicate());
 
-    verify(dao, metaInfo, request);
+    verify(dao, metaInfo, request, managementController);
   }
 
   private Set<Map<String, Object>> getTestProperties() {
@@ -715,7 +745,8 @@ public class BlueprintResourceProviderTest {
   private BlueprintResourceProvider createProvider() {
     return new BlueprintResourceProvider(
         PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
-        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint));
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        null);
   }
 
   private BlueprintEntity createEntity(Map<String, Object> properties) {

+ 56 - 3
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java

@@ -59,7 +59,6 @@ import org.apache.ambari.server.controller.StackServiceComponentRequest;
 import org.apache.ambari.server.controller.StackServiceComponentResponse;
 import org.apache.ambari.server.controller.StackServiceRequest;
 import org.apache.ambari.server.controller.StackServiceResponse;
-import org.apache.ambari.server.controller.internal.ClusterResourceProvider.HostGroup;
 import org.apache.ambari.server.controller.internal.ClusterResourceProvider.PropertyUpdater;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
@@ -68,12 +67,14 @@ import org.apache.ambari.server.controller.spi.Resource;
 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.controller.internal.BaseBlueprintProcessor.HostGroup;
 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.HostGroupConfigEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
+import org.apache.ambari.server.state.DependencyInfo;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.State;
 import org.apache.commons.collections.CollectionUtils;
@@ -279,6 +280,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.validateConfigurations(metaInfo, PropertyInfo.PropertyType.PASSWORD)).andReturn(
         Collections.<String, Map<String, Collection<String>>>emptyMap());
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -623,6 +631,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getConfigurations()).andReturn(Collections.<BlueprintConfigEntity>singletonList(blueprintConfig));
     expect(blueprint.validateConfigurations(metaInfo, PropertyInfo.PropertyType.PASSWORD)).andReturn(allMissingPasswords);
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -796,6 +811,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -971,6 +993,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -1146,6 +1175,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -1348,6 +1384,13 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getConfigurations()).andReturn(Collections.<BlueprintConfigEntity>singletonList(blueprintConfig));
     expect(blueprint.validateConfigurations(metaInfo, PropertyInfo.PropertyType.PASSWORD)).andReturn(allMissingPasswords);
 
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
     expect(stackServiceResponse2.getServiceName()).andReturn("service2");
@@ -1586,6 +1629,7 @@ public class ClusterResourceProviderTest {
     String clusterName = "c1";
 
     BlueprintDAO blueprintDAO = createStrictMock(BlueprintDAO.class);
+    AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
     AmbariManagementController managementController = createStrictMock(AmbariManagementController.class);
     Request request = createNiceMock(Request.class);
     RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
@@ -1702,6 +1746,15 @@ public class ClusterResourceProviderTest {
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
     expect(blueprint.getConfigurations()).andReturn(Collections.<BlueprintConfigEntity>singletonList(blueprintConfig));
+    expect(blueprint.validateConfigurations(metaInfo, PropertyInfo.PropertyType.PASSWORD)).andReturn(
+        Collections.<String, Map<String, Collection<String>>>emptyMap());
+
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
+        andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
+        andReturn(Collections.<DependencyInfo>emptyList());
 
     expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(stackServiceResponses);
     expect(stackServiceResponse1.getServiceName()).andReturn("service1");
@@ -1776,10 +1829,10 @@ public class ClusterResourceProviderTest {
         stackConfigurationResponse1, stackConfigurationResponse2, stackConfigurationResponse3, stackConfigurationResponse4,
         blueprintConfig, hostGroup, hostGroupComponent1, hostGroupComponent2, hostGroupComponent3, hostGroupConfig,
         serviceResourceProvider, componentResourceProvider, hostResourceProvider, hostComponentResourceProvider,
-        configGroupResourceProvider);
+        configGroupResourceProvider, metaInfo);
 
     // test
-    ClusterResourceProvider.init(blueprintDAO, null);
+    ClusterResourceProvider.init(blueprintDAO, metaInfo);
     ResourceProvider provider = new TestClusterResourceProvider(
         managementController, serviceResourceProvider, componentResourceProvider,
         hostResourceProvider, hostComponentResourceProvider, configGroupResourceProvider);