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 com.sixsq.slipstream.credentials.Credentials; import com.sixsq.slipstream.event.Event; import com.sixsq.slipstream.event.Event.EventType; import com.sixsq.slipstream.exceptions.AbortException; import com.sixsq.slipstream.exceptions.ConfigurationException; import com.sixsq.slipstream.exceptions.NotFoundException; import com.sixsq.slipstream.exceptions.ValidationException; import com.sixsq.slipstream.run.RunView; import com.sixsq.slipstream.run.RunsQueryParameters; import com.sixsq.slipstream.statemachine.States; import org.apache.commons.lang.StringUtils; 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 javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.MapKey; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Query; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import java.util.regex.Pattern; import com.sixsq.slipstream.metrics.Metrics; import com.sixsq.slipstream.metrics.MetricsTimer; @SuppressWarnings("serial") @Entity(name="Run") @NamedQueries({ @NamedQuery(name = "allRuns", query = "SELECT r FROM Run r ORDER BY r.startTime DESC"), @NamedQuery(name = "runWithRuntimeParameters", query = "SELECT r FROM Run r JOIN FETCH r.runtimeParameters p WHERE r.uuid = :uuid"), @NamedQuery(name = "oldInStatesRuns", query = "SELECT r FROM Run r WHERE r.user_ = :user AND r.lastStateChangeTime < :before AND r.state IN (:states)"), @NamedQuery(name = "runByInstanceId", query = "SELECT r FROM Run r JOIN FETCH r.runtimeParameters p WHERE r.user_ = :user AND p.name_ = :instanceidkey AND p.value = :instanceidvalue ORDER BY r.startTime DESC") }) public class Run extends Parameterized<Run, RunParameter> { private static final int DEFAULT_TIMEOUT = 60; // In minutes public static final int DEFAULT_LIMIT = 20; public static final String ORCHESTRATOR_CLOUD_SERVICE_SEPARATOR = "-"; public static final String NODE_NAME_PARAMETER_SEPARATOR = "--"; // Orchestrator public final static String ORCHESTRATOR_NAME = "orchestrator"; public static final String SERVICENAME_NODENAME_SEPARATOR = RuntimeParameter.NODE_PROPERTY_SEPARATOR; // Default machine name for image and disk creation public final static String MACHINE_NAME = "machine"; public final static String MACHINE_NAME_PREFIX = MACHINE_NAME + RuntimeParameter.NODE_PROPERTY_SEPARATOR; // The initial state of each node public final static String INITIAL_NODE_STATE_MESSAGE = States.Initializing .toString(); public final static String INITIAL_NODE_STATE = States.Initializing.toString(); public final static String RESOURCE_URI_PREFIX = "run/"; public final static String TAGS_PARAMETER_DESCRIPTION = "Tags (comma separated) or annotations for this run"; public final static String CPU_PARAMETER_NAME = ImageModule.CPU_KEY; public final static String CPU_PARAMETER_DESCRIPTION = "Number of CPUs (i.e. virtual cores)"; public final static String RAM_PARAMETER_NAME = ImageModule.RAM_KEY; public final static String RAM_PARAMETER_DESCRIPTION = "Amount of RAM, in GB"; public final static String GARBAGE_COLLECTED_PARAMETER_NAME = "garbage_collected"; public final static String GARBAGE_COLLECTED_PARAMETER_DESCRIPTION = "true if the Run was already garbage collected"; public static Run abortOrReset(String abortMessage, String nodename, String uuid) { EntityManager em = PersistenceUtil.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); Run run = Run.abortOrReset(abortMessage, nodename, em, uuid); transaction.commit(); em.close(); return run; } public static Run abortOrReset(String abortMessage, String nodename, EntityManager em, String uuid) { Run run = Run.loadFromUuid(uuid, em); RuntimeParameter globalAbort = getGlobalAbort(run); String nodeAbortKey = getNodeAbortKey(nodename); RuntimeParameter nodeAbort = run.getRuntimeParameters().get(nodeAbortKey); if (abortMessage == null || "".equals(abortMessage)) { run.postEventAbortReset(nodename); globalAbort.reset(); if (nodeAbort != null) { nodeAbort.reset(); } resetRecoveryMode(run); } else { run.postEventAbort(nodename, abortMessage); if (nodeAbort != null) { nodeAbort.setValue(abortMessage); } if (!globalAbort.isSet()) { setGlobalAbortState(abortMessage, globalAbort); if (run.state == States.Provisioning) { setRecoveryMode(run); } } } return run; } private static void setGlobalAbortState(String abortMessage, RuntimeParameter globalAbort) { globalAbort.setValue(abortMessage); globalAbort.store(); Run run = globalAbort.getContainer(); run.setState(run.getState()); } public static Run abort(String abortMessage, String uuid) { EntityManager em = PersistenceUtil.createEntityManager(); Run run = Run.loadFromUuid(uuid, em); RuntimeParameter globalAbort = getGlobalAbort(run); if (!globalAbort.isSet()) { setGlobalAbortState(abortMessage, globalAbort); } if (run.state == States.Provisioning) { setRecoveryMode(run); } em.close(); return run; } private static RuntimeParameter getGlobalAbort(Run run) { RuntimeParameter abort = run.getRuntimeParameters().get( RuntimeParameter.GLOBAL_ABORT_KEY); return abort; } private static String getNodeAbortKey(String nodeName) { return nodeName + RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter.ABORT_KEY; } private static RuntimeParameter getRecoveryModeParameter(Run run) { return run.getRuntimeParameters().get( RuntimeParameter.GLOBAL_RECOVERY_MODE_KEY); } public static void setRecoveryMode(Run run) { RuntimeParameter recoveryModeParam = getRecoveryModeParameter(run); recoveryModeParam.setValue("true"); recoveryModeParam.store(); run.postEventRecoveryMode(recoveryModeParam.getValue()); } public static void resetRecoveryMode(Run run) { RuntimeParameter recoveryModeParam = getRecoveryModeParameter(run); recoveryModeParam.setValue("false"); recoveryModeParam.store(); run.postEventRecoveryMode(recoveryModeParam.getValue()); } public static boolean isInRecoveryMode(Run run) { RuntimeParameter recoveryModeParam = getRecoveryModeParameter(run); boolean result = false; if (recoveryModeParam != null) { String recoveryMode = recoveryModeParam.getValue(); result = ! ("".equals(recoveryMode) || "false".equalsIgnoreCase(recoveryMode)); } return result; } private static RunParameter getGarbageCollectedParameter(Run run) { return run.getParameter(Run.GARBAGE_COLLECTED_PARAMETER_NAME); } public static void setGarbageCollected(Run run) throws ValidationException { RunParameter garbageCollected = getGarbageCollectedParameter(run); if (garbageCollected == null) { run.setParameter(new RunParameter(Run.GARBAGE_COLLECTED_PARAMETER_NAME, "true", Run.GARBAGE_COLLECTED_PARAMETER_DESCRIPTION)); } else if (!garbageCollected.isTrue()) { garbageCollected.setValue("true"); } } public static boolean isGarbageCollected(Run run) { RunParameter garbageCollected = getGarbageCollectedParameter(run); boolean result = false; if (garbageCollected != null) { String recoveryMode = garbageCollected.getValue(); result = Boolean.parseBoolean(recoveryMode); } return result; } public static Run updateRunState(Run run, States newState, boolean retry) { EntityManager em = PersistenceUtil.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); try { run = Run.loadFromUuid(run.getUuid(), em); run.setState(newState); transaction.commit(); em.close(); } catch (Exception e) { String error = "error setting run state: " + newState; if (retry) { Logger.getLogger("restlet").warning(error + " retrying..."); } else { Logger.getLogger("restlet").severe(error); } // retry once if (retry) { updateRunState(run, newState, false); } } return run; } public static Run loadFromUuid(String uuid) { String resourceUri = RESOURCE_URI_PREFIX + uuid; return load(resourceUri); } public static Run loadFromUuid(String uuid, EntityManager em) { String resourceUri = RESOURCE_URI_PREFIX + uuid; return load(resourceUri, em); } private static final MetricsTimer loadTimer = Metrics.newTimer(Run.class, "load"); public static Run load(String resourceUri) { EntityManager em = PersistenceUtil.createEntityManager(); Run run = null; try { loadTimer.start(); run = load(resourceUri, em); } finally { loadTimer.stop(); em.close(); } return run; } public static Run load(String resourceUri, EntityManager em) { Run run = em.find(Run.class, resourceUri); return run; } private static List<RunView> convertRunsToRunViews(List<Run> runs, Map<String, Long> vmCountPerRun) throws ConfigurationException, ValidationException { List<RunView> views = new ArrayList<RunView>(); RunView runView; for (Run r : runs) { runView = convertRunToRunView(r, vmCountPerRun); views.add(runView); } return views; } private static RunView convertRunToRunView(Run run, Map<String, Long> vmCountPerRun) { if (run == null) { return null; } RuntimeParameter globalAbort = Run.getGlobalAbort(run); String abortMessage = (globalAbort != null)? globalAbort.getValue() : ""; RunView runView = new RunView(run.getResourceUri(), run.getUuid(), run.getModuleResourceUrl(), run.getState().toString(), run.getStart(), run.getUser(), run.getType(), run.getCloudServiceNames(), abortMessage, vmCountPerRun.getOrDefault(run.getUuid(), 0L)); try { runView.setServiceUrl(run.getRuntimeParameterValueIgnoreAbort(RuntimeParameter.GLOBAL_URL_SERVICE_KEY)); } catch (NotFoundException e) { // it ok if it's not there } try { runView.setTags(run.getRuntimeParameterValueIgnoreAbort(RuntimeParameter.GLOBAL_TAGS_KEY)); } catch (NotFoundException e) { // it ok if it's not there } return runView; } private static Predicate andPredicate(CriteriaBuilder builder, Predicate currentPredicate, Predicate newPredicate){ return (currentPredicate != null) ? builder.and(currentPredicate, newPredicate) : newPredicate; } public static List<RunView> viewList(RunsQueryParameters queryParameters) throws ConfigurationException, ValidationException { return viewList(queryParameters, new HashMap<>()); } private static final MetricsTimer viewListTimer = Metrics.newTimer(Run.class, "viewList"); public static List<RunView> viewList(RunsQueryParameters queryParameters, Map<String, Long> vmCountPerRun) throws ConfigurationException, ValidationException { List<RunView> views = null; EntityManager em = PersistenceUtil.createEntityManager(); try { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Run> critQuery = builder.createQuery(Run.class); Root<Run> rootQuery = critQuery.from(Run.class); critQuery.select(rootQuery); Predicate where = viewListCommonQueryOptions(builder, rootQuery, queryParameters); if (where != null) { critQuery.where(where); } critQuery.orderBy(builder.desc(rootQuery.get("startTime"))); TypedQuery<Run> query = em.createQuery(critQuery); if (queryParameters.offset != null) { query.setFirstResult(queryParameters.offset); } query.setMaxResults((queryParameters.limit != null) ? queryParameters.limit : DEFAULT_LIMIT); viewListTimer.start(); try { List<Run> runs = query.getResultList(); views = convertRunsToRunViews(runs, vmCountPerRun); } finally { viewListTimer.stop(); } } finally { em.close(); } return views; } private static final MetricsTimer viewListCountTimer = Metrics.newTimer(Run.class, "viewListCount"); public static int viewListCount(RunsQueryParameters queryParameters) throws ConfigurationException, ValidationException { int count = 0; EntityManager em = PersistenceUtil.createEntityManager(); try { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Long> critQuery = builder.createQuery(Long.class); Root<Run> rootQuery = critQuery.from(Run.class); critQuery.select(builder.count(rootQuery)); Predicate where = viewListCommonQueryOptions(builder, rootQuery, queryParameters); if (where != null){ critQuery.where(where); } TypedQuery<Long> query = em.createQuery(critQuery); try { viewListCountTimer.start(); count = (int)(long) query.getSingleResult(); } finally { viewListCountTimer.stop(); } } finally { em.close(); } return count; } private static Predicate viewListCommonQueryOptions(CriteriaBuilder builder, Root<Run> rootQuery, RunsQueryParameters queryParameters) { User user = queryParameters.user; String owner = (queryParameters.runOwner != null)? queryParameters.runOwner : queryParameters.userFilter; String moduleResourceUri = queryParameters.moduleResourceUri; String cloud = queryParameters.cloud; boolean activeOnly = queryParameters.activeOnly; Predicate where = null; if (!user.isSuper()) { where = andPredicate(builder, where, builder.equal(rootQuery.get("user_"), user.getName())); } else if (owner != null) { where = andPredicate(builder, where, builder.equal(rootQuery.get("user_"), owner)); } if (moduleResourceUri != null && !"".equals(moduleResourceUri)) { where = andPredicate(builder, where, builder.equal(rootQuery.get("moduleResourceUri"), moduleResourceUri)); } if (cloud != null && !"".equals(cloud)) { // TODO: Replace the 'like' by an 'equals' where = andPredicate(builder, where, builder.like(rootQuery.<String>get("cloudServiceNames"), "%" + cloud + "%")); } if (activeOnly) { Expression<String> exp = rootQuery.<String>get("state"); Predicate predicate = exp.in(States.active()); where = andPredicate(builder, where, predicate); } return where; } public static List<Run> listAll() throws ConfigurationException, ValidationException { EntityManager em = PersistenceUtil.createEntityManager(); Query q = createNamedQuery(em, "allRuns"); @SuppressWarnings("unchecked") List<Run> runs = q.getResultList(); em.close(); return runs; } public static List<Run> listOldTransient(User user) throws ConfigurationException, ValidationException { return listOldTransient(user, 0); } @SuppressWarnings("unchecked") public static List<Run> listOldTransient(User user, int timeout) throws ConfigurationException, ValidationException { if (timeout <= 0) { timeout = DEFAULT_TIMEOUT; } Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.MINUTE, -timeout); Date back = calendar.getTime(); EntityManager em = PersistenceUtil.createEntityManager(); Query q = createNamedQuery(em, "oldInStatesRuns"); q.setParameter("user", user.getName()); q.setParameter("before", back); q.setParameter("states", States.transition()); List<Run> runs = q.getResultList(); em.close(); return runs; } public static Run loadRunWithRuntimeParameters(String uuid) throws ConfigurationException, ValidationException { EntityManager em = PersistenceUtil.createEntityManager(); Query q = createNamedQuery(em, "runWithRuntimeParameters"); q.setParameter("uuid", uuid); Run run = (Run) q.getSingleResult(); em.close(); return run; } private static Query createNamedQuery(EntityManager em, String query) { Query q = em.createNamedQuery(query); q.setMaxResults(DEFAULT_LIMIT); return q; } @Attribute @Id private String resourceUri; @Attribute private String uuid; @Attribute(empty = "Orchestration") private RunType type = RunType.Orchestration; /** * Cloud service names comma separated */ @Attribute(required = true) @Column(length=65536) private String cloudServiceNames; @Attribute(required = false) @Enumerated private States state = States.Initializing; @Attribute private String moduleResourceUri; private transient Credentials credentials; @OneToMany(mappedBy = "container", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @MapKey(name = "key_") @CollectionType(type = "com.sixsq.slipstream.persistence.ConcurrentHashMapType") @ElementMap(name = "runtimeParameters", required = false, data = true, valueType = RuntimeParameter.class) private Map<String, RuntimeParameter> runtimeParameters = new ConcurrentHashMap<String, RuntimeParameter>(); @Attribute @Temporal(TemporalType.TIMESTAMP) private Date startTime = new Date(); @Attribute(required = false) @Temporal(TemporalType.TIMESTAMP) private Date endTime; @Attribute(required = false) @Temporal(TemporalType.TIMESTAMP) protected Date lastStateChangeTime = new Date(); /** * Comma separated list of node names - e.g. apache1.1, apache1.2, ... * Including the orchestrator: orchestrator-local, ... * * FIXME: Should be changed to instanceNames (or nodeInstanceNames). * NB: orchestrator is part of this list as well. */ @Column(length=65536) @Attribute private String nodeNames = ""; private static final String NODE_NAMES_SEPARATOR = ","; /** * Comma separated list of nodes, including the associated orchestror name - * e.g. orchestrator:apache1, orchestrator:testclient1, ... or * orchestrator-stratuslab:apache1, orchestrator-openstack:testclient1, ... */ @Column(length=65536) private String groups = ""; @Attribute(name = "user", required = false) private String user_; @Element(required = false) @Transient private Module module; @Attribute(required = false) @Column(nullable=false, columnDefinition="boolean default false") private boolean mutable; @Transient private Map<String, Integer> cloudServiceUsage = new HashMap<String, Integer>(); /** * List of cloud service names used in the current run */ @ElementArray(required = false) public String[] getCloudServiceNamesList() { if (cloudServiceNames == null) { return new String[] {}; } Set<String> uniqueCloudServiceNames = new HashSet<String>(Arrays.asList(cloudServiceNames.split(","))); return uniqueCloudServiceNames.toArray(new String[uniqueCloudServiceNames.size()]); } @ElementArray(required = false) private void setCloudServiceNamesList(String[] cloudServiceNames) { Set<String> uniqueCloudServiceNames = new HashSet<String>(Arrays.asList(cloudServiceNames)); this.cloudServiceNames = StringUtils.join( uniqueCloudServiceNames.toArray(new String[uniqueCloudServiceNames.size()]), ","); } @SuppressWarnings("unused") private Run() throws NotFoundException { } public Run(Module module, RunType type, Set<String> cloudServiceNames, User user) throws ValidationException { uuid = UUID.randomUUID().toString(); resourceUri = RESOURCE_URI_PREFIX + uuid; this.category = module.getCategory(); this.moduleResourceUri = module.getResourceUri(); this.type = type; this.cloudServiceNames = StringUtils.join(cloudServiceNames, ","); this.user_ = user.getName(); this.module = module; setStart(); postEventStateTransition(this.state, true); } @Override @ElementMap(name = "parameters", required = false, valueType = RunParameter.class) protected void setParameters(Map<String, RunParameter> parameters) { this.parameters = parameters; } @Override @ElementMap(name = "parameters", required = false, valueType = RunParameter.class) public Map<String, RunParameter> getParameters() { return parameters; } public Module getModule(boolean load) { if (module == null && load) { module = Module.load(getModuleResourceUrl()); } return module; } public Module getModule() { return getModule(true); } public void setModule(Module module) throws ValidationException { this.module = module; } @Override public String getName() { return uuid; } @Override public void setName(String name) { this.uuid = name; } public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } public String getModuleResourceUrl() { return moduleResourceUri; } public void setModuleResourceUrl(String moduleResourceUri) { this.moduleResourceUri = moduleResourceUri; } public Map<String, RuntimeParameter> getRuntimeParameters() { return runtimeParameters; } public void setRuntimeParameters( Map<String, RuntimeParameter> runtimeParameters) { this.runtimeParameters = runtimeParameters; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } public String getRefqname() { return moduleResourceUri; } public void setRefqname(String refqname) { this.moduleResourceUri = refqname; } public void setCredentials(Credentials credentials) { this.credentials = credentials; } public Credentials getCredentials() { return this.credentials; } /** * Set value to key, ignoring the abort flag, such that no exception is * thrown. * * @param key * @return new value * @throws AbortException * @throws NotFoundException */ public String getRuntimeParameterValueIgnoreAbort(String key) throws NotFoundException { RuntimeParameter parameter = extractRuntimeParameter(key); return parameter.getValue(); } private RuntimeParameter extractRuntimeParameter(String key) throws NotFoundException { if (!runtimeParameters.containsKey(key)) { throwNotFoundException(key); } return runtimeParameters.get(key); } public String getRuntimeParameterValue(String key) throws AbortException, NotFoundException { if (isAbort()) { throw new AbortException("Abort flag raised!"); } return getRuntimeParameterValueIgnoreAbort(key); } public void removeRuntimeParameter(String key) throws NotFoundException { assert (runtimeParameters != null); runtimeParameters.remove(key); } public boolean isAbort() { RuntimeParameter abort = null; try { abort = extractRuntimeParameter(RuntimeParameter.GLOBAL_ABORT_KEY); } catch (NotFoundException e) { return false; } return abort.isSet(); } public void createRuntimeParameter(Node node, int nodeInstanceId, String key, String value) throws ValidationException { createRuntimeParameter(node, nodeInstanceId, key, value, "", ParameterType.String); } public void createRuntimeParameter(Node node, int nodeInstanceId, String key, String value, String description) throws ValidationException { createRuntimeParameter(node, nodeInstanceId, key, value, description, ParameterType.String); } public void createRuntimeParameter(Node node, int nodeInstanceId, String key, String value, String description, ParameterType type) throws ValidationException { String parameterName = composeNodeInstanceParameterName(node, nodeInstanceId, key); assignRuntimeParameter(parameterName, value, description, type); } public static String composeNodeInstanceParameterName(Node node, int nodeInstanceId, String key) { return composeNodeInstanceName(node, nodeInstanceId) + RuntimeParameter.NODE_PROPERTY_SEPARATOR + key; } public static String composeNodeInstanceName(Node node, int nodeInstanceId) { return RuntimeParameter.constructNodeInstanceName(node.getName(), nodeInstanceId); } public Date getStart() { return (Date) startTime.clone(); } public void setStart() { startTime = now(); } public void setStart(Date start) { this.startTime = (Date) start.clone(); } public Date getEnd() { return endTime == null ? null : (Date) endTime.clone(); } public void setEnd(Date end) { this.endTime = (Date) end.clone(); } public void setEnd() { if (endTime == null) { endTime = now(); } } public Date now() { return new Date(); } public void addNodeInstanceName(Node node, int nodeInstanceId) { String nodeInstanceName = composeNodeInstanceName(node, nodeInstanceId); String cloudServiceName = getCloudServiceNameForNode(node.getName()); addNodeInstanceName(nodeInstanceName, cloudServiceName); } public void addNodeInstanceName(String nodeInstanceName, String cloudServiceName) { List<String> nodeNamesList = new ArrayList<String>(getNodeInstanceNamesList()); nodeNamesList.remove(""); if (!nodeNamesList.contains(nodeInstanceName)) { nodeNamesList.add(nodeInstanceName); nodeNames = StringUtils.join(nodeNamesList, NODE_NAMES_SEPARATOR); Integer nb = cloudServiceUsage.get(cloudServiceName); if (nb == null){ nb = 0; } cloudServiceUsage.put(cloudServiceName, nb + 1); } } public void removeNodeInstanceName(String nodeInstanceName, String cloudServiceName) { // removeNodeInstanceName(nodeInstanceName); Integer nb = cloudServiceUsage.get(cloudServiceName); if (nb != null && nb > 0){ cloudServiceUsage.put(cloudServiceName, nb - 1); } } public void removeNodeInstanceName(String nodeInstanceName) { List<String> nodeNamesList = new ArrayList<String>(getNodeInstanceNamesList()); while (nodeNamesList.contains(nodeInstanceName)) { nodeNamesList.remove(nodeInstanceName); } nodeNames = StringUtils.join(nodeNamesList, NODE_NAMES_SEPARATOR); } /** * Return nodenames, including a value for each index from 1 to multiplicity * (e.g. apache1.1, apache1.2...) * * @return comma separated nodenames */ public String getNodeNames() { return nodeNames; } /** * Builds a list of node instance names (e.g. node.1, node.2, machine) * @return node instance name */ public List<String> getNodeInstanceNamesList() { String[] rawNodeNames = getNodeNames().split(NODE_NAMES_SEPARATOR); List<String> nodeNames = new ArrayList<String>(rawNodeNames.length); for (int i=0; i < rawNodeNames.length; i++) { String nodeName = rawNodeNames[i].trim(); if (!nodeName.isEmpty()) { nodeNames.add(nodeName); } } return nodeNames; } /** * Builds a list of node instance names (e.g. nodeA, nodeB, machine) * @return node names */ public List<String> getNodeNamesList() { List<String> groupNames = getGroupNameList(); List<String> nodeNames = new ArrayList<String>(); for (String groupName : groupNames) { String nodeName = ""; try { nodeName = groupName.split(SERVICENAME_NODENAME_SEPARATOR)[1]; } catch (IndexOutOfBoundsException ex) { // } if (!nodeName.isEmpty()) { nodeNames.add(nodeName); } } return nodeNames; } public List<String> getNodeInstanceNames(String nodeName) { Pattern INSTANCENAME = Pattern.compile("^(" + nodeName + "\\.\\d+)$"); List<String> requested = new ArrayList<String>(); for (String instanceName : getNodeInstanceNamesList()) { if (INSTANCENAME.matcher(instanceName).matches()) requested.add(instanceName); } return requested; } public Map<String, Integer> getCloudServiceUsage() { return cloudServiceUsage; } @Override public String getResourceUri() { return resourceUri; } @Override public void setContainer(RunParameter parameter) { parameter.setContainer(this); } public String getUser() { return user_; } public void setUser(String user) { this.user_ = user; } public RuntimeParameter assignRuntimeParameter(String key, String value, String description) throws ValidationException { return assignRuntimeParameter(key, value, description, ParameterType.String); } public RuntimeParameter assignRuntimeParameter(String key, String value, String description, ParameterType type) throws ValidationException { if (key == null) { throw new ValidationException("Key cannot be null"); } if (runtimeParameters.containsKey(key)) { throw new ValidationException("Key " + key + " already exists, cannot re-define"); } RuntimeParameter parameter = new RuntimeParameter(this, key, value, description); parameter.setType(type); runtimeParameters.put(key, parameter); return parameter; } public RuntimeParameter assignRuntimeParameter(String key, String description) throws ValidationException { return assignRuntimeParameter(key, "", description); } public RuntimeParameter updateRuntimeParameter(String key, String value) throws NotFoundException, ValidationException { if (!runtimeParameters.containsKey(key)) { throwNotFoundException(key); } RuntimeParameter parameter = runtimeParameters.get(key); if (RuntimeParameter.GLOBAL_ABORT_KEY.equals(key)) { if (isAbort()) { return parameter; } } parameter.setValue(value); return getRuntimeParameters().get(key); } public void updateRuntimeParameters(Map<String, RuntimeParameter> runtimeParameters) { for (String key : runtimeParameters.keySet()) { this.runtimeParameters.put(key, runtimeParameters.get(key)); } } private void throwNotFoundException(String key) throws NotFoundException { throw new NotFoundException("Couldn't find key '" + key + "' in execution instance: '" + getName() + "'"); } public Run store() { return (Run) super.store(); } public void setType(RunType type) { this.type = type; } public RunType getType() { return type; } public States getState() { // required to keep backward compatibility return state == null ? States.Unknown : state; } public void setState(States state) { postEventStateTransition(state); this.state = state; } private void postEventStateTransition(States newState) { postEventStateTransition(newState, false); } public void postEventTerminate() { postEventRun(Event.Severity.medium, "Terminated", EventType.action); } public void postEventScaleUp(String nodename, List<String> nodeInstanceNames, int nbInstancesToAdd) { String message = "Scaling up '" + nodename + "' with " + nbInstancesToAdd + " new instances: " + nodeInstanceNames; postEventRun(Event.Severity.medium, message, EventType.action); } public void postEventScaleDown(String nodename, List<String> nodeInstanceIds) { int nbInstancesToDelete = 0; if (nodeInstanceIds != null) { nbInstancesToDelete = nodeInstanceIds.size(); } String message = "Scaling down '" + nodename + "' by deleting " + nbInstancesToDelete +" instances: " + nodeInstanceIds; postEventRun(Event.Severity.medium, message, EventType.action); } private void postEventStateTransition(States newState, boolean forcePost) { boolean stateWillChange = this.state != newState; boolean shouldPost = forcePost || stateWillChange; if (shouldPost) { postEventRun(Event.Severity.medium, newState.toString(), EventType.state); } } private void postEventAbort(String origin, String abortMessage) { String message = "Abort from '" + origin + "', message:" + abortMessage; postEventRun(Event.Severity.high, message, EventType.state); } private void postEventAbortReset(String origin) { String message = "Reset abort from '" + origin + "'"; postEventRun(Event.Severity.high, message, EventType.state); } private void postEventRecoveryMode(String newValue) { String message = "Recovery mode set to '" + newValue + "'"; postEventRun(Event.Severity.high, message, EventType.state); } public void postEventCreated() { String message = "Created"; postEventRun(Event.Severity.medium, message, EventType.action); } public void postEventGarbageCollectorTimedOut() { String message = "GarbageCollector: The Run has timed out"; postEventRun(Event.Severity.medium, message, EventType.system); } public void postEventGarbageCollectorTerminated() { String message = "GarbageCollector: The Run was terminated"; postEventRun(Event.Severity.medium, message, EventType.system); } private void postEventRun(Event.Severity severity, String message, EventType type) { String resourceRef = RESOURCE_URI_PREFIX + uuid; Event.postEvent(resourceRef, severity, message, getUser(), type); } public Date getLastStateChange() { return this.lastStateChangeTime; } public void setLastStateChange() { setLastStateChange(now()); } public void setLastStateChange(Date date){ this.lastStateChangeTime = date; } public List<String> getOrchestrators() { List<String> orchestrators = new ArrayList<String>(); for (String nodename : getNodeInstanceNamesList()) { if (nodename.startsWith(Run.ORCHESTRATOR_NAME)) { orchestrators.add(nodename); } } return orchestrators; } public void addGroup(String group, String serviceName) { if (!this.groups.isEmpty()) { this.groups += Run.NODE_NAMES_SEPARATOR; } this.groups += serviceName + SERVICENAME_NODENAME_SEPARATOR + group; getGroups(); } @Attribute @Column(length=1024) public String getGroups() { getRuntimeParameters().get(RuntimeParameter.GLOBAL_NODE_GROUPS_KEY).setValue(groups); return groups; } @Attribute @Column(length=1024) public void setGroups(String groups) { this.groups = groups; } public List<String> getGroupNameList() { return Arrays.asList(getGroups().split(",")); } public String nodeRuntimeParameterKeyName(Node node, String nodeParameterName) { return node.getName() + NODE_NAME_PARAMETER_SEPARATOR + nodeParameterName; } public static String constructOrchestratorName(String cloudService) { return ORCHESTRATOR_NAME + ORCHESTRATOR_CLOUD_SERVICE_SEPARATOR + cloudService; } public String getCloudServiceNames() { return cloudServiceNames; } public void setCloudServiceNames(String cloudServiceNames) { this.cloudServiceNames = cloudServiceNames; } public String getCloudServiceNameForNode(String nodeName) { String key = RuntimeParameter.constructParamName(nodeName, RuntimeParameter.CLOUD_SERVICE_NAME); return getParameter(key).getValue(); } public boolean isMutable() { return mutable; } public void setMutable() { this.mutable = true; } public void setImmutable() { this.mutable = false; } // public void remove() { // List<VmRuntimeParameterMapping> ms = VmRuntimeParameterMapping.getMappings(getUuid()); // for(VmRuntimeParameterMapping m : ms) { // try { // m.remove(); // } catch (IllegalArgumentException e) { // // } // } // super.remove(); // } }