package com.sixsq.slipstream.factory;
/*
* +=================================================================+
* SlipStream Server (WAR)
* =====
* Copyright (C) 2013 SixSq Sarl (sixsq.com)
* =====
* Licensed 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.
* -=================================================================-
*/
import com.sixsq.slipstream.connector.CloudService;
import com.sixsq.slipstream.connector.Connector;
import com.sixsq.slipstream.connector.ConnectorFactory;
import com.sixsq.slipstream.exceptions.NotFoundException;
import com.sixsq.slipstream.exceptions.SlipStreamClientException;
import com.sixsq.slipstream.exceptions.SlipStreamInternalException;
import com.sixsq.slipstream.exceptions.ValidationException;
import com.sixsq.slipstream.persistence.CloudImageIdentifier;
import com.sixsq.slipstream.persistence.DeploymentModule;
import com.sixsq.slipstream.persistence.ImageModule;
import com.sixsq.slipstream.persistence.Module;
import com.sixsq.slipstream.persistence.ModuleCategory;
import com.sixsq.slipstream.persistence.ModuleParameter;
import com.sixsq.slipstream.persistence.Node;
import com.sixsq.slipstream.persistence.NodeParameter;
import com.sixsq.slipstream.persistence.Parameter;
import com.sixsq.slipstream.persistence.ParameterCategory;
import com.sixsq.slipstream.persistence.Run;
import com.sixsq.slipstream.persistence.RunParameter;
import com.sixsq.slipstream.persistence.RunType;
import com.sixsq.slipstream.persistence.RuntimeParameter;
import com.sixsq.slipstream.persistence.User;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DeploymentFactory extends RunFactory {
@Override
protected RunType getRunType() {
return RunType.Orchestration;
}
@Override
protected void init(Module module, Run run, User user) throws ValidationException, NotFoundException {
initNodesInstancesRuntimeParameters(run);
initNodesRuntimeParameters(run);
}
@Override
protected void validateRun(Run run, User user)
throws SlipStreamClientException {
super.validateRun(run, user);
checkIsDeploymentModule(run);
checkAllImagesHaveReferenceOrImageId(run);
}
private static void checkIsDeploymentModule(Run run) throws ValidationException {
if (!(run.getModule() instanceof DeploymentModule)) {
throw new ValidationException("Only deployment modules can be deployed");
}
}
private static void checkAllImagesHaveReferenceOrImageId(Run run)
throws ValidationException {
DeploymentModule deployment = (DeploymentModule) run.getModule();
for (Node node : deployment.getNodes().values()) {
String cloudServiceName = run.getCloudServiceNameForNode(node.getName());
ImageModule image = node.getImage();
if (image == null) {
throw new ValidationException("Unknown image: " + node.getImageUri());
}
try {
checkImageHasReferenceOrImageId(image, cloudServiceName);
} catch (ValidationException ex) {
throw new ValidationException("Node " + node.getName()
+ " refers to image " + ex.getMessage());
}
}
}
private static void checkImageHasReferenceOrImageId(ImageModule image, String cloudServiceName)
throws ValidationException {
if (!"".equals(image.getCloudImageId(cloudServiceName))) {
return;
}
boolean mustHaveImageId = image.isBase();
if (mustHaveImageId && "".equals(image.getCloudImageId(cloudServiceName))) {
throw new ValidationException(image.getName() + " missing an image id for cloud: " + cloudServiceName);
} else if (image.getModuleReference() == null || "".equals(image.getModuleReference())) {
throw new ValidationException(image.getName() + " missing a machine image reference");
} else {
String referenceUri = image.getModuleReference();
ImageModule reference = (ImageModule) ImageModule.load(referenceUri);
if (reference == null) {
throw new ValidationException(image.getName() + " referring to an unknown image "
+ image.getModuleReference());
}
checkImageHasReferenceOrImageId(reference, cloudServiceName);
}
}
private static void initNodesInstancesRuntimeParameters(Run run) throws ValidationException,
NotFoundException {
List<String> filter = new ArrayList<String>();
for (ParameterCategory c : ParameterCategory.values()) {
filter.add(c.toString());
}
DeploymentModule deployment = (DeploymentModule) run.getModule();
for (Node node : deployment.getNodes().values()) {
for (int i = 1; i <= getNodeMultiplicity(run, node); i++) {
initNodeInstanceRuntimeParameters(run, node, i);
}
run.addGroup(node.getName(), run.getCloudServiceNameForNode(node.getName()));
}
// mapping
for (Node node : deployment.getNodes().values()) {
int multiplicity = getNodeMultiplicity(run, node);
for (NodeParameter param : node.getParameterMappings().values()) {
for (int i = 1; i <= multiplicity; i++) {
if (!param.isStringValue()) {
addParameterMapping(run, param, i);
}
}
}
}
}
public static Run initNodeInstanceRuntimeParameters(Run run, Node node, int nodeInstanceId)
throws ValidationException, NotFoundException {
String cloudServiceName = run.getCloudServiceNameForNode(node.getName());
initNodeInstanceCommonRuntimeParameters(run, node, nodeInstanceId);
run.createRuntimeParameter(node, nodeInstanceId,
RuntimeParameter.NODE_NAME_KEY, node.getName(),
RuntimeParameter.NODE_NAME_DESCRIPTION);
run.createRuntimeParameter(node, nodeInstanceId,
RuntimeParameter.NODE_ID_KEY, String.valueOf(nodeInstanceId),
RuntimeParameter.NODE_ID_DESCRIPTION);
run.createRuntimeParameter(node, nodeInstanceId,
RuntimeParameter.CLOUD_SERVICE_NAME, cloudServiceName,
RuntimeParameter.CLOUD_SERVICE_DESCRIPTION);
ImageModule image = node.getImage();
String imageId = image.extractBaseImageId(cloudServiceName);
run.createRuntimeParameter(node, nodeInstanceId,
RuntimeParameter.IMAGE_ID_PARAMETER_NAME, imageId,
RuntimeParameter.IMAGE_ID_PARAMETER_DESCRIPTION);
String imagePlatform = image.getPlatform();
run.createRuntimeParameter(node, nodeInstanceId,
RuntimeParameter.IMAGE_PLATFORM_PARAMETER_NAME, imagePlatform,
RuntimeParameter.IMAGE_PLATFORM_PARAMETER_DESCRIPTION);
run = initNodeInstanceRuntimeParametersFromImageParameters(run, node, nodeInstanceId);
run.addNodeInstanceName(node, nodeInstanceId);
return run;
}
public static void initNodeInstanceCommonRuntimeParameters(Run run, Node node, int nodeInstanceId)
throws ValidationException {
assignCommonNodeInstanceRuntimeParameters(run, Run.composeNodeInstanceName(node, nodeInstanceId));
}
private static Run initNodeInstanceRuntimeParametersFromImageParameters(Run run, Node node, int nodeInstanceId)
throws ValidationException {
List<String> filter = new ArrayList<String>();
for (ParameterCategory c : ParameterCategory.values()) {
filter.add(c.toString());
}
String cloudService = run.getCloudServiceNameForNode(node.getName());
ImageModule image = node.getImage();
Map<String, ModuleParameter> parameters = new HashMap<>();
Connector connector = null;
try {
connector = ConnectorFactory.getConnector(cloudService);
parameters = connector.getImageParametersTemplate();
} catch (ValidationException e) {
}
parameters.putAll(image.getParameters());
ImageModule parent = image.getParentModule();
if (parent != null) {
findAndAddImagesApplicationParameters(parameters, parent);
}
for (ModuleParameter param : parameters.values()) {
String category = param.getCategory();
if (filter.contains(category) || cloudService.equals(category)) {
String initialValue = extractInitialValue(param, node, run, cloudService);
run.createRuntimeParameter(node, nodeInstanceId,
param.getName(),
initialValue,
param.getDescription(),
param.getType());
}
}
return run;
}
private static void initNodesRuntimeParameters(Run run) throws ValidationException, NotFoundException {
DeploymentModule deployment = (DeploymentModule) run.getModule();
for (Node node : deployment.getNodes().values()) {
String nodeName = node.getName();
int multiplicity = getNodeMultiplicity(run, node);
int multStartIndex = RuntimeParameter.MULTIPLICITY_NODE_START_INDEX;
String nodeRunParameterKeyName = constructParamName(nodeName,
RuntimeParameter.IDS_PARAMETER_NAME);
ArrayList<String> ids = new ArrayList<String>();
int maxIndex = multStartIndex == 0 ? (multiplicity - 1)
: multiplicity;
for (int i = multStartIndex; i <= maxIndex; i++) {
ids.add(String.valueOf(i));
}
run.assignRuntimeParameter(nodeRunParameterKeyName,
StringUtils.join(ids.toArray(), ","),
RuntimeParameter.IDS_PARAMETER_DESCRIPTION);
run.assignRuntimeParameter(
constructParamName(nodeName,
RuntimeParameter.MULTIPLICITY_PARAMETER_NAME),
String.valueOf(multiplicity),
RuntimeParameter.MULTIPLICITY_PARAMETER_DESCRIPTION);
run.assignRuntimeParameter(
constructParamName(nodeName,
RuntimeParameter.MAX_PROVISIONING_FAILURES),
getMaxProvisioningFailures(run, node),
RuntimeParameter.MAX_PROVISIONING_FAILURES_DESCRIPTION);
}
}
public static void addParameterMapping(Run run, NodeParameter param, int i) {
String name = insertMultiplicityIndexInParameterName(param.getValue(), 1);
RuntimeParameter input = run.getRuntimeParameters().get(name);
if (null != input) {
input.setMapsOthers(true);
input.addMappedRuntimeParameterName(insertMultiplicityIndexInParameterName(
param.getContainer().getName()
+ RuntimeParameter.NODE_PROPERTY_SEPARATOR
+ param.getName(), i));
if (input.isSet()) {
input.setValue(input.getValue());
}
run.getRuntimeParameters().put(input.getName(), input);
}
}
public static String insertMultiplicityIndexInParameterName(String name,
int index) {
String[] parts = name.split(RuntimeParameter.NODE_PROPERTY_SEPARATOR);
return parts[0] + RuntimeParameter.NODE_MULTIPLICITY_INDEX_SEPARATOR
+ index + RuntimeParameter.NODE_PROPERTY_SEPARATOR + parts[1];
}
public static String extractInitialValue(ModuleParameter parameter, Node node, Run run, String cloudService)
throws ValidationException {
String parameterName = parameter.getName();
ImageModule image = node.getImage();
String value = run.getParameterValue(constructNodeParamName(node, parameterName), null);
if (value == null) {
value = extractNodeParameterValue(node.getParameter(parameterName));
}
if (value == null && image != null && cloudService.equals(parameter.getCategory())) {
ModuleParameter imageParameter = image.getParameter(parameterName);
if (imageParameter != null) {
value = imageParameter.getValue();
}
}
if (value == null) {
value = parameter.getValue();
}
return value;
}
private static String extractNodeParameterValue(NodeParameter parameter) {
if (parameter == null) {
return null;
}
String value = "";
if (parameter.isStringValue()) {
int length = parameter.getValue().length();
value = parameter.getValue().substring(1, length - 1);
}
return value;
}
public static String constructNodeParamName(Node node, String parameterName) {
return constructParamName(node.getName(), parameterName);
}
public static Map<String, Node> getNodes(Run run) throws ValidationException {
Module module = run.getModule(false);
if (module == null) {
module = loadModule(run);
}
if (module.getCategory() != ModuleCategory.Deployment) {
throw new SlipStreamInternalException(
"getNodes can only be used with a Deployment module");
}
return ((DeploymentModule) module).getNodes();
}
@Override
protected void addUserFormParametersAsRunParameters(Module module, Run run,
Map<String, List<Parameter<?>>> userChoices) throws ValidationException {
List<String> ignoreList = new ArrayList<String>();
ignoreList.add(RuntimeParameter.CLOUD_SERVICE_NAME);
Map<String, List<Parameter<?>>> parametersPerNode = userChoices;
DeploymentModule deployment = (DeploymentModule) module;
for (Map.Entry<String, List<Parameter<?>>> entry : parametersPerNode.entrySet()) {
String nodeName = entry.getKey();
if (!deployment.getNodes().containsKey(nodeName)) {
throw new ValidationException("Unknown node: " + nodeName);
}
Node node = deployment.getNodes().get(nodeName);
for (Parameter<?> parameter : entry.getValue()) {
checkParameterIsValid(node, parameter);
String value = extractNodeParameterValue((NodeParameter)parameter);
insertNewRunParameterForNode(run, node, parameter.getName(), value, "", ignoreList);
}
}
}
private void checkParameterIsValid(Node node, Parameter<?> parameter) throws ValidationException {
List<String> paramsToFilter = new ArrayList<String>();
paramsToFilter.add(RuntimeParameter.MULTIPLICITY_PARAMETER_NAME);
paramsToFilter.add(RuntimeParameter.CLOUD_SERVICE_NAME);
paramsToFilter.add(RuntimeParameter.MAX_PROVISIONING_FAILURES);
String paramName = parameter.getName();
if (!node.getParameters().containsKey(paramName) &&
!node.getImage().getParameters().containsKey(paramName) &&
!paramsToFilter.contains(paramName)) {
throw new ValidationException("Unknown parameter: " + parameter.getName() + " in node: "
+ node.getName());
}
}
@Override
protected Map<String, String> resolveCloudServiceNames(Module module, User user,
Map<String, List<Parameter<?>>> userChoices) {
Map<String, String> cloudServiceNamesPerNode = new HashMap<String, String>();
DeploymentModule deployment = castToRequiredModuleType(module);
for (Node node: deployment.getNodes().values()) {
String nodeName = node.getName();
List<Parameter<?>> userChoicesForNode = userChoices.get(nodeName);
String cloudServiceName = resolveCloudServiceNameForNode(user, userChoicesForNode, node);
cloudServiceNamesPerNode.put(nodeName, cloudServiceName);
}
return cloudServiceNamesPerNode;
}
@Override
protected DeploymentModule castToRequiredModuleType(Module module) {
return (DeploymentModule) module;
}
private String resolveCloudServiceNameForNode(User user, List<Parameter<?>> userChoicesForNode, Node node) {
String cloudService = null;
if (userChoicesForNode != null) {
for (Parameter<?> parameter : userChoicesForNode) {
if (parameter.getName().equals(RuntimeParameter.CLOUD_SERVICE_NAME)) {
cloudService = extractNodeParameterValue((NodeParameter) parameter);
break;
}
}
}
if (cloudService == null) {
cloudService = node.getCloudService();
}
if (CloudService.isDefaultCloudService(cloudService)) {
cloudService = user.getDefaultCloudService();
}
return cloudService;
}
@Override
protected void initExtraRunParameters(Module module, Run run) throws ValidationException {
DeploymentModule deployment = (DeploymentModule) run.getModule();
for (Node node : deployment.getNodes().values()) {
int multiplicity = node.getMultiplicity();
insertNewRunParameterForNode(run, node,
RuntimeParameter.MULTIPLICITY_PARAMETER_NAME, String.valueOf(multiplicity),
RuntimeParameter.MULTIPLICITY_PARAMETER_DESCRIPTION);
insertNewRunParameterForNode(run, node,
RuntimeParameter.MAX_PROVISIONING_FAILURES, String.valueOf(node.getMaxProvisioningFailures()),
RuntimeParameter.MAX_PROVISIONING_FAILURES_DESCRIPTION);
insertNewRunParameterForNode(run, node,
RunParameter.NODE_INCREMENT_KEY, String.valueOf(multiplicity + 1),
RunParameter.NODE_INCREMENT_DESCRIPTION);
String cloudService = run.getParameterValue(constructParamName(
node.getName(),
RuntimeParameter.CLOUD_SERVICE_NAME), CloudImageIdentifier.DEFAULT_CLOUD_SERVICE);
boolean runBuildRecipes = node.getImage().hasToRunBuildRecipes(cloudService);
insertNewRunParameterForNode(run, node,
RunParameter.NODE_RUN_BUILD_RECIPES_KEY, String.valueOf(runBuildRecipes),
RunParameter.NODE_RUN_BUILD_RECIPES_DESCRIPTION);
}
}
@Override
protected void updateExtraRunParameters(Module module, Run run, Map<String, List<Parameter<?>>> userChoices)
throws ValidationException {
DeploymentModule deployment = (DeploymentModule) run.getModule();
for (Node node : deployment.getNodes().values()) {
List<Parameter<?>> params = userChoices.get(node.getName());
if (params != null) {
for (Parameter<?> parameter : params) {
if (parameter.getName().equals(RuntimeParameter.MULTIPLICITY_PARAMETER_NAME)){
String key = constructNodeParamName(node, RuntimeParameter.MULTIPLICITY_PARAMETER_NAME);
String multiplicity = extractNodeParameterValue((NodeParameter)parameter);
run.getParameter(key).setValue(multiplicity);
key = constructNodeParamName(node, RunParameter.NODE_INCREMENT_KEY);
String increment = String.valueOf(Integer.parseInt(multiplicity) + 1);
run.getParameter(key).setValue(increment);
} else if (parameter.getName().equals(RuntimeParameter.MAX_PROVISIONING_FAILURES)) {
String key = constructNodeParamName(node, RuntimeParameter.MAX_PROVISIONING_FAILURES);
String value = extractNodeParameterValue((NodeParameter)parameter);
run.getParameter(key).setValue(value);
}
}
}
}
}
protected void validateModuleForRun(Module module) throws ValidationException {
super.validateModuleForRun(module);
// Deployment module that is ready for run should define nodes on itself.
DeploymentModule deploymentModule = castToRequiredModuleType(module);
if (deploymentModule.getNodes().isEmpty()) {
throw new ValidationException("Deployment module provided for run is missing nodes.");
}
}
private static int getNodeMultiplicity(Run run, Node node) throws NotFoundException {
String key = constructNodeParamName(node, RuntimeParameter.MULTIPLICITY_PARAMETER_NAME);
return Integer.parseInt(run.getParameterValue(key, null));
}
private static String getMaxProvisioningFailures(Run run, Node node) throws NotFoundException {
String key = constructNodeParamName(node, RuntimeParameter.MAX_PROVISIONING_FAILURES);
return run.getParameterValue(key, null);
}
private static void insertNewRunParameterForNode(Run run, Node node, String name, String value, String description)
throws ValidationException {
insertNewRunParameterForNode(run, node, name, value, description, new ArrayList<String>());
}
private static void insertNewRunParameterForNode(Run run, Node node, String name, String value, String description,
List<String> ignore) throws ValidationException {
if (ignore.contains(name)) {
return;
}
String key = constructNodeParamName(node, name);
RunParameter rp = new RunParameter(key, value, description);
run.setParameter(rp);
}
}