ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jspei...@apache.org
Subject ambari git commit: AMBARI-11093. Implement host predicate property validation and fix issue where host name is specified explicitly in scaling request
Date Wed, 13 May 2015 16:37:59 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk 597951c1f -> 43dd0cddf


AMBARI-11093.  Implement host predicate property validation and fix issue where host
name is specified explicitly in scaling request


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

Branch: refs/heads/trunk
Commit: 43dd0cddf656bd54cda90e182786e304655b4765
Parents: 597951c
Author: John Speidel <jspeidel@hortonworks.com>
Authored: Tue May 12 22:17:01 2015 -0400
Committer: John Speidel <jspeidel@hortonworks.com>
Committed: Wed May 13 12:37:50 2015 -0400

----------------------------------------------------------------------
 .../ambari/server/controller/AmbariServer.java  |   6 +-
 .../controller/internal/BaseClusterRequest.java | 186 +++++++++
 .../internal/HostResourceProvider.java          |  11 +-
 .../internal/ProvisionClusterRequest.java       |  98 ++---
 .../internal/ScaleClusterRequest.java           | 155 +++++---
 .../internal/ProvisionClusterRequestTest.java   | 263 ++++++++++++-
 .../internal/ScaleClusterRequestTest.java       | 394 +++++++++++++++++++
 7 files changed, 987 insertions(+), 126 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index 77f6d2c..4a30c0d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -48,14 +48,13 @@ import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.AbstractControllerResourceProvider;
 import org.apache.ambari.server.controller.internal.AmbariPrivilegeResourceProvider;
+import org.apache.ambari.server.controller.internal.BaseClusterRequest;
 import org.apache.ambari.server.controller.internal.BlueprintResourceProvider;
 import org.apache.ambari.server.controller.internal.ClusterPrivilegeResourceProvider;
 import org.apache.ambari.server.controller.internal.ClusterResourceProvider;
 import org.apache.ambari.server.controller.internal.HostResourceProvider;
 import org.apache.ambari.server.controller.internal.PermissionResourceProvider;
 import org.apache.ambari.server.controller.internal.PrivilegeResourceProvider;
-import org.apache.ambari.server.controller.internal.ProvisionClusterRequest;
-import org.apache.ambari.server.controller.internal.ScaleClusterRequest;
 import org.apache.ambari.server.controller.internal.StackAdvisorResourceProvider;
 import org.apache.ambari.server.controller.internal.StackDefinedPropertyProvider;
 import org.apache.ambari.server.controller.internal.StackDependencyResourceProvider;
@@ -616,8 +615,7 @@ public class AmbariServer {
         injector.getInstance(TopologyRequestFactoryImpl.class));
     HostResourceProvider.setTopologyManager(injector.getInstance(TopologyManager.class));
     BlueprintFactory.init(injector.getInstance(BlueprintDAO.class));
-    ProvisionClusterRequest.init(injector.getInstance(BlueprintFactory.class));
-    ScaleClusterRequest.init(injector.getInstance(BlueprintFactory.class));
+    BaseClusterRequest.init(injector.getInstance(BlueprintFactory.class));
     AmbariContext.init(injector.getInstance(HostRoleCommandFactory.class));
 
     PermissionResourceProvider.init(injector.getInstance(PermissionDAO.class));

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
new file mode 100644
index 0000000..7f6a634
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
@@ -0,0 +1,186 @@
+/**
+ * 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 java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.api.predicate.InvalidQueryException;
+import org.apache.ambari.server.api.predicate.QueryLexer;
+import org.apache.ambari.server.api.predicate.Token;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
+import org.apache.ambari.server.topology.Blueprint;
+import org.apache.ambari.server.topology.BlueprintFactory;
+import org.apache.ambari.server.topology.Configuration;
+import org.apache.ambari.server.topology.HostGroupInfo;
+import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
+import org.apache.ambari.server.topology.TopologyRequest;
+
+/**
+ * Provides common cluster request functionality.
+ */
+public abstract class BaseClusterRequest implements TopologyRequest {
+  /**
+   * host group info map
+   */
+  protected final Map<String, HostGroupInfo> hostGroupInfoMap = new HashMap<String, HostGroupInfo>();
+
+  /**
+   * cluster name
+   */
+  protected String clusterName;
+
+  /**
+   * blueprint
+   */
+  //todo: change interface to only return blueprint name
+  protected Blueprint blueprint;
+
+  /**
+   * configuration
+   */
+  protected Configuration configuration;
+
+  /**
+   * blueprint factory
+   */
+  protected static BlueprintFactory blueprintFactory;
+
+  /**
+   * Lexer used to obtain property names from a predicate string
+   */
+  private static final QueryLexer queryLexer = new QueryLexer();
+
+  /**
+   * host resource provider used to validate predicate properties
+   */
+  private static ResourceProvider hostResourceProvider;
+
+
+  /**
+   * inject blueprint factory
+   * @param factory  blueprint factory
+   */
+  public static void init(BlueprintFactory factory) {
+    blueprintFactory = factory;
+  }
+
+  @Override
+  public String getClusterName() {
+    return clusterName;
+  }
+
+  @Override
+  public Blueprint getBlueprint() {
+    return blueprint;
+  }
+
+  @Override
+  public Configuration getConfiguration() {
+    return configuration;
+  }
+
+  @Override
+  public Map<String, HostGroupInfo> getHostGroupInfo() {
+    return hostGroupInfoMap;
+  }
+
+  /**
+   * Validate that all properties specified in the predicate are valid for the Host resource.
+   *
+   * @param predicate  predicate to validate
+   *
+   * @throws InvalidTopologyTemplateException  if any of the properties specified in the predicate are invalid
+   *                                           for the Host resource type
+   */
+  protected void validateHostPredicateProperties(String predicate) throws InvalidTopologyTemplateException {
+    Token[] tokens;
+    try {
+      tokens = queryLexer.tokens(predicate);
+    } catch (InvalidQueryException e) {
+      throw new InvalidTopologyTemplateException(
+          String.format("The specified host query is invalid: %s", e.getMessage()));
+    }
+
+    Set<String> propertyIds = new HashSet<String>();
+    for (Token token : tokens) {
+      if (token.getType() == Token.TYPE.PROPERTY_OPERAND) {
+        propertyIds.add(token.getValue());
+      }
+    }
+
+    Set<String> invalidProperties = ensureHostProvider().checkPropertyIds(propertyIds);
+    if (! invalidProperties.isEmpty()) {
+      throw new InvalidTopologyTemplateException(String.format(
+          "Invalid Host Predicate.  The following properties are not valid for a host predicate: %s",
+          invalidProperties));
+    }
+  }
+
+  /**
+   * Set the request cluster name.
+   *
+   * @param clusterName  cluster name
+   */
+  protected void setClusterName(String clusterName) {
+    this.clusterName = clusterName;
+  }
+
+  /**
+   * Set the request blueprint.
+   *
+   * @param blueprint blueprint
+   */
+  protected void setBlueprint(Blueprint blueprint) {
+    this.blueprint = blueprint;
+  }
+
+  /**
+   * Set the request configuration.
+   *
+   * @param configuration  configuration
+   */
+  protected void setConfiguration(Configuration configuration) {
+    this.configuration = configuration;
+  }
+
+  /**
+   * Get the blueprint factory.
+   */
+  protected BlueprintFactory getBlueprintFactory() {
+    return blueprintFactory;
+  }
+
+  /**
+   * Get the host resource provider instance.
+   *
+   * @return host resourece provider instance
+   */
+  private static synchronized ResourceProvider ensureHostProvider() {
+    if (hostResourceProvider == null) {
+      hostResourceProvider = ClusterControllerHelper.getClusterController().
+          ensureResourceProvider(Resource.Type.Host);
+    }
+    return hostResourceProvider;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 47a4ce0..4c14426 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -132,6 +132,10 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
       PropertyHelper.getPropertyId(null, "host_group");
   public static final String HOST_NAME_NO_CATEGORY_PROPERTY_ID =
       PropertyHelper.getPropertyId(null, "host_name");
+  public static final String HOST_COUNT_PROPERTY_ID =
+      PropertyHelper.getPropertyId(null, "host_count");
+  public static final String HOST_PREDICATE_PROPERTY_ID =
+      PropertyHelper.getPropertyId(null, "host_predicate");
 
   private static Set<String> pkPropertyIds =
       new HashSet<String>(Arrays.asList(new String[]{
@@ -173,7 +177,6 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
 
     RequestStatusResponse createResponse = null;
     if (isHostGroupRequest(request)) {
-//      createResponse = addHostsUsingHostgroup(request);
       createResponse = submitHostRequests(request);
     } else {
       createResources(new Command<Void>() {
@@ -334,8 +337,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
     baseUnsupported.remove(HOSTGROUP_PROPERTY_ID);
     baseUnsupported.remove(HOST_NAME_NO_CATEGORY_PROPERTY_ID);
     //todo: constants
-    baseUnsupported.remove("host_count");
-    baseUnsupported.remove("host_predicate");
+    baseUnsupported.remove(HOST_COUNT_PROPERTY_ID);
+    baseUnsupported.remove(HOST_PREDICATE_PROPERTY_ID);
 
     return checkConfigPropertyIds(baseUnsupported, "Hosts");
   }
@@ -830,7 +833,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider {
   private RequestStatusResponse submitHostRequests(Request request) throws SystemException {
     TopologyRequest requestRequest;
     try {
-      requestRequest = new ScaleClusterRequest(request);
+      requestRequest = new ScaleClusterRequest(request.getProperties());
     } catch (InvalidTopologyTemplateException e) {
       throw new IllegalArgumentException("Invalid Add Hosts Template: " + e, e);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
index a1a0ac6..1a4520e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
@@ -22,41 +22,43 @@ package org.apache.ambari.server.controller.internal;
 
 import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.stack.NoSuchStackException;
-import org.apache.ambari.server.topology.Blueprint;
-import org.apache.ambari.server.topology.BlueprintFactory;
 import org.apache.ambari.server.topology.Configuration;
 import org.apache.ambari.server.topology.ConfigurationFactory;
 import org.apache.ambari.server.topology.HostGroupInfo;
 import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
 import org.apache.ambari.server.topology.NoSuchBlueprintException;
 import org.apache.ambari.server.topology.RequiredPasswordValidator;
-import org.apache.ambari.server.topology.TopologyRequest;
 import org.apache.ambari.server.topology.TopologyValidator;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 /**
  * Request for provisioning a cluster.
  */
-public class ProvisionClusterRequest implements TopologyRequest {
+public class ProvisionClusterRequest extends BaseClusterRequest {
 
-  private static BlueprintFactory blueprintFactory;
+  /**
+   * configuration factory
+   */
   private static ConfigurationFactory configurationFactory = new ConfigurationFactory();
 
-  private String clusterName;
+  /**
+   * default password
+   */
   private String defaultPassword;
-  private Blueprint blueprint;
-  private Configuration configuration;
-  private Map<String, HostGroupInfo> hostGroupInfoMap = new HashMap<String, HostGroupInfo>();
 
   @SuppressWarnings("unchecked")
+  /**
+   * Constructor.
+   *
+   * @param properties  request properties
+   */
   public ProvisionClusterRequest(Map<String, Object> properties) throws InvalidTopologyTemplateException {
-    this.clusterName = String.valueOf(properties.get(
-        ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID));
+    setClusterName(String.valueOf(properties.get(
+        ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID)));
 
     //todo: constant
     if (properties.containsKey("default_password")) {
@@ -70,21 +72,13 @@ public class ProvisionClusterRequest implements TopologyRequest {
     } catch (NoSuchBlueprintException e) {
       throw new InvalidTopologyTemplateException("The specified blueprint doesn't exist: " + e, e);
     }
-    this.configuration = configurationFactory.getConfiguration(
-        (Collection<Map<String, String>>) properties.get("configurations"));
-    this.configuration.setParentConfiguration(blueprint.getConfiguration());
-    //parseConfiguration(properties);
-    parseHostGroupInfo(properties);
-  }
 
-  //todo:
-  public static void init(BlueprintFactory factory) {
-    blueprintFactory = factory;
-  }
+    Configuration configuration = configurationFactory.getConfiguration(
+        (Collection<Map<String, String>>) properties.get("configurations"));
+    configuration.setParentConfiguration(blueprint.getConfiguration());
+    setConfiguration(configuration);
 
-  @Override
-  public String getClusterName() {
-    return clusterName;
+    parseHostGroupInfo(properties);
   }
 
   @Override
@@ -93,22 +87,6 @@ public class ProvisionClusterRequest implements TopologyRequest {
   }
 
   @Override
-  public Blueprint getBlueprint() {
-    return blueprint;
-  }
-
-  @Override
-  public Configuration getConfiguration() {
-    return configuration;
-  }
-
-  @Override
-  //todo: return copy?
-  public Map<String, HostGroupInfo> getHostGroupInfo() {
-    return hostGroupInfoMap;
-  }
-
-  @Override
   public List<TopologyValidator> getTopologyValidators() {
     return Collections.<TopologyValidator>singletonList(new RequiredPasswordValidator(defaultPassword));
   }
@@ -118,15 +96,30 @@ public class ProvisionClusterRequest implements TopologyRequest {
     return String.format("Provision Cluster '%s'", clusterName);
   }
 
+  /**
+   * Parse blueprint.
+   *
+   * @param properties  request properties
+   *
+   * @throws NoSuchStackException     if specified stack doesn't exist
+   * @throws NoSuchBlueprintException if specified blueprint doesn't exist
+   */
   private void parseBlueprint(Map<String, Object> properties) throws NoSuchStackException, NoSuchBlueprintException {
     String blueprintName = String.valueOf(properties.get(ClusterResourceProvider.BLUEPRINT_PROPERTY_ID));
-    blueprint = blueprintFactory.getBlueprint(blueprintName);
+    // set blueprint field
+    setBlueprint(getBlueprintFactory().getBlueprint(blueprintName));
 
     if (blueprint == null) {
       throw new NoSuchBlueprintException(blueprintName);
     }
   }
 
+  /**
+   * Parse host group information.
+   *
+   * @param properties  request properties
+   * @throws InvalidTopologyTemplateException  if any validation checks on properties fail
+   */
   @SuppressWarnings("unchecked")
   private void parseHostGroupInfo(Map<String, Object> properties) throws InvalidTopologyTemplateException {
     Collection<Map<String, Object>> hostGroups =
@@ -149,18 +142,27 @@ public class ProvisionClusterRequest implements TopologyRequest {
         throw new InvalidTopologyTemplateException("Host group '" + name + "' must contain a 'hosts' element");
       }
 
-      // blueprint was parsed already
       HostGroupInfo hostGroupInfo = new HostGroupInfo(name);
-      hostGroupInfoMap.put(name, hostGroupInfo);
+      getHostGroupInfo().put(name, hostGroupInfo);
 
       for (Object oHost : hosts) {
         Map<String, String> hostProperties = (Map<String, String>) oHost;
+
+        String hostName = hostProperties.get("fqdn");
+        boolean containsHostCount = hostProperties.containsKey("host_count");
+        boolean containsHostPredicate = hostProperties.containsKey("host_predicate");
+
+        if (hostName != null && (containsHostCount || containsHostPredicate)) {
+          throw new InvalidTopologyTemplateException(
+              "Can't specify host_count or host_predicate if host_name is specified in hostgroup: " + name);
+        }
+
         //add host information to host group
-        String fqdn = hostProperties.get("fqdn");
-        if (fqdn == null || fqdn.isEmpty()) {
+        if (hostName == null || hostName.isEmpty()) {
           //todo: validate the host_name and host_predicate are not both specified for same group
           String predicate = hostProperties.get("host_predicate");
           if (predicate != null && ! predicate.isEmpty()) {
+            validateHostPredicateProperties(predicate);
             try {
               hostGroupInfo.setPredicate(predicate);
             } catch (InvalidQueryException e) {
@@ -169,7 +171,7 @@ public class ProvisionClusterRequest implements TopologyRequest {
             }
           }
 
-          if (hostProperties.containsKey("host_count")) {
+          if (containsHostCount) {
             hostGroupInfo.setRequestedCount(Integer.valueOf(hostProperties.get("host_count")));
           } else {
             throw new InvalidTopologyTemplateException(
@@ -177,7 +179,7 @@ public class ProvisionClusterRequest implements TopologyRequest {
                 " or a host_count must be specified");
           }
         } else {
-          hostGroupInfo.addHost(fqdn);
+          hostGroupInfo.addHost(hostName);
         }
       }
       // don't set the parent configuration

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
index 1530a3e..b9a4173 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
@@ -20,51 +20,42 @@
 package org.apache.ambari.server.controller.internal;
 
 import org.apache.ambari.server.api.predicate.InvalidQueryException;
-import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.stack.NoSuchStackException;
 import org.apache.ambari.server.topology.Blueprint;
-import org.apache.ambari.server.topology.BlueprintFactory;
 import org.apache.ambari.server.topology.Configuration;
 import org.apache.ambari.server.topology.HostGroupInfo;
 import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
-import org.apache.ambari.server.topology.TopologyRequest;
 import org.apache.ambari.server.topology.TopologyValidator;
 
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A request for a scaling an existing cluster.
  */
-public class ScaleClusterRequest implements TopologyRequest {
-
-  private static BlueprintFactory blueprintFactory;
-
-  private String clusterName;
-
-  private Blueprint blueprint;
-
-  private Map<String, HostGroupInfo> hostGroupInfoMap = new HashMap<String, HostGroupInfo>();
-
-  public static void init(BlueprintFactory factory) {
-    blueprintFactory = factory;
-  }
-
-  public ScaleClusterRequest(Request request) throws InvalidTopologyTemplateException {
-    for (Map<String, Object> properties : request.getProperties()) {
+public class ScaleClusterRequest extends BaseClusterRequest {
+
+  /**
+   * Constructor.
+   *
+   * @param propertySet  set of request properties
+   *
+   * @throws InvalidTopologyTemplateException if any validation of properties fails
+   */
+  public ScaleClusterRequest(Set<Map<String, Object>> propertySet) throws InvalidTopologyTemplateException {
+    for (Map<String, Object> properties : propertySet) {
       // can only operate on a single cluster per logical request
-      if (clusterName == null) {
-        clusterName = String.valueOf(properties.get(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID));
+      if (getClusterName() == null) {
+        setClusterName(String.valueOf(properties.get(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID)));
       }
-      parseHostGroup(properties);
-    }
-  }
+      // currently don't allow cluster scoped configuration in scaling operation
+      setConfiguration(new Configuration(Collections.<String, Map<String, String>>emptyMap(),
+          Collections.<String, Map<String, Map<String, String>>>emptyMap()));
 
-  @Override
-  public String getClusterName() {
-    return clusterName;
+      parseHostGroups(properties);
+    }
   }
 
   @Override
@@ -73,23 +64,6 @@ public class ScaleClusterRequest implements TopologyRequest {
   }
 
   @Override
-  public Blueprint getBlueprint() {
-    return blueprint;
-  }
-
-  @Override
-  public Configuration getConfiguration() {
-    // currently don't allow cluster scoped configuration in scaling operation
-    return new Configuration(Collections.<String, Map<String, String>>emptyMap(),
-        Collections.<String, Map<String, Map<String, String>>>emptyMap());
-  }
-
-  @Override
-  public Map<String, HostGroupInfo> getHostGroupInfo() {
-    return hostGroupInfoMap;
-  }
-
-  @Override
   public List<TopologyValidator> getTopologyValidators() {
     return Collections.emptyList();
   }
@@ -99,25 +73,54 @@ public class ScaleClusterRequest implements TopologyRequest {
     return String.format("Scale Cluster '%s' (+%s hosts)", clusterName, getTotalRequestedHostCount());
   }
 
-  private void parseHostGroup(Map<String, Object> properties) throws InvalidTopologyTemplateException {
+  /**
+   * Parse and set host group information.
+   *
+   * @param properties  request properties
+   * @throws InvalidTopologyTemplateException if any property validation fails
+   */
+  //todo: need to use fully qualified host group name.  For now, disregard name collisions across BP's
+  private void parseHostGroups(Map<String, Object> properties) throws InvalidTopologyTemplateException {
     String blueprintName = String.valueOf(properties.get(HostResourceProvider.BLUEPRINT_PROPERTY_ID));
-    if (blueprint == null) {
+    if (blueprintName == null || blueprintName.equals("null")) {
+      throw new InvalidTopologyTemplateException("Blueprint name must be specified for all host groups");
+    }
+
+    String hgName = String.valueOf(properties.get(HostResourceProvider.HOSTGROUP_PROPERTY_ID));
+    if (hgName == null || hgName.equals("null")) {
+      throw new InvalidTopologyTemplateException("A name must be specified for all host groups");
+    }
+
+    Blueprint blueprint = getBlueprint();
+    if (getBlueprint() == null) {
       blueprint = parseBlueprint(blueprintName);
+      setBlueprint(blueprint);
     } else if (! blueprintName.equals(blueprint.getName())) {
       throw new InvalidTopologyTemplateException(
           "Currently, a scaling request may only refer to a single blueprint");
     }
 
-    String hgName = String.valueOf(properties.get(HostResourceProvider.HOSTGROUP_PROPERTY_ID));
-    //todo: need to use fully qualified host group name.  For now, disregard name collisions across BP's
-    HostGroupInfo hostGroupInfo = hostGroupInfoMap.get(hgName);
+    String hostName = getHostNameFromProperties(properties);
+    boolean containsHostCount = properties.containsKey(HostResourceProvider.HOST_COUNT_PROPERTY_ID);
+    boolean containsHostPredicate = properties.containsKey(HostResourceProvider.HOST_PREDICATE_PROPERTY_ID);
 
+    if (hostName != null && (containsHostCount || containsHostPredicate)) {
+      throw new InvalidTopologyTemplateException(
+          "Can't specify host_count or host_predicate if host_name is specified in hostgroup: " + hgName);
+    }
+
+    if (hostName == null && ! containsHostCount) {
+      throw new InvalidTopologyTemplateException(
+          "Must specify either host_name or host_count for hostgroup: " + hgName);
+    }
+
+    HostGroupInfo hostGroupInfo = getHostGroupInfo().get(hgName);
     if (hostGroupInfo == null) {
       if (blueprint.getHostGroup(hgName) == null) {
         throw new InvalidTopologyTemplateException("Invalid host group specified in request: " + hgName);
       }
       hostGroupInfo = new HostGroupInfo(hgName);
-      hostGroupInfoMap.put(hgName, hostGroupInfo);
+      getHostGroupInfo().put(hgName, hostGroupInfo);
     }
 
     // specifying configuration is scaling request isn't permitted
@@ -125,11 +128,11 @@ public class ScaleClusterRequest implements TopologyRequest {
         Collections.<String, Map<String, Map<String, String>>>emptyMap()));
 
     // process host_name and host_count
-    if (properties.containsKey("host_count")) {
-      //todo: validate the host_name and host_predicate are not both specified for same group
-      //todo: validate that when predicate is specified that only a single host group entry is specified
-      String predicate = String.valueOf(properties.get("host_predicate"));
-      if (predicate != null && ! predicate.isEmpty()) {
+    if (containsHostCount) {
+      //todo: host_count and host_predicate up one level
+      if (containsHostPredicate) {
+        String predicate = String.valueOf(properties.get(HostResourceProvider.HOST_PREDICATE_PROPERTY_ID));
+        validateHostPredicateProperties(predicate);
         try {
           hostGroupInfo.setPredicate(predicate);
         } catch (InvalidQueryException e) {
@@ -139,21 +142,32 @@ public class ScaleClusterRequest implements TopologyRequest {
       }
 
       if (! hostGroupInfo.getHostNames().isEmpty()) {
-        throw new InvalidTopologyTemplateException("Can't specify both host_name and host_count for the same hostgroup: " + hgName);
+        throw new InvalidTopologyTemplateException(
+            "Can't specify both host_name and host_count for the same hostgroup: " + hgName);
       }
-      hostGroupInfo.setRequestedCount(Integer.valueOf(String.valueOf(properties.get("host_count"))));
+      hostGroupInfo.setRequestedCount(Integer.valueOf(String.valueOf(
+          properties.get(HostResourceProvider.HOST_COUNT_PROPERTY_ID))));
     } else {
       if (hostGroupInfo.getRequestedHostCount() != hostGroupInfo.getHostNames().size()) {
-        throw new InvalidTopologyTemplateException("Can't specify both host_name and host_count for the same hostgroup: " + hgName);
+        // host_name specified in one host block and host_count in another for the same group
+        throw new InvalidTopologyTemplateException("Invalid host group specified in request: " + hgName);
       }
-      hostGroupInfo.addHost(getHostNameFromProperties(properties));
+      hostGroupInfo.addHost(hostName);
     }
   }
 
+  /**
+   * Parse blueprint.
+   *
+   * @param blueprintName  blueprint name
+   * @return blueprint instance
+   *
+   * @throws InvalidTopologyTemplateException if specified blueprint or stack doesn't exist
+   */
   private Blueprint parseBlueprint(String blueprintName) throws InvalidTopologyTemplateException  {
     Blueprint blueprint;
     try {
-      blueprint = blueprintFactory.getBlueprint(blueprintName);
+      blueprint = getBlueprintFactory().getBlueprint(blueprintName);
     } catch (NoSuchStackException e) {
       throw new InvalidTopologyTemplateException("Invalid stack specified in the blueprint: " + blueprintName);
     }
@@ -164,14 +178,25 @@ public class ScaleClusterRequest implements TopologyRequest {
     return blueprint;
   }
 
+  /**
+   * Get the host name from the request properties.
+   *
+   * @param properties  request properties
+   * @return host name
+   */
   //todo: this was copied exactly from HostResourceProvider
   private String getHostNameFromProperties(Map<String, Object> properties) {
-    String hostname = String.valueOf(properties.get(HostResourceProvider.HOST_NAME_PROPERTY_ID));
-
-    return hostname != null ? hostname :
-        String.valueOf(properties.get(HostResourceProvider.HOST_NAME_NO_CATEGORY_PROPERTY_ID));
+    String hostName = (String) properties.get(HostResourceProvider.HOST_NAME_PROPERTY_ID);
+    if (hostName == null) {
+      hostName = (String) properties.get(HostResourceProvider.HOST_NAME_NO_CATEGORY_PROPERTY_ID);
+    }
+    return hostName;
   }
 
+  /**
+   * Get the total number of requested hosts for the request.
+   * @return  total requested host count
+   */
   private int getTotalRequestedHostCount() {
     int count = 0;
     for (HostGroupInfo groupInfo : getHostGroupInfo().values()) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
index acfd426..82dd705 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.controller.internal;
 
+import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.topology.Blueprint;
 import org.apache.ambari.server.topology.BlueprintFactory;
 import org.apache.ambari.server.topology.Configuration;
@@ -30,6 +31,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -38,6 +40,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.verify;
@@ -61,6 +64,7 @@ public class ProvisionClusterRequestTest {
 
   private static final BlueprintFactory blueprintFactory = createStrictMock(BlueprintFactory.class);
   private static final Blueprint blueprint = createNiceMock(Blueprint.class);
+  private static final ResourceProvider hostResourceProvider = createMock(ResourceProvider.class);
   private static final Configuration blueprintConfig = new Configuration(
       Collections.<String, Map<String, String>>emptyMap(),
       Collections.<String, Map<String, Map<String, String>>>emptyMap());
@@ -68,27 +72,146 @@ public class ProvisionClusterRequestTest {
   @Before
   public void setUp() throws Exception {
     ProvisionClusterRequest.init(blueprintFactory);
+    // set host resource provider field
+    Class clazz = BaseClusterRequest.class;
+    Field f = clazz.getDeclaredField("hostResourceProvider");
+    f.setAccessible(true);
+    f.set(null, hostResourceProvider);
 
     expect(blueprintFactory.getBlueprint(BLUEPRINT_NAME)).andReturn(blueprint).once();
     expect(blueprint.getConfiguration()).andReturn(blueprintConfig).anyTimes();
+    expect(hostResourceProvider.checkPropertyIds(Collections.singleton("Hosts/host_name"))).
+        andReturn(Collections.<String>emptySet()).once();
 
-    replay(blueprintFactory, blueprint);
+    replay(blueprintFactory, blueprint, hostResourceProvider);
   }
 
   @After
   public void tearDown() {
-    reset(blueprintFactory, blueprint);
+    verify(blueprintFactory, blueprint, hostResourceProvider);
+    reset(blueprintFactory, blueprint, hostResourceProvider);
   }
 
   @Test
-  public void test_basic() throws Exception {
+  public void testHostNameSpecified() throws Exception {
+    // reset host resource provider expectations to none since we are not specifying a host predicate
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Map<String, Object> properties = createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
+    TopologyRequest provisionClusterRequest = new ProvisionClusterRequest(properties);
+
+    assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
+    assertEquals(TopologyRequest.Type.PROVISION, provisionClusterRequest.getType());
+    assertEquals(String.format("Provision Cluster '%s'", CLUSTER_NAME) , provisionClusterRequest.getCommandDescription());
+    assertSame(blueprint, provisionClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(1, provisionClusterRequest.getTopologyValidators().size());
+
+    // group1
+    // host info
+    HostGroupInfo group1Info = hostGroupInfo.get("group1");
+    assertEquals("group1", group1Info.getHostGroupName());
+    assertEquals(1, group1Info.getHostNames().size());
+    assertTrue(group1Info.getHostNames().contains("host1.myDomain.com"));
+    assertEquals(1, group1Info.getRequestedHostCount());
+    assertNull(group1Info.getPredicate());
+    // configuration
+    Configuration group1Configuration = group1Info.getConfiguration();
+    assertNull(group1Configuration.getParentConfiguration());
+    assertEquals(1, group1Configuration.getProperties().size());
+    Map<String, String> group1TypeProperties = group1Configuration.getProperties().get("foo-type");
+    assertEquals(2, group1TypeProperties.size());
+    assertEquals("prop1Value", group1TypeProperties.get("hostGroup1Prop1"));
+    assertEquals("prop2Value", group1TypeProperties.get("hostGroup1Prop2"));
+    assertTrue(group1Configuration.getAttributes().isEmpty());
+
+    // cluster scoped configuration
+    Configuration clusterScopeConfiguration = provisionClusterRequest.getConfiguration();
+    assertSame(blueprintConfig, clusterScopeConfiguration.getParentConfiguration());
+    assertEquals(1, clusterScopeConfiguration.getProperties().size());
+    Map<String, String> clusterScopedProperties = clusterScopeConfiguration.getProperties().get("someType");
+    assertEquals(1, clusterScopedProperties.size());
+    assertEquals("someValue", clusterScopedProperties.get("property1"));
+    // attributes
+    Map<String, Map<String, Map<String, String>>> clusterScopedAttributes = clusterScopeConfiguration.getAttributes();
+    assertEquals(1, clusterScopedAttributes.size());
+    Map<String, Map<String, String>> clusterScopedTypeAttributes = clusterScopedAttributes.get("someType");
+    assertEquals(1, clusterScopedTypeAttributes.size());
+    Map<String, String> clusterScopedTypePropertyAttributes = clusterScopedTypeAttributes.get("property1");
+    assertEquals(1, clusterScopedTypePropertyAttributes.size());
+    assertEquals("someAttributePropValue", clusterScopedTypePropertyAttributes.get("attribute1"));
+  }
+
+  @Test
+  public void testHostCountSpecified() throws Exception {
+    // reset host resource provider expectations to none since we are not specifying a host predicate
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Map<String, Object> properties = createBlueprintRequestPropertiesCountOnly(CLUSTER_NAME, BLUEPRINT_NAME);
+    TopologyRequest provisionClusterRequest = new ProvisionClusterRequest(properties);
+
+    assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
+    assertEquals(TopologyRequest.Type.PROVISION, provisionClusterRequest.getType());
+    assertEquals(String.format("Provision Cluster '%s'", CLUSTER_NAME) , provisionClusterRequest.getCommandDescription());
+    assertSame(blueprint, provisionClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(1, provisionClusterRequest.getTopologyValidators().size());
+
+    // group2
+    HostGroupInfo group2Info = hostGroupInfo.get("group2");
+    assertEquals("group2", group2Info.getHostGroupName());
+    assertTrue(group2Info.getHostNames().isEmpty());
+    assertEquals(5, group2Info.getRequestedHostCount());
+    assertNull(group2Info.getPredicate());
+    // configuration
+    Configuration group2Configuration = group2Info.getConfiguration();
+    assertNull(group2Configuration.getParentConfiguration());
+    assertEquals(1, group2Configuration.getProperties().size());
+    Map<String, String> group2TypeProperties = group2Configuration.getProperties().get("foo-type");
+    assertEquals(1, group2TypeProperties.size());
+    assertEquals("prop1Value", group2TypeProperties.get("hostGroup2Prop1"));
+    //attributes
+    Map<String, Map<String, Map<String, String>>> group2Attributes = group2Configuration.getAttributes();
+    assertEquals(1, group2Attributes.size());
+    Map<String, Map<String, String>> group2Type1Attributes = group2Attributes.get("foo-type");
+    assertEquals(1, group2Type1Attributes.size());
+    Map<String, String> group2Type1Prop1Attributes = group2Type1Attributes.get("hostGroup2Prop10");
+    assertEquals(1, group2Type1Prop1Attributes.size());
+    assertEquals("attribute1Prop10-value", group2Type1Prop1Attributes.get("attribute1"));
+
+    // cluster scoped configuration
+    Configuration clusterScopeConfiguration = provisionClusterRequest.getConfiguration();
+    assertSame(blueprintConfig, clusterScopeConfiguration.getParentConfiguration());
+    assertEquals(1, clusterScopeConfiguration.getProperties().size());
+    Map<String, String> clusterScopedProperties = clusterScopeConfiguration.getProperties().get("someType");
+    assertEquals(1, clusterScopedProperties.size());
+    assertEquals("someValue", clusterScopedProperties.get("property1"));
+    // attributes
+    Map<String, Map<String, Map<String, String>>> clusterScopedAttributes = clusterScopeConfiguration.getAttributes();
+    assertEquals(1, clusterScopedAttributes.size());
+    Map<String, Map<String, String>> clusterScopedTypeAttributes = clusterScopedAttributes.get("someType");
+    assertEquals(1, clusterScopedTypeAttributes.size());
+    Map<String, String> clusterScopedTypePropertyAttributes = clusterScopedTypeAttributes.get("property1");
+    assertEquals(1, clusterScopedTypePropertyAttributes.size());
+    assertEquals("someAttributePropValue", clusterScopedTypePropertyAttributes.get("attribute1"));
+  }
+
+  @Test
+  public void testMultipleGroups() throws Exception {
     Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
     TopologyRequest provisionClusterRequest = new ProvisionClusterRequest(properties);
 
     assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
+    assertEquals(TopologyRequest.Type.PROVISION, provisionClusterRequest.getType());
+    assertEquals(String.format("Provision Cluster '%s'", CLUSTER_NAME) , provisionClusterRequest.getCommandDescription());
     assertSame(blueprint, provisionClusterRequest.getBlueprint());
     Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo();
     assertEquals(2, hostGroupInfo.size());
+    assertEquals(1, provisionClusterRequest.getTopologyValidators().size());
 
     // group1
     // host info
@@ -145,8 +268,6 @@ public class ProvisionClusterRequestTest {
     Map<String, String> clusterScopedTypePropertyAttributes = clusterScopedTypeAttributes.get("property1");
     assertEquals(1, clusterScopedTypePropertyAttributes.size());
     assertEquals("someAttributePropValue", clusterScopedTypePropertyAttributes.get("attribute1"));
-
-    verify(blueprintFactory, blueprint);
   }
 
   @Test(expected= InvalidTopologyTemplateException.class)
@@ -154,6 +275,9 @@ public class ProvisionClusterRequestTest {
     Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
     ((Collection)properties.get("host_groups")).clear();
 
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
     // should result in an exception
     new ProvisionClusterRequest(properties);
   }
@@ -163,6 +287,9 @@ public class ProvisionClusterRequestTest {
     Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
     ((Collection<Map<String, Object>>)properties.get("host_groups")).iterator().next().remove("name");
 
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
     // should result in an exception
     new ProvisionClusterRequest(properties);
   }
@@ -172,6 +299,9 @@ public class ProvisionClusterRequestTest {
     Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
     ((Collection<Map<String, Object>>)properties.get("host_groups")).iterator().next().remove("hosts");
 
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
     // should result in an exception
     new ProvisionClusterRequest(properties);
   }
@@ -189,6 +319,9 @@ public class ProvisionClusterRequestTest {
       }
     }
 
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
     // should result in an exception
     new ProvisionClusterRequest(properties);
   }
@@ -221,6 +354,47 @@ public class ProvisionClusterRequestTest {
     assertEquals(pwdValidator, defaultPwdValidator);
   }
 
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testInvalidPredicateProperty() throws Exception {
+    reset(hostResourceProvider);
+    // checkPropertyIds() returns invalid property names
+    expect(hostResourceProvider.checkPropertyIds(Collections.singleton("Hosts/host_name"))).
+        andReturn(Collections.singleton("Hosts/host_name"));
+    replay(hostResourceProvider);
+
+    // should result in an exception due to invalid property in host predicate
+    new ProvisionClusterRequest(createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME));
+  }
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testHostNameAndCountSpecified() throws Exception {
+    // reset host resource provider expectations to none since we are not specifying a host predicate
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Map<String, Object> properties = createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
+    List hostGroups = (List) properties.get("host_groups");
+    Map hostGroup = (Map) hostGroups.iterator().next();
+    List hostInfo = (List) hostGroup.get("hosts");
+    ((Map) hostInfo.iterator().next()).put("host_count", "5");
+    // should result in an exception due to both host name and host count being specified
+    TopologyRequest provisionClusterRequest = new ProvisionClusterRequest(properties);
+  }
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testNeitherHostNameOrCountSpecified() throws Exception {
+    // reset host resource provider expectations to none since we are not specifying a host predicate
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Map<String, Object> properties = createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
+    List hostGroups = (List) properties.get("host_groups");
+    Map hostGroup = (Map) hostGroups.iterator().next();
+    List hostInfo = (List) hostGroup.get("hosts");
+    ((Map) hostInfo.iterator().next()).remove("fqdn");
+    // should result in an exception due to both host name and host count being specified
+    TopologyRequest provisionClusterRequest = new ProvisionClusterRequest(properties);
+  }
 
   public static Map<String, Object> createBlueprintRequestProperties(String clusterName, String blueprintName) {
     Map<String, Object> properties = new LinkedHashMap<String, Object>();
@@ -279,4 +453,83 @@ public class ProvisionClusterRequestTest {
 
     return properties;
   }
+
+  public static Map<String, Object> createBlueprintRequestPropertiesNameOnly(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(ClusterResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+
+    Collection<Map<String, Object>> hostGroups = new ArrayList<Map<String, Object>>();
+    properties.put("host_groups", hostGroups);
+
+    // host group 1
+    Map<String, Object> hostGroup1Properties = new HashMap<String, Object>();
+    hostGroups.add(hostGroup1Properties);
+    hostGroup1Properties.put("name", "group1");
+    Collection<Map<String, String>> hostGroup1Hosts = new ArrayList<Map<String, String>>();
+    hostGroup1Properties.put("hosts", hostGroup1Hosts);
+    Map<String, String> hostGroup1HostProperties = new HashMap<String, String>();
+    hostGroup1HostProperties.put("fqdn", "host1.myDomain.com");
+    hostGroup1Hosts.add(hostGroup1HostProperties);
+    // host group 1 scoped configuration
+    // version 1 configuration syntax
+    Collection<Map<String, String>> hostGroup1Configurations = new ArrayList<Map<String, String>>();
+    hostGroup1Properties.put("configurations", hostGroup1Configurations);
+    Map<String, String> hostGroup1Configuration1 = new HashMap<String, String>();
+    hostGroup1Configuration1.put("foo-type/hostGroup1Prop1", "prop1Value");
+    hostGroup1Configuration1.put("foo-type/hostGroup1Prop2", "prop2Value");
+    hostGroup1Configurations.add(hostGroup1Configuration1);
+
+    // cluster scoped configuration
+    Collection<Map<String, String>> clusterConfigurations = new ArrayList<Map<String, String>>();
+    properties.put("configurations", clusterConfigurations);
+
+    Map<String, String> clusterConfigurationProperties = new HashMap<String, String>();
+    clusterConfigurations.add(clusterConfigurationProperties);
+    clusterConfigurationProperties.put("someType/properties/property1", "someValue");
+    clusterConfigurationProperties.put("someType/properties_attributes/attribute1/property1", "someAttributePropValue");
+
+    return properties;
+  }
+
+  public static Map<String, Object> createBlueprintRequestPropertiesCountOnly(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(ClusterResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+
+    Collection<Map<String, Object>> hostGroups = new ArrayList<Map<String, Object>>();
+    properties.put("host_groups", hostGroups);
+
+    // host group 2
+    Map<String, Object> hostGroup2Properties = new HashMap<String, Object>();
+    hostGroups.add(hostGroup2Properties);
+    hostGroup2Properties.put("name", "group2");
+    Collection<Map<String, String>> hostGroup2Hosts = new ArrayList<Map<String, String>>();
+    hostGroup2Properties.put("hosts", hostGroup2Hosts);
+    Map<String, String> hostGroup2HostProperties = new HashMap<String, String>();
+    // count with no predicate
+    hostGroup2HostProperties.put("host_count", "5");
+    hostGroup2Hosts.add(hostGroup2HostProperties);
+    // host group 2 scoped configuration
+    // version 2 configuration syntax
+    Collection<Map<String, String>> hostGroup2Configurations = new ArrayList<Map<String, String>>();
+    hostGroup2Properties.put("configurations", hostGroup2Configurations);
+    Map<String, String> hostGroup2Configuration1 = new HashMap<String, String>();
+    hostGroup2Configuration1.put("foo-type/properties/hostGroup2Prop1", "prop1Value");
+    hostGroup2Configuration1.put("foo-type/properties_attributes/attribute1/hostGroup2Prop10", "attribute1Prop10-value");
+    hostGroup2Configurations.add(hostGroup2Configuration1);
+
+    // cluster scoped configuration
+    Collection<Map<String, String>> clusterConfigurations = new ArrayList<Map<String, String>>();
+    properties.put("configurations", clusterConfigurations);
+
+    Map<String, String> clusterConfigurationProperties = new HashMap<String, String>();
+    clusterConfigurations.add(clusterConfigurationProperties);
+    clusterConfigurationProperties.put("someType/properties/property1", "someValue");
+    clusterConfigurationProperties.put("someType/properties_attributes/attribute1/property1", "someAttributePropValue");
+
+    return properties;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/43dd0cdd/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
new file mode 100644
index 0000000..91b6c6f
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
@@ -0,0 +1,394 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.topology.Blueprint;
+import org.apache.ambari.server.topology.BlueprintFactory;
+import org.apache.ambari.server.topology.Configuration;
+import org.apache.ambari.server.topology.HostGroup;
+import org.apache.ambari.server.topology.HostGroupInfo;
+import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
+import org.apache.ambari.server.topology.TopologyRequest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.powermock.api.easymock.PowerMock.createStrictMock;
+import static org.powermock.api.easymock.PowerMock.replay;
+import static org.powermock.api.easymock.PowerMock.reset;
+
+/**
+ * Unit tests for ScaleClusterRequest.
+ */
+@SuppressWarnings("unchecked")
+public class ScaleClusterRequestTest {
+
+  private static final String CLUSTER_NAME = "cluster_name";
+  private static final String BLUEPRINT_NAME = "blueprint_name";
+  private static final String HOST1_NAME = "host1.test.com";
+  private static final String HOST2_NAME = "host2.test.com";
+  private static final String GROUP1_NAME = "group1";
+  private static final String GROUP2_NAME = "group2";
+  private static final String GROUP3_NAME = "group3";
+  private static final String PREDICATE = "test/prop=foo";
+
+  private static final BlueprintFactory blueprintFactory = createStrictMock(BlueprintFactory.class);
+  private static final Blueprint blueprint = createNiceMock(Blueprint.class);
+  private static final ResourceProvider hostResourceProvider = createMock(ResourceProvider.class);
+  private static final HostGroup hostGroup1 = createNiceMock(HostGroup.class);
+  private static final Configuration blueprintConfig = new Configuration(
+      Collections.<String, Map<String, String>>emptyMap(),
+      Collections.<String, Map<String, Map<String, String>>>emptyMap());
+
+  @Before
+  public void setUp() throws Exception {
+    ScaleClusterRequest.init(blueprintFactory);
+    // set host resource provider field
+    Class clazz = BaseClusterRequest.class;
+    Field f = clazz.getDeclaredField("hostResourceProvider");
+    f.setAccessible(true);
+    f.set(null, hostResourceProvider);
+
+    expect(blueprintFactory.getBlueprint(BLUEPRINT_NAME)).andReturn(blueprint).anyTimes();
+    expect(blueprint.getConfiguration()).andReturn(blueprintConfig).anyTimes();
+    expect(blueprint.getHostGroup(GROUP1_NAME)).andReturn(hostGroup1).anyTimes();
+    expect(blueprint.getHostGroup(GROUP2_NAME)).andReturn(hostGroup1).anyTimes();
+    expect(blueprint.getHostGroup(GROUP3_NAME)).andReturn(hostGroup1).anyTimes();
+    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).anyTimes();
+    expect(hostResourceProvider.checkPropertyIds(Collections.singleton("test/prop"))).
+        andReturn(Collections.<String>emptySet()).once();
+
+    replay(blueprintFactory, blueprint, hostResourceProvider, hostGroup1);
+  }
+
+  @After
+  public void tearDown() {
+    verify(blueprintFactory, blueprint, hostResourceProvider, hostGroup1);
+    reset(blueprintFactory, blueprint, hostResourceProvider, hostGroup1);
+  }
+
+  @Test
+  public void test_basic_hostName() throws Exception {
+    // reset default host resource provider expectations to none since no host predicate is used
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(Collections.singleton(
+        createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME)));
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "1"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group1
+    // host info
+    HostGroupInfo group1Info = hostGroupInfo.get(GROUP1_NAME);
+    assertEquals(GROUP1_NAME, group1Info.getHostGroupName());
+    assertEquals(1, group1Info.getHostNames().size());
+    assertTrue(group1Info.getHostNames().contains(HOST1_NAME));
+    assertEquals(1, group1Info.getRequestedHostCount());
+    assertNull(group1Info.getPredicate());
+  }
+
+  @Test
+  public void testMultipleHostNames() throws Exception {
+    // reset default host resource provider expectations to none since no host predicate is used
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Set<Map<String, Object>> propertySet = new HashSet<Map<String, Object>>();
+    propertySet.add(createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME));
+    propertySet.add(createScaleClusterPropertiesGroup1_HostName2(CLUSTER_NAME, BLUEPRINT_NAME));
+
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(propertySet);
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "2"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group1
+    // host info
+    HostGroupInfo group1Info = hostGroupInfo.get(GROUP1_NAME);
+    assertEquals(GROUP1_NAME, group1Info.getHostGroupName());
+    assertEquals(2, group1Info.getHostNames().size());
+    assertTrue(group1Info.getHostNames().contains(HOST1_NAME));
+    assertTrue(group1Info.getHostNames().contains(HOST2_NAME));
+    assertEquals(2, group1Info.getRequestedHostCount());
+    assertNull(group1Info.getPredicate());
+  }
+
+  @Test
+  public void test_basic_hostCount() throws Exception {
+    // reset default host resource provider expectations to none since no host predicate is used
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(Collections.singleton(
+        createScaleClusterPropertiesGroup1_HostCount(CLUSTER_NAME, BLUEPRINT_NAME)));
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "1"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group2
+    // host info
+    HostGroupInfo group2Info = hostGroupInfo.get(GROUP2_NAME);
+    assertEquals(GROUP2_NAME, group2Info.getHostGroupName());
+    assertEquals(0, group2Info.getHostNames().size());
+    assertEquals(1, group2Info.getRequestedHostCount());
+    assertNull(group2Info.getPredicate());
+  }
+
+  @Test
+  public void test_basic_hostCount2() throws Exception {
+    // reset default host resource provider expectations to none since no host predicate is used
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(Collections.singleton(
+        createScaleClusterPropertiesGroup1_HostCount2(CLUSTER_NAME, BLUEPRINT_NAME)));
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "2"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group2
+    // host info
+    HostGroupInfo group2Info = hostGroupInfo.get(GROUP3_NAME);
+    assertEquals(GROUP3_NAME, group2Info.getHostGroupName());
+    assertEquals(0, group2Info.getHostNames().size());
+    assertEquals(2, group2Info.getRequestedHostCount());
+    assertNull(group2Info.getPredicate());
+  }
+
+  @Test
+  public void test_basic_hostCountAndPredicate() throws Exception {
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(Collections.singleton(
+        createScaleClusterPropertiesGroup1_HostCountAndPredicate(CLUSTER_NAME, BLUEPRINT_NAME)));
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "1"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(1, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group3
+    // host info
+    HostGroupInfo group3Info = hostGroupInfo.get(GROUP3_NAME);
+    assertEquals(GROUP3_NAME, group3Info.getHostGroupName());
+    assertEquals(0, group3Info.getHostNames().size());
+    assertEquals(1, group3Info.getRequestedHostCount());
+    assertEquals(PREDICATE, group3Info.getPredicateString());
+  }
+
+  @Test
+  public void testMultipleHostGroups() throws Exception {
+    Set<Map<String, Object>> propertySet = new HashSet<Map<String, Object>>();
+    propertySet.add(createScaleClusterPropertiesGroup1_HostCountAndPredicate(CLUSTER_NAME, BLUEPRINT_NAME));
+    propertySet.add(createScaleClusterPropertiesGroup1_HostCount(CLUSTER_NAME, BLUEPRINT_NAME));
+    propertySet.add(createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME));
+
+    TopologyRequest scaleClusterRequest = new ScaleClusterRequest(propertySet);
+
+    assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
+    assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, "3"),
+        scaleClusterRequest.getCommandDescription());
+    assertEquals(CLUSTER_NAME, scaleClusterRequest.getClusterName());
+    assertSame(blueprint, scaleClusterRequest.getBlueprint());
+    Map<String, HostGroupInfo> hostGroupInfo = scaleClusterRequest.getHostGroupInfo();
+    assertEquals(3, hostGroupInfo.size());
+    assertEquals(0, scaleClusterRequest.getTopologyValidators().size());
+
+    // group
+    // host info
+    HostGroupInfo group1Info = hostGroupInfo.get(GROUP1_NAME);
+    assertEquals(GROUP1_NAME, group1Info.getHostGroupName());
+    assertEquals(1, group1Info.getHostNames().size());
+    assertTrue(group1Info.getHostNames().contains(HOST1_NAME));
+    assertEquals(1, group1Info.getRequestedHostCount());
+    assertNull(group1Info.getPredicate());
+
+    // group2
+    // host info
+    HostGroupInfo group2Info = hostGroupInfo.get(GROUP2_NAME);
+    assertEquals(GROUP2_NAME, group2Info.getHostGroupName());
+    assertEquals(0, group2Info.getHostNames().size());
+    assertEquals(1, group2Info.getRequestedHostCount());
+    assertNull(group2Info.getPredicate());
+
+    // group3
+    // host info
+    HostGroupInfo group3Info = hostGroupInfo.get(GROUP3_NAME);
+    assertEquals(GROUP3_NAME, group3Info.getHostGroupName());
+    assertEquals(0, group3Info.getHostNames().size());
+    assertEquals(1, group3Info.getRequestedHostCount());
+    assertEquals(PREDICATE, group3Info.getPredicateString());
+  }
+
+
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void test_GroupInfoMissingName() throws Exception {
+    Map<String, Object> properties = createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME);
+    // remove host group name
+    properties.remove("host_group");
+
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+    // should result in an exception
+    new ScaleClusterRequest(Collections.singleton(properties));
+  }
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void test_NoHostNameOrHostCount() throws Exception {
+    Map<String, Object> properties = createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME);
+    // remove host name
+    properties.remove("host_name");
+
+    // reset default host resource provider expectations to none
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+    // should result in an exception because neither host name or host count are specified
+    new ScaleClusterRequest(Collections.singleton(properties));
+  }
+
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testInvalidPredicateProperty() throws Exception {
+    reset(hostResourceProvider);
+    // checkPropertyIds() returns invalid property names
+    expect(hostResourceProvider.checkPropertyIds(Collections.singleton("test/prop"))).
+        andReturn(Collections.singleton("test/prop"));
+    replay(hostResourceProvider);
+
+    // should result in an exception due to invalid property in host predicate
+    new ScaleClusterRequest(Collections.singleton(
+        createScaleClusterPropertiesGroup1_HostCountAndPredicate(CLUSTER_NAME, BLUEPRINT_NAME)));
+  }
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testMultipleBlueprints() throws Exception {
+    reset(hostResourceProvider);
+    replay(hostResourceProvider);
+
+    Set<Map<String, Object>> propertySet = new LinkedHashSet<Map<String, Object>>();
+    propertySet.add(createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, BLUEPRINT_NAME));
+    propertySet.add(createScaleClusterPropertiesGroup1_HostName2(CLUSTER_NAME, "OTHER_BLUEPRINT"));
+
+    // should result in an exception due to different blueprints being specified
+    new ScaleClusterRequest(propertySet);
+  }
+
+  public static Map<String, Object> createScaleClusterPropertiesGroup1_HostName(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(HostResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+    properties.put(HostResourceProvider.HOSTGROUP_PROPERTY_ID, GROUP1_NAME);
+    properties.put(HostResourceProvider.HOST_NAME_NO_CATEGORY_PROPERTY_ID, HOST1_NAME);
+
+    return properties;
+  }
+
+  public static Map<String, Object> createScaleClusterPropertiesGroup1_HostCount(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(HostResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+    properties.put(HostResourceProvider.HOSTGROUP_PROPERTY_ID, GROUP2_NAME);
+    properties.put(HostResourceProvider.HOST_COUNT_PROPERTY_ID, 1);
+
+    return properties;
+  }
+
+  public static Map<String, Object> createScaleClusterPropertiesGroup1_HostCountAndPredicate(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(HostResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+    properties.put(HostResourceProvider.HOSTGROUP_PROPERTY_ID, GROUP3_NAME);
+    properties.put(HostResourceProvider.HOST_COUNT_PROPERTY_ID, 1);
+    properties.put(HostResourceProvider.HOST_PREDICATE_PROPERTY_ID, PREDICATE);
+
+    return properties;
+  }
+
+  public static Map<String, Object> createScaleClusterPropertiesGroup1_HostCount2(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(HostResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+    properties.put(HostResourceProvider.HOSTGROUP_PROPERTY_ID, GROUP3_NAME);
+    properties.put(HostResourceProvider.HOST_COUNT_PROPERTY_ID, 2);
+
+    return properties;
+  }
+
+  public static Map<String, Object> createScaleClusterPropertiesGroup1_HostName2(String clusterName, String blueprintName) {
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID, clusterName);
+    properties.put(HostResourceProvider.BLUEPRINT_PROPERTY_ID, blueprintName);
+    properties.put(HostResourceProvider.HOSTGROUP_PROPERTY_ID, GROUP1_NAME);
+    properties.put(HostResourceProvider.HOST_NAME_NO_CATEGORY_PROPERTY_ID, HOST2_NAME);
+
+    return properties;
+  }
+}


Mime
View raw message