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.Arrays; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.NoResultException; import javax.persistence.Query; import org.simpleframework.xml.Attribute; import org.simpleframework.xml.Text; import com.sixsq.slipstream.exceptions.ValidationException; /** * Unit tests: * * @see RuntimeParameterTest * */ @Entity @SuppressWarnings("serial") @NamedQueries({ @NamedQuery(name = "getParameterByInstanceId", query = "SELECT p FROM RuntimeParameter p WHERE p.key_ = 'instanceid' AND p.value = :instanceid"), @NamedQuery(name = "isSet", query = "SELECT p.isSet FROM RuntimeParameter p WHERE p.resourceUri = :resourceuri"), @NamedQuery(name = "getValueAndSet", query = "SELECT p.value, p.isSet FROM RuntimeParameter p WHERE p.resourceUri = :resourceuri"), @NamedQuery(name = "getValueByResourceUri", query = "SELECT p.value FROM RuntimeParameter p WHERE p.resourceUri = :resourceuri") }) public class RuntimeParameter extends Metadata { // Define the constants for properties: // Normal: // <nodename>:<property> // ss:<property> // Multiplicity: // <nodename>.<index>:<property> public final static String NODE_PROPERTY_SEPARATOR = ":"; public final static String NODE_MULTIPLICITY_INDEX_SEPARATOR = "."; public final static String PARAM_WORD_SEPARATOR = "."; public final static String STATE_KEY = "state"; public static final String STATE_DESCRIPTION = "Machine state"; public static final String STATE_MESSAGE_DESCRIPTION = "Machine state message"; public final static String STATE_CUSTOM_KEY = "statecustom"; public static final String STATE_CUSTOM_DESCRIPTION = "Custom state"; public static final String STATE_VM_KEY = "vmstate"; public static final String STATE_VM_DESCRIPTION = "State of the VM, according to the cloud layer"; public final static String ABORT_KEY = "abort"; public final static String ABORT_DESCRIPTION = "Machine abort flag, set when aborting"; public final static String GLOBAL_NAMESPACE = "ss"; public final static String GLOBAL_NAMESPACE_PREFIX = GLOBAL_NAMESPACE + NODE_PROPERTY_SEPARATOR; public final static String GLOBAL_ABORT_KEY = GLOBAL_NAMESPACE_PREFIX + ABORT_KEY; public final static String GLOBAL_ABORT_DESCRIPTION = "Run abort flag, set when aborting"; public final static String GLOBAL_STATE_KEY = GLOBAL_NAMESPACE_PREFIX + STATE_KEY; public final static String GLOBAL_STATE_DESCRIPTION = "Global execution state"; public final static String GLOBAL_CATEGORY_KEY = GLOBAL_NAMESPACE_PREFIX + "category"; public final static String GLOBAL_URL_SERVICE_KEY = GLOBAL_NAMESPACE_PREFIX + "url.service"; public final static String GLOBAL_URL_SERVICE_DESCRIPTION = "Optional service URL for the deployment"; public static final String TAGS_KEY = "tags"; public static final String TAGS_DESCRIPTION = "Tags (comma separated) or annotations for this VM"; public static final String GLOBAL_TAGS_KEY = GLOBAL_NAMESPACE_PREFIX + TAGS_KEY; public static final String GLOBAL_TAGS_DESCRIPTION = "Comma separated tag values"; public static final String NODE_GROUPS_KEY = "groups"; public static final String GLOBAL_NODE_GROUPS_KEY = GLOBAL_NAMESPACE_PREFIX + NODE_GROUPS_KEY; public static final String GLOBAL_NODE_GROUPS_DESCRIPTION = "Comma separated node groups"; public static final String COMPLETE_KEY = "complete"; public static final String COMPLETE_DESCRIPTION = "'true' when current state is completed"; public static final String GLOBAL_COMPLETE_KEY = GLOBAL_NAMESPACE_PREFIX + COMPLETE_KEY; public static final String GLOBAL_COMPLETE_DESCRIPTION = "Global complete flag, set when run completed"; public final static String GLOBAL_RECOVERY_MODE_KEY = GLOBAL_NAMESPACE_PREFIX + "recovery.mode"; public final static String GLOBAL_RECOVERY_MDDE_DESCRIPTION = "Run abort flag, set when aborting"; public final static String IMAGE_ID_PARAMETER_NAME = "image.id"; public final static String IMAGE_ID_PARAMETER_DESCRIPTION = "Cloud image id"; public final static String IMAGE_PLATFORM_PARAMETER_NAME = "image.platform"; public final static String IMAGE_PLATFORM_PARAMETER_DESCRIPTION = "Platform (eg: ubuntu, windows)"; public final static String MULTIPLICITY_PARAMETER_NAME = "multiplicity"; public final static String MULTIPLICITY_PARAMETER_DESCRIPTION = "Multiplicity number"; public static final String MAX_PROVISIONING_FAILURES = "max-provisioning-failures"; public static final String MAX_PROVISIONING_FAILURES_DESCRIPTION = "Max provisioning failures"; public final static String IDS_PARAMETER_NAME = "ids"; public final static String IDS_PARAMETER_DESCRIPTION = "IDs of the machines in a mutable deployment."; public static final String INSTANCE_ID_KEY = SpecialValues.instanceid.name(); public static final String INSTANCE_ID_DESCRIPTION = "Cloud instance id"; public static final String HOSTNAME_KEY = "hostname"; public static final String HOSTNAME_DESCRIPTION = "hostname/ip of the image"; public static final String CLOUD_SERVICE_NAME = "cloudservice"; public static final String CLOUD_SERVICE_DESCRIPTION = "Cloud Service where the node resides"; public static final String URL_SSH_KEY = "url.ssh"; public static final String URL_SSH_DESCRIPTION = "SSH URL to connect to virtual machine"; public static final String URL_SERVICE_KEY = "url.service"; public static final String URL_SERVICE_DESCRIPTION = "Optional service URL for virtual machine"; public static final String IS_ORCHESTRATOR_KEY = "is.orchestrator"; public static final String IS_ORCHESTRATOR_DESCRIPTION = "True if it's an orchestrator"; public static final String MAX_JAAS_WORKERS_KEY = "max.iaas.workers"; public static final String MAX_JAAS_WORKERS_DESCRIPTION = "Max number of concurrently provisioned VMs by orchestrator"; public static final String MAX_JAAS_WORKERS_DEFAULT = "20"; public final static int MULTIPLICITY_NODE_START_INDEX = 1; private final static Pattern KEY_PATTERN = Pattern.compile("^(.*?):(.*)$"); public final static String NODE_NAME_REGEX = "\\w+[\\w\\d]*"; public final static Pattern NODE_NAME_ONLY_PATTERN = Pattern.compile("^(" + NODE_NAME_REGEX + ")*$"); private final static String ORCHESTRATOR_INSTANCE_NAME_REGEX = Run.ORCHESTRATOR_NAME + "(-\\w[-\\w]*)?"; private final static Pattern NODE_NAME_PART_PATTERN = Pattern.compile("(" + NODE_NAME_REGEX + "(\\.\\d+)?)|(" + RuntimeParameter.GLOBAL_NAMESPACE + ")|(" + ORCHESTRATOR_INSTANCE_NAME_REGEX + ")|(" + Run.MACHINE_NAME + ")"); private static final Pattern NAME_PATTERN = Pattern.compile("\\w[\\w\\d\\.-]*"); public static final String NODE_NAME_KEY = "nodename"; public static final String NODE_NAME_DESCRIPTION = "Nodename"; public static final String NODE_ID_KEY = "id"; public static final String NODE_ID_DESCRIPTION = "Node instance id"; public enum ScaleStates { creating, created, operational, removing, removed, gone } public enum SpecialValues { instanceid } public static final String SCALE_STATE_KEY = "scale.state"; public static final String SCALE_STATE_DEFAULT_VALUE = ScaleStates.creating.name(); public static final String SCALE_STATE_DESCRIPTION = "Defined scalability state"; public static final String PRE_SCALE_DONE_KEY = "pre.scale.done"; public static final String PRE_SCALE_DONE_DEFAULT_VALUE = "false"; public static final String PRE_SCALE_DONE_DESCRIPTION = "Node instance sets to 'true' after running pre-scale script"; public static final String SCALE_IAAS_DONE_KEY = "scale.iaas.done"; public static final String SCALE_IAAS_DONE_DEFAULT_VALUE = "false"; public static final String SCALE_IAAS_DONE_DESCRIPTION = "Orchestrator sets to 'true' after scaling the node instance"; public static final String SCALE_DISK_ATTACH_SIZE_KEY = "disk.attach.size"; public static final String SCALE_DISK_ATTACH_SIZE_DEFAULT_VALUE = ""; public static final String SCALE_DISK_ATTACH_SIZE_DESCRIPTION = "Size of the extra disk to attach to the VM during vertical scaling"; public static final String SCALE_DISK_ATTACHED_DEVICE_KEY = "disk.attached.device"; public static final String SCALE_DISK_ATTACHED_DEVICE_DEFAULT_VALUE = ""; public static final String SCALE_DISK_ATTACHED_DEVICE_DESCRIPTION = "Attached device name after the VM's vertical scaling"; public static final String SCALE_DISK_DETACH_DEVICE_KEY = "disk.detach.device"; public static final String SCALE_DISK_DETACH_DEVICE_DEFAULT_VALUE = ""; public static final String SCALE_DISK_DETACH_DEVICE_DESCRIPTION = "Name of the block device to detach from the VM during vertical scaling"; public static final List<String> SPECIAL_PARAMETERS = Arrays.asList(RuntimeParameter.INSTANCE_ID_KEY); public static String extractNodeNamePart(String name) { if (!name.contains(NODE_PROPERTY_SEPARATOR)) { return null; } return name.split(NODE_PROPERTY_SEPARATOR)[0]; } public static String extractParamNamePart(String name) { if (!name.contains(NODE_PROPERTY_SEPARATOR)) { return null; } try { return name.split(NODE_PROPERTY_SEPARATOR)[1]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } public static String constructParamName(String nodeName, String paramname) { String prefix = nodeName + RuntimeParameter.NODE_PROPERTY_SEPARATOR; return prefix + paramname; } public static String constructParamName(String nodeName, int nodeInstanceId, String paramname) { String prefix = constructNodeInstanceName(nodeName, nodeInstanceId) + RuntimeParameter.NODE_PROPERTY_SEPARATOR; return prefix + paramname; } public static String constructNodeInstanceName(String nodeName, int nodeInstanceId) { return nodeName + RuntimeParameter.NODE_MULTIPLICITY_INDEX_SEPARATOR + nodeInstanceId; } public static final int VALUE_MAX_LENGTH = 4096; @Id private String resourceUri; @Attribute(name = "key") private String key_; @Text(required = false, data = true) @Column(length = VALUE_MAX_LENGTH) private String value = ""; @Attribute private boolean isSet = false; @Attribute(required = false, name = "name") private String name_ = null; @Attribute(required = false, name = "group") private String group_ = "Global"; @Attribute(required = false) private boolean mapsOthers; @Attribute(required = false) private ParameterType type = ParameterType.String; @Attribute(required = false) @Column(length = 65536) private String mappedRuntimeParameterNames = ""; @ManyToOne private Run container; /** * Determines whether the given value is a real data value (string) or is a * reference key to a runtime parameter in another node. */ @Attribute boolean isMappedValue = false; @SuppressWarnings("unused") private RuntimeParameter() { } public RuntimeParameter(Run run, String key, String value, String description) throws ValidationException { this.container = run; this.key_ = key; this.value = value; this.description = description; validate(); init(); } public boolean isSet() { return isSet; } public void setIsSet(boolean isSet) { this.isSet = isSet; } public void reset() { isSet = false; value = ""; } public void validate() throws ValidationException { super.validate(); if (container == null) { throwValidationException("runtime parameter container cannot be null"); } if (this.key_ == null) { throwValidationException("runtime parameter key cannot be null or empty"); } Matcher matcher = KEY_PATTERN.matcher(key_); if (!matcher.matches()) { String error = String.format("invalid runtime parameter name: %s. Should match the following regex: %s", key_, KEY_PATTERN); throwValidationException(error); } String nodeNamePart = matcher.group(1); String keyNamePart = matcher.group(2); matcher = NODE_NAME_PART_PATTERN.matcher(nodeNamePart); if (!matcher.matches()) { throwValidationException("invalid node specification: " + nodeNamePart); } matcher = NAME_PATTERN.matcher(keyNamePart); if (!matcher.matches()) { throwValidationException("invalid parameter name specification: " + keyNamePart); } } private boolean isNullOrEmpty(String value) { return (value == null || "".equals(value)); } private void init() { resourceUri = container.getResourceUri() + "/" + key_; setIsSet(!isNullOrEmpty(value)); group_ = RuntimeParameter.extractNodeNamePart(key_); if (GLOBAL_NAMESPACE.equals(group_)) { group_ = "Global"; } setValue(value); } public boolean isMappedValue() { return isMappedValue; } public void setMappedValue(boolean isMappedValue) { this.isMappedValue = isMappedValue; } public Run getContainer() { return container; } public static RuntimeParameter loadFromUuidAndKey(String uuid, String key) { String resourceUri = getResourceUrl(uuid, key); return load(resourceUri); } private static String getResourceUrl(String uuid, String key) { String resourceUri = Run.RESOURCE_URI_PREFIX + uuid + "/" + key; return resourceUri; } public static RuntimeParameter load(String resourceUri) { EntityManager em = PersistenceUtil.createEntityManager(); RuntimeParameter rp = em.find(RuntimeParameter.class, resourceUri); em.close(); return rp; } public String getNodeName() { return key_.split(NODE_PROPERTY_SEPARATOR)[0]; } @Override public String getResourceUri() { return resourceUri; } public String getValue() { return value; } public void setValue(String value) { setIsSet(!isNullOrEmpty(value)); this.value = value; processValue(); } private void processValue() { if (isMapsOthers()) { updateMappedRuntimeParameters(); } } public void setGroup(String group) { this.group_ = group; } public String getGroup() { return group_; } public void setMapsOthers(boolean mapsOthers) { this.mapsOthers = mapsOthers; } public boolean isMapsOthers() { return mapsOthers; } public void setMappedRuntimeParameterNames(String mappedRuntimeParameters) { setMapsOthers(true); this.mappedRuntimeParameterNames = mappedRuntimeParameters; } public void addMappedRuntimeParameterName(String runtimeParameterName) { setMapsOthers(true); this.mappedRuntimeParameterNames += runtimeParameterName + ","; } public String getMappedRuntimeParameterNames() { return mappedRuntimeParameterNames; } private void updateMappedRuntimeParameters() { for (String mappedRuntimaParameterName : getMappedRuntimeParameterNames().split(",")) { RuntimeParameter mappedRuntimeParameter = getContainer().getRuntimeParameters().get( mappedRuntimaParameterName.trim()); mappedRuntimeParameter.setValue(getValue()); } } @SuppressWarnings("unchecked") public static List<RuntimeParameter> listRuntimeParameterByInstanceId(String instanceId) { EntityManager em = PersistenceUtil.createEntityManager(); Query q = em.createNamedQuery("getParameterByInstanceId"); q.setParameter("instanceid", instanceId); List<RuntimeParameter> list = q.getResultList(); em.close(); return list; } public void setType(ParameterType type) { this.type = type; } public ParameterType getType() { return type; } @Column public void setName(String name) { this.name_ = name; } @Column public String getName() { if (this.name_ == null) { this.name_ = extractParamNamePart(this.key_); } return this.name_; } public static boolean isAbort(String runId) { EntityManager em = PersistenceUtil.createEntityManager(); Query q = em.createNamedQuery("isSet"); q.setParameter("resourceuri", "run/" + runId + "/ss:abort"); boolean res = (Boolean) q.getSingleResult(); em.close(); return res; } public static Properties getValueAndSet(String runId, String key) { EntityManager em = PersistenceUtil.createEntityManager(); Query q = em.createNamedQuery("getValueAndSet"); q.setParameter("resourceuri", "run/" + runId + "/" + key); Properties valueAndSet = null; try { Object res = q.getSingleResult(); valueAndSet = new Properties(); Object[] objs = (Object[]) res; String value = (String) objs[0]; boolean isSet = (Boolean) objs[1]; valueAndSet.put("value", value); valueAndSet.put("isSet", isSet); } catch (NoResultException ex) { } em.close(); return valueAndSet; } }