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/07/31 03:28:35 UTC
[2/2] git commit: AMBARI-6686. BE: Provide host-layout validations
via /validations endpoint on stack-version
AMBARI-6686. BE: Provide host-layout validations via /validations endpoint on stack-version
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a7426939
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a7426939
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a7426939
Branch: refs/heads/trunk
Commit: a742693978b49188cf12129356c88ba754e2fe95
Parents: 15b5443
Author: Srimanth Gunturi <sg...@hortonworks.com>
Authored: Wed Jul 30 17:16:06 2014 -0700
Committer: Srimanth Gunturi <sg...@hortonworks.com>
Committed: Wed Jul 30 18:13:59 2014 -0700
----------------------------------------------------------------------
.../RecommendationResourceDefinition.java | 2 +-
.../resources/ResourceInstanceFactoryImpl.java | 4 +
.../resources/ValidationResourceDefinition.java | 43 +++
.../server/api/services/ValidationService.java | 70 +++++
.../stackadvisor/StackAdvisorHelper.java | 208 ++-------------
.../stackadvisor/StackAdvisorRequest.java | 105 ++++++++
.../stackadvisor/StackAdvisorRunner.java | 39 +--
...GetComponentLayoutRecommnedationCommand.java | 63 +++++
.../GetComponentLayoutValidationCommand.java | 113 ++++++++
.../commands/StackAdvisorCommand.java | 263 +++++++++++++++++++
.../commands/StackAdvisorCommandType.java | 44 ++++
.../recommendations/RecommendationRequest.java | 91 -------
.../validations/ValidationResponse.java | 132 ++++++++++
.../ambari/server/controller/AmbariServer.java | 4 +-
.../AbstractControllerResourceProvider.java | 2 +
.../RecommendationResourceProvider.java | 58 +---
.../internal/StackAdvisorResourceProvider.java | 207 +++++++++++++++
.../internal/ValidationResourceProvider.java | 129 +++++++++
.../ambari/server/controller/spi/Resource.java | 2 +
.../src/main/resources/key_properties.json | 5 +
.../src/main/resources/properties.json | 17 ++
.../stacks/HDP/2.0.6/services/stack_advisor.py | 29 +-
.../api/services/ValidationServiceTest.java | 85 ++++++
23 files changed, 1352 insertions(+), 363 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RecommendationResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RecommendationResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RecommendationResourceDefinition.java
index 2c9e1f2..5333b2b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RecommendationResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/RecommendationResourceDefinition.java
@@ -23,7 +23,7 @@ import org.apache.ambari.server.controller.spi.Resource;
/**
* Recommendation resource definition.
*/
-public class RecommendationResourceDefinition extends BaseResourceDefinition {
+public class RecommendationResourceDefinition extends BaseResourceDefinition {
/**
* Constructor.
*/
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index bc2e634..6a71b77 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -235,6 +235,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
resourceDefinition = new RecommendationResourceDefinition();
break;
+ case Validation:
+ resourceDefinition = new ValidationResourceDefinition();
+ break;
+
case HostComponentProcess:
resourceDefinition = new HostComponentProcessResourceDefinition();
break;
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ValidationResourceDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ValidationResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ValidationResourceDefinition.java
new file mode 100644
index 0000000..027ee4b
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ValidationResourceDefinition.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.resources;
+
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * Validation resource definition.
+ */
+public class ValidationResourceDefinition extends BaseResourceDefinition {
+ /**
+ * Constructor.
+ */
+ public ValidationResourceDefinition() {
+ super(Resource.Type.Validation);
+ }
+
+ @Override
+ public String getPluralName() {
+ return "validations";
+ }
+
+ @Override
+ public String getSingularName() {
+ return "validation";
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/services/ValidationService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ValidationService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ValidationService.java
new file mode 100644
index 0000000..87cb6ac
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ValidationService.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * Service responsible for validation of host-layout and configurations.
+ */
+@Path("/stacks/{stackName}/versions/{stackVersion}/validations")
+public class ValidationService extends BaseService {
+
+ /**
+ * Returns validation of host-layout.
+ *
+ * @param body http body
+ * @param headers http headers
+ * @param ui uri info
+ * @param stackName stack name
+ * @param stackVersion stack version
+ * @return validation items if any
+ */
+ @POST
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response getValidation(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+ @PathParam("stackName") String stackName, @PathParam("stackVersion") String stackVersion) {
+
+ return handleRequest(headers, body, ui, Request.Type.POST,
+ createValidationResource(stackName, stackVersion));
+ }
+
+ ResourceInstance createValidationResource(String stackName, String stackVersion) {
+ Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+ mapIds.put(Resource.Type.Stack, stackName);
+ mapIds.put(Resource.Type.StackVersion, stackVersion);
+
+ return createResource(Resource.Type.Validation, mapIds);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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 c0799c9..b390f2f 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
@@ -20,57 +20,24 @@ package org.apache.ambari.server.api.services.stackadvisor;
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-
-import org.apache.ambari.server.api.resources.ResourceInstance;
-import org.apache.ambari.server.api.services.BaseService;
-import org.apache.ambari.server.api.services.LocalUriInfo;
-import org.apache.ambari.server.api.services.RecommendationService;
-import org.apache.ambari.server.api.services.Request;
-import org.apache.ambari.server.api.services.StacksService.StackUriInfo;
-import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner.StackAdvisorCommand;
-import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationRequest;
+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.recommendations.RecommendationResponse;
+import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
import org.apache.ambari.server.configuration.Configuration;
-import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.map.ObjectMapper;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@Singleton
-public class StackAdvisorHelper extends BaseService {
-
- private static Log LOG = LogFactory.getLog(RecommendationService.class);
-
- private static final String GET_HOSTS_INFO_URI = "/api/v1/hosts"
- + "?fields=*&Hosts/host_name.in(%s)";
- private static final String GET_SERVICES_INFO_URI = "/api/v1/stacks/%s/versions/%s"
- + "?fields=Versions/stack_name,Versions/stack_version,Versions/parent_stack_version"
- + ",services/StackServices/service_name,services/StackServices/service_version"
- + ",services/components/StackServiceComponents,services/components/dependencies,services/components/auto_deploy"
- + "&services/StackServices/service_name.in(%s)";
+public class StackAdvisorHelper {
private File recommendationsDir;
private String stackAdvisorScript;
/* Monotonically increasing requestid */
private int requestId = 0;
- private File requestDirectory;
private StackAdvisorRunner saRunner;
@Inject
@@ -81,161 +48,36 @@ public class StackAdvisorHelper extends BaseService {
}
/**
- * Return component-layout recommendation based on hosts and services
- * information.
+ * Return component-layout validation result.
*
- * @param hosts list of hosts
- * @param services list of services
- * @return {@link String} representation of recommendations
- * @throws StackAdvisorException
+ * @param validationRequest the validation request
+ * @return {@link ValidationResponse} instance
+ * @throws StackAdvisorException in case of stack advisor script errors
*/
- public synchronized RecommendationResponse getComponentLayoutRecommnedation(
- RecommendationRequest request) throws StackAdvisorException {
-
- validateRecommendationRequest(request);
- String hostsJSON = getHostsInformation(request);
- String servicesJSON = getServicesInformation(request);
-
- try {
- createRequestDirectory();
-
- FileUtils.writeStringToFile(new File(requestDirectory, "hosts.json"), hostsJSON);
- FileUtils.writeStringToFile(new File(requestDirectory, "services.json"), servicesJSON);
-
- boolean success = saRunner.runScript(stackAdvisorScript,
- StackAdvisorCommand.RECOMMEND_COMPONENT_LAYOUT, requestDirectory);
- if (!success) {
- String message = "Stack advisor script finished with errors";
- LOG.warn(message);
- throw new StackAdvisorException(message);
- }
-
- String result = FileUtils
- .readFileToString(new File(requestDirectory, "component-layout.json"));
+ public synchronized ValidationResponse getComponentLayoutValidation(StackAdvisorRequest request)
+ throws StackAdvisorException {
+ requestId += 1;
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(result, RecommendationResponse.class);
- } catch (Exception e) {
- String message = "Error occured during preparing component-layout recommendations";
- LOG.warn(message, e);
- throw new StackAdvisorException(message, e);
- }
+ GetComponentLayoutValidationCommand command = new GetComponentLayoutValidationCommand(
+ recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ return command.invoke(request);
}
/**
- * Create request id directory for each call
+ * Return component-layout recommendation based on hosts and services
+ * information.
+ *
+ * @param request the recommendation request
+ * @return {@link RecommendationResponse} instance
+ * @throws StackAdvisorException in case of stack advisor script errors
*/
- private void createRequestDirectory() throws IOException {
- if (!recommendationsDir.exists()) {
- if (!recommendationsDir.mkdirs()) {
- throw new IOException("Cannot create " + recommendationsDir);
- }
- }
-
+ public synchronized RecommendationResponse getComponentLayoutRecommnedation(
+ StackAdvisorRequest request) throws StackAdvisorException {
requestId += 1;
- requestDirectory = new File(recommendationsDir, Integer.toString(requestId));
-
- if (requestDirectory.exists()) {
- FileUtils.deleteDirectory(requestDirectory);
- }
- if (!requestDirectory.mkdirs()) {
- throw new IOException("Cannot create " + requestDirectory);
- }
- }
-
- private String getHostsInformation(RecommendationRequest request) throws StackAdvisorException {
- String hostsURI = String.format(GET_HOSTS_INFO_URI, request.getHostsCommaSeparated());
-
- Response response = handleRequest(null, null, new LocalUriInfo(hostsURI), Request.Type.GET,
- createHostResource());
- if (response.getStatus() != Status.OK.getStatusCode()) {
- String message = String.format(
- "Error occured during hosts information retrieving, status=%s, response=%s",
- response.getStatus(), (String) response.getEntity());
- LOG.warn(message);
- throw new StackAdvisorException(message);
- }
-
- String hostsJSON = (String) response.getEntity();
- if (LOG.isDebugEnabled()) {
- LOG.debug("Hosts information: " + hostsJSON);
- }
-
- Collection<String> unregistered = getUnregisteredHosts(hostsJSON, request.getHosts());
- if (unregistered.size() > 0) {
- String message = String.format("There are unregistered hosts in the request, %s",
- Arrays.toString(unregistered.toArray()));
- LOG.warn(message);
- throw new StackAdvisorException(message);
- }
-
- return hostsJSON;
- }
-
- @SuppressWarnings("unchecked")
- private Collection<String> getUnregisteredHosts(String hostsJSON, List<String> hosts)
- throws StackAdvisorException {
- ObjectMapper mapper = new ObjectMapper();
- List<String> registeredHosts = new ArrayList<String>();
-
- try {
- JsonNode root = mapper.readTree(hostsJSON);
- Iterator<JsonNode> iterator = root.get("items").getElements();
- while (iterator.hasNext()) {
- JsonNode next = iterator.next();
- String hostName = next.get("Hosts").get("host_name").getTextValue();
- registeredHosts.add(hostName);
- }
-
- return CollectionUtils.subtract(hosts, registeredHosts);
- } catch (Exception e) {
- throw new StackAdvisorException("Error occured during calculating unregistered hosts", e);
- }
- }
-
- private String getServicesInformation(RecommendationRequest request) throws StackAdvisorException {
- String stackName = request.getStackName();
- String stackVersion = request.getStackVersion();
- String servicesURI = String.format(GET_SERVICES_INFO_URI, stackName, stackVersion,
- request.getServicesCommaSeparated());
-
- Response response = handleRequest(null, null, new StackUriInfo(new LocalUriInfo(servicesURI)),
- Request.Type.GET, createStackVersionResource(stackName, stackVersion));
-
- if (response.getStatus() != Status.OK.getStatusCode()) {
- String message = String.format(
- "Error occured during services information retrieving, status=%s, response=%s",
- response.getStatus(), (String) response.getEntity());
- LOG.warn(message);
- throw new StackAdvisorException(message);
- }
-
- String servicesJSON = (String) response.getEntity();
- if (LOG.isDebugEnabled()) {
- LOG.debug("Services information: " + servicesJSON);
- }
- return servicesJSON;
- }
-
- private ResourceInstance createHostResource() {
- Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
- return createResource(Resource.Type.Host, mapIds);
- }
-
- private ResourceInstance createStackVersionResource(String stackName, String stackVersion) {
- Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
- mapIds.put(Resource.Type.Stack, stackName);
- mapIds.put(Resource.Type.StackVersion, stackVersion);
-
- return createResource(Resource.Type.StackVersion, mapIds);
- }
-
- private void validateRecommendationRequest(RecommendationRequest request)
- throws StackAdvisorException {
- if (request.getHosts().isEmpty() || request.getServices().isEmpty()) {
- throw new StackAdvisorException("Hosts and services must not be empty");
- }
+ GetComponentLayoutRecommnedationCommand command = new GetComponentLayoutRecommnedationCommand(
+ recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ return command.invoke(request);
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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
new file mode 100644
index 0000000..a6896fb
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
@@ -0,0 +1,105 @@
+/**
+ * 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Stack advisor request.
+ */
+public class StackAdvisorRequest {
+
+ private String stackName;
+ private String stackVersion;
+ 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>>();
+
+ public String getStackName() {
+ return stackName;
+ }
+
+ public String getStackVersion() {
+ return stackVersion;
+ }
+
+ public List<String> getHosts() {
+ return hosts;
+ }
+
+ public List<String> getServices() {
+ return services;
+ }
+
+ public Map<String, Set<String>> getComponentHostsMap() {
+ return componentHostsMap;
+ }
+
+ public String getHostsCommaSeparated() {
+ return StringUtils.join(hosts, ",");
+ }
+
+ public String getServicesCommaSeparated() {
+ return StringUtils.join(services, ",");
+ }
+
+ private StackAdvisorRequest(String stackName, String stackVersion) {
+ this.stackName = stackName;
+ this.stackVersion = stackVersion;
+ }
+
+ public static class StackAdvisorRequestBuilder {
+ StackAdvisorRequest instance;
+
+ private StackAdvisorRequestBuilder(String stackName, String stackVersion) {
+ this.instance = new StackAdvisorRequest(stackName, stackVersion);
+ }
+
+ public static StackAdvisorRequestBuilder forStack(String stackName, String stackVersion) {
+ return new StackAdvisorRequestBuilder(stackName, stackVersion);
+ }
+
+ public StackAdvisorRequestBuilder forHosts(List<String> hosts) {
+ this.instance.hosts = hosts;
+ return this;
+ }
+
+ public StackAdvisorRequestBuilder forServices(List<String> services) {
+ this.instance.services = services;
+ return this;
+ }
+
+ public StackAdvisorRequestBuilder withComponentHostsMap(
+ Map<String, Set<String>> componentHostsMap) {
+ this.instance.componentHostsMap = componentHostsMap;
+ return this;
+ }
+
+ public StackAdvisorRequest build() {
+ return this.instance;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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 bb2cc4e..1cc666b 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
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommandType;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,19 +39,19 @@ public class StackAdvisorRunner {
* Runs stack_advisor.py script in the specified {@code actionDirectory}.
*
* @param script stack advisor script
- * @param saCommand {@link StackAdvisorCommand} to run.
+ * @param saCommandType {@link StackAdvisorCommandType} to run.
* @param actionDirectory directory for the action
* @return {@code true} if script completed successfully, {@code false}
* otherwise.
*/
- public boolean runScript(String script, StackAdvisorCommand saCommand, File actionDirectory) {
- LOG.info("Script={}, actionDirectory={}, command={}", script,
- actionDirectory, saCommand);
+ public boolean runScript(String script, StackAdvisorCommandType saCommandType, File actionDirectory) {
+ LOG.info(String.format("Script=%s, actionDirectory=%s, command=%s", script, actionDirectory,
+ saCommandType));
String outputFile = actionDirectory + File.separator + "stackadvisor.out";
String errorFile = actionDirectory + File.separator + "stackadvisor.err";
- ProcessBuilder builder = prepareShellCommand(script, saCommand,
+ ProcessBuilder builder = prepareShellCommand(script, saCommandType,
actionDirectory, outputFile,
errorFile);
@@ -87,14 +88,14 @@ public class StackAdvisorRunner {
* environment variables from the current process.
*
* @param script
- * @param saCommand
+ * @param saCommandType
* @param actionDirectory
* @param outputFile
* @param errorFile
* @return
*/
private ProcessBuilder prepareShellCommand(String script,
- StackAdvisorCommand saCommand,
+ StackAdvisorCommandType saCommandType,
File actionDirectory, String outputFile, String errorFile) {
String hostsFile = actionDirectory + File.separator + "hosts.json";
String servicesFile = actionDirectory + File.separator + "services.json";
@@ -107,7 +108,7 @@ public class StackAdvisorRunner {
// for the 3rd argument, build a single parameter since we use -c
// ProcessBuilder doesn't support output redirection until JDK 1.7
String commandStringParameters[] = new String[] { script,
- saCommand.toString(), hostsFile,
+ saCommandType.toString(), hostsFile,
servicesFile, "1>", outputFile, "2>", errorFile };
StringBuilder commandString = new StringBuilder();
@@ -121,26 +122,4 @@ public class StackAdvisorRunner {
return new ProcessBuilder(builderParameters);
}
-
- public enum StackAdvisorCommand {
- RECOMMEND_COMPONENT_LAYOUT("recommend-component-layout"),
-
- VALIDATE_COMPONENT_LAYOUT("validate-component-layout"),
-
- RECOMMEND_CONFIGURATIONS("recommend-configurations"),
-
- VALIDATE_CONFIGURATIONS("validate-configurations");
-
- private final String name;
-
- private StackAdvisorCommand(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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
new file mode 100644
index 0000000..c6761b9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutRecommnedationCommand.java
@@ -0,0 +1,63 @@
+/**
+ * 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 java.io.File;
+
+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.recommendations.RecommendationResponse;
+
+/**
+ * {@link StackAdvisorCommand} implementation for component-layout
+ * recommendation.
+ */
+public class GetComponentLayoutRecommnedationCommand extends
+ StackAdvisorCommand<RecommendationResponse> {
+
+ public GetComponentLayoutRecommnedationCommand(File recommendationsDir,
+ String stackAdvisorScript, int requestId, StackAdvisorRunner saRunner) {
+ super(recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ }
+
+ @Override
+ protected StackAdvisorCommandType getCommandType() {
+ return StackAdvisorCommandType.RECOMMEND_COMPONENT_LAYOUT;
+ }
+
+ @Override
+ protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
+ if (request.getHosts().isEmpty() || 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/a7426939/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
new file mode 100644
index 0000000..ebe1333
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/GetComponentLayoutValidationCommand.java
@@ -0,0 +1,113 @@
+/**
+ * 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 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.
+ */
+public class GetComponentLayoutValidationCommand extends StackAdvisorCommand<ValidationResponse> {
+
+ public GetComponentLayoutValidationCommand(File recommendationsDir, String stackAdvisorScript,
+ int requestId, StackAdvisorRunner saRunner) {
+ super(recommendationsDir, stackAdvisorScript, requestId, saRunner);
+ }
+
+ @Override
+ protected StackAdvisorCommandType getCommandType() {
+ return StackAdvisorCommandType.VALIDATE_COMPONENT_LAYOUT;
+ }
+
+ @Override
+ protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
+ if (request.getHosts().isEmpty() || request.getServices().isEmpty()
+ || 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/a7426939/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
new file mode 100644
index 0000000..f32e205
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
@@ -0,0 +1,263 @@
+/**
+ * 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 java.io.File;
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.api.services.BaseService;
+import org.apache.ambari.server.api.services.LocalUriInfo;
+import org.apache.ambari.server.api.services.Request;
+import org.apache.ambari.server.api.services.StacksService.StackUriInfo;
+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.controller.spi.Resource;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+
+/**
+ * Parent for all commands.
+ */
+public abstract class StackAdvisorCommand<T> extends BaseService {
+
+ /**
+ * Type of response object provided by extending classes when
+ * {@link #invoke(StackAdvisorRequest)} is called.
+ */
+ private Class<T> type;
+
+ protected static Log LOG = LogFactory.getLog(StackAdvisorCommand.class);
+
+ private static final String GET_HOSTS_INFO_URI = "/api/v1/hosts"
+ + "?fields=*&Hosts/host_name.in(%s)";
+ private static final String GET_SERVICES_INFO_URI = "/api/v1/stacks/%s/versions/%s"
+ + "?fields=Versions/stack_name,Versions/stack_version,Versions/parent_stack_version"
+ + ",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 File recommendationsDir;
+ private String stackAdvisorScript;
+
+ private int requestId;
+ private File requestDirectory;
+ private StackAdvisorRunner saRunner;
+
+ protected ObjectMapper mapper;
+
+ @SuppressWarnings("unchecked")
+ public StackAdvisorCommand(File recommendationsDir, String stackAdvisorScript, int requestId,
+ StackAdvisorRunner saRunner) {
+ this.type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
+ .getActualTypeArguments()[0];
+
+ this.mapper = new ObjectMapper();
+ this.mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
+
+ this.recommendationsDir = recommendationsDir;
+ this.stackAdvisorScript = stackAdvisorScript;
+ this.requestId = requestId;
+ this.saRunner = saRunner;
+ }
+
+ protected abstract StackAdvisorCommandType getCommandType();
+
+ /**
+ * Simple holder for 'hosts.json' and 'services.json' data.
+ */
+ protected class StackAdvisorData {
+ protected String hostsJSON;
+ protected String servicesJSON;
+
+ public StackAdvisorData(String hostsJSON, String servicesJSON) {
+ this.hostsJSON = hostsJSON;
+ this.servicesJSON = servicesJSON;
+ }
+ }
+
+ /**
+ * Name with the result JSON, e.g. "component-layout.json" or
+ * "validations.json" .
+ *
+ * @return the file name
+ */
+ protected abstract String getResultFileName();
+
+ protected abstract void validate(StackAdvisorRequest request) throws StackAdvisorException;
+
+ protected abstract StackAdvisorData adjust(StackAdvisorData data, StackAdvisorRequest request);
+
+ public synchronized T invoke(StackAdvisorRequest request) throws StackAdvisorException {
+ validate(request);
+ String hostsJSON = getHostsInformation(request);
+ String servicesJSON = getServicesInformation(request);
+
+ StackAdvisorData adjusted = adjust(new StackAdvisorData(hostsJSON, servicesJSON), request);
+
+ try {
+ createRequestDirectory();
+
+ FileUtils.writeStringToFile(new File(requestDirectory, "hosts.json"), adjusted.hostsJSON);
+ FileUtils.writeStringToFile(new File(requestDirectory, "services.json"),
+ adjusted.servicesJSON);
+
+ boolean success = saRunner.runScript(stackAdvisorScript, getCommandType(), requestDirectory);
+ if (!success) {
+ String message = "Stack advisor script finished with errors";
+ LOG.warn(message);
+ throw new StackAdvisorException(message);
+ }
+
+ String result = FileUtils.readFileToString(new File(requestDirectory, getResultFileName()));
+
+ return this.mapper.readValue(result, this.type);
+ } catch (Exception e) {
+ String message = "Error occured during stack advisor command invocation";
+ LOG.warn(message, e);
+ throw new StackAdvisorException(message, e);
+ }
+ }
+
+ /**
+ * Create request id directory for each call
+ */
+ private void createRequestDirectory() throws IOException {
+ if (!recommendationsDir.exists()) {
+ if (!recommendationsDir.mkdirs()) {
+ throw new IOException("Cannot create " + recommendationsDir);
+ }
+ }
+
+ requestDirectory = new File(recommendationsDir, Integer.toString(requestId));
+
+ if (requestDirectory.exists()) {
+ FileUtils.deleteDirectory(requestDirectory);
+ }
+ if (!requestDirectory.mkdirs()) {
+ throw new IOException("Cannot create " + requestDirectory);
+ }
+ }
+
+ private 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,
+ createHostResource());
+
+ if (response.getStatus() != Status.OK.getStatusCode()) {
+ String message = String.format(
+ "Error occured during hosts information retrieving, status=%s, response=%s",
+ response.getStatus(), (String) response.getEntity());
+ LOG.warn(message);
+ throw new StackAdvisorException(message);
+ }
+
+ String hostsJSON = (String) response.getEntity();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Hosts information: " + hostsJSON);
+ }
+
+ Collection<String> unregistered = getUnregisteredHosts(hostsJSON, request.getHosts());
+ if (unregistered.size() > 0) {
+ String message = String.format("There are unregistered hosts in the request, %s",
+ Arrays.toString(unregistered.toArray()));
+ LOG.warn(message);
+ throw new StackAdvisorException(message);
+ }
+
+ return hostsJSON;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Collection<String> getUnregisteredHosts(String hostsJSON, List<String> hosts)
+ throws StackAdvisorException {
+ ObjectMapper mapper = new ObjectMapper();
+ List<String> registeredHosts = new ArrayList<String>();
+
+ try {
+ JsonNode root = mapper.readTree(hostsJSON);
+ Iterator<JsonNode> iterator = root.get("items").getElements();
+ while (iterator.hasNext()) {
+ JsonNode next = iterator.next();
+ String hostName = next.get("Hosts").get("host_name").getTextValue();
+ registeredHosts.add(hostName);
+ }
+
+ return CollectionUtils.subtract(hosts, registeredHosts);
+ } catch (Exception e) {
+ throw new StackAdvisorException("Error occured during calculating unregistered hosts", e);
+ }
+ }
+
+ private String getServicesInformation(StackAdvisorRequest request) throws StackAdvisorException {
+ String stackName = request.getStackName();
+ String stackVersion = request.getStackVersion();
+ String servicesURI = String.format(GET_SERVICES_INFO_URI, stackName, stackVersion,
+ request.getServicesCommaSeparated());
+
+ Response response = handleRequest(null, null, new StackUriInfo(new LocalUriInfo(servicesURI)),
+ Request.Type.GET, createStackVersionResource(stackName, stackVersion));
+
+ if (response.getStatus() != Status.OK.getStatusCode()) {
+ String message = String.format(
+ "Error occured during services information retrieving, status=%s, response=%s",
+ response.getStatus(), (String) response.getEntity());
+ LOG.warn(message);
+ throw new StackAdvisorException(message);
+ }
+
+ String servicesJSON = (String) response.getEntity();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Services information: " + servicesJSON);
+ }
+ return servicesJSON;
+ }
+
+ private ResourceInstance createHostResource() {
+ Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+ return createResource(Resource.Type.Host, mapIds);
+ }
+
+ private ResourceInstance createStackVersionResource(String stackName, String stackVersion) {
+ Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+ mapIds.put(Resource.Type.Stack, stackName);
+ mapIds.put(Resource.Type.StackVersion, stackVersion);
+
+ return createResource(Resource.Type.StackVersion, mapIds);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java
new file mode 100644
index 0000000..380b81d
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java
@@ -0,0 +1,44 @@
+/**
+ * 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;
+
+/**
+ * StackAdvisorCommand types enumeration.
+ */
+public enum StackAdvisorCommandType {
+
+ RECOMMEND_COMPONENT_LAYOUT("recommend-component-layout"),
+
+ VALIDATE_COMPONENT_LAYOUT("validate-component-layout"),
+
+ RECOMMEND_CONFIGURATIONS("recommend-configurations"),
+
+ VALIDATE_CONFIGURATIONS("validate-configurations");
+
+ private final String name;
+
+ private StackAdvisorCommandType(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationRequest.java
deleted file mode 100644
index 5ab6b7a..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationRequest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * 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.recommendations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.lang.StringUtils;
-
-/**
- * Recommendations request.
- */
-public class RecommendationRequest {
-
- private String stackName;
- private String stackVersion;
- private List<String> hosts = new ArrayList<String>();
- private List<String> services = new ArrayList<String>();
-
-
- public String getStackName() {
- return stackName;
- }
-
- public String getStackVersion() {
- return stackVersion;
- }
-
- public List<String> getHosts() {
- return hosts;
- }
-
- public List<String> getServices() {
- return services;
- }
-
- public String getHostsCommaSeparated() {
- return StringUtils.join(hosts, ",");
- }
-
- public String getServicesCommaSeparated() {
- return StringUtils.join(services, ",");
- }
-
- private RecommendationRequest(String stackName, String stackVersion) {
- this.stackName = stackName;
- this.stackVersion = stackVersion;
- }
-
- public static class RecommendationRequestBuilder {
- RecommendationRequest instance;
-
- private RecommendationRequestBuilder(String stackName, String stackVersion) {
- this.instance = new RecommendationRequest(stackName, stackVersion);
- }
-
- public static RecommendationRequestBuilder forStack(String stackName, String stackVersion) {
- return new RecommendationRequestBuilder(stackName, stackVersion);
- }
-
- public RecommendationRequestBuilder forHosts(List<String> hosts) {
- this.instance.hosts = hosts;
- return this;
- }
-
- public RecommendationRequestBuilder forServices(List<String> services) {
- this.instance.services = services;
- return this;
- }
-
- public RecommendationRequest build() {
- return this.instance;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/validations/ValidationResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/validations/ValidationResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/validations/ValidationResponse.java
new file mode 100644
index 0000000..145bcd9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/validations/ValidationResponse.java
@@ -0,0 +1,132 @@
+/**
+ * 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.validations;
+
+import java.util.Set;
+
+import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.Version;
+import org.codehaus.jackson.annotate.JsonProperty;
+
+/**
+ * Validation response POJO.
+ */
+public class ValidationResponse {
+
+ @JsonProperty("Versions")
+ private Version version;
+
+ @JsonProperty
+ private Set<ValidationItem> items;
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public Set<ValidationItem> getItems() {
+ return items;
+ }
+
+ public void setItems(Set<ValidationItem> items) {
+ this.items = items;
+ }
+
+ public static class ValidationItem {
+ @JsonProperty
+ private String type;
+
+ @JsonProperty
+ private String level;
+
+ @JsonProperty
+ private String message;
+
+ @JsonProperty("component-name")
+ private String componentName;
+
+ @JsonProperty
+ private String host;
+
+ @JsonProperty("config-type")
+ private String configType;
+
+ @JsonProperty("config-name")
+ private String configName;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getLevel() {
+ return level;
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public void setComponentName(String componentName) {
+ this.componentName = componentName;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getConfigType() {
+ return configType;
+ }
+
+ public void setConfigType(String configType) {
+ this.configType = configType;
+ }
+
+ public String getConfigName() {
+ return configName;
+ }
+
+ public void setConfigName(String configName) {
+ this.configName = configName;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index 77d2a77..906cba4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -50,7 +50,7 @@ import org.apache.ambari.server.controller.internal.BlueprintResourceProvider;
import org.apache.ambari.server.controller.internal.ClusterResourceProvider;
import org.apache.ambari.server.controller.internal.PermissionResourceProvider;
import org.apache.ambari.server.controller.internal.PrivilegeResourceProvider;
-import org.apache.ambari.server.controller.internal.RecommendationResourceProvider;
+import org.apache.ambari.server.controller.internal.StackAdvisorResourceProvider;
import org.apache.ambari.server.controller.internal.StackDefinedPropertyProvider;
import org.apache.ambari.server.controller.internal.StackDependencyResourceProvider;
import org.apache.ambari.server.controller.internal.ViewPermissionResourceProvider;
@@ -532,7 +532,7 @@ public class AmbariServer {
PersistKeyValueService.init(injector.getInstance(PersistKeyValueImpl.class));
KeyService.init(injector.getInstance(PersistKeyValueImpl.class));
BootStrapResource.init(injector.getInstance(BootStrapImpl.class));
- RecommendationResourceProvider.init(injector.getInstance(StackAdvisorHelper.class));
+ StackAdvisorResourceProvider.init(injector.getInstance(StackAdvisorHelper.class));
StageUtils.setGson(injector.getInstance(Gson.class));
WorkflowJsonService.setDBProperties(
injector.getInstance(Configuration.class));
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
index 6c33bdf..034b97c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
@@ -148,6 +148,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractResourc
return new BlueprintResourceProvider(propertyIds, keyPropertyIds, managementController);
case Recommendation:
return new RecommendationResourceProvider(propertyIds, keyPropertyIds, managementController);
+ case Validation:
+ return new ValidationResourceProvider(propertyIds, keyPropertyIds, managementController);
case AlertDefinition:
return new AlertDefinitionResourceProvider(propertyIds, keyPropertyIds, managementController);
case Controller:
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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 d4fd1ce..6b4b1a6 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
@@ -32,9 +32,7 @@ import javax.ws.rs.core.Response.Status;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
-import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
-import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationRequest;
-import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationRequest.RecommendationRequestBuilder;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.BindingHostGroup;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.HostGroup;
@@ -49,23 +47,10 @@ import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
-import com.google.inject.Inject;
-
-public class RecommendationResourceProvider extends ReadOnlyResourceProvider {
-
- private static StackAdvisorHelper saHelper;
-
- @Inject
- public static void init(StackAdvisorHelper instance) {
- saHelper = instance;
- }
+public class RecommendationResourceProvider extends StackAdvisorResourceProvider {
protected static final String RECOMMENDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId(
"Recommendations", "id");
- protected static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Versions",
- "stack_name");
- protected static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId(
- "Versions", "stack_version");
protected static final String HOSTS_PROPERTY_ID = "hosts";
protected static final String SERVICES_PROPERTY_ID = "services";
@@ -94,7 +79,7 @@ public class RecommendationResourceProvider extends ReadOnlyResourceProvider {
@Override
public RequestStatus createResources(final Request request) throws SystemException,
UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
- RecommendationRequest recommendationRequest = prepareRecommendationRequest(request);
+ StackAdvisorRequest recommendationRequest = prepareStackAdvisorRequest(request);
final RecommendationResponse response;
try {
@@ -154,43 +139,6 @@ public class RecommendationResourceProvider extends ReadOnlyResourceProvider {
return new RequestStatusImpl(null, resources);
}
- @SuppressWarnings("unchecked")
- private RecommendationRequest prepareRecommendationRequest(Request request) {
- try {
- String stackName = (String) getRequestProperty(request, STACK_NAME_PROPERTY_ID);
- String stackVersion = (String) getRequestProperty(request, STACK_VERSION_PROPERTY_ID);
-
- /*
- * ClassCastException will occur if hosts or services are empty in the
- * request.
- *
- * @see JsonRequestBodyParser for arrays parsing
- */
- List<String> hosts = (List<String>) getRequestProperty(request, "hosts");
- List<String> services = (List<String>) getRequestProperty(request, "services");
-
- RecommendationRequest recommendationRequest = RecommendationRequestBuilder
- .forStack(stackName, stackVersion).forHosts(hosts).forServices(services).build();
-
- return recommendationRequest;
- } catch (Exception e) {
- LOG.warn("Error occured during preparation of recommendation request", e);
-
- Response response = Response.status(Status.BAD_REQUEST)
- .entity("Hosts and services must not be empty").build();
- throw new WebApplicationException(response);
- }
- }
-
- private Object getRequestProperty(Request request, String propertyName) {
- for (Map<String, Object> propertyMap : request.getProperties()) {
- if (propertyMap.containsKey(propertyName)) {
- return propertyMap.get(propertyName);
- }
- }
- return null;
- }
-
@Override
protected Set<String> getPKPropertyIds() {
return pkPropertyIds;
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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
new file mode 100644
index 0000000..3d0d907
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
@@ -0,0 +1,207 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.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;
+
+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.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource.Type;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+
+import com.google.inject.Inject;
+
+/**
+ * Abstract superclass for recommendations and validations.
+ */
+public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvider {
+
+ protected static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Versions",
+ "stack_name");
+ protected static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId(
+ "Versions", "stack_version");
+
+ private static final String HOST_PROPERTY = "hosts";
+ private static final String SERVICES_PROPERTY = "services";
+
+ private static final String BLUEPRINT_HOST_GROUPS_PROPERTY = "recommendations/blueprint/host_groups";
+ private static final String BINDING_HOST_GROUPS_PROPERTY = "recommendations/blueprint_cluster_binding/host_groups";
+
+ private static final String BLUEPRINT_HOST_GROUPS_NAME_PROPERTY = "name";
+ private static final String BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY = "components";
+ private static final String BLUEPRINT_HOST_GROUPS_COMPONENTS_NAME_PROPERTY = "name";
+
+ private static final String BINDING_HOST_GROUPS_NAME_PROPERTY = "name";
+ private static final String BINDING_HOST_GROUPS_HOSTS_PROPERTY = "hosts";
+ private static final String BINDING_HOST_GROUPS_HOSTS_NAME_PROPERTY = "fqdn";
+
+ protected static StackAdvisorHelper saHelper;
+
+ @Inject
+ public static void init(StackAdvisorHelper instance) {
+ saHelper = instance;
+ }
+
+ protected StackAdvisorResourceProvider(Set<String> propertyIds, Map<Type, String> keyPropertyIds,
+ AmbariManagementController managementController) {
+ super(propertyIds, keyPropertyIds, managementController);
+ }
+
+ @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);
+
+ /*
+ * ClassCastException will occur if hosts or services are empty in the
+ * request.
+ *
+ * @see JsonRequestBodyParser for arrays parsing
+ */
+ List<String> hosts = (List<String>) getRequestProperty(request, HOST_PROPERTY);
+ List<String> services = (List<String>) getRequestProperty(request, SERVICES_PROPERTY);
+ Map<String, Set<String>> componentHostsMap = calculateComponentHostsMap(request);
+
+ StackAdvisorRequest saRequest = StackAdvisorRequestBuilder.forStack(stackName, stackVersion)
+ .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();
+ // TODO: Hosts and services must not be empty
+ throw new WebApplicationException(response);
+ }
+ }
+
+ /**
+ * Will prepare host-group names to components names map from the
+ * recommendation blueprint host groups.
+ *
+ * @param hostGroups the blueprint host groups
+ * @return host-group to components map
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String, Set<String>> calculateHostGroupComponentsMap(
+ Set<Map<String, Object>> hostGroups) {
+ Map<String, Set<String>> map = new HashMap<String, Set<String>>();
+ for (Map<String, Object> hostGroup : hostGroups) {
+ String hostGroupName = (String) hostGroup.get(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY);
+
+ Set<Map<String, Object>> componentsSet = (Set<Map<String, Object>>) hostGroup
+ .get(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY);
+
+ Set<String> components = new HashSet<String>();
+ for (Map<String, Object> component : componentsSet) {
+ components.add((String) component.get(BLUEPRINT_HOST_GROUPS_COMPONENTS_NAME_PROPERTY));
+ }
+
+ map.put(hostGroupName, components);
+ }
+
+ return map;
+ }
+
+ /**
+ * Will prepare host-group names to hosts names map from the recommendation
+ * binding host groups.
+ *
+ * @param bindingHostGroups the binding host groups
+ * @return host-group to hosts map
+ */
+ @SuppressWarnings("unchecked")
+ private Map<String, Set<String>> calculateHostGroupHostsMap(
+ Set<Map<String, Object>> bindingHostGroups) {
+ Map<String, Set<String>> map = new HashMap<String, Set<String>>();
+
+ for (Map<String, Object> hostGroup : bindingHostGroups) {
+ String hostGroupName = (String) hostGroup.get(BINDING_HOST_GROUPS_NAME_PROPERTY);
+
+ Set<Map<String, Object>> hostsSet = (Set<Map<String, Object>>) hostGroup
+ .get(BINDING_HOST_GROUPS_HOSTS_PROPERTY);
+
+ Set<String> hosts = new HashSet<String>();
+ for (Map<String, Object> host : hostsSet) {
+ hosts.add((String) host.get(BINDING_HOST_GROUPS_HOSTS_NAME_PROPERTY));
+ }
+
+ map.put(hostGroupName, hosts);
+ }
+
+ return map;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Set<String>> calculateComponentHostsMap(Request request) {
+ /*
+ * ClassCastException may occur in case of body inconsistency: property
+ * missed, etc.
+ */
+ Set<Map<String, Object>> hostGroups = (Set<Map<String, Object>>) getRequestProperty(request,
+ BLUEPRINT_HOST_GROUPS_PROPERTY);
+ Set<Map<String, Object>> bindingHostGroups = (Set<Map<String, Object>>) getRequestProperty(
+ request, BINDING_HOST_GROUPS_PROPERTY);
+
+ Map<String, Set<String>> componentHostsMap = new HashMap<String, Set<String>>();
+ if (null != bindingHostGroups && null != hostGroups) {
+ Map<String, Set<String>> hgComponentsMap = calculateHostGroupComponentsMap(hostGroups);
+ Map<String, Set<String>> hgHostsMap = calculateHostGroupHostsMap(bindingHostGroups);
+
+ for (Map.Entry<String, Set<String>> hgComponents : hgComponentsMap.entrySet()) {
+ String hgName = hgComponents.getKey();
+ Set<String> components = hgComponents.getValue();
+
+ Set<String> hosts = hgHostsMap.get(hgName);
+ for (String component : components) {
+ Set<String> componentHosts = componentHostsMap.get(component);
+ if (componentHosts == null) { // if was not initialized
+ componentHosts = new HashSet<String>();
+ componentHostsMap.put(component, componentHosts);
+ }
+ componentHosts.addAll(hosts);
+ }
+ }
+ }
+
+ return componentHostsMap;
+ }
+
+ protected Object getRequestProperty(Request request, String propertyName) {
+ for (Map<String, Object> propertyMap : request.getProperties()) {
+ if (propertyMap.containsKey(propertyName)) {
+ return propertyMap.get(propertyName);
+ }
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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
new file mode 100644
index 0000000..2ec4085
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ValidationResourceProvider.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+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;
+
+import org.apache.ambari.server.AmbariException;
+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.validations.ValidationResponse;
+import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse.ValidationItem;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.Resource.Type;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+
+public class ValidationResourceProvider extends StackAdvisorResourceProvider {
+
+ protected static final String VALIDATION_ID_PROPERTY_ID = PropertyHelper.getPropertyId(
+ "Validations", "id");
+
+ protected static final String ITEMS_PROPERTY_ID = "items";
+ protected static final String ITEMS_TYPE_PROPERTY_ID = "type";
+ protected static final String ITEMS_LEVE_PROPERTY_ID = "level";
+ protected static final String ITEMS_MESSAGE_PROPERTY_ID = "message";
+ protected static final String ITEMS_COMPONENT_NAME_PROPERTY_ID = "component-name";
+ protected static final String ITEMS_HOST_PROPERTY_ID = "host";
+ protected static final String ITEMS_CONFIG_TYPE_PROPERTY_ID = "config-type";
+ protected static final String ITEMS_CONFIG_NAME_PROPERTY_ID = "config-name";
+
+ private static Set<String> pkPropertyIds = new HashSet<String>(
+ Arrays.asList(new String[] { VALIDATION_ID_PROPERTY_ID }));
+
+ protected ValidationResourceProvider(Set<String> propertyIds, Map<Type, String> keyPropertyIds,
+ AmbariManagementController managementController) {
+ super(propertyIds, keyPropertyIds, managementController);
+ }
+
+ @Override
+ public RequestStatus createResources(final Request request) throws SystemException,
+ UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException {
+ StackAdvisorRequest validationRequest = prepareStackAdvisorRequest(request);
+
+ final ValidationResponse response;
+ try {
+ response = saHelper.getComponentLayoutValidation(validationRequest);
+ } catch (StackAdvisorException e) {
+ LOG.warn("Error occured during component-layout validation", e);
+ throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(e.getMessage())
+ .build());
+ }
+
+ Resource validation = createResources(new Command<Resource>() {
+ @Override
+ public Resource invoke() throws AmbariException {
+
+ Resource resource = new ResourceImpl(Resource.Type.Validation);
+ setResourceProperty(resource, VALIDATION_ID_PROPERTY_ID, "1", getPropertyIds());
+ setResourceProperty(resource, STACK_NAME_PROPERTY_ID, response.getVersion().getStackName(), getPropertyIds());
+ setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, response.getVersion().getStackVersion(), getPropertyIds());
+
+ List<Map<String, Object>> listItemProps = new ArrayList<Map<String, Object>>();
+
+ Set<ValidationItem> items = response.getItems();
+ for (ValidationItem item : items) {
+ Map<String, Object> mapItemProps = new HashMap<String, Object>();
+ mapItemProps.put(ITEMS_TYPE_PROPERTY_ID, item.getType());
+ mapItemProps.put(ITEMS_LEVE_PROPERTY_ID, item.getLevel());
+ mapItemProps.put(ITEMS_MESSAGE_PROPERTY_ID, item.getMessage());
+
+ if (item.getComponentName() != null) {
+ mapItemProps.put(ITEMS_COMPONENT_NAME_PROPERTY_ID, item.getComponentName());
+ mapItemProps.put(ITEMS_HOST_PROPERTY_ID, item.getHost());
+ }
+ if (item.getConfigType() != null) {
+ mapItemProps.put(ITEMS_CONFIG_TYPE_PROPERTY_ID, item.getConfigType());
+ mapItemProps.put(ITEMS_CONFIG_NAME_PROPERTY_ID, item.getConfigName());
+ }
+ listItemProps.add(mapItemProps);
+ }
+ setResourceProperty(resource, ITEMS_PROPERTY_ID, listItemProps, getPropertyIds());
+
+ return resource;
+ }
+ });
+ notifyCreate(Resource.Type.Validation, request);
+
+ Set<Resource> resources = new HashSet<Resource>(Arrays.asList(validation));
+ return new RequestStatusImpl(null, resources);
+ }
+
+ @Override
+ protected Set<String> getPKPropertyIds() {
+ return pkPropertyIds;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index 41b38fa..4c649df 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -111,6 +111,7 @@ public interface Resource {
ViewInstance,
Blueprint,
Recommendation,
+ Validation,
HostComponentProcess,
Permission,
AlertDefinition,
@@ -190,6 +191,7 @@ public interface Resource {
public static final Type ViewInstance = InternalType.ViewInstance.getType();
public static final Type Blueprint = InternalType.Blueprint.getType();
public static final Type Recommendation = InternalType.Recommendation.getType();
+ public static final Type Validation = InternalType.Validation.getType();
public static final Type HostComponentProcess = InternalType.HostComponentProcess.getType();
public static final Type Permission = InternalType.Permission.getType();
public static final Type AlertDefinition = InternalType.AlertDefinition.getType();
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/ambari-server/src/main/resources/key_properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/key_properties.json b/ambari-server/src/main/resources/key_properties.json
index b693dec..8f6ddc8 100644
--- a/ambari-server/src/main/resources/key_properties.json
+++ b/ambari-server/src/main/resources/key_properties.json
@@ -133,6 +133,11 @@
"Stack": "Versions/stack_name",
"StackVersion": "Versions/stack_version"
},
+ "Validation": {
+ "Validation": "Validation/id",
+ "Stack": "Versions/stack_name",
+ "StackVersion": "Versions/stack_version"
+ },
"HostComponentProcess": {
"Cluster": "HostComponentProcess/cluster_name",
"Host": "HostComponentProcess/host_name",
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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 1716973..6397ecc 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -380,6 +380,23 @@
"recommendations/blueprint_cluster_binding/host_groups/name",
"recommendations/blueprint_cluster_binding/host_groups/hosts"
],
+ "Validation":[
+ "Validation/id",
+ "Versions/stack_name",
+ "Versions/stack_version",
+ "items",
+ "items/type",
+ "items/level",
+ "items/message",
+ "items/component-name",
+ "items/host",
+ "items/config-type",
+ "items/config-name",
+ "items/host-group",
+ "hosts",
+ "services",
+ "recommendations"
+ ],
"HostComponentProcess": [
"HostComponentProcess/cluster_name",
"HostComponentProcess/host_name",
http://git-wip-us.apache.org/repos/asf/ambari/blob/a7426939/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 01a38cf..7cf2e7b 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
@@ -118,7 +118,34 @@ class HDP206StackAdvisor(StackAdvisor):
def validateComponentLayout(self, services, hosts):
"""Returns array of Validation objects about issues with hostnames components assigned to"""
- pass
+ stackName = services["Versions"]["stack_name"]
+ stackVersion = services["Versions"]["stack_version"]
+
+ validations = {
+ "Versions": {"stack_name": stackName, "stack_version": stackVersion},
+ "items": [ ]
+ }
+ items = validations["items"]
+
+ # Validating NAMENODE and SECONDARY_NAMENODE are on different hosts if possible
+ hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
+ servicesList = [service["StackServices"]["service_name"] for service in services["services"]]
+
+ componentsListList = [service["components"] for service in services["services"]]
+ componentsList = [item for sublist in componentsListList for item in sublist]
+ 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:
+ 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 } )
+
+ return validations
+ pass
def recommendConfigurations(self, services, hosts):
"""Returns Services object with configurations object populated"""