You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sr...@apache.org on 2014/08/07 22:10:18 UTC
git commit: AMBARI-6740. Cardinality based recommendation,
partial-recommendation and validation of components needed from
stack-service endpoint
Repository: ambari
Updated Branches:
refs/heads/trunk 9355f8f7c -> 9f74a44a1
AMBARI-6740. Cardinality based recommendation, partial-recommendation and validation of components needed from stack-service endpoint
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9f74a44a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9f74a44a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9f74a44a
Branch: refs/heads/trunk
Commit: 9f74a44a1d27597259912c0c2141e43907536223
Parents: 9355f8f
Author: Srimanth Gunturi <sg...@hortonworks.com>
Authored: Thu Aug 7 12:25:23 2014 -0700
Committer: Srimanth Gunturi <sg...@hortonworks.com>
Committed: Thu Aug 7 12:25:31 2014 -0700
----------------------------------------------------------------------
.../stackadvisor/StackAdvisorHelper.java | 53 ++++-
.../stackadvisor/StackAdvisorRequest.java | 38 ++++
.../stackadvisor/StackAdvisorRunner.java | 2 +-
...GetComponentLayoutRecommnedationCommand.java | 9 +-
.../GetComponentLayoutValidationCommand.java | 60 +-----
.../commands/StackAdvisorCommand.java | 57 +++++-
.../RecommendationResourceProvider.java | 8 +-
.../internal/StackAdvisorResourceProvider.java | 12 +-
.../internal/ValidationResourceProvider.java | 10 +-
.../src/main/resources/properties.json | 2 +
.../stacks/HDP/2.0.6/services/stack_advisor.py | 112 +++++++---
.../stackadvisor/StackAdvisorExceptionTest.java | 46 +++++
.../stackadvisor/StackAdvisorHelperTest.java | 152 ++++++++++++++
.../StackAdvisorRequestTypeTest.java | 56 +++++
.../stackadvisor/StackAdvisorRunnerTest.java | 116 +++++++++++
.../commands/StackAdvisorCommandTest.java | 203 +++++++++++++++++++
16 files changed, 828 insertions(+), 108 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
index b390f2f..213b0f0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
@@ -21,8 +21,10 @@ package org.apache.ambari.server.api.services.stackadvisor;
import java.io.File;
import java.io.IOException;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
import org.apache.ambari.server.api.services.stackadvisor.commands.GetComponentLayoutRecommnedationCommand;
import org.apache.ambari.server.api.services.stackadvisor.commands.GetComponentLayoutValidationCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
import org.apache.ambari.server.configuration.Configuration;
@@ -48,36 +50,67 @@ public class StackAdvisorHelper {
}
/**
- * Return component-layout validation result.
+ * Returns validation (component-layout or configurations) result for the
+ * request.
*
* @param validationRequest the validation request
* @return {@link ValidationResponse} instance
* @throws StackAdvisorException in case of stack advisor script errors
*/
- public synchronized ValidationResponse getComponentLayoutValidation(StackAdvisorRequest request)
+ public synchronized ValidationResponse validate(StackAdvisorRequest request)
throws StackAdvisorException {
requestId += 1;
- GetComponentLayoutValidationCommand command = new GetComponentLayoutValidationCommand(
- recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ StackAdvisorCommand<ValidationResponse> command = createValidationCommand(request
+ .getRequestType());
+
return command.invoke(request);
}
+ StackAdvisorCommand<ValidationResponse> createValidationCommand(
+ StackAdvisorRequestType requestType) throws StackAdvisorException {
+ StackAdvisorCommand<ValidationResponse> command;
+ if (requestType == StackAdvisorRequestType.HOST_GROUPS) {
+ command = new GetComponentLayoutValidationCommand(recommendationsDir, stackAdvisorScript,
+ requestId, saRunner);
+ } else {
+ throw new StackAdvisorException(String.format("Unsupported request type, type=%s",
+ requestType));
+ }
+
+ return command;
+ }
+
/**
- * Return component-layout recommendation based on hosts and services
- * information.
+ * Returns recommendation (component-layout or configurations) based on the
+ * request.
*
* @param request the recommendation request
* @return {@link RecommendationResponse} instance
* @throws StackAdvisorException in case of stack advisor script errors
*/
- public synchronized RecommendationResponse getComponentLayoutRecommnedation(
- StackAdvisorRequest request) throws StackAdvisorException {
+ public synchronized RecommendationResponse recommend(StackAdvisorRequest request)
+ throws StackAdvisorException {
requestId += 1;
- GetComponentLayoutRecommnedationCommand command = new GetComponentLayoutRecommnedationCommand(
- recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ StackAdvisorCommand<RecommendationResponse> command = createRecommendationCommand(request
+ .getRequestType());
+
return command.invoke(request);
}
+ StackAdvisorCommand<RecommendationResponse> createRecommendationCommand(
+ StackAdvisorRequestType requestType) throws StackAdvisorException {
+ StackAdvisorCommand<RecommendationResponse> command;
+ if (requestType == StackAdvisorRequestType.HOST_GROUPS) {
+ command = new GetComponentLayoutRecommnedationCommand(recommendationsDir, stackAdvisorScript,
+ requestId, saRunner);
+ } else {
+ throw new StackAdvisorException(String.format("Unsupported request type, type=%s",
+ requestType));
+ }
+
+ return command;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
index a6896fb..b82047e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
@@ -19,6 +19,7 @@
package org.apache.ambari.server.api.services.stackadvisor;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -33,6 +34,7 @@ public class StackAdvisorRequest {
private String stackName;
private String stackVersion;
+ private StackAdvisorRequestType requestType;
private List<String> hosts = new ArrayList<String>();
private List<String> services = new ArrayList<String>();
private Map<String, Set<String>> componentHostsMap = new HashMap<String, Set<String>>();
@@ -45,6 +47,10 @@ public class StackAdvisorRequest {
return stackVersion;
}
+ public StackAdvisorRequestType getRequestType() {
+ return requestType;
+ }
+
public List<String> getHosts() {
return hosts;
}
@@ -81,6 +87,11 @@ public class StackAdvisorRequest {
return new StackAdvisorRequestBuilder(stackName, stackVersion);
}
+ public StackAdvisorRequestBuilder ofType(StackAdvisorRequestType requestType) {
+ this.instance.requestType = requestType;
+ return this;
+ }
+
public StackAdvisorRequestBuilder forHosts(List<String> hosts) {
this.instance.hosts = hosts;
return this;
@@ -102,4 +113,31 @@ public class StackAdvisorRequest {
}
}
+ public enum StackAdvisorRequestType {
+ HOST_GROUPS("host_groups"), CONFIGURATIONS("configurations");
+
+ private String type;
+
+ private StackAdvisorRequestType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return type;
+ }
+
+ public static StackAdvisorRequestType fromString(String text) throws StackAdvisorException {
+ if (text != null) {
+ for (StackAdvisorRequestType next : StackAdvisorRequestType.values()) {
+ if (text.equalsIgnoreCase(next.type)) {
+ return next;
+ }
+ }
+ }
+ throw new StackAdvisorException(String.format(
+ "Unknown request type: %s, possible values: %s", text,
+ Arrays.toString(StackAdvisorRequestType.values())));
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunner.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunner.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunner.java
index 1cc666b..c57f54e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunner.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunner.java
@@ -94,7 +94,7 @@ public class StackAdvisorRunner {
* @param errorFile
* @return
*/
- private ProcessBuilder prepareShellCommand(String script,
+ ProcessBuilder prepareShellCommand(String script,
StackAdvisorCommandType saCommandType,
File actionDirectory, String outputFile, String errorFile) {
String hostsFile = actionDirectory + File.separator + "hosts.json";
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java
index c6761b9..b3b5057 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java
@@ -44,18 +44,13 @@ public class GetComponentLayoutRecommnedationCommand extends
@Override
protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
- if (request.getHosts().isEmpty() || request.getServices().isEmpty()) {
+ if (request.getHosts() == null || request.getHosts().isEmpty() || request.getServices() == null
+ || request.getServices().isEmpty()) {
throw new StackAdvisorException("Hosts and services must not be empty");
}
}
@Override
- protected StackAdvisorData adjust(StackAdvisorData data, StackAdvisorRequest request) {
- // do nothing
- return data;
- }
-
- @Override
protected String getResultFileName() {
return "component-layout.json";
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java
index ebe1333..ac24d07 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java
@@ -19,21 +19,11 @@
package org.apache.ambari.server.api.services.stackadvisor.commands;
import java.io.File;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner;
import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.node.ArrayNode;
-import org.codehaus.jackson.node.ObjectNode;
/**
* {@link StackAdvisorCommand} implementation for component-layout validation.
@@ -52,59 +42,13 @@ public class GetComponentLayoutValidationCommand extends StackAdvisorCommand<Val
@Override
protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
- if (request.getHosts().isEmpty() || request.getServices().isEmpty()
+ if (request.getHosts() == null || request.getHosts().isEmpty() || request.getServices() == null
+ || request.getServices().isEmpty() || request.getComponentHostsMap() == null
|| request.getComponentHostsMap().isEmpty()) {
throw new StackAdvisorException("Hosts, services and recommendations must not be empty");
}
}
- private static final String SERVICES_PROPETRY = "services";
- private static final String SERVICES_COMPONENTS_PROPETRY = "components";
- private static final String COMPONENT_INFO_PROPETRY = "StackServiceComponents";
- private static final String COMPONENT_NAME_PROPERTY = "component_name";
- private static final String COMPONENT_HOSTNAMES_PROPETRY = "hostnames";
-
- @Override
- protected StackAdvisorData adjust(StackAdvisorData data, StackAdvisorRequest request) {
- // do nothing
- Map<String, Set<String>> componentHostsMap = request.getComponentHostsMap();
-
- try {
- JsonNode root = this.mapper.readTree(data.servicesJSON);
- ArrayNode services = (ArrayNode) root.get(SERVICES_PROPETRY);
- Iterator<JsonNode> servicesIter = services.getElements();
-
- while (servicesIter.hasNext()) {
- JsonNode service = servicesIter.next();
- ArrayNode components = (ArrayNode) service.get(SERVICES_COMPONENTS_PROPETRY);
- Iterator<JsonNode> componentsIter = components.getElements();
-
- while (componentsIter.hasNext()) {
- JsonNode component = componentsIter.next();
- ObjectNode componentInfo = (ObjectNode) component.get(COMPONENT_INFO_PROPETRY);
- String componentName = componentInfo.get(COMPONENT_NAME_PROPERTY).getTextValue();
-
- Set<String> componentHosts = componentHostsMap.get(componentName);
- ArrayNode hostnames = componentInfo.putArray(COMPONENT_HOSTNAMES_PROPETRY);
- if (null != componentHosts) {
- for (String hostName : componentHosts) {
- hostnames.add(hostName);
- }
- }
- }
- }
-
- data.servicesJSON = mapper.writeValueAsString(root);
- } catch (Exception e) {
- // should not happen
- String message = "Error parsing services.json file content: " + e.getMessage();
- LOG.warn(message, e);
- throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(message).build());
- }
-
- return data;
- }
-
@Override
protected String getResultFileName() {
return "component-layout-validation.json";
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
index f32e205..a9ff24b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
@@ -28,7 +28,9 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@@ -48,6 +50,8 @@ import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.node.ArrayNode;
+import org.codehaus.jackson.node.ObjectNode;
/**
* Parent for all commands.
@@ -69,6 +73,11 @@ public abstract class StackAdvisorCommand<T> extends BaseService {
+ ",services/StackServices/service_name,services/StackServices/service_version"
+ ",services/components/StackServiceComponents,services/components/dependencies,services/components/auto_deploy"
+ "&services/StackServices/service_name.in(%s)";
+ private static final String SERVICES_PROPETRY = "services";
+ private static final String SERVICES_COMPONENTS_PROPETRY = "components";
+ private static final String COMPONENT_INFO_PROPETRY = "StackServiceComponents";
+ private static final String COMPONENT_NAME_PROPERTY = "component_name";
+ private static final String COMPONENT_HOSTNAMES_PROPETRY = "hostnames";
private File recommendationsDir;
private String stackAdvisorScript;
@@ -99,7 +108,7 @@ public abstract class StackAdvisorCommand<T> extends BaseService {
/**
* Simple holder for 'hosts.json' and 'services.json' data.
*/
- protected class StackAdvisorData {
+ public static class StackAdvisorData {
protected String hostsJSON;
protected String servicesJSON;
@@ -119,7 +128,47 @@ public abstract class StackAdvisorCommand<T> extends BaseService {
protected abstract void validate(StackAdvisorRequest request) throws StackAdvisorException;
- protected abstract StackAdvisorData adjust(StackAdvisorData data, StackAdvisorRequest request);
+ protected StackAdvisorData adjust(StackAdvisorData data, StackAdvisorRequest request) {
+ try {
+ ObjectNode root = (ObjectNode) this.mapper.readTree(data.servicesJSON);
+
+ populateComponentHostsMap(root, request.getComponentHostsMap());
+
+ data.servicesJSON = mapper.writeValueAsString(root);
+ } catch (Exception e) {
+ // should not happen
+ String message = "Error parsing services.json file content: " + e.getMessage();
+ LOG.warn(message, e);
+ throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(message).build());
+ }
+
+ return data;
+ }
+
+ private void populateComponentHostsMap(ObjectNode root, Map<String, Set<String>> componentHostsMap) {
+ ArrayNode services = (ArrayNode) root.get(SERVICES_PROPETRY);
+ Iterator<JsonNode> servicesIter = services.getElements();
+
+ while (servicesIter.hasNext()) {
+ JsonNode service = servicesIter.next();
+ ArrayNode components = (ArrayNode) service.get(SERVICES_COMPONENTS_PROPETRY);
+ Iterator<JsonNode> componentsIter = components.getElements();
+
+ while (componentsIter.hasNext()) {
+ JsonNode component = componentsIter.next();
+ ObjectNode componentInfo = (ObjectNode) component.get(COMPONENT_INFO_PROPETRY);
+ String componentName = componentInfo.get(COMPONENT_NAME_PROPERTY).getTextValue();
+
+ Set<String> componentHosts = componentHostsMap.get(componentName);
+ ArrayNode hostnames = componentInfo.putArray(COMPONENT_HOSTNAMES_PROPETRY);
+ if (null != componentHosts) {
+ for (String hostName : componentHosts) {
+ hostnames.add(hostName);
+ }
+ }
+ }
+ }
+ }
public synchronized T invoke(StackAdvisorRequest request) throws StackAdvisorException {
validate(request);
@@ -172,7 +221,7 @@ public abstract class StackAdvisorCommand<T> extends BaseService {
}
}
- private String getHostsInformation(StackAdvisorRequest request) throws StackAdvisorException {
+ String getHostsInformation(StackAdvisorRequest request) throws StackAdvisorException {
String hostsURI = String.format(GET_HOSTS_INFO_URI, request.getHostsCommaSeparated());
Response response = handleRequest(null, null, new LocalUriInfo(hostsURI), Request.Type.GET,
@@ -223,7 +272,7 @@ public abstract class StackAdvisorCommand<T> extends BaseService {
}
}
- private String getServicesInformation(StackAdvisorRequest request) throws StackAdvisorException {
+ String getServicesInformation(StackAdvisorRequest request) throws StackAdvisorException {
String stackName = request.getStackName();
String stackVersion = request.getStackVersion();
String servicesURI = String.format(GET_SERVICES_INFO_URI, stackName, stackVersion,
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
index 6b4b1a6..3e5aef4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
@@ -54,6 +54,7 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
protected static final String HOSTS_PROPERTY_ID = "hosts";
protected static final String SERVICES_PROPERTY_ID = "services";
+ protected static final String RECOMMEND_PROPERTY_ID = "recommend";
protected static final String BLUEPRINT_CONFIGURATIONS_PROPERTY_ID = PropertyHelper
.getPropertyId("recommendations/blueprint", "configurations");
@@ -77,13 +78,18 @@ public class RecommendationResourceProvider extends StackAdvisorResourceProvider
}
@Override
+ protected String getRequestTypePropertyId() {
+ return RECOMMEND_PROPERTY_ID;
+ }
+
+ @Override
public RequestStatus createResources(final Request request) throws SystemException,
UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
StackAdvisorRequest recommendationRequest = prepareStackAdvisorRequest(request);
final RecommendationResponse response;
try {
- response = saHelper.getComponentLayoutRecommnedation(recommendationRequest);
+ response = saHelper.recommend(recommendationRequest);
} catch (StackAdvisorException e) {
LOG.warn("Error occured during component-layout recommnedation", e);
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(e.getMessage())
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
index 3d0d907..9cf387a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
@@ -31,6 +31,7 @@ import javax.ws.rs.core.Response.Status;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.Resource.Type;
@@ -74,11 +75,15 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
super(propertyIds, keyPropertyIds, managementController);
}
+ protected abstract String getRequestTypePropertyId();
+
@SuppressWarnings("unchecked")
protected StackAdvisorRequest prepareStackAdvisorRequest(Request request) {
try {
String stackName = (String) getRequestProperty(request, STACK_NAME_PROPERTY_ID);
String stackVersion = (String) getRequestProperty(request, STACK_VERSION_PROPERTY_ID);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType
+ .fromString((String) getRequestProperty(request, getRequestTypePropertyId()));
/*
* ClassCastException will occur if hosts or services are empty in the
@@ -91,13 +96,14 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
Map<String, Set<String>> componentHostsMap = calculateComponentHostsMap(request);
StackAdvisorRequest saRequest = StackAdvisorRequestBuilder.forStack(stackName, stackVersion)
- .forHosts(hosts).forServices(services).withComponentHostsMap(componentHostsMap).build();
+ .ofType(requestType).forHosts(hosts).forServices(services)
+ .withComponentHostsMap(componentHostsMap).build();
return saRequest;
} catch (Exception e) {
LOG.warn("Error occured during preparation of stack advisor request", e);
- Response response = Response.status(Status.BAD_REQUEST).entity("Request body is not correct")
- .build();
+ Response response = Response.status(Status.BAD_REQUEST)
+ .entity(String.format("Request body is not correct, error: %s", e.getMessage())).build();
// TODO: Hosts and services must not be empty
throw new WebApplicationException(response);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java
index 2ec4085..bab1473 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java
@@ -50,6 +50,7 @@ public class ValidationResourceProvider extends StackAdvisorResourceProvider {
protected static final String VALIDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId(
"Validations", "id");
+ protected static final String VALIDATE_PROPERTY_ID = "validate";
protected static final String ITEMS_PROPERTY_ID = "items";
protected static final String ITEMS_TYPE_PROPERTY_ID = "type";
@@ -69,13 +70,18 @@ public class ValidationResourceProvider extends StackAdvisorResourceProvider {
}
@Override
+ protected String getRequestTypePropertyId() {
+ return VALIDATE_PROPERTY_ID;
+ }
+
+ @Override
public RequestStatus createResources(final Request request) throws SystemException,
UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
StackAdvisorRequest validationRequest = prepareStackAdvisorRequest(request);
final ValidationResponse response;
try {
- response = saHelper.getComponentLayoutValidation(validationRequest);
+ response = saHelper.validate(validationRequest);
} catch (StackAdvisorException e) {
LOG.warn("Error occured during component-layout validation", e);
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(e.getMessage())
@@ -102,6 +108,8 @@ public class ValidationResourceProvider extends StackAdvisorResourceProvider {
if (item.getComponentName() != null) {
mapItemProps.put(ITEMS_COMPONENT_NAME_PROPERTY_ID, item.getComponentName());
+ }
+ if (item.getHost() != null) {
mapItemProps.put(ITEMS_HOST_PROPERTY_ID, item.getHost());
}
if (item.getConfigType() != null) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index 7b6150c..b1ef323 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -371,6 +371,7 @@
"Recommendation/id",
"Versions/stack_name",
"Versions/stack_version",
+ "recommend",
"hosts",
"services",
"recommendations",
@@ -397,6 +398,7 @@
"items/config-type",
"items/config-name",
"items/host-group",
+ "validate",
"hosts",
"services",
"recommendations"
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py b/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
index f7470ea..85c3a8e 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
+++ b/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
@@ -18,13 +18,17 @@ limitations under the License.
"""
import socket
+import sys
from stack_advisor import StackAdvisor
class HDP206StackAdvisor(StackAdvisor):
def recommendComponentLayout(self, services, hosts):
- """Returns Services object with hostnames array populated for components"""
+ """
+ Returns Services object with hostnames array populated for components
+ If hostnames are populated for some components (partial blueprint) - these components will not be processed
+ """
stackName = services["Versions"]["stack_name"]
stackVersion = services["Versions"]["stack_version"]
hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
@@ -54,26 +58,31 @@ class HDP206StackAdvisor(StackAdvisor):
}
hostsComponentsMap = {}
+
+ #extend 'hostsComponentsMap' with MASTER components
for service in services["services"]:
masterComponents = [component for component in service["components"] if isMaster(component)]
for component in masterComponents:
componentName = component["StackServiceComponents"]["component_name"]
hostsForComponent = []
- availableHosts = hostsList
- if len(hostsList) > 1 and isNotPreferableOnAmbariServerHost(component):
- availableHosts = [hostName for hostName in hostsList if not isLocalHost(hostName)]
-
- if isMasterWithMultipleInstances(component):
- hostsCount = defaultNoOfMasterHosts(component)
- if hostsCount > 1: # get first 'hostsCount' available hosts
- if len(availableHosts) < hostsCount:
- hostsCount = len(availableHosts)
- hostsForComponent = availableHosts[:hostsCount]
+ if isAlreadyPopulated(component):
+ hostsForComponent = component["StackServiceComponents"]["hostnames"]
+ else:
+ availableHosts = hostsList
+ if len(hostsList) > 1 and isNotPreferableOnAmbariServerHost(component):
+ availableHosts = [hostName for hostName in hostsList if not isLocalHost(hostName)]
+
+ if isMasterWithMultipleInstances(component):
+ hostsCount = defaultNoOfMasterHosts(component)
+ if hostsCount > 1: # get first 'hostsCount' available hosts
+ if len(availableHosts) < hostsCount:
+ hostsCount = len(availableHosts)
+ hostsForComponent = availableHosts[:hostsCount]
+ else:
+ hostsForComponent = [getHostForComponent(component, availableHosts)]
else:
hostsForComponent = [getHostForComponent(component, availableHosts)]
- else:
- hostsForComponent = [getHostForComponent(component, availableHosts)]
#extend 'hostsComponentsMap' with 'hostsForComponent'
for hostName in hostsForComponent:
@@ -82,7 +91,10 @@ class HDP206StackAdvisor(StackAdvisor):
hostsComponentsMap[hostName].append( { "name":componentName } )
#extend 'hostsComponentsMap' with Slave and Client Components
- utilizedHosts = hostsComponentsMap.keys()
+ componentsListList = [service["components"] for service in services["services"]]
+ componentsList = [item for sublist in componentsListList for item in sublist]
+ usedHostsListList = [component["StackServiceComponents"]["hostnames"] for component in componentsList if not isNotValuable(component)]
+ utilizedHosts = [item for sublist in usedHostsListList for item in sublist]
freeHosts = [hostName for hostName in hostsList if hostName not in utilizedHosts]
for service in services["services"]:
@@ -90,12 +102,18 @@ class HDP206StackAdvisor(StackAdvisor):
for component in slaveClientComponents:
componentName = component["StackServiceComponents"]["component_name"]
hostsForComponent = []
- if len(freeHosts) == 0:
- hostsForComponent = hostsList[-1:]
- else: # len(freeHosts) >= 1
- hostsForComponent = freeHosts
- if isClient(component):
- hostsForComponent = freeHosts[0:1]
+
+ if isAlreadyPopulated(component):
+ hostsForComponent = component["StackServiceComponents"]["hostnames"]
+ elif component["StackServiceComponents"]["cardinality"] == "ALL":
+ hostsForComponent = hostsList
+ else:
+ if len(freeHosts) == 0:
+ hostsForComponent = hostsList[-1:]
+ else: # len(freeHosts) >= 1
+ hostsForComponent = freeHosts
+ if isClient(component):
+ hostsForComponent = freeHosts[0:1]
#extend 'hostsComponentsMap' with 'hostsForComponent'
for hostName in hostsForComponent:
@@ -129,6 +147,7 @@ class HDP206StackAdvisor(StackAdvisor):
# Validating NAMENODE and SECONDARY_NAMENODE are on different hosts if possible
hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
+ hostsCount = len(hostsList)
servicesList = [service["StackServices"]["service_name"] for service in services["services"]]
componentsListList = [service["components"] for service in services["services"]]
@@ -136,13 +155,50 @@ class HDP206StackAdvisor(StackAdvisor):
nameNodeHosts = [component["StackServiceComponents"]["hostnames"] for component in componentsList if component["StackServiceComponents"]["component_name"] == "NAMENODE"]
secondaryNameNodeHosts = [component["StackServiceComponents"]["hostnames"] for component in componentsList if component["StackServiceComponents"]["component_name"] == "SECONDARY_NAMENODE"]
- if len(hostsList) > 1 and len(nameNodeHosts) > 0 and len(secondaryNameNodeHosts) > 0:
+ if hostsCount > 1 and len(nameNodeHosts) > 0 and len(secondaryNameNodeHosts) > 0:
nameNodeHosts = nameNodeHosts[0]
secondaryNameNodeHosts = secondaryNameNodeHosts[0]
commonHosts = list(set(nameNodeHosts).intersection(secondaryNameNodeHosts))
for host in commonHosts:
- items.append( { "type": 'host-component', "level": 'ERROR', "message": 'NameNode and Secondary NameNode cannot be hosted on same machine', "component-name": 'NAMENODE', "host": host } )
- items.append( { "type": 'host-component', "level": 'ERROR', "message": 'NameNode and Secondary NameNode cannot be hosted on same machine', "component-name": 'SECONDARY_NAMENODE', "host": host } )
+ items.append( { "type": 'host-component', "level": 'ERROR', "message": 'NameNode and Secondary NameNode cannot be hosted on same machine', "component-name": 'NAMENODE', "host": str(host) } )
+ items.append( { "type": 'host-component', "level": 'ERROR', "message": 'NameNode and Secondary NameNode cannot be hosted on same machine', "component-name": 'SECONDARY_NAMENODE', "host": str(host) } )
+
+ # Validating cardinality
+ for component in componentsList:
+ if component["StackServiceComponents"]["cardinality"] is not None:
+ componentName = component["StackServiceComponents"]["component_name"]
+ componentHostsCount = 0
+ if component["StackServiceComponents"]["hostnames"] is not None:
+ componentHostsCount = len(component["StackServiceComponents"]["hostnames"])
+ cardinality = str(component["StackServiceComponents"]["cardinality"])
+ # cardinality types: null, 1+, 1-2, 1, ALL
+ hostsMax = -sys.maxint - 1
+ hostsMin = sys.maxint
+ hostsMin = 0
+ hostsMax = 0
+ if "+" in cardinality:
+ hostsMin = int(cardinality[:-1])
+ hostsMax = sys.maxint
+ elif "-" in cardinality:
+ nums = cardinality.split("-")
+ hostsMin = int(nums[0])
+ hostsMax = int(nums[1])
+ elif "ALL" == cardinality:
+ hostsMin = hostsCount
+ hostsMax = hostsCount
+ else:
+ hostsMin = int(cardinality)
+ hostsMax = int(cardinality)
+
+ if componentHostsCount > hostsMax or componentHostsCount < hostsMin:
+ items.append( { "type": 'host-component', "level": 'ERROR', "message": 'Cardinality violation, cardinality={0}, hosts count={1}'.format(cardinality, str(componentHostsCount)), "component-name": str(componentName) } )
+
+ # Validating host-usage
+ usedHostsListList = [component["StackServiceComponents"]["hostnames"] for component in componentsList if not isNotValuable(component)]
+ usedHostsList = [item for sublist in usedHostsListList for item in sublist]
+ nonUsedHostsList = [item for item in hostsList if item not in usedHostsList]
+ for host in nonUsedHostsList:
+ items.append( { "type": 'host-component', "level": 'ERROR', "message": 'Host is not used', "host": str(host) } )
return validations
pass
@@ -170,6 +226,16 @@ def getHostForComponent(component, hostsList):
return hostsList[scheme[key]]
return hostsList[scheme['else']]
+def isNotValuable(component):
+ componentName = component["StackServiceComponents"]["component_name"]
+ service = ['JOURNALNODE', 'ZKFC', 'APP_TIMELINE_SERVER', 'GANGLIA_MONITOR']
+ return componentName in service
+
+def isAlreadyPopulated(component):
+ if component["StackServiceComponents"]["hostnames"] is not None:
+ return len(component["StackServiceComponents"]["hostnames"]) > 0
+ return False
+
def isClient(component):
return component["StackServiceComponents"]["component_category"] == 'CLIENT'
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorExceptionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorExceptionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorExceptionTest.java
new file mode 100644
index 0000000..9df5f57
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorExceptionTest.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.stackadvisor;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/**
+ * StackAdvisorException unit tests.
+ */
+public class StackAdvisorExceptionTest {
+
+ @Test
+ public void testCreateFromString() {
+ String message = "message";
+ StackAdvisorException e = new StackAdvisorException(message);
+
+ assertEquals(message, e.getMessage());
+ }
+
+ @Test
+ public void testCreateFromException() {
+ String message = "message";
+ Exception e = new Exception("another message");
+ StackAdvisorException sae = new StackAdvisorException(message, e);
+
+ assertEquals(message, sae.getMessage());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
new file mode 100644
index 0000000..6f8c42b
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.stackadvisor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
+import org.apache.ambari.server.api.services.stackadvisor.commands.GetComponentLayoutRecommnedationCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.GetComponentLayoutValidationCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand;
+import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
+import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
+import org.apache.ambari.server.configuration.Configuration;
+import org.junit.Test;
+
+/**
+ * StackAdvisorHelper unit tests.
+ */
+public class StackAdvisorHelperTest {
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testValidate_returnsCommandResult() throws StackAdvisorException, IOException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = spy(new StackAdvisorHelper(configuration, saRunner));
+
+ StackAdvisorCommand<ValidationResponse> command = mock(StackAdvisorCommand.class);
+ ValidationResponse expected = mock(ValidationResponse.class);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .ofType(requestType).build();
+
+ when(command.invoke(request)).thenReturn(expected);
+ doReturn(command).when(helper).createValidationCommand(requestType);
+ ValidationResponse response = helper.validate(request);
+
+ assertEquals(expected, response);
+ }
+
+ @Test(expected = StackAdvisorException.class)
+ @SuppressWarnings("unchecked")
+ public void testValidate_commandThrowsException_throwsException() throws StackAdvisorException,
+ IOException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = spy(new StackAdvisorHelper(configuration, saRunner));
+
+ StackAdvisorCommand<ValidationResponse> command = mock(StackAdvisorCommand.class);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .ofType(requestType).build();
+
+ when(command.invoke(request)).thenThrow(new StackAdvisorException("message"));
+ doReturn(command).when(helper).createValidationCommand(requestType);
+ helper.validate(request);
+
+ assertTrue(false);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testRecommend_returnsCommandResult() throws StackAdvisorException, IOException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = spy(new StackAdvisorHelper(configuration, saRunner));
+
+ StackAdvisorCommand<RecommendationResponse> command = mock(StackAdvisorCommand.class);
+ RecommendationResponse expected = mock(RecommendationResponse.class);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .ofType(requestType).build();
+
+ when(command.invoke(request)).thenReturn(expected);
+ doReturn(command).when(helper).createRecommendationCommand(requestType);
+ RecommendationResponse response = helper.recommend(request);
+
+ assertEquals(expected, response);
+ }
+
+ @Test(expected = StackAdvisorException.class)
+ @SuppressWarnings("unchecked")
+ public void testRecommend_commandThrowsException_throwsException() throws StackAdvisorException,
+ IOException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = spy(new StackAdvisorHelper(configuration, saRunner));
+
+ StackAdvisorCommand<RecommendationResponse> command = mock(StackAdvisorCommand.class);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .ofType(requestType).build();
+
+ when(command.invoke(request)).thenThrow(new StackAdvisorException("message"));
+ doReturn(command).when(helper).createRecommendationCommand(requestType);
+ helper.recommend(request);
+
+ assertTrue(false);
+ }
+
+ @Test
+ public void testCreateRecommendationCommand_returnsGetComponentLayoutRecommnedationCommand()
+ throws IOException, StackAdvisorException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+
+ StackAdvisorCommand<RecommendationResponse> command = helper
+ .createRecommendationCommand(requestType);
+
+ assertEquals(GetComponentLayoutRecommnedationCommand.class, command.getClass());
+ }
+
+ @Test
+ public void testCreateValidationCommand_returnsGetComponentLayoutValidationCommand()
+ throws IOException, StackAdvisorException {
+ Configuration configuration = mock(Configuration.class);
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner);
+ StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
+
+ StackAdvisorCommand<ValidationResponse> command = helper.createValidationCommand(requestType);
+
+ assertEquals(GetComponentLayoutValidationCommand.class, command.getClass());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java
new file mode 100644
index 0000000..959823e
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.stackadvisor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
+import org.junit.Test;
+
+/**
+ * StackAdvisorRequestTypeTest unit tests.
+ */
+public class StackAdvisorRequestTypeTest {
+
+ @Test
+ public void testFromString_returnsHostGroupType() throws StackAdvisorException {
+ String text = "host_groups";
+ StackAdvisorRequestType type = StackAdvisorRequestType.fromString(text);
+
+ assertEquals(type, StackAdvisorRequestType.HOST_GROUPS);
+ }
+
+ @Test
+ public void testFromString_returnsConfigurationsType() throws StackAdvisorException {
+ String text = "configurations";
+ StackAdvisorRequestType type = StackAdvisorRequestType.fromString(text);
+
+ assertEquals(type, StackAdvisorRequestType.CONFIGURATIONS);
+ }
+
+ @Test(expected = StackAdvisorException.class)
+ public void testFromString_throwsException() throws StackAdvisorException {
+ String text = "unknown_type";
+ StackAdvisorRequestType.fromString(text);
+
+ assertTrue(false);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunnerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunnerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunnerTest.java
new file mode 100644
index 0000000..6afe5c4
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRunnerTest.java
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.stackadvisor;
+
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.powermock.api.easymock.PowerMock.createNiceMock;
+import static org.powermock.api.easymock.PowerMock.replay;
+import static org.powermock.api.support.membermodification.MemberModifier.stub;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommandType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+/**
+ * StackAdvisorRunner unit tests.
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(StackAdvisorRunner.class)
+public class StackAdvisorRunnerTest {
+
+ private TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ temp.create();
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ temp.delete();
+ }
+
+ @Test
+ public void testRunScript_processStartThrowsException_returnFalse() throws IOException {
+ String script = "echo";
+ StackAdvisorCommandType saCommandType = StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
+ File actionDirectory = temp.newFolder("actionDir");
+ ProcessBuilder processBuilder = createNiceMock(ProcessBuilder.class);
+ StackAdvisorRunner saRunner = new StackAdvisorRunner();
+
+ stub(PowerMock.method(StackAdvisorRunner.class, "prepareShellCommand"))
+ .toReturn(processBuilder);
+ expect(processBuilder.start()).andThrow(new IOException());
+ replay(processBuilder);
+ boolean result = saRunner.runScript(script, saCommandType, actionDirectory);
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ public void testRunScript_processExitCodeNonZero_returnFalse() throws IOException,
+ InterruptedException {
+ String script = "echo";
+ StackAdvisorCommandType saCommandType = StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
+ File actionDirectory = temp.newFolder("actionDir");
+ ProcessBuilder processBuilder = createNiceMock(ProcessBuilder.class);
+ Process process = createNiceMock(Process.class);
+ StackAdvisorRunner saRunner = new StackAdvisorRunner();
+
+ stub(PowerMock.method(StackAdvisorRunner.class, "prepareShellCommand"))
+ .toReturn(processBuilder);
+ expect(processBuilder.start()).andReturn(process);
+ expect(process.waitFor()).andReturn(1);
+ replay(processBuilder, process);
+ boolean result = saRunner.runScript(script, saCommandType, actionDirectory);
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ public void testRunScript_processExitCodeZero_returnTrue() throws IOException,
+ InterruptedException {
+ String script = "echo";
+ StackAdvisorCommandType saCommandType = StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
+ File actionDirectory = temp.newFolder("actionDir");
+ ProcessBuilder processBuilder = createNiceMock(ProcessBuilder.class);
+ Process process = createNiceMock(Process.class);
+ StackAdvisorRunner saRunner = new StackAdvisorRunner();
+
+ stub(PowerMock.method(StackAdvisorRunner.class, "prepareShellCommand"))
+ .toReturn(processBuilder);
+ expect(processBuilder.start()).andReturn(process);
+ expect(process.waitFor()).andReturn(0);
+ replay(processBuilder, process);
+ boolean result = saRunner.runScript(script, saCommandType, actionDirectory);
+
+ assertEquals(true, result);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9f74a44a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
new file mode 100644
index 0000000..90dafcb
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
@@ -0,0 +1,203 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.stackadvisor.commands;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.ws.rs.WebApplicationException;
+
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand.StackAdvisorData;
+import org.apache.commons.io.FileUtils;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * StackAdvisorCommand unit tests.
+ */
+public class StackAdvisorCommandTest {
+ private TemporaryFolder temp = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws IOException {
+ temp.create();
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ temp.delete();
+ }
+
+ @Test(expected = StackAdvisorException.class)
+ public void testInvoke_invalidRequest_throwsException() throws StackAdvisorException {
+ File recommendationsDir = temp.newFolder("recommendationDir");
+ String stackAdvisorScript = "echo";
+ int requestId = 0;
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir,
+ stackAdvisorScript, requestId, saRunner));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .build();
+
+ doThrow(new StackAdvisorException("message")).when(command).validate(request);
+ command.invoke(request);
+
+ assertTrue(false);
+ }
+
+ @Test(expected = StackAdvisorException.class)
+ public void testInvoke_saRunnerNotSucceed_throwsException() throws StackAdvisorException {
+ File recommendationsDir = temp.newFolder("recommendationDir");
+ String stackAdvisorScript = "echo";
+ int requestId = 0;
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir,
+ stackAdvisorScript, requestId, saRunner));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .build();
+
+ String hostsJSON = "{\"hosts\" : \"localhost\"";
+ String servicesJSON = "{\"services\" : \"HDFS\"";
+ StackAdvisorData data = new StackAdvisorData(hostsJSON, servicesJSON);
+ doReturn(hostsJSON).when(command).getHostsInformation(request);
+ doReturn(servicesJSON).when(command).getServicesInformation(request);
+ doReturn(data).when(command)
+ .adjust(any(StackAdvisorData.class), any(StackAdvisorRequest.class));
+ when(saRunner.runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class)))
+ .thenReturn(false);
+ command.invoke(request);
+
+ assertTrue(false);
+ }
+
+ @Test(expected = WebApplicationException.class)
+ public void testInvoke_adjustThrowsException_throwsException() throws StackAdvisorException {
+ File recommendationsDir = temp.newFolder("recommendationDir");
+ String stackAdvisorScript = "echo";
+ int requestId = 0;
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(recommendationsDir,
+ stackAdvisorScript, requestId, saRunner));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .build();
+
+ doReturn("{\"hosts\" : \"localhost\"").when(command).getHostsInformation(request);
+ doReturn("{\"services\" : \"HDFS\"").when(command).getServicesInformation(request);
+ doThrow(new WebApplicationException()).when(command).adjust(any(StackAdvisorData.class),
+ any(StackAdvisorRequest.class));
+ when(saRunner.runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class)))
+ .thenReturn(false);
+ command.invoke(request);
+
+ assertTrue(false);
+ }
+
+ @Test
+ public void testInvoke_success() throws StackAdvisorException {
+ String expected = "success";
+ final String testResourceString = String.format("{\"type\": \"%s\"}", expected);
+ final File recommendationsDir = temp.newFolder("recommendationDir");
+ String stackAdvisorScript = "echo";
+ final int requestId = 0;
+ StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+ final StackAdvisorCommand<TestResource> command = spy(new TestStackAdvisorCommand(
+ recommendationsDir, stackAdvisorScript, requestId, saRunner));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+ .build();
+
+ String hostsJSON = "{\"hosts\" : \"localhost\"";
+ String servicesJSON = "{\"services\" : \"HDFS\"";
+ StackAdvisorData data = new StackAdvisorData(hostsJSON, servicesJSON);
+ doReturn(hostsJSON).when(command).getHostsInformation(request);
+ doReturn(servicesJSON).when(command).getServicesInformation(request);
+ doReturn(data).when(command)
+ .adjust(any(StackAdvisorData.class), any(StackAdvisorRequest.class));
+ when(saRunner.runScript(any(String.class), any(StackAdvisorCommandType.class), any(File.class)))
+ .thenAnswer(new Answer<Boolean>() {
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String resultFilePath = String.format("%s/%s", requestId, command.getResultFileName());
+ File resultFile = new File(recommendationsDir, resultFilePath);
+ resultFile.getParentFile().mkdirs();
+ FileUtils.writeStringToFile(resultFile, testResourceString);
+ return true;
+ }
+ });
+ TestResource result = command.invoke(request);
+
+ assertEquals(expected, result.getType());
+ }
+
+ class TestStackAdvisorCommand extends StackAdvisorCommand<TestResource> {
+ public TestStackAdvisorCommand(File recommendationsDir, String stackAdvisorScript,
+ int requestId, StackAdvisorRunner saRunner) {
+ super(recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ }
+
+ @Override
+ protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
+ // do nothing
+ }
+
+ @Override
+ protected String getResultFileName() {
+ return "result.json";
+ }
+
+ @Override
+ protected StackAdvisorCommandType getCommandType() {
+ return StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
+ }
+ }
+
+ public static class TestResource {
+ @JsonProperty
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+ }
+
+}