package com.sixsq.slipstream.persistence;
/*
* +=================================================================+
* 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.Transient;
import org.hibernate.annotations.CollectionType;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementArray;
import org.simpleframework.xml.ElementMap;
import com.sixsq.slipstream.exceptions.ValidationException;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@SuppressWarnings("serial")
public class Node extends Parameterized<Node, NodeParameter> {
private static final String NETWORK_KEY = ImageModule.NETWORK_KEY;
@Id
@GeneratedValue
private Long id;
@Attribute
private String name;
@Attribute
private int multiplicity = RuntimeParameter.MULTIPLICITY_NODE_START_INDEX;
@Attribute(empty = "0")
private int maxProvisioningFailures = 0;
@Attribute
private String cloudService = CloudImageIdentifier.DEFAULT_CLOUD_SERVICE;
public String getCloudService() {
return cloudService;
}
public void setCloudService(String cloudService) {
this.cloudService = cloudService;
}
@Attribute(required = false)
private String imageUri;
public String getImageUri() {
return imageUri;
}
public void setImageUri(String imageUri) {
this.imageUri = imageUri;
}
@Transient
private ImageModule image;
/**
* Holds the <parameter-name> and the corresponding
* <node-name>.<index>:<parameter-name>.
*/
@ElementMap(required = false)
@MapKey(name = "name")
@OneToMany(mappedBy = "container", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@CollectionType(type = "com.sixsq.slipstream.persistence.ConcurrentHashMapType")
private Map<String, NodeParameter> parameterMappings = new ConcurrentHashMap<String, NodeParameter>();
@ManyToOne
private DeploymentModule module;
protected Node() {
}
public Node(String name, String imageUri) throws ValidationException {
this.name = name;
this.imageUri = imageUri;
}
public Node(String name, ImageModule image) throws ValidationException {
this(name, image.getResourceUri());
this.image = image;
}
@Override
@ElementMap(name = "parameters", required = false, valueType = NodeParameter.class)
protected void setParameters(Map<String, NodeParameter> parameters) {
this.parameters = parameters;
}
@Override
@ElementMap(name = "parameters", required = false, valueType = NodeParameter.class)
public Map<String, NodeParameter> getParameters() {
return parameters;
}
public DeploymentModule getModule() {
return module;
}
public void setModule(DeploymentModule module) {
this.module = module;
}
public void validate() throws ValidationException {
super.validate();
Matcher matcher = RuntimeParameter.NODE_NAME_ONLY_PATTERN.matcher(name);
if (!matcher.matches()) {
throwValidationException("invalid node name: " + name);
}
image.validate();
}
public Long getId() {
return id;
}
public int getMultiplicity() {
return multiplicity;
}
public void setMultiplicity(String multiplicity) throws ValidationException {
int parsedMultiplicity;
try {
parsedMultiplicity = Integer.parseInt(multiplicity);
} catch (NumberFormatException ex) {
throw (new ValidationException("Invalid multiplicity value"));
}
setMultiplicity(parsedMultiplicity);
}
public void setMultiplicity(int multiplicity) throws ValidationException {
if (multiplicity < 0) {
throw (new ValidationException("Invalid multiplicity, it must be positive"));
}
this.multiplicity = multiplicity;
}
public void setMaxProvisioningFailures(int value) {
this.maxProvisioningFailures = value;
}
public int getMaxProvisioningFailures() {
return this.maxProvisioningFailures;
}
@Attribute(required = false)
public void setNetwork(String network) {
}
@Attribute(required = false)
public String getNetwork() throws ValidationException {
return extractParameterWithOverride(NETWORK_KEY);
}
/**
* Look for a value in the local parameter list, otherwise return the value
* from the image parameter list
*/
private String extractParameterWithOverride(String key) throws ValidationException {
ImageModule image = getImage();
if (image != null) {
return getParameterValue(key, image.getParameterValue(key, null));
} else {
// The image is missing, but this will be picked-up when running
return null;
}
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
public Map<String, NodeParameter> getParameterMappings() {
return parameterMappings;
}
/**
* Assembled notes. Includes notes from inherited images.
*/
@Transient
@ElementArray(required = false, entry = "note")
public String[] getNotes() {
List<String> notes = new ArrayList<String>();
ImageModule image = getImage();
if (image != null) {
notes.addAll(Arrays.asList(image.getNotes()));
}
return notes.toArray(new String[0]);
}
@Transient
@ElementArray(required = false, entry = "note")
private void setNotes(String[] notes) {
}
public void setParameterMappings(Map<String, NodeParameter> parameterMappings) {
this.parameterMappings = parameterMappings;
}
public void setParameterMapping(NodeParameter nodeParameter, DeploymentModule deployment)
throws ValidationException {
validateMapping(nodeParameter, deployment);
getParameterMappings().put(nodeParameter.getName(), nodeParameter);
}
private void validateMapping(NodeParameter nodeParameter, DeploymentModule deployment) throws ValidationException {
ModuleParameter inputParameter = image.getParameter(nodeParameter.getName());
if (!ParameterCategory.Input.name().equals(inputParameter.getCategory())) {
throw new ValidationException("Input parameter " + nodeParameter.getName() + " not Input category");
}
if (nodeParameter.isStringValue()) {
return;
}
ModuleParameter outputParameter = extractModuleParameterFromNodeString(nodeParameter.getValue(), deployment);
if (!ParameterCategory.Output.name().equals(outputParameter.getCategory())) {
throw new ValidationException("Output parameter " + outputParameter.getName() + " not Output category");
}
}
private ModuleParameter extractModuleParameterFromNodeString(String fullyQualifiedParameterName,
DeploymentModule deployment) {
String[] parts = fullyQualifiedParameterName.split(":");
String nodeName = parts[0];
String paramName = parts[1];
return deployment.getNodes().get(nodeName).getImage().getParameter(paramName);
}
@Element(required = false)
public ImageModule getImage() {
if (image == null) {
image = (ImageModule) ImageModule.load(imageUri);
}
return image;
}
@Element(required = false)
public void setImage(ImageModule image) {
this.image = image;
}
@Override
public String getResourceUri() {
return null;
}
public void setParameterMapping(NodeParameter parameter) {
parameter.setContainer(this);
this.getParameterMappings().put(parameter.getName(), parameter);
}
@Override
public void setContainer(NodeParameter parameter) {
parameter.setContainer(this);
}
public Node copy() throws ValidationException {
Node copy = new Node(getName(), getImageUri());
copy = (Node) copyTo(copy);
copy.setCloudService(getCloudService());
copy.setMultiplicity(getMultiplicity());
copy.setNetwork(getNetwork());
return copy;
}
@Override
public Node store() {
return (Node) super.store();
}
}