You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2014/08/30 01:41:00 UTC
git commit: AMBARI-6125. Ambari Groovy client enhancements. (Janos
Matyas and Krisztian Horvath via yusaku)
Repository: ambari
Updated Branches:
refs/heads/trunk 1af0a4417 -> 733f0345c
AMBARI-6125. Ambari Groovy client enhancements. (Janos Matyas and Krisztian Horvath via yusaku)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/733f0345
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/733f0345
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/733f0345
Branch: refs/heads/trunk
Commit: 733f0345c5b726a5a4164214e740102ba8d7a8d1
Parents: 1af0a44
Author: Yusaku Sako <yu...@hortonworks.com>
Authored: Fri Aug 29 16:39:44 2014 -0700
Committer: Yusaku Sako <yu...@hortonworks.com>
Committed: Fri Aug 29 16:39:44 2014 -0700
----------------------------------------------------------------------
ambari-client/groovy-client/pom.xml | 4 +
.../ambari/groovy/client/AmbariClient.groovy | 474 +++++++++++++++++--
.../client/InvalidBlueprintException.groovy | 28 ++
.../InvalidHostGroupHostAssociation.groovy | 39 ++
.../resources/blueprints/hdp-multinode-default | 182 +++++++
.../resources/blueprints/hdp-singlenode-default | 133 ++++++
.../resources/blueprints/lambda-architecture | 8 +-
.../resources/blueprints/multi-node-hdfs-yarn | 4 +-
.../resources/blueprints/single-node-hdfs-yarn | 2 +-
.../groovy/client/AmbariBlueprintsTest.groovy | 95 ++++
.../ambari/groovy/client/AmbariHostsTest.groovy | 34 +-
.../groovy/client/AmbariRecommendTest.groovy | 150 ++++++
.../ambari/groovy/client/TestResources.groovy | 2 +
.../src/test/resources/blueprint-config.json | 61 +++
.../test/resources/hdp-multinode-default.json | 200 ++++++++
.../test/resources/hdp-multinode-default2.json | 164 +++++++
.../resources/multi-node-hdfs-yarn-config.json | 89 ++++
.../test/resources/multi-node-hdfs-yarn.json | 83 ++++
.../ambari/shell/commands/ClusterCommands.java | 3 +-
.../ambari/shell/flash/InstallProgress.java | 2 +-
.../shell/commands/ClusterCommandsTest.java | 5 +-
21 files changed, 1719 insertions(+), 43 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/pom.xml b/ambari-client/groovy-client/pom.xml
index 9600cd7..fbedbd1 100644
--- a/ambari-client/groovy-client/pom.xml
+++ b/ambari-client/groovy-client/pom.xml
@@ -53,6 +53,10 @@
<artifactId>spock-core</artifactId>
<version>0.7-groovy-2.0</version>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ </dependency>
</dependencies>
<build>
<plugins>
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/AmbariClient.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/AmbariClient.groovy b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/AmbariClient.groovy
index 85c52a4..1a515f5 100644
--- a/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/AmbariClient.groovy
+++ b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/AmbariClient.groovy
@@ -23,6 +23,7 @@ import groovy.util.logging.Slf4j
import groovyx.net.http.ContentType
import groovyx.net.http.HttpResponseException
import groovyx.net.http.RESTClient
+import org.apache.commons.io.IOUtils
import org.apache.http.NoHttpResponseException
import org.apache.http.client.ClientProtocolException
import java.net.ConnectException
@@ -37,6 +38,7 @@ class AmbariClient {
private static final int PAD = 30
private static final int OK_RESPONSE = 200
+ private static final String SLAVE = "slave_"
boolean debugEnabled = false;
def RESTClient ambari
def slurper = new JsonSlurper()
@@ -92,6 +94,203 @@ class AmbariClient {
}
/**
+ * Adds a registered host to the cluster.
+ *
+ * @param hostName new node's hostname
+ * @throws HttpResponseException if the node is not registered with ambari
+ */
+ def addHost(String hostName) throws HttpResponseException {
+ if (debugEnabled) {
+ println "[DEBUG] POST ${ambari.getUri()}clusters/${getClusterName()}/hosts/$hostName"
+ }
+ ambari.post(path: "clusters/${getClusterName()}/hosts/$hostName", { it })
+ }
+
+ /**
+ * Decommission and remove a host from the cluster.
+ * NOTE: this is a synchronous call, it wont return until all
+ * requests are finished
+ *
+ * Steps:
+ * 1, decommission services
+ * 2, stop services
+ * 3, delete host components
+ * 4, delete host
+ * 5, restart services
+ *
+ * @param hostName host to be deleted
+ */
+ def removeHost(String hostName) {
+ def components = getHostComponentsMap(hostName).keySet() as List
+
+ // decommission
+ if (components.contains("NODEMANAGER")) {
+ decommissionNodeManager(hostName)
+ }
+ if (components.contains("DATANODE")) {
+ decommissionDataNode(hostName)
+ }
+
+ // stop services
+ def requests = stopComponentsOnHost(hostName, components)
+ waitForRequestsToFinish(requests.values() as List)
+
+ // delete host components
+ deleteHostComponents(hostName, components)
+
+ // delete host
+ deleteHost(hostName)
+
+ // restart zookeper
+ def id = restartServiceComponents("ZOOKEEPER", ["ZOOKEEPER_SERVER"])
+ waitForRequestsToFinish([id])
+
+ // restart nagios
+ if (getServiceComponentsMap().containsKey("NAGIOS")) {
+ id = restartServiceComponents("NAGIOS", ["NAGIOS_SERVER"])
+ waitForRequestsToFinish([id])
+ }
+ }
+
+ /**
+ * Does not return until all the requests are finished.
+ * @param requestIds ids of the requests
+ */
+ def waitForRequestsToFinish(List<Integer> requestIds) {
+ def stopped = false
+ while (!stopped) {
+ def state = true
+ for (int id : requestIds) {
+ if (getRequestProgress(id) != 100.0) {
+ state = false;
+ break;
+ }
+ }
+ stopped = state
+ Thread.sleep(2000)
+ }
+ }
+
+ /**
+ * Decommission the data node on a given host.
+ *
+ * @return id of the request to keep track its progress
+ */
+ def int decommissionDataNode(String host) {
+ decommission(host, "DATANODE", "HDFS", "NAMENODE")
+ }
+
+ /**
+ * Decommission the node manager on a given host.
+ *
+ * @return id of the request to keep track its progress
+ */
+ def int decommissionNodeManager(String host) {
+ decommission(host, "NODEMANAGER", "YARN", "RESOURCEMANAGER")
+ }
+
+ /**
+ * Decommission a host component on a given host.
+ *
+ * @param host hostName where the component is installed to
+ * @param slaveName slave to be decommissioned
+ * @param serviceName where the slave belongs to
+ * @param componentName where the slave belongs to
+ * @return id of the request to keep track its progress
+ */
+ def int decommission(String host, String slaveName, String serviceName, String componentName) {
+ def requestInfo = [
+ command : "DECOMMISSION",
+ context : "Decommission $slaveName",
+ parameters: ["slave_type": slaveName, "excluded_hosts": host]
+ ]
+ def filter = [
+ ["service_name": serviceName, "component_name": componentName]
+ ]
+ Map bodyMap = [
+ "RequestInfo" : requestInfo,
+ "Requests/resource_filters": filter
+ ]
+ ambari.post(path: "clusters/${getClusterName()}/requests", body: new JsonBuilder(bodyMap).toPrettyString(), {
+ getRequestId(it)
+ })
+ }
+
+ /**
+ * Deletes the components from the host.
+ */
+ def deleteHostComponents(String hostName, List<String> components) {
+ components.each {
+ ambari.delete(path: "clusters/${getClusterName()}/hosts/$hostName/host_components/$it")
+ }
+ }
+
+ /**
+ * Deletes the host from the cluster.
+ */
+ def deleteHost(String hostName) {
+ ambari.delete(path: "clusters/${getClusterName()}/hosts/$hostName")
+ }
+
+ /**
+ * Install all the components from a given blueprint's host group. The services must be installed
+ * in order to install its components. It is recommended to use the same blueprint's host group from which
+ * the cluster was created.
+ *
+ * @param hostName components will be installed on this host
+ * @param blueprint id of the blueprint
+ * @param hostGroup host group of the blueprint
+ * @return map of the component names and their request id since its an async call
+ */
+ def Map<String, Integer> installComponentsToHost(String hostName, String blueprint, String hostGroup) throws HttpResponseException {
+ def bpMap = getBlueprint(blueprint)
+ def components = bpMap?.host_groups?.find { it.name.equals(hostGroup) }?.components?.collect { it.name }
+ if (components) {
+ return installComponentsToHost(hostName, components)
+ } else {
+ return [:]
+ }
+ }
+
+ /**
+ * Installs the given components to the given host.
+ * Only existing service components can be installed.
+ *
+ * @param hostName host to install the component to
+ * @param components components to be installed
+ * @throws HttpResponseException in case the component's service is not installed
+ * @return map of the component names and their request id since its an async call
+ */
+ def Map<String, Integer> installComponentsToHost(String hostName, List<String> components) throws HttpResponseException {
+ def resp = [:]
+ components.each {
+ addComponentToHost(hostName, it)
+ resp << [(it): setComponentState(hostName, it, "INSTALLED")]
+ }
+ resp
+ }
+
+ /**
+ * Starts the given components on a host.
+ *
+ * @return map of the component names and their request id since its an async call
+ * @throws HttpResponseException in case the component is not found
+ */
+ def Map<String, Integer> startComponentsOnHost(String hostName, List<String> components) throws HttpResponseException {
+ setComponentsState(hostName, components, "STARTED")
+ }
+
+ /**
+ * Stops the given components on a host.
+ *
+ * @return map of the component names and their request id since its an async call
+ * @throws HttpResponseException in case the component is not found
+ */
+ def Map<String, Integer> stopComponentsOnHost(String hostName, List<String> components) throws HttpResponseException {
+ setComponentsState(hostName, components, "INSTALLED")
+ }
+
+ /**
* Checks whether the blueprint exists or not.
*
* @param id id of the blueprint
@@ -178,27 +377,34 @@ class AmbariClient {
* @param blueprint id of the blueprint
* @return recommended assignments
*/
- def Map<String, List<String>> recommendAssignments(String blueprint) {
+ def Map<String, List<String>> recommendAssignments(String blueprint) throws InvalidHostGroupHostAssociation {
def result = [:]
def hostNames = getHostNames().keySet() as List
def groups = getBlueprint(blueprint)?.host_groups?.collect { ["name": it.name, "cardinality": it.cardinality] }
if (hostNames && groups) {
def groupSize = groups.size()
def hostSize = hostNames.size()
- if (hostSize == groupSize) {
- def i = 0
- result = groups.collectEntries { [(it.name): [hostNames[i++]]] }
- } else if (groupSize == 2 && hostSize > 2) {
- def grouped = groups.groupBy { it.cardinality }
- if (grouped["1"] && grouped["1"].size() == 1) {
- groups.each {
- if (it["cardinality"] == "1") {
- result << [(it["name"]): [hostNames[0]]]
- } else {
- result << [(it["name"]): hostNames.subList(1, hostSize)]
- }
+ if (hostSize == 1 && groupSize == 1) {
+ result = [(groups[0].name): [hostNames[0]]]
+ } else if (hostSize >= groupSize) {
+ int i = 0
+ groups.findAll { !it.name.toLowerCase().startsWith(SLAVE) }.each {
+ result << [(it.name): [hostNames[i++]]]
+ }
+ def slaves = groups.findAll { it.name.toLowerCase().startsWith(SLAVE) }
+ if (slaves) {
+ int k = 0
+ for (int j = i; j < hostSize; j++) {
+ result[slaves[k].name] = result[slaves[k].name] ?: []
+ result[slaves[k].name] << hostNames[j]
+ result << [(slaves[k].name): result[slaves[k++].name]]
+ k = k == slaves.size ? 0 : k
}
+ } else {
+ throw new InvalidHostGroupHostAssociation("At least one '$SLAVE' is required", groupSize)
}
+ } else {
+ throw new InvalidHostGroupHostAssociation("At least $groupSize host is required", groupSize)
}
}
return result
@@ -228,11 +434,49 @@ class AmbariClient {
* Adds a blueprint to the Ambari server. Exception is thrown if fails.
*
* @param json blueprint as json
+ * @return blueprint json
* @throws HttpResponseException in case of error
*/
- def void addBlueprint(String json) throws HttpResponseException {
+ def String addBlueprint(String json) throws HttpResponseException {
+ addBlueprint(json, [:])
+ }
+
+ /**
+ * Adds a blueprint with the desired configurations.
+ *
+ * @param json blueprint to be added
+ * @param configurations blueprint will be extended with these configurations
+ * @return the extended blueprint as json
+ */
+ def String addBlueprint(String json, Map<String, Map<String, String>> configurations) throws HttpResponseException {
if (json) {
- postBlueprint(json)
+ def text = slurper.parseText(json)
+ def bpMap = extendBlueprintConfiguration(text, configurations)
+ def builder = new JsonBuilder(bpMap)
+ def resultJson = builder.toPrettyString()
+ postBlueprint(resultJson)
+ resultJson
+ }
+ }
+
+ /**
+ * Only validates the multinode blueprints, at least 1 slave host group must exist.
+ * Throws an exception if the blueprint is not valid.
+ *
+ * @param json blueprint json
+ * @throws InvalidBlueprintException if the blueprint is not valid
+ */
+ def void validateBlueprint(String json) throws InvalidBlueprintException {
+ if (json) {
+ def bpMap = slurper.parseText(json)
+ if (bpMap?.host_groups?.size > 1) {
+ def find = bpMap.host_groups.find { it.name.toLowerCase().startsWith(SLAVE) }
+ if (!find) {
+ throw new InvalidBlueprintException("At least one '$SLAVE' host group is required.")
+ }
+ }
+ } else {
+ throw new InvalidBlueprintException("No blueprint specified")
}
}
@@ -246,6 +490,8 @@ class AmbariClient {
addBlueprint(getResourceContent("blueprints/single-node-hdfs-yarn"))
addBlueprint(getResourceContent("blueprints/lambda-architecture"))
addBlueprint(getResourceContent("blueprints/warmup"))
+ addBlueprint(getResourceContent("blueprints/hdp-singlenode-default"))
+ addBlueprint(getResourceContent("blueprints/hdp-multinode-default"))
}
/**
@@ -301,6 +547,24 @@ class AmbariClient {
}
/**
+ * Modify an existing configuration. Be ware you'll have to provide the whole configuration
+ * otherwise properties might get lost.
+ *
+ * @param type type of the configuration e.g capacity-scheduler
+ * @param properties properties to be used
+ */
+ def modifyConfiguration(String type, Map<String, String> properties) {
+ Map bodyMap = [
+ "Clusters": ["desired_config": ["type": type, "tag": "version${System.currentTimeMillis()}", "properties": properties]]
+ ]
+ def Map<String, ?> putRequestMap = [:]
+ putRequestMap.put('requestContentType', ContentType.URLENC)
+ putRequestMap.put('path', "clusters/${getClusterName()}")
+ putRequestMap.put('body', new JsonBuilder(bodyMap).toPrettyString());
+ ambari.put(putRequestMap)
+ }
+
+ /**
* Returns a pre-formatted String of the clusters.
*
* @return pre-formatted cluster list
@@ -322,18 +586,18 @@ class AmbariClient {
}
/**
- * Returns the install progress state. If the install failed -1 returned.
+ * Returns the requests progress.
*
* @param request request id; default is 1
* @return progress in percentage
*/
- def BigDecimal getInstallProgress(request = 1) {
+ def BigDecimal getRequestProgress(request = 1) {
def response = getAllResources("requests/$request", "Requests")
- def String status = response.Requests?.request_status
+ def String status = response?.Requests?.request_status
if (status && status.equals("FAILED")) {
return new BigDecimal(-1)
}
- return response.Requests?.progress_percent
+ return response?.Requests?.progress_percent
}
/**
@@ -358,7 +622,9 @@ class AmbariClient {
}
/**
- * Returns the available host names and its states.
+ * Returns the available host names and their states. It also
+ * contains hosts which are not part of the cluster, but are connected
+ * to ambari.
*
* @return hostname state association
*/
@@ -367,6 +633,15 @@ class AmbariClient {
}
/**
+ * Returns the names of the hosts which have the given state. It also
+ * contains hosts which are not part of the cluster, but are connected
+ * to ambari.
+ */
+ def Map<String, String> getHostNamesByState(String state) {
+ getHostNames().findAll { it.value == state }
+ }
+
+ /**
* Returns a pre-formatted list of the hosts.
*
* @return pre-formatted String
@@ -458,7 +733,7 @@ class AmbariClient {
*/
def Map<String, String> getHostComponentsMap(host) {
def result = getHostComponents(host)?.items?.collectEntries { [(it.HostRoles.component_name): it.HostRoles.state] }
- result ?: new HashMap()
+ result ?: [:]
}
/**
@@ -472,11 +747,11 @@ class AmbariClient {
return getRawResource(resourceRequestMap)
}
-/**
- * Returns a map with service configurations. The keys are the service names, values are maps with <propertyName, propertyValue> entries
- *
- * @return a Map with entries of format <servicename, Map<property, value>>
- */
+ /**
+ * Returns a map with service configurations. The keys are the service names, values are maps with <propertyName, propertyValue> entries
+ *
+ * @return a Map with entries of format <servicename, Map<property, value>>
+ */
def Map<String, Map<String, String>> getServiceConfigMap() {
def Map<String, Integer> serviceToTags = new HashMap<>()
@@ -499,12 +774,22 @@ class AmbariClient {
return finalMap
}
- def startAllServices() {
+ /**
+ * Starts all the services.
+ *
+ * @return id of the request since its an async call
+ */
+ def int startAllServices() {
log.debug("Starting all services ...")
manageAllServices("Start All Services", "STARTED")
}
- def stopAllServices() {
+ /**
+ * Stops all the services.
+ *
+ * @return id of the request since its an async call
+ */
+ def int stopAllServices() {
log.debug("Stopping all services ...")
manageAllServices("Stop All Services", "INSTALLED")
}
@@ -517,6 +802,61 @@ class AmbariClient {
return servicesStatus(false)
}
+ /**
+ * Returns the public hostnames of the hosts which the host components are installed to.
+ */
+ def List<String> getPublicHostNames(String hostComponent) {
+ def hosts = getInternalHostNames(hostComponent)
+ if (hosts) {
+ return hosts.collect() { resolveInternalHostName(it) }
+ } else {
+ return []
+ }
+ }
+
+ /**
+ * Returns the internal hostnames of the hosts which the host components are installed to.
+ */
+ def List<String> getInternalHostNames(String hostComponent) {
+ def hosts = []
+ getClusterHosts().each {
+ if (getHostComponentsMap(it).keySet().contains(hostComponent)) {
+ hosts << it
+ }
+ }
+ hosts
+ }
+
+ /**
+ * Restarts the given components of a service.
+ */
+ def int restartServiceComponents(String service, List<String> components) {
+ def filter = components.collect {
+ ["service_name": service, "component_name": it, "hosts": getInternalHostNames(it).join(",")]
+ }
+ Map bodyMap = [
+ "RequestInfo" : [command: "RESTART", context: "Restart $service components $components"],
+ "Requests/resource_filters": filter
+ ]
+ ambari.post(path: "clusters/${getClusterName()}/requests", body: new JsonBuilder(bodyMap).toPrettyString(), {
+ getRequestId(it)
+ })
+ }
+
+ /**
+ * Returns the names of the hosts which are in the cluster.
+ */
+ def List<String> getClusterHosts() {
+ slurp("clusters/${getClusterName()}")?.hosts?.Hosts?.host_name
+ }
+
+ /**
+ * Resolves an internal hostname to a public one.
+ */
+ def String resolveInternalHostName(String internalHostName) {
+ slurp("clusters/${getClusterName()}/hosts/$internalHostName")?.Hosts?.public_host_name
+ }
+
def private boolean servicesStatus(boolean starting) {
def String status = (starting) ? "STARTED" : "INSTALLED"
Map serviceComponents = getServicesMap();
@@ -540,7 +880,8 @@ class AmbariClient {
putRequestMap.put('query', ['params/run_smoke_test': 'false'])
putRequestMap.put('body', builder.toPrettyString());
- ambari.put(putRequestMap)
+ def reponse = ambari.put(putRequestMap)
+ slurper.parseText(reponse.getAt("responseData")?.getAt("str"))?.Requests?.id
}
private def processServiceVersions(Map<String, Integer> serviceToVersions, String service, def version) {
@@ -617,7 +958,7 @@ class AmbariClient {
*/
private getSlurpedResource(Map resourceRequestMap) {
def rawResource = getRawResource(resourceRequestMap)
- def slurpedResource = (rawResource) ? slurper.parseText(rawResource) : null
+ def slurpedResource = (rawResource != null) ? slurper.parseText(rawResource) : rawResource
return slurpedResource
}
@@ -716,6 +1057,38 @@ class AmbariClient {
getAllResources("services", "ServiceInfo")
}
+ private def addComponentToHost(String hostName, String component) {
+ if (debugEnabled) {
+ println "[DEBUG] POST ${ambari.getUri()}clusters/${getClusterName()}/hosts/$hostName/host_components"
+ }
+ ambari.post(path: "clusters/${getClusterName()}/hosts/$hostName/host_components/${component.toUpperCase()}", { it })
+ }
+
+ private def Map<String, Integer> setComponentsState(String hostName, List<String> components, String state)
+ throws HttpResponseException {
+ def resp = [:]
+ components.each {
+ resp << [(it): setComponentState(hostName, it, state)]
+ }
+ return resp
+ }
+
+ private def setComponentState(String hostName, String component, String state) {
+ if (debugEnabled) {
+ println "[DEBUG] PUT ${ambari.getUri()}clusters/${getClusterName()}/hosts/$hostName/host_components/$component"
+ }
+ Map bodyMap = [
+ HostRoles : [state: state.toUpperCase()],
+ RequestInfo: [context: "${component.toUpperCase()} ${state.toUpperCase()}"]
+ ]
+ def Map<String, ?> putRequestMap = [:]
+ putRequestMap.put('requestContentType', ContentType.URLENC)
+ putRequestMap.put('path', "clusters/${getClusterName()}/hosts/$hostName/host_components/${component.toUpperCase()}")
+ putRequestMap.put('body', new JsonBuilder(bodyMap).toPrettyString());
+ def reponse = ambari.put(putRequestMap)
+ slurper.parseText(reponse.getAt("responseData")?.getAt("str"))?.Requests?.id
+ }
+
/**
* Returns the properties of the host components as a Map parsed from the Ambari response json.
*
@@ -730,4 +1103,43 @@ class AmbariClient {
getClass().getClassLoader().getResourceAsStream(name)?.text
}
-}
\ No newline at end of file
+ private def extendBlueprintConfiguration(Map blueprintMap, Map newConfigs) {
+ def configurations = blueprintMap.configurations
+ if (!configurations) {
+ if (newConfigs) {
+ def conf = []
+ newConfigs.each { conf << [(it.key): it.value] }
+ blueprintMap << ["configurations": conf]
+ }
+ return blueprintMap
+ }
+ newConfigs.each {
+ def site = it.key
+ def index = indexOfConfig(configurations, site)
+ if (index == -1) {
+ configurations << ["$site": it.value]
+ } else {
+ def existingConf = configurations.get(index)
+ existingConf."$site" << it.value
+ }
+ }
+ blueprintMap
+ }
+
+ private int indexOfConfig(List<Map> configurations, String site) {
+ def index = 0
+ for (Map conf : configurations) {
+ if (conf.containsKey(site)) {
+ return index;
+ }
+ index++
+ }
+ return -1;
+ }
+
+ private def int getRequestId(def responseDecorator) {
+ def resp = IOUtils.toString(new InputStreamReader(responseDecorator.entity.content.wrappedStream))
+ slurper.parseText(resp)?.Requests?.id
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidBlueprintException.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidBlueprintException.groovy b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidBlueprintException.groovy
new file mode 100644
index 0000000..36a7e62
--- /dev/null
+++ b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidBlueprintException.groovy
@@ -0,0 +1,28 @@
+/**
+ * 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.groovy.client
+
+/**
+ * Thrown when the blueprint validation fails.
+ */
+public class InvalidBlueprintException extends Exception {
+
+ public InvalidBlueprintException(String message) {
+ super(message)
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidHostGroupHostAssociation.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidHostGroupHostAssociation.groovy b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidHostGroupHostAssociation.groovy
new file mode 100644
index 0000000..1483eb9
--- /dev/null
+++ b/ambari-client/groovy-client/src/main/groovy/org/apache/ambari/groovy/client/InvalidHostGroupHostAssociation.groovy
@@ -0,0 +1,39 @@
+/**
+ * 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.groovy.client
+
+/**
+ * Thrown when the host group and host association offends the criteria of
+ * recommendation.
+ */
+public class InvalidHostGroupHostAssociation extends Exception {
+
+ /**
+ * For recommendation at least host group number of host is expected.
+ */
+ private final int minRequiredHost
+
+ public InvalidHostGroupHostAssociation(String message, int minRequiredHost) {
+ super(message)
+ this.minRequiredHost = minRequiredHost
+ }
+
+ public int getMinRequiredHost() {
+ return minRequiredHost
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/resources/blueprints/hdp-multinode-default
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/resources/blueprints/hdp-multinode-default b/ambari-client/groovy-client/src/main/resources/blueprints/hdp-multinode-default
new file mode 100644
index 0000000..4247c41
--- /dev/null
+++ b/ambari-client/groovy-client/src/main/resources/blueprints/hdp-multinode-default
@@ -0,0 +1,182 @@
+{
+ "configurations" : [
+ {
+ "global" : {
+ "nagios_contact" : "admin@localhost"
+ }
+ }
+ ],
+ "host_groups" : [
+ {
+ "name" : "master_1",
+ "components" : [
+ {
+ "name" : "NAMENODE"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "HBASE_MASTER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_2",
+ "components" : [
+
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "HISTORYSERVER"
+ },
+ {
+ "name" : "HIVE_SERVER"
+ },
+ {
+ "name" : "SECONDARY_NAMENODE"
+ },
+ {
+ "name" : "HIVE_METASTORE"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MYSQL_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ },
+ {
+ "name" : "WEBHCAT_SERVER"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_3",
+ "components" : [
+ {
+ "name" : "RESOURCEMANAGER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_4",
+ "components" : [
+ {
+ "name" : "OOZIE_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "slave_1",
+ "components" : [
+ {
+ "name" : "HBASE_REGIONSERVER"
+ },
+ {
+ "name" : "NODEMANAGER"
+ },
+ {
+ "name" : "DATANODE"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "${slavesCount}"
+ },
+ {
+ "name" : "gateway",
+ "components" : [
+ {
+ "name" : "AMBARI_SERVER"
+ },
+ {
+ "name" : "NAGIOS_SERVER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "PIG"
+ },
+ {
+ "name" : "OOZIE_CLIENT"
+ },
+ {
+ "name" : "HBASE_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "SQOOP"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ }
+ ],
+ "Blueprints" : {
+ "blueprint_name" : "hdp-multinode-default",
+ "stack_name" : "HDP",
+ "stack_version" : "2.1"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/resources/blueprints/hdp-singlenode-default
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/resources/blueprints/hdp-singlenode-default b/ambari-client/groovy-client/src/main/resources/blueprints/hdp-singlenode-default
new file mode 100644
index 0000000..aa752de
--- /dev/null
+++ b/ambari-client/groovy-client/src/main/resources/blueprints/hdp-singlenode-default
@@ -0,0 +1,133 @@
+{
+ "configurations" : [
+ {
+ "global" : {
+ "nagios_contact" : "admin@localhost"
+ }
+ }
+ ],
+ "host_groups" : [
+ {
+ "name" : "master",
+ "components" : [
+ {
+ "name" : "STORM_REST_API"
+ },
+ {
+ "name" : "PIG"
+ },
+ {
+ "name" : "HISTORYSERVER"
+ },
+ {
+ "name" : "HBASE_REGIONSERVER"
+ },
+ {
+ "name" : "OOZIE_CLIENT"
+ },
+ {
+ "name" : "HBASE_CLIENT"
+ },
+ {
+ "name" : "NAMENODE"
+ },
+ {
+ "name" : "SUPERVISOR"
+ },
+ {
+ "name" : "FALCON_SERVER"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "AMBARI_SERVER"
+ },
+ {
+ "name" : "APP_TIMELINE_SERVER"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "NODEMANAGER"
+ },
+ {
+ "name" : "DATANODE"
+ },
+ {
+ "name" : "WEBHCAT_SERVER"
+ },
+ {
+ "name" : "RESOURCEMANAGER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "STORM_UI_SERVER"
+ },
+ {
+ "name" : "HBASE_MASTER"
+ },
+ {
+ "name" : "HIVE_SERVER"
+ },
+ {
+ "name" : "OOZIE_SERVER"
+ },
+ {
+ "name" : "FALCON_CLIENT"
+ },
+ {
+ "name" : "NAGIOS_SERVER"
+ },
+ {
+ "name" : "SECONDARY_NAMENODE"
+ },
+ {
+ "name" : "TEZ_CLIENT"
+ },
+ {
+ "name" : "HIVE_METASTORE"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "SQOOP"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name" : "MYSQL_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ },
+ {
+ "name" : "DRPC_SERVER"
+ },
+ {
+ "name" : "NIMBUS"
+ }
+ ],
+ "cardinality" : "1"
+ }
+ ],
+ "Blueprints" : {
+ "blueprint_name" : "hdp-singlenode-default",
+ "stack_name" : "HDP",
+ "stack_version" : "2.1"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/resources/blueprints/lambda-architecture
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/resources/blueprints/lambda-architecture b/ambari-client/groovy-client/src/main/resources/blueprints/lambda-architecture
index 3deec4c..1750e59 100644
--- a/ambari-client/groovy-client/src/main/resources/blueprints/lambda-architecture
+++ b/ambari-client/groovy-client/src/main/resources/blueprints/lambda-architecture
@@ -8,7 +8,7 @@
],
"host_groups": [
{
- "name": "host_group_1",
+ "name": "master_1",
"components": [
{
"name": "ZOOKEEPER_SERVER"
@@ -65,7 +65,7 @@
"cardinality": "1"
},
{
- "name": "host_group_2",
+ "name": "slave_1",
"components": [
{
"name": "ZOOKEEPER_SERVER"
@@ -122,7 +122,7 @@
"cardinality": "1"
},
{
- "name": "host_group_3",
+ "name": "slave_2",
"components": [
{
"name": "ZOOKEEPER_SERVER"
@@ -172,4 +172,4 @@
"stack_name": "HDP",
"stack_version": "2.1"
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/resources/blueprints/multi-node-hdfs-yarn
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/resources/blueprints/multi-node-hdfs-yarn b/ambari-client/groovy-client/src/main/resources/blueprints/multi-node-hdfs-yarn
index 27a602a..6d6a364 100644
--- a/ambari-client/groovy-client/src/main/resources/blueprints/multi-node-hdfs-yarn
+++ b/ambari-client/groovy-client/src/main/resources/blueprints/multi-node-hdfs-yarn
@@ -32,7 +32,7 @@
"cardinality": "1"
},
{
- "name": "slaves",
+ "name": "slave_1",
"components": [
{
"name": "DATANODE"
@@ -64,4 +64,4 @@
"stack_name": "HDP",
"stack_version": "2.1"
}
-}
\ No newline at end of file
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/main/resources/blueprints/single-node-hdfs-yarn
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/main/resources/blueprints/single-node-hdfs-yarn b/ambari-client/groovy-client/src/main/resources/blueprints/single-node-hdfs-yarn
index 46ca508..3772a7f 100644
--- a/ambari-client/groovy-client/src/main/resources/blueprints/single-node-hdfs-yarn
+++ b/ambari-client/groovy-client/src/main/resources/blueprints/single-node-hdfs-yarn
@@ -1,7 +1,7 @@
{
"host_groups" : [
{
- "name" : "host_group_1",
+ "name" : "master",
"components" : [
{
"name" : "NAMENODE"
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariBlueprintsTest.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariBlueprintsTest.groovy b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariBlueprintsTest.groovy
index b467c56..d47b1a2 100644
--- a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariBlueprintsTest.groovy
+++ b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariBlueprintsTest.groovy
@@ -17,11 +17,14 @@
*/
package org.apache.ambari.groovy.client
+import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
@Slf4j
class AmbariBlueprintsTest extends AbstractAmbariClientTest {
+ def slurper = new JsonSlurper()
+
private enum Scenario {
CLUSTERS, NO_CLUSTERS, BLUEPRINT_EXISTS, NO_BLUEPRINT, HOSTS, NO_HOSTS
}
@@ -138,6 +141,98 @@ class AmbariBlueprintsTest extends AbstractAmbariClientTest {
[:] == result
}
+ def "test validate blueprint"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("blueprint.json").text
+
+ when:
+ ambari.validateBlueprint(json)
+
+ then:
+ noExceptionThrown()
+ }
+
+ def "test validate blueprint no slaves_"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("hdp-multinode-default2.json").text
+
+ when:
+ ambari.validateBlueprint(json)
+
+ then:
+ thrown(InvalidBlueprintException)
+ }
+
+ def "test validate blueprint with uppercase SLAVE_"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("hdp-multinode-default.json").text
+
+ when:
+ ambari.validateBlueprint(json)
+
+ then:
+ notThrown(InvalidBlueprintException)
+ }
+
+ def "test validate blueprint for null json"() {
+ when:
+ ambari.validateBlueprint(null)
+
+ then:
+ thrown(InvalidBlueprintException)
+ }
+
+ def "test add blueprint with configuration"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("blueprint.json").text
+ ambari.metaClass.postBlueprint = { String blueprint -> return }
+
+ when:
+ def config = [
+ "yarn-site": ["property-key": "property-value", "yarn.nodemanager.local-dirs": "/mnt/fs1/,/mnt/fs2/"],
+ "hdfs-site": ["dfs.datanode.data.dir": "/mnt/fs1/,/mnt/fs2/"]
+ ]
+ def blueprint = ambari.addBlueprint(json, config)
+
+ then:
+ def expected = slurper.parseText(getClass().getClassLoader().getResourceAsStream("blueprint-config.json").text)
+ def actual = slurper.parseText(blueprint)
+ actual == expected
+ }
+
+ def "test add blueprint with existing configuration"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("multi-node-hdfs-yarn.json").text
+ ambari.metaClass.postBlueprint = { String blueprint -> return }
+
+ when:
+ def config = [
+ "yarn-site": ["property-key": "property-value", "yarn.nodemanager.local-dirs": "apple"],
+ "hdfs-site": ["dfs.datanode.data.dir": "/mnt/fs1/,/mnt/fs2/"],
+ "core-site": ["fs.defaultFS": "localhost:9000"]
+ ]
+ def blueprint = ambari.addBlueprint(json, config)
+
+ then:
+ def expected = slurper.parseText(getClass().getClassLoader().getResourceAsStream("multi-node-hdfs-yarn-config.json").text)
+ def actual = slurper.parseText(blueprint)
+ actual == expected
+ }
+
+ def "test add blueprint with empty configuration"() {
+ given:
+ def json = getClass().getClassLoader().getResourceAsStream("blueprint.json").text
+ ambari.metaClass.postBlueprint = { String blueprint -> return }
+
+ when:
+ def blueprint = ambari.addBlueprint(json, [:])
+
+ then:
+ def expected = slurper.parseText(json)
+ def actual = slurper.parseText(blueprint)
+ actual == expected
+ }
+
def protected String selectResponseJson(Map resourceRequestMap, String scenarioStr) {
def thePath = resourceRequestMap.get("path");
def query = resourceRequestMap.get("query");
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariHostsTest.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariHostsTest.groovy b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariHostsTest.groovy
index 80d2f51..7efb95d 100644
--- a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariHostsTest.groovy
+++ b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariHostsTest.groovy
@@ -59,9 +59,39 @@ class AmbariHostsTest extends AbstractAmbariClientTest {
] == result
}
+ def "install host components to a host from an existing valid blueprint"() {
+ given:
+ mockResponses(Scenario.CLUSTERS.name())
+ ambari.metaClass.addComponentToHost = { String host, String component -> return null }
+ ambari.metaClass.setComponentState = { String host, String component, String state -> return 10 }
+
+ when:
+ def result = ambari.installComponentsToHost("amb0", "hdp-multinode-default", "slave_1")
+
+ then:
+ [
+ "HBASE_REGIONSERVER": 10,
+ "NODEMANAGER" : 10,
+ "DATANODE" : 10,
+ "GANGLIA_MONITOR" : 10
+ ] == result
+ }
+
+ def "install host components to a host from an existing valid blueprint but invalid group"() {
+ given:
+ mockResponses(Scenario.CLUSTERS.name())
+ ambari.metaClass.addComponentToHost = { String host, String component -> return null }
+ ambari.metaClass.setComponentState = { String host, String component, String state -> return null }
+
+ when:
+ def result = ambari.installComponentsToHost("amb0", "hdp-multinode-default", "slave_2")
+
+ then:
+ [:] == result
+ }
+
def protected String selectResponseJson(Map resourceRequestMap, String scenarioStr) {
def thePath = resourceRequestMap.get("path");
- def query = resourceRequestMap.get("query");
def Scenario scenario = Scenario.valueOf(scenarioStr)
def json = null
if (thePath == TestResources.CLUSTERS.uri()) {
@@ -71,6 +101,8 @@ class AmbariHostsTest extends AbstractAmbariClientTest {
}
} else if (thePath == TestResources.HOST_COMPONENTS.uri()) {
json = "host-components.json"
+ } else if (thePath == TestResources.BLUEPRINT_MULTI.uri) {
+ json = "hdp-multinode-default.json"
} else {
log.error("Unsupported resource path: {}", thePath)
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariRecommendTest.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariRecommendTest.groovy b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariRecommendTest.groovy
new file mode 100644
index 0000000..9f7ff03
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/AmbariRecommendTest.groovy
@@ -0,0 +1,150 @@
+/**
+ * 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.groovy.client
+
+import groovy.util.logging.Slf4j
+
+@Slf4j
+class AmbariRecommendTest extends AbstractAmbariClientTest {
+
+ private enum Scenario {
+ SINGLE_NODE_BLUEPRINT, MULTI_NODE_BLUEPRINT, MULTI_NODE_BLUEPRINT2
+ }
+
+ def "test recommend for single node"() {
+ given:
+ mockResponses(Scenario.SINGLE_NODE_BLUEPRINT.name())
+ ambari.metaClass.getHostNames = { return ["amb0": "HEALTHY"] }
+
+ when:
+ def result = ambari.recommendAssignments("single-node-hdfs-yarn")
+
+ then:
+ [host_group_1: ["amb0"]] == result
+ }
+
+ def "test recommend for invalid host number"() {
+ given:
+ mockResponses(Scenario.MULTI_NODE_BLUEPRINT.name())
+ ambari.metaClass.getHostNames = { return ["amb0": "HEALTHY"] }
+
+ when:
+ def result
+ try {
+ result = ambari.recommendAssignments("hdp-multinode-default")
+ } catch (InvalidHostGroupHostAssociation e) {
+ result = e.getMinRequiredHost()
+ }
+
+ then:
+ result == 7
+ }
+
+ def "test recommend for no slave group"() {
+ given:
+ mockResponses(Scenario.MULTI_NODE_BLUEPRINT2.name())
+ ambari.metaClass.getHostNames = {
+ return [
+ "amb0": "HEALTHY",
+ "amb1": "HEALTHY",
+ "amb2": "HEALTHY",
+ "amb3": "HEALTHY",
+ "amb4": "HEALTHY",
+ "amb5": "HEALTHY",
+ "amb6": "HEALTHY",
+ "amb7": "HEALTHY",
+ "amb8": "HEALTHY",
+ "amb9": "HEALTHY",
+ "am10": "HEALTHY",
+ "am10": "HEALTHY",
+ "am20": "HEALTHY",
+ "am30": "HEALTHY",
+ "am40": "HEALTHY",
+ ]
+ }
+
+ when:
+ def result
+ def msg
+ try {
+ result = ambari.recommendAssignments("hdp-multinode-default2")
+ } catch (InvalidHostGroupHostAssociation e) {
+ msg = e.getMessage()
+ result = e.getMinRequiredHost()
+ }
+
+ then:
+ result == 5
+ msg == "At least one 'slave_' is required"
+ }
+
+ def "test recommend for multi node"() {
+ given:
+ mockResponses(Scenario.MULTI_NODE_BLUEPRINT.name())
+ ambari.metaClass.getHostNames = {
+ return [
+ "amb0": "HEALTHY",
+ "amb1": "HEALTHY",
+ "amb2": "HEALTHY",
+ "amb3": "HEALTHY",
+ "amb4": "HEALTHY",
+ "amb5": "HEALTHY",
+ "amb6": "HEALTHY",
+ "amb7": "HEALTHY",
+ "amb8": "HEALTHY",
+ "amb9": "HEALTHY",
+ "am10": "HEALTHY",
+ "am10": "HEALTHY",
+ "am20": "HEALTHY",
+ "am30": "HEALTHY",
+ "am40": "HEALTHY",
+ ]
+ }
+
+ when:
+ def result = ambari.recommendAssignments("hdp-multinode-default")
+
+ then:
+ [master_1: ["amb0"],
+ master_2: ["amb1"],
+ master_3: ["amb2"],
+ master_4: ["amb3"],
+ gateway : ["amb4"],
+ slave_1 : ["amb5", "amb7", "amb9", "am20", "am40"],
+ SLAVE_2 : ["amb6", "amb8", "am10", "am30"]
+ ] == result
+ }
+
+ def protected String selectResponseJson(Map resourceRequestMap, String scenarioStr) {
+ def thePath = resourceRequestMap.get("path");
+ def query = resourceRequestMap.get("query");
+ def Scenario scenario = Scenario.valueOf(scenarioStr)
+ def json = null
+ if (thePath == TestResources.BLUEPRINT.uri()) {
+ json = "blueprint.json"
+ } else if (thePath == TestResources.BLUEPRINT_MULTI.uri()) {
+ json = "hdp-multinode-default.json"
+ } else if (thePath == TestResources.BLUEPRINT_MULTI2.uri()) {
+ json = "hdp-multinode-default2.json"
+ } else {
+ log.error("Unsupported resource path: {}", thePath)
+ }
+ return json
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/TestResources.groovy
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/TestResources.groovy b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/TestResources.groovy
index f9ee519..716d700 100644
--- a/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/TestResources.groovy
+++ b/ambari-client/groovy-client/src/test/groovy/org/apache/ambari/groovy/client/TestResources.groovy
@@ -23,6 +23,8 @@ enum TestResources {
CONFIGURATIONS("http://localhost:8080/api/v1/clusters/MySingleNodeCluster/configurations"),
BLUEPRINTS("http://localhost:8080/api/v1/blueprints"),
BLUEPRINT("http://localhost:8080/api/v1/blueprints/single-node-hdfs-yarn"),
+ BLUEPRINT_MULTI("http://localhost:8080/api/v1/blueprints/hdp-multinode-default"),
+ BLUEPRINT_MULTI2("http://localhost:8080/api/v1/blueprints/hdp-multinode-default2"),
INEXISTENT_BLUEPRINT("http://localhost:8080/api/v1/blueprints/inexistent-blueprint"),
HOSTS("http://localhost:8080/api/v1/hosts"),
TASKS("http://localhost:8080/api/v1/clusters/MySingleNodeCluster/requests/1"),
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/resources/blueprint-config.json
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/resources/blueprint-config.json b/ambari-client/groovy-client/src/test/resources/blueprint-config.json
new file mode 100644
index 0000000..bf3f67d
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/resources/blueprint-config.json
@@ -0,0 +1,61 @@
+{
+ "Blueprints": {
+ "blueprint_name": "single-node-hdfs-yarn",
+ "stack_version": "2.0",
+ "stack_name": "HDP"
+ },
+ "configurations": [
+ {
+ "yarn-site": {
+ "property-key": "property-value",
+ "yarn.nodemanager.local-dirs": "/mnt/fs1/,/mnt/fs2/"
+ }
+ },
+ {
+ "hdfs-site": {
+ "dfs.datanode.data.dir": "/mnt/fs1/,/mnt/fs2/"
+ }
+ }
+ ],
+ "host_groups": [
+ {
+ "name": "host_group_1",
+ "components": [
+ {
+ "name": "NAMENODE"
+ },
+ {
+ "name": "SECONDARY_NAMENODE"
+ },
+ {
+ "name": "DATANODE"
+ },
+ {
+ "name": "HDFS_CLIENT"
+ },
+ {
+ "name": "RESOURCEMANAGER"
+ },
+ {
+ "name": "NODEMANAGER"
+ },
+ {
+ "name": "YARN_CLIENT"
+ },
+ {
+ "name": "HISTORYSERVER"
+ },
+ {
+ "name": "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name": "ZOOKEEPER_SERVER"
+ },
+ {
+ "name": "ZOOKEEPER_CLIENT"
+ }
+ ],
+ "cardinality": "1"
+ }
+ ]
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/resources/hdp-multinode-default.json
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/resources/hdp-multinode-default.json b/ambari-client/groovy-client/src/test/resources/hdp-multinode-default.json
new file mode 100644
index 0000000..f6c7f3f
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/resources/hdp-multinode-default.json
@@ -0,0 +1,200 @@
+{
+ "configurations" : [
+ {
+ "global" : {
+ "nagios_contact" : "admin@localhost"
+ }
+ }
+ ],
+ "host_groups" : [
+ {
+ "name" : "master_1",
+ "components" : [
+ {
+ "name" : "NAMENODE"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "HBASE_MASTER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_2",
+ "components" : [
+
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "HISTORYSERVER"
+ },
+ {
+ "name" : "HIVE_SERVER"
+ },
+ {
+ "name" : "SECONDARY_NAMENODE"
+ },
+ {
+ "name" : "HIVE_METASTORE"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MYSQL_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ },
+ {
+ "name" : "WEBHCAT_SERVER"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_3",
+ "components" : [
+ {
+ "name" : "RESOURCEMANAGER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_4",
+ "components" : [
+ {
+ "name" : "OOZIE_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "slave_1",
+ "components" : [
+ {
+ "name" : "HBASE_REGIONSERVER"
+ },
+ {
+ "name" : "NODEMANAGER"
+ },
+ {
+ "name" : "DATANODE"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "${slavesCount}"
+ },
+ {
+ "name" : "SLAVE_2",
+ "components" : [
+ {
+ "name" : "HBASE_REGIONSERVER"
+ },
+ {
+ "name" : "NODEMANAGER"
+ },
+ {
+ "name" : "DATANODE"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "${slavesCount}"
+ },
+ {
+ "name" : "gateway",
+ "components" : [
+ {
+ "name" : "AMBARI_SERVER"
+ },
+ {
+ "name" : "NAGIOS_SERVER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "PIG"
+ },
+ {
+ "name" : "OOZIE_CLIENT"
+ },
+ {
+ "name" : "HBASE_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "SQOOP"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ }
+ ],
+ "Blueprints" : {
+ "blueprint_name" : "hdp-multinode-default",
+ "stack_name" : "HDP",
+ "stack_version" : "2.1"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/resources/hdp-multinode-default2.json
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/resources/hdp-multinode-default2.json b/ambari-client/groovy-client/src/test/resources/hdp-multinode-default2.json
new file mode 100644
index 0000000..4db3680
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/resources/hdp-multinode-default2.json
@@ -0,0 +1,164 @@
+{
+ "configurations" : [
+ {
+ "global" : {
+ "nagios_contact" : "admin@localhost"
+ }
+ }
+ ],
+ "host_groups" : [
+ {
+ "name" : "master_1",
+ "components" : [
+ {
+ "name" : "NAMENODE"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "HBASE_MASTER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_2",
+ "components" : [
+
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "HISTORYSERVER"
+ },
+ {
+ "name" : "HIVE_SERVER"
+ },
+ {
+ "name" : "SECONDARY_NAMENODE"
+ },
+ {
+ "name" : "HIVE_METASTORE"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MYSQL_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ },
+ {
+ "name" : "WEBHCAT_SERVER"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_3",
+ "components" : [
+ {
+ "name" : "RESOURCEMANAGER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "master_4",
+ "components" : [
+ {
+ "name" : "OOZIE_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_SERVER"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ },
+ {
+ "name" : "gateway",
+ "components" : [
+ {
+ "name" : "AMBARI_SERVER"
+ },
+ {
+ "name" : "NAGIOS_SERVER"
+ },
+ {
+ "name" : "GANGLIA_SERVER"
+ },
+ {
+ "name" : "ZOOKEEPER_CLIENT"
+ },
+ {
+ "name" : "PIG"
+ },
+ {
+ "name" : "OOZIE_CLIENT"
+ },
+ {
+ "name" : "HBASE_CLIENT"
+ },
+ {
+ "name" : "HCAT"
+ },
+ {
+ "name" : "SQOOP"
+ },
+ {
+ "name" : "HDFS_CLIENT"
+ },
+ {
+ "name" : "HIVE_CLIENT"
+ },
+ {
+ "name" : "YARN_CLIENT"
+ },
+ {
+ "name" : "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name" : "GANGLIA_MONITOR"
+ }
+ ],
+ "cardinality" : "1"
+ }
+ ],
+ "Blueprints" : {
+ "blueprint_name" : "hdp-multinode-default",
+ "stack_name" : "HDP",
+ "stack_version" : "2.1"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn-config.json
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn-config.json b/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn-config.json
new file mode 100644
index 0000000..16f4938
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn-config.json
@@ -0,0 +1,89 @@
+{
+ "configurations": [
+ {
+ "global": {
+ "nagios_contact": "admin@localhost"
+ }
+ },
+ {
+ "hdfs-site": {
+ "dfs.datanode.data.dir": "/mnt/fs1/,/mnt/fs2/"
+ }
+ },
+ {
+ "yarn-site": {
+ "yarn.nodemanager.local-dirs": "apple",
+ "property-key": "property-value"
+ }
+ },
+ {
+ "core-site": {
+ "fs.defaultFS": "localhost:9000"
+ }
+ }
+ ],
+ "host_groups": [
+ {
+ "name": "master",
+ "components": [
+ {
+ "name": "NAMENODE"
+ },
+ {
+ "name": "GANGLIA_SERVER"
+ },
+ {
+ "name": "HISTORYSERVER"
+ },
+ {
+ "name": "SECONDARY_NAMENODE"
+ },
+ {
+ "name": "RESOURCEMANAGER"
+ },
+ {
+ "name": "HISTORYSERVER"
+ },
+ {
+ "name": "NAGIOS_SERVER"
+ },
+ {
+ "name": "ZOOKEEPER_SERVER"
+ }
+ ],
+ "cardinality": "1"
+ },
+ {
+ "name": "slave_1",
+ "components": [
+ {
+ "name": "DATANODE"
+ },
+ {
+ "name": "GANGLIA_MONITOR"
+ },
+ {
+ "name": "HDFS_CLIENT"
+ },
+ {
+ "name": "NODEMANAGER"
+ },
+ {
+ "name": "YARN_CLIENT"
+ },
+ {
+ "name": "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name": "ZOOKEEPER_CLIENT"
+ }
+ ],
+ "cardinality": "2"
+ }
+ ],
+ "Blueprints": {
+ "blueprint_name": "multi-node-hdfs-yarn",
+ "stack_name": "HDP",
+ "stack_version": "2.1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn.json
----------------------------------------------------------------------
diff --git a/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn.json b/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn.json
new file mode 100644
index 0000000..fce02ad
--- /dev/null
+++ b/ambari-client/groovy-client/src/test/resources/multi-node-hdfs-yarn.json
@@ -0,0 +1,83 @@
+{
+ "configurations": [
+ {
+ "global": {
+ "nagios_contact": "admin@localhost"
+ }
+ },
+ {
+ "hdfs-site": {
+ "dfs.datanode.data.dir": "/mnt/fs1/,/mnt/fs2/"
+ }
+ },
+ {
+ "yarn-site": {
+ "yarn.nodemanager.local-dirs": "/mnt/fs1/,/mnt/fs2/"
+ }
+ }
+ ],
+ "host_groups": [
+ {
+ "name": "master",
+ "components": [
+ {
+ "name": "NAMENODE"
+ },
+ {
+ "name": "GANGLIA_SERVER"
+ },
+ {
+ "name": "HISTORYSERVER"
+ },
+ {
+ "name": "SECONDARY_NAMENODE"
+ },
+ {
+ "name": "RESOURCEMANAGER"
+ },
+ {
+ "name": "HISTORYSERVER"
+ },
+ {
+ "name": "NAGIOS_SERVER"
+ },
+ {
+ "name": "ZOOKEEPER_SERVER"
+ }
+ ],
+ "cardinality": "1"
+ },
+ {
+ "name": "slave_1",
+ "components": [
+ {
+ "name": "DATANODE"
+ },
+ {
+ "name": "GANGLIA_MONITOR"
+ },
+ {
+ "name": "HDFS_CLIENT"
+ },
+ {
+ "name": "NODEMANAGER"
+ },
+ {
+ "name": "YARN_CLIENT"
+ },
+ {
+ "name": "MAPREDUCE2_CLIENT"
+ },
+ {
+ "name": "ZOOKEEPER_CLIENT"
+ }
+ ],
+ "cardinality": "2"
+ }
+ ],
+ "Blueprints": {
+ "blueprint_name": "multi-node-hdfs-yarn",
+ "stack_name": "HDP",
+ "stack_version": "2.1"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
index dafdb85..a773c3c 100644
--- a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/commands/ClusterCommands.java
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Map;
import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.groovy.client.InvalidHostGroupHostAssociation;
import org.apache.ambari.shell.completion.Blueprint;
import org.apache.ambari.shell.completion.Host;
import org.apache.ambari.shell.flash.FlashService;
@@ -148,7 +149,7 @@ public class ClusterCommands implements CommandMarker {
* @return prints the auto assignments
*/
@CliCommand(value = "cluster autoAssign", help = "Automatically assigns hosts to different host groups base on the provided strategy")
- public String autoAssign() {
+ public String autoAssign() throws InvalidHostGroupHostAssociation {
Map<String, List<String>> assignments = client.recommendAssignments(context.getFocusValue());
if (!assignments.isEmpty()) {
hostGroups = assignments;
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/flash/InstallProgress.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/flash/InstallProgress.java b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/flash/InstallProgress.java
index 69164ea..dbf8e65 100644
--- a/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/flash/InstallProgress.java
+++ b/ambari-shell/ambari-groovy-shell/src/main/java/org/apache/ambari/shell/flash/InstallProgress.java
@@ -45,7 +45,7 @@ public class InstallProgress extends AbstractFlash {
public String getText() {
StringBuilder sb = new StringBuilder();
if (!done) {
- BigDecimal progress = client.getInstallProgress();
+ BigDecimal progress = client.getRequestProgress();
if (progress != null) {
BigDecimal decimal = progress.setScale(2, BigDecimal.ROUND_HALF_UP);
int intValue = decimal.intValue();
http://git-wip-us.apache.org/repos/asf/ambari/blob/733f0345/ambari-shell/ambari-groovy-shell/src/test/java/org/apache/ambari/shell/commands/ClusterCommandsTest.java
----------------------------------------------------------------------
diff --git a/ambari-shell/ambari-groovy-shell/src/test/java/org/apache/ambari/shell/commands/ClusterCommandsTest.java b/ambari-shell/ambari-groovy-shell/src/test/java/org/apache/ambari/shell/commands/ClusterCommandsTest.java
index 777d05d..ab0608b 100644
--- a/ambari-shell/ambari-groovy-shell/src/test/java/org/apache/ambari/shell/commands/ClusterCommandsTest.java
+++ b/ambari-shell/ambari-groovy-shell/src/test/java/org/apache/ambari/shell/commands/ClusterCommandsTest.java
@@ -35,6 +35,7 @@ import java.util.List;
import java.util.Map;
import org.apache.ambari.groovy.client.AmbariClient;
+import org.apache.ambari.groovy.client.InvalidHostGroupHostAssociation;
import org.apache.ambari.shell.completion.Blueprint;
import org.apache.ambari.shell.completion.Host;
import org.apache.ambari.shell.flash.FlashService;
@@ -250,7 +251,7 @@ public class ClusterCommandsTest {
}
@Test
- public void testAutoAssignForEmptyResult() {
+ public void testAutoAssignForEmptyResult() throws InvalidHostGroupHostAssociation {
Map<String, List<String>> hostGroups = singletonMap("group1", asList("host1"));
ReflectionTestUtils.setField(clusterCommands, "hostGroups", hostGroups);
when(context.getFocusValue()).thenReturn("blueprint");
@@ -263,7 +264,7 @@ public class ClusterCommandsTest {
}
@Test
- public void testAutoAssign() {
+ public void testAutoAssign() throws InvalidHostGroupHostAssociation {
Map<String, List<String>> hostGroups = singletonMap("group1", asList("host1"));
Map<String, List<String>> newAssignments = singletonMap("group1", asList("host1"));
ReflectionTestUtils.setField(clusterCommands, "hostGroups", hostGroups);