/* * Copyright (c) 2013 Technische Universitat Wien (TUW), Distributed Systems Group. http://dsg.tuwien.ac.at * * This work was partially supported by the European Commission in terms of the CELAR FP7 project (FP7-ICT-2011-8 #317790), http://www.celarcloud.eu/ * * 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. */ package at.ac.tuwien.dsg.cloud.salsa.engine.services; import generated.oasis.tosca.TArtifactReference; import generated.oasis.tosca.TArtifactTemplate; import generated.oasis.tosca.TArtifactTemplate.ArtifactReferences; import generated.oasis.tosca.TCapability; import generated.oasis.tosca.TDefinitions; import generated.oasis.tosca.TDeploymentArtifact; import generated.oasis.tosca.TDeploymentArtifacts; import generated.oasis.tosca.TNodeTemplate; import generated.oasis.tosca.TRelationshipTemplate; import generated.oasis.tosca.TRelationshipTemplate.SourceElement; import generated.oasis.tosca.TRelationshipTemplate.TargetElement; import generated.oasis.tosca.TRequirement; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Service; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.CloudService; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.PrimitiveOperation; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.SalsaEntity; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceInstance; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceInstance.Capabilities; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceInstance.Properties; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceTopology; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceTopology.SalsaReplicaRelationships; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceUnit; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceUnitRelationship; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.enums.SalsaEntityState; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.enums.SalsaEntityType; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.rSYBL.deploymentDescription.AssociatedVM; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.rSYBL.deploymentDescription.DeploymentDescription; import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.rSYBL.deploymentDescription.DeploymentUnit; import at.ac.tuwien.dsg.cloud.salsa.engine.capabilityinterface.SalsaEngineServiceIntenal; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SalsaCenterConnector; import at.ac.tuwien.dsg.cloud.salsa.engine.dataprocessing.SalsaXmlDataProcess; import at.ac.tuwien.dsg.cloud.salsa.engine.capabilityinterface.UnitCapabilityInterface; import at.ac.tuwien.dsg.cloud.salsa.engine.capabilityinterface.WholeAppCapabilityInterface; import at.ac.tuwien.dsg.cloud.salsa.common.interfaces.SalsaException; import at.ac.tuwien.dsg.cloud.salsa.engine.exceptions.ServicedataProcessingException; import at.ac.tuwien.dsg.cloud.salsa.engine.exceptions.IllegalConfigurationAPICallException; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.richInformationCapability.AsyncUnitCapability; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.genericCapability.InfoParser; import at.ac.tuwien.dsg.cloud.salsa.engine.services.jsondata.ServiceJsonList; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.ActionIDManager; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.EngineLogger; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.MutualFileAccessControl; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.PioneerManager; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SalsaConfiguration; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SystemFunctions; import at.ac.tuwien.dsg.cloud.salsa.messaging.messageInterface.MessagePublishInterface; import at.ac.tuwien.dsg.cloud.salsa.messaging.protocol.SalsaMessage; import at.ac.tuwien.dsg.cloud.salsa.messaging.protocol.SalsaMessageTopic; import at.ac.tuwien.dsg.cloud.salsa.messaging.model.Salsa.SalsaMsgConfigureArtifact; import at.ac.tuwien.dsg.cloud.salsa.domainmodels.types.SalsaArtifactType; import at.ac.tuwien.dsg.cloud.salsa.domainmodels.types.ServiceCategory; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.richInformationCapability.RichInformationWholeAppCapability; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.EventPublisher; import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaCapaReqString; import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_Docker; import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_VM; import at.ac.tuwien.dsg.cloud.salsa.engine.dataprocessing.ToscaStructureQuery; import at.ac.tuwien.dsg.cloud.salsa.engine.dataprocessing.ToscaXmlProcess; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.base.DynamicPlacementHelper; import static at.ac.tuwien.dsg.cloud.salsa.engine.services.ViewGenerator.logger; import at.ac.tuwien.dsg.cloud.salsa.messaging.model.Salsa.SalsaMsgUpdateMetadata; import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaMappingProperties; import java.net.UnknownHostException; import java.util.logging.Level; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MalformedObjectNameException; import javax.management.ReflectionException; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Service public class SalsaEngineImplAll implements SalsaEngineServiceIntenal { private static final Logger LOGGER; WholeAppCapabilityInterface wholeAppCapability = new RichInformationWholeAppCapability(); UnitCapabilityInterface unitCapability = new AsyncUnitCapability(); private final String dataFileExtension = ".data"; static { LOGGER = Logger.getLogger("EngineLogger"); } /* * MAIN SERVICES TO EXPOSE TO USERS */ @Override public Response deployService(String serviceNameParam, InputStream uploadedInputStream) throws SalsaException { //EventPublisher.publishSALSAEvent(serviceNameParam, "Recieved a deployment request with service name: " + serviceNameParam); String tmpID = UUID.randomUUID().toString(); String tmpFile = "/tmp/salsa_tmp_" + tmpID; String serviceName = serviceNameParam.replaceAll("\\s", ""); if (!checkForServiceName(serviceName)) { return Response.status(404).entity("Error. Service Name is bad: " + serviceName).build(); } try { MutualFileAccessControl.writeToFile(uploadedInputStream, tmpFile); TDefinitions def = ToscaXmlProcess.readToscaFile(tmpFile); CloudService service = wholeAppCapability.addService(serviceName, def); String output = "Deployed service. Id: " + service.getId(); LOGGER.debug(output); // delete tmp file File file = new File(tmpFile); file.delete(); // return 201: resource created return Response.status(201).entity(service.getId()).build(); } catch (JAXBException e) { LOGGER.error("Error when parsing Tosca: " + e); // return 400: bad request, the XML is malformed and could not process return Response.status(400).entity("Error. Unable to parse Tosca. Error: " + e).build(); } catch (IOException e) { LOGGER.error("Error reading file: " + tmpFile + ". Error: " + e); //return 500: intenal server error. The server cannot create and process tmp Tosca file return Response.status(500).entity("Error when process Tosca file. Error: " + e).build(); } } @Override public Response deployServiceFromXML(String uploadedInputStream) throws SalsaException { String tmpID = UUID.randomUUID().toString(); String tmpFile = "/tmp/salsa_tmp_" + tmpID; try { FileUtils.writeStringToFile(new File(tmpFile), uploadedInputStream); TDefinitions def = ToscaXmlProcess.readToscaFile(tmpFile); String serviceId = def.getId(); if (!checkForServiceName(serviceId)) { return Response.status(404).entity("Error. Service Name is bad: " + serviceId).build(); } //CloudService service = deployer.deployNewService(def, serviceId); CloudService service = this.wholeAppCapability.addService(serviceId, def); String output = "Deployed service. Id: " + service.getId(); LOGGER.debug(output); // delete tmp file File file = new File(tmpFile); file.delete(); // return 201: resource created return Response.status(201).entity(serviceId).build(); } catch (JAXBException e) { LOGGER.error("Error when parsing Tosca: " + e); // return 400: bad request, the XML is malformed and could not process return Response.status(400).entity("Unable to parse the Tosca XML. Error: " + e).build(); } catch (IOException e) { LOGGER.error("Error reading file: " + tmpFile + ". Error: " + e); return Response.status(500).entity("Error when process Tosca file. Error: " + e).build(); } } @Override public Response redeployService(String serviceId) throws SalsaException { String ogininalToscaFile = SalsaConfiguration.getServiceStorageDir() + "/" + serviceId + ".original"; try { String originalTosca = FileUtils.readFileToString(new File(ogininalToscaFile)); undeployService(serviceId); Thread.sleep(3000); deployService(serviceId, new ByteArrayInputStream(originalTosca.getBytes("UTF-8"))); } catch (IOException e) { LOGGER.error("Error when reading data file! Error: " + e); return Response.status(500).entity("Error when reading orgininal tosca file for service: " + serviceId + ". Error: " + e).build(); } catch (InterruptedException ie) { LOGGER.error("Interrup error"); return Response.status(500).entity("Error when reading orgininal tosca file for service: " + serviceId + ". Error: " + ie).build(); } return Response.status(201).entity(serviceId).build(); } /* (non-Javadoc) * @see at.ac.tuwien.dsg.cloud.salsa.engine.services.SalsaEngineIntenalInterface#undeployService(java.lang.String) */ @Override public Response undeployService(String serviceId) throws SalsaException { LOGGER.debug("DELETING SERVICE: " + serviceId); boolean cleaned = wholeAppCapability.cleanService(serviceId); EngineLogger.logger.debug("The lower capability return: {}", cleaned); // wait and check if it is really cleaned String fileName = SalsaConfiguration.getServiceStorageDir() + "/" + serviceId; File file = new File(fileName); File datafile = new File(fileName.concat(this.dataFileExtension)); File originalFile = new File(fileName.concat(".original")); if (originalFile.delete() && file.delete() && datafile.delete()) { if (cleaned) {// deregister service here LOGGER.debug("Deregister service done: " + serviceId); return Response.status(200).entity("Deregistered service: " + serviceId).build(); } else { // return 404: not found the service to be undeployed LOGGER.error("Could not found service to deregister: " + serviceId); return Response.status(404).entity("An error occurs when cleaning service: " + serviceId).build(); } } else { LOGGER.debug("Could not found service to deregister: " + serviceId); return Response.status(500).entity("Service not found to deregister: " + serviceId).build(); } } /* (non-Javadoc) * @see at.ac.tuwien.dsg.cloud.salsa.engine.services.SalsaEngineIntenalInterface#spawnInstance(java.lang.String, java.lang.String, java.lang.String, int) */ @Override public Response spawnInstance(String serviceId, String nodeId, int quantity) throws SalsaException { LOGGER.debug("SPAWNING MORE INSTANCE: " + serviceId + "/" + nodeId + ". Quantity:" + quantity); SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceUnit node = service.getComponentById(nodeId); if (node == null) { throw new IllegalConfigurationAPICallException("Cannot find the node with id: " + serviceId + "/" + nodeId); } String correctTopologyID = service.getTopologyOfNode(node.getId()).getId(); // update first the number + quantity centerCon.updateNodeIdCounter(serviceId, correctTopologyID, nodeId, node.getIdCounter() + quantity); String returnVal = ""; for (int i = node.getIdCounter() + 1; i < node.getIdCounter() + quantity + 1; i++) { unitCapability.deploy(serviceId, nodeId, i); returnVal += i + " "; } return Response.status(201).entity(returnVal).build(); } @Override public Response scaleOutNode(String serviceId, String nodeId) throws SalsaException { SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceTopology topo = service.getTopologyOfNode(nodeId); String instanceId = ((String) spawnInstance(serviceId, nodeId, 1).getEntity()).trim(); LOGGER.debug("Generate instance id: " + instanceId); service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceUnit unit = service.getComponentById(nodeId); ServiceInstance instance = unit.getInstanceById(Integer.parseInt(instanceId)); int counter = 0; while (instance == null || !instance.getState().equals(SalsaEntityState.DEPLOYED)) { try { if (counter > 300) { break; } Thread.sleep(3000); counter++; } catch (Exception e) { LOGGER.debug("Interrupt when waiting for the status of the node"); return Response.status(500).entity("Could not get the IP of the scaled out node").build(); } service = centerCon.getUpdateCloudServiceRuntime(serviceId); unit = service.getComponentById(nodeId); instance = unit.getInstanceById(Integer.parseInt(instanceId)); } ServiceUnit hostedUnit = service.getComponentById(unit.getHostedId()); LOGGER.debug("HostNode: " + hostedUnit.getId() + ". Prepare to get its instance: " + instance.getHostedId_Integer()); ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer()); LOGGER.debug("HostNode/InstanceId: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId()); while (!hostedUnit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString()) && !hostedUnit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) { hostedUnit = service.getComponentById(hostedUnit.getHostedId()); hostedInstance = hostedUnit.getInstanceById(hostedInstance.getHostedId_Integer()); LOGGER.debug("HostNode: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId()); } SalsaInstanceDescription_VM vm = (SalsaInstanceDescription_VM) hostedInstance.getProperties().getAny(); return Response.status(201).entity(vm.getPrivateIp()).build(); } @Override public Response scaleInNode(String serviceId, String nodeId) throws SalsaException { SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceTopology topo = service.getTopologyOfNode(nodeId); ServiceUnit unit = topo.getComponentById(nodeId); List<ServiceInstance> instances = unit.getInstancesList(); if (instances.size() > 0) { return destroyInstance(serviceId, nodeId, instances.get(0).getInstanceId()); } return Response.status(404).entity("Found no instance to remove").build(); } @Override public Response scaleInVM(String serviceId, String vmIp) throws SalsaException { SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); for (ServiceTopology topo : service.getComponentTopologyList()) { for (ServiceUnit unit : topo.getComponentsByType(SalsaEntityType.OPERATING_SYSTEM)) { EngineLogger.logger.debug("Scaling in VM. Checking OS unit: " + unit.getId()); for (ServiceInstance vm : unit.getInstancesList()) { SalsaInstanceDescription_VM vmProp = (SalsaInstanceDescription_VM) vm.getProperties().getAny(); if (vmProp.getPrivateIp().equals(vmIp)) { return destroyInstance(serviceId, unit.getId(), vm.getInstanceId()); } } } for (ServiceUnit unit : topo.getComponentsByType(SalsaEntityType.DOCKER)) { EngineLogger.logger.debug("Scaling in VM/docker. Checking docker unit: " + unit.getId()); for (ServiceInstance vm : unit.getInstancesList()) { EngineLogger.logger.debug("Scaling in VM/docker. Checking docker instance: " + vm.getInstanceId()); SalsaInstanceDescription_VM vmProp = (SalsaInstanceDescription_VM) vm.getProperties().getAny(); if (vmProp.getPrivateIp().equals(vmIp)) { EngineLogger.logger.debug("Scaling in VM. GOT A DOCKER NODE TO SCALE-IN: " + vm.getInstanceId() + "/" + vmProp.getPrivateIp()); return destroyInstance(serviceId, unit.getId(), vm.getInstanceId()); } } } } return Response.status(404).entity("Not found a VM nodes of IP: " + vmIp).build(); } @Override public Response scaleOutVM(String serviceId, String vmIp) throws SalsaException { SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); centerCon.getUpdateCloudServiceRuntime(serviceId); return null; } @Override public Response deployInstance( String serviceId, String topologyId, String nodeId, int instanceId) throws SalsaException { LOGGER.debug("Deployment request for this node: " + serviceId + " - " + nodeId + " - " + instanceId); LOGGER.debug("PUT 1 MORE INSTANCE: " + serviceId + "/" + topologyId + "/" + nodeId); SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); TDefinitions def = centerCon.getToscaDescription(serviceId); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceUnit node = service.getComponentById(nodeId); if (node == null) { EngineLogger.logger.error("May be the id of node is invalided"); return Response.status(500).entity("Error: Node ID is not found.").build(); } unitCapability.deploy(serviceId, nodeId, instanceId); return Response.status(201).entity(instanceId).build(); //TODO: What happen if it is fail to spawn a VM ? } @Override public Response destroyInstance( String serviceId, String nodeId, int instanceId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service; try { service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit unit = service.getComponentById(nodeId); if (unit.getReference() != null && !unit.getReference().isEmpty()) { return Response.status(200).entity("Do not destroy reference instance: " + serviceId + "/" + nodeId + "/" + instanceId).build(); } } catch (JAXBException | IOException ex) { return Response.status(500).entity("Cannot get instance information: " + serviceId + "/" + nodeId + "/" + instanceId).build(); } LOGGER.debug("Removing service instance: " + serviceId + "/" + nodeId + "/" + instanceId); unitCapability.remove(serviceId, nodeId, instanceId); LOGGER.debug("Removing service instance: " + serviceId + "/" + nodeId + "/" + instanceId + " - DONE!"); return Response.status(200).entity("Undeployed instance: " + serviceId + "/" + nodeId + "/" + instanceId).build(); } @Override public Response getInstanceStatus(String serviceId, String nodeId, int instanceId) throws SalsaException { try { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit nodeData = service.getComponentById(nodeId); ServiceInstance instanceData = nodeData.getInstanceById(instanceId); return Response.status(200).entity(instanceData.getState().getNodeStateString()).build(); } catch (JAXBException | IOException | NullPointerException e) { return Response.status(404).entity("Cannot get configuration status of instance: " + instanceId + ", on node: " + nodeId + ", on service " + serviceId).build(); } } @Override public Response removeInstanceMetadata( String serviceId, String nodeId, int instanceId) throws SalsaException { // remove metadata try { MutualFileAccessControl.lockFile(); String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit nodeData = service.getComponentById(nodeId); ServiceInstance instanceData = nodeData.getInstanceById(instanceId); if (nodeData.getReference() != null) { return Response.status(404).entity("Cannot remove instance because it is a reference: " + instanceId + ", on node: " + nodeId + ", on service " + serviceId).build(); } // remove hosted on node for (ServiceUnit unit : service.getAllComponent()) { if (unit.getHostedId().equals(nodeData.getId())) { for (ServiceInstance instance : unit.getInstancesList()) { if (instance.getHostedId_Integer() == instanceData.getInstanceId()) { unit.getInstancesList().remove(instance); } } } } nodeData.getInstancesList().remove(instanceData); updateComponentStateBasedOnInstance(service); SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } catch (JAXBException | IOException e) { LOGGER.error(e.getMessage()); return Response.status(404).entity("Cannot remove instance: " + instanceId + ", on node: " + nodeId + ", on service " + serviceId).build(); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Undeployed instance: " + serviceId + "/" + nodeId + "/" + instanceId).build(); } private boolean checkForServiceName(String serviceName) { if (serviceName.equals("")) { return false; } String pathName = SalsaConfiguration.getServiceStorageDir(); ServiceJsonList serviceList = new ServiceJsonList(pathName); for (ServiceJsonList.ServiceInfo serv : serviceList.getServicesList()) { if (serviceName.equals(serv.getServiceId())) { return false; } } return true; } @Override public Response deployTopology(String serviceId, String topologyId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceTopology topo = service.getComponentTopologyById(topologyId); List<ServiceUnit> units = topo.getComponentsByType(SalsaEntityType.SOFTWARE); for (ServiceUnit u : units) { spawnInstance(serviceId, u.getId(), 1); } return Response.status(200).entity("Deploy the whole topology").build(); } catch (IOException e) { LOGGER.error(e.getMessage()); return Response.status(404).entity("Cannot read the service data file").build(); } catch (JAXBException e1) { LOGGER.error(e1.getMessage()); return Response.status(500).entity("Cannot parse the service data file").build(); } } @Override public Response undeployTopology(String serviceId, String topologyId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceTopology topo = service.getComponentTopologyById(topologyId); List<ServiceUnit> units = topo.getComponentsByType(SalsaEntityType.OPERATING_SYSTEM); for (ServiceUnit u : units) { for (ServiceInstance instance : u.getInstancesList()) { destroyInstance(serviceId, u.getId(), instance.getInstanceId()); } } return Response.status(200).entity("Deploy the whole topology").build(); } catch (IOException e) { LOGGER.error(e.getMessage()); return Response.status(404).entity("Cannot read the service data file").build(); } catch (JAXBException e1) { LOGGER.error(e1.getMessage()); return Response.status(500).entity("Cannot parse the service data file").build(); } } @Override public Response destroyInstanceOfNodeType(String serviceId, String topologyId, String nodeId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; LOGGER.debug("Remove all instances of node: " + nodeId); try { CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit unit = service.getComponentById(nodeId); for (ServiceInstance instance : unit.getInstancesList()) { destroyInstance(serviceId, nodeId, instance.getInstanceId()); } } catch (IOException e) { LOGGER.error(e.getMessage()); return Response.status(404).entity("Cannot read the service data file").build(); } catch (JAXBException e1) { LOGGER.error(e1.getMessage()); return Response.status(500).entity("Cannot parse the service data file").build(); } return Response.status(200).entity("Undeploy nodes").build(); } /* * INTERNAL SERVICES FOR CLOUD SERVICES */ @Override public Response getService(String serviceDeployId) throws SalsaException { if (serviceDeployId.equals("")) { throw new IllegalConfigurationAPICallException("Query service data, but the service ID is empty !"); } String fileName = SalsaConfiguration.getServiceStorageDir() + "/" + serviceDeployId + this.dataFileExtension; try { String xml = FileUtils.readFileToString(new File(fileName)); return Response.status(200).entity(xml).build(); } catch (Exception e) { LOGGER.error("Could not find service: " + serviceDeployId + ". Data did not be sent. Error: " + e.toString()); LOGGER.debug("THROWING AN EXCEPTION OF FAILURE TO GET SERVICE !"); throw new ServicedataProcessingException("Cannot read the service data for service ID: " + serviceDeployId); } } @Override public Response getToscaService(String serviceDeployId) throws SalsaException { LOGGER.debug("Read Tosca file and return !"); if (serviceDeployId.equals("")) { throw new IllegalConfigurationAPICallException("Query TOSCA, but the service ID is empty !"); } String fileName = SalsaConfiguration.getServiceStorageDir() + "/" + serviceDeployId; try { String xml = FileUtils.readFileToString(new File(fileName)); return Response.status(200).entity(xml).build(); } catch (Exception e) { throw new ServicedataProcessingException("Cannot read the TOSCA for service ID: " + serviceDeployId); } } @Override public Response getServiceSYBL_DEP_DESP(String serviceId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; LOGGER.debug("Generating deployment desp for SYBL"); try { CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); LOGGER.debug("Service id: " + service.getId()); DeploymentDescription sybl = new DeploymentDescription(); sybl.setAccessIP("localhost"); for (ServiceTopology topo : service.getComponentTopologyList()) { LOGGER.debug("Topo ID: " + topo.getId()); List<ServiceUnit> units = topo.getComponentsByType(SalsaEntityType.SOFTWARE); units.addAll(topo.getComponentsByType(SalsaEntityType.WAR)); sybl.setCloudServiceID(serviceId); for (ServiceUnit unit : units) { LOGGER.debug("NodeID: " + unit.getId()); DeploymentUnit syblDepUnit = new DeploymentUnit(); syblDepUnit.setServiceUnitID(unit.getId()); for (ServiceInstance instance : unit.getInstancesList()) { ServiceUnit hostedUnit = topo.getComponentById(unit.getHostedId()); ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer()); // in the case we have more than one software stack while (!hostedUnit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString()) && !hostedUnit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) { hostedUnit = topo.getComponentById(hostedUnit.getHostedId()); hostedInstance = hostedUnit.getInstanceById(hostedInstance.getHostedId_Integer()); } LOGGER.debug("Host instance: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId()); SalsaInstanceDescription_VM vm = (SalsaInstanceDescription_VM) hostedInstance.getProperties().getAny(); AssociatedVM assVM = new AssociatedVM(); assVM.setIp(vm.getPrivateIp().trim()); assVM.setUuid(vm.getInstanceId()); syblDepUnit.addAssociatedVM(assVM); } sybl.getDeployments().add(syblDepUnit); } } JAXBContext a = JAXBContext.newInstance(DeploymentDescription.class); Marshaller mar = a.createMarshaller(); mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter xmlWriter = new StringWriter(); mar.marshal(sybl, xmlWriter); return Response.status(200).entity(xmlWriter.toString()).build(); } catch (JAXBException e1) { throw new ServicedataProcessingException("Cannot parse the data using JAXB for service ID: " + serviceId); } catch (IOException e2) { throw new ServicedataProcessingException("Cannot read the data for service ID: " + serviceId); } } private String getIpOfServiceInstance(String serviceId, String nodeId, int instanceId) { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { LOGGER.debug("Searching IP of instance"); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit unit = service.getComponentById(nodeId); ServiceInstance instance = unit.getInstanceById(instanceId); LOGGER.debug("Searching IP of instance 1"); ServiceUnit hostedUnit = service.getComponentById(unit.getHostedId()); LOGGER.debug("Searching IP of instance 2"); ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer()); LOGGER.debug("Searching IP of instance 3"); while (!hostedUnit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString()) && !hostedUnit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) { LOGGER.debug("Searching IP of instance 4"); hostedUnit = service.getComponentById(hostedUnit.getHostedId()); hostedInstance = hostedUnit.getInstanceById(hostedInstance.getHostedId_Integer()); } LOGGER.debug("Searching IP of instance 5"); if (hostedInstance.getProperties() == null) { return ""; } SalsaInstanceDescription_VM vm = (SalsaInstanceDescription_VM) hostedInstance.getProperties().getAny(); LOGGER.debug("Searching IP of instance 6. VMID=" + vm.getInstanceId()); LOGGER.debug("Searching IP of instance 6. IP=" + vm.getPrivateIp()); return vm.getPrivateIp().trim(); } catch (IOException | JAXBException e) { LOGGER.debug(e.getMessage()); return ""; } } /* * INTERFNAL SERVICES FOR TOPOLOGY */ /* (non-Javadoc) * @see at.ac.tuwien.dsg.cloud.salsa.engine.services.SalsaEngineIntenalInterface#addRelationship(javax.xml.bind.JAXBElement, java.lang.String, java.lang.String) */ @Override public Response addRelationship( ServiceUnitRelationship data, String serviceId, String topologyId) throws SalsaException { String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { MutualFileAccessControl.lockFile(); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceTopology topo = service.getComponentTopologyById(topologyId); SalsaReplicaRelationships rels = topo.getRelationships(); if (rels == null) { rels = new SalsaReplicaRelationships(); topo.setRelationships(rels); } //rels.addRelationship(data.getValue()); rels.addRelationship(data); SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Updated relationship ").build(); } /* (non-Javadoc) * @see at.ac.tuwien.dsg.cloud.salsa.engine.services.SalsaEngineIntenalInterface#updateNodeIdCounter(java.lang.String, java.lang.String, java.lang.String, int) */ @Override public Response updateNodeIdCounter( String serviceId, String topologyId, String nodeId, int value) throws SalsaException { try { MutualFileAccessControl.lockFile(); String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess .readSalsaServiceFile(salsaFile); ServiceTopology topo = service .getComponentTopologyById(topologyId); ServiceUnit nodeData = topo.getComponentById(nodeId); nodeData.setIdCounter(value); SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Updated node " + nodeId + " on service " + serviceId + " node ID counter: " + value).build(); } /* * INTERNAL SERVICES FOR INSTANCE UNITS */ @Override public Response addInstanceUnitMetaData(ServiceInstance data, String serviceId, String topologyId, String nodeId) throws SalsaException { EngineLogger.logger.debug("addInstanceUnitMetaData1. STARTING: " + serviceId + "/" + topologyId + "/" + nodeId + "/" + data.getInstanceId()); String fileName = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { MutualFileAccessControl.lockFile(); EngineLogger.logger.debug("addInstanceUnitMetaData2. STARTING TO TRY BLOCK"); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(fileName); EngineLogger.logger.debug("addInstanceUnitMetaData3. Compo.id" + service.getId()); ServiceTopology topo = service.getComponentTopologyById(topologyId); EngineLogger.logger.debug("addInstanceUnitMetaData4. Compo.id" + topo.getId()); ServiceUnit compo = topo.getComponentById(nodeId); EngineLogger.logger.debug("addInstanceUnitMetaData5. Compo.id" + compo.getId()); ServiceInstance replicaData = data;//.getValue(); EngineLogger.logger.debug("addInstanceUnitMetaData6. Instance id: " + replicaData.getInstanceId()); int id = replicaData.getInstanceId(); ServiceInstance existedInstance = compo.getInstanceById(id); if (existedInstance == null) { EngineLogger.logger.debug("Create new instance meta-data"); compo.addInstance(replicaData); } else { EngineLogger.logger.debug("Update old instance meta-data"); compo.getInstanceById(id).setHostedId_Integer(replicaData.getHostedId_Integer()); } EngineLogger.logger.debug("addInstanceUnitMetaData. writing down service id: " + service.getId()); SalsaXmlDataProcess.writeCloudServiceToFile(service, fileName); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } MutualFileAccessControl.releaseFile(); return Response.status(201).entity("Spawned VM").build(); //TODO: What happen if it is fail to spawn a VM ? } @Override public Response addServiceUnitMetaData(ServiceUnit data, String serviceId, String topologyId) throws SalsaException { String fileName = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; String toscaFileName = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId; try { MutualFileAccessControl.lockFile(); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(fileName); ServiceTopology topo = service.getComponentTopologyById(topologyId); topo.addComponent(data); SalsaXmlDataProcess.writeCloudServiceToFile(service, fileName); // update the Tosca TDefinitions def = ToscaXmlProcess.readToscaFile(toscaFileName); TNodeTemplate toscaNode = new TNodeTemplate(); toscaNode.setId(data.getId()); toscaNode.setMinInstances(data.getMin()); toscaNode.setMaxInstances(Integer.toString(data.getMax())); toscaNode.setType(new QName(data.getType())); TDeploymentArtifact dA = new TDeploymentArtifact(); dA.setArtifactRef(new QName(toscaNode.getId() + "_artifact")); dA.setArtifactType(new QName(data.getArtifactType())); TDeploymentArtifacts dAs = new TDeploymentArtifacts(); dAs.getDeploymentArtifact().add(dA); toscaNode.setDeploymentArtifacts(dAs); //ToscaStructureQuery.getFirstServiceTemplate(def).getTopologyTemplate().getNodeTemplateOrRelationshipTemplate().add(toscaNode); ToscaStructureQuery.getTopologyTemplate(topologyId, def).getNodeTemplateOrRelationshipTemplate().add(toscaNode); TArtifactTemplate artTemp = new TArtifactTemplate(); ArtifactReferences artRefs = new ArtifactReferences(); TArtifactReference artRef = new TArtifactReference(); artRefs.getArtifactReference().add(artRef); artTemp.setArtifactReferences(artRefs); artTemp.setId(toscaNode.getId() + "_artifact"); artTemp.setType(new QName(data.getArtifactType())); def.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(artTemp); ServiceUnit hostNode = service.getComponentById(topologyId, data.getHostedId()); if (hostNode == null) { LOGGER.error("Cannot find the host node for this node."); return Response.status(401).entity("Cannot find the host node for this node.").build(); } TNodeTemplate toscaHostNode = ToscaStructureQuery.getNodetemplateById(hostNode.getId(), def); TRelationshipTemplate rela = new TRelationshipTemplate(); SourceElement source = new SourceElement(); source.setRef(toscaNode); TargetElement target = new TargetElement(); target.setRef(toscaHostNode); rela.setSourceElement(source); rela.setTargetElement(target); //ToscaStructureQuery.getFirstServiceTemplate(def).getTopologyTemplate().getNodeTemplateOrRelationshipTemplate().add(rela); ToscaStructureQuery.getTopologyTemplate(topologyId, def).getNodeTemplateOrRelationshipTemplate().add(rela); ToscaXmlProcess.writeToscaDefinitionToFile(def, toscaFileName); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } MutualFileAccessControl.releaseFile(); return Response.status(201).entity("Added service unit").build(); } @Override public Response addServiceUnitMetaData(String toscaXML, String serviceId, String topologyId) throws SalsaException { String fileName = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; try { MutualFileAccessControl.lockFile(); TNodeTemplate toscaNode = ToscaXmlProcess.readToscaNodeTemplateFromString(toscaXML); if (toscaNode == null || toscaNode.getId() == null || toscaNode.getType() == null) { LOGGER.error("Wrong format of ToscaXML !"); return Response.status(404).entity("Wrong format of ToscaXML !").build(); } String id = toscaNode.getId(); String type = toscaNode.getType().getLocalPart(); ServiceUnit data = new ServiceUnit(id, type); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(fileName); ServiceTopology topo = service.getComponentTopologyById(topologyId); topo.addComponent(data); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } MutualFileAccessControl.releaseFile(); return Response.status(201).entity("Added service unit").build(); } @Override public Response updateInstanceUnitCapability(SalsaCapaReqString data, String serviceId, String topologyId, String nodeId, int instanceId) throws SalsaException { MutualFileAccessControl.lockFile(); try { String serviceFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(serviceFile); ServiceInstance rep = service.getInstanceById(nodeId, instanceId); Capabilities capas = rep.getCapabilities(); if (capas == null) { // there is no capability list before, create a new capas = new Capabilities(); rep.setCapabilities(capas); } List<SalsaCapaReqString> capaLst = capas.getCapability(); //capaLst.add(data.getValue()); // replace data if the value is "salsa:ip" if (data.getValue().equals("salsa:localIP")) { LOGGER.debug("Update instance unit capability - Get string => salsa:localIP"); data.setValue(getIpOfServiceInstance(serviceId, nodeId, instanceId)); } capaLst.add(data); SalsaXmlDataProcess.writeCloudServiceToFile(service, serviceFile); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Updated capability for node: " + nodeId + " on service " + serviceId).build(); } @Override public Response updateInstanceUnitProperties( //JAXBElement<Object> data, String data, String serviceId, String topologyId, String nodeId, int instanceId) throws SalsaException { LOGGER.debug("update instance unit prop 1. Raw string data: " + data); MutualFileAccessControl.lockFile(); try { LOGGER.debug("update instance unit prop 2"); String serviceFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; LOGGER.debug("Setting property. Read service file: " + serviceFile); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(serviceFile); ServiceUnit unit = service.getComponentById(nodeId); LOGGER.debug("update instance unit prop 3: " + service.getId()); ServiceInstance rep = service.getInstanceById(nodeId, instanceId); Properties props = rep.getProperties(); if (props == null) { props = new Properties(); } // marshall data and add to props. Currently only can receive VM and Docker properties JAXBContext context; if (unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) { LOGGER.debug("update instance unit: marshall VM description "); context = JAXBContext.newInstance(SalsaInstanceDescription_VM.class); Unmarshaller um = context.createUnmarshaller(); SalsaInstanceDescription_VM propData = (SalsaInstanceDescription_VM) um.unmarshal(new StringReader(data)); LOGGER.debug("VM properties captured: " + propData.getInstanceId() + ", ip: " + propData.getPrivateIp()); props.setAny(propData); } else if (unit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) { LOGGER.debug("update instance unit: marshall DOCKER description "); context = JAXBContext.newInstance(SalsaInstanceDescription_Docker.class); Unmarshaller um = context.createUnmarshaller(); SalsaInstanceDescription_Docker propData = (SalsaInstanceDescription_Docker) um.unmarshal(new StringReader(data)); LOGGER.debug("Docker properties captured: " + propData.getDockername() + ", portmap: " + propData.getPortmap()); props.setAny(propData); } rep.setProperties(props); SalsaXmlDataProcess.writeCloudServiceToFile(service, serviceFile); LOGGER.debug("update instance unit prop END: " + service.getId()); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Updated capability for node: " + nodeId + ", replica: " + instanceId + " on service " + serviceId).build(); } @Override public Response updateNodeMetadata( String metadata, String serviceId, String topologyId, String nodeId) throws SalsaException { LOGGER.debug("update metadata: " + metadata); MutualFileAccessControl.lockFile(); try { SalsaMsgUpdateMetadata data = SalsaMsgUpdateMetadata.fromJson(metadata); String serviceFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(serviceFile); ServiceUnit node = service.getComponentById(nodeId); for (Map.Entry<String, String> entry : data.getActions().entrySet()) { node.addPrimitiveOperation(PrimitiveOperation.newCommandType(entry.getKey(), entry.getValue())); } SalsaXmlDataProcess.writeCloudServiceToFile(service, serviceFile); // save to the ELISE database } catch (JAXBException | IOException ex) { EngineLogger.logger.error(ex.getMessage()); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(200).entity("Updated metadata for node: " + nodeId + ", on service " + serviceId).build(); } @Override public Response updateNodeState(String serviceId, // topology is not need actually String topologyId, String nodeId, int instanceId, String value, String extra) throws SalsaException { try { LOGGER.debug("UPDATE NODE STATE: " + nodeId + ", instance: " + instanceId + ", state: " + value); MutualFileAccessControl.lockFile(); String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit nodeData = service.getComponentById(nodeId); if (instanceId == -1) { nodeData.setState(SalsaEntityState.fromString(value)); updateComponentStateBasedOnInstance(service); SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } else { // update for instance ServiceInstance replicaInst = nodeData.getInstanceById(instanceId); if (SalsaEntityState.fromString(value) != null) { replicaInst.setState(SalsaEntityState.fromString(value)); replicaInst.setExtra(extra); updateComponentStateBasedOnInstance(service); // update the Tosca node SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } else { throw new ServicedataProcessingException(serviceId + "/" + nodeId + "/" + instanceId); } } } catch (JAXBException | IOException ex) { LOGGER.error("Failed to update node state of node: " + nodeId + ", instance: " + instanceId + ", state: " + value); throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } LOGGER.debug("UPDATE NODE STATE: " + nodeId + ", instance: " + instanceId + ", state: " + value + " - DONE !"); return Response.status(200).entity("Updated node " + nodeId + " on service " + serviceId + " deployed status: " + value).build(); } @Override public Response getRequirementValue( String serviceId, String topologyId, String nodeId, int instanceId, String reqId) throws SalsaException { try { // read current TOSCA and SalsaCloudService String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceTopology topo = service.getComponentTopologyById(topologyId); String toscaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId; TDefinitions def = ToscaXmlProcess.readToscaFile(toscaFile); TRequirement req = (TRequirement) ToscaStructureQuery.getRequirementOrCapabilityById(reqId, def); TCapability capa = ToscaStructureQuery.getCapabilitySuitsRequirement(req, def); String capaid = capa.getId(); TNodeTemplate toscanode = ToscaStructureQuery.getNodetemplateOfRequirementOrCapability(capa, def); // get the capability of the first instance of the node which have id ServiceUnit nodeData = topo.getComponentById(toscanode.getId()); if (nodeData.getInstancesList().isEmpty()) { return Response.status(201).entity("").build(); } ServiceInstance nodeInstanceOfCapa = nodeData.getInstanceById(0); String reqAndCapaValue = nodeInstanceOfCapa.getCapabilityValue(capaid); return Response.status(200).entity(reqAndCapaValue).build(); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } } @Override public Response queueAction(String serviceId, String nodeId, int instanceId, String actionName) throws SalsaException { return queueActionWithParameter(serviceId, nodeId, instanceId, actionName, ""); } @Override public Response queueActionWithParameter( String serviceId, String nodeId, int instanceId, String actionName, String parameters) throws SalsaException { try { EngineLogger.logger.debug("Queueing action: " + serviceId + "/" + nodeId + "/" + instanceId + "/" + actionName); MutualFileAccessControl.lockFile(); String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); ServiceUnit nodeData = service.getComponentById(nodeId); String topoID = service.getTopologyOfNode(nodeData.getId()).getId(); ServiceInstance instance = nodeData.getInstanceById(instanceId); instance.queueAction(actionName); instance.setState(SalsaEntityState.STAGING_ACTION); // update to data file SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); MutualFileAccessControl.releaseFile(); String runByMe = ""; if (nodeData.getPrimitiveByName(actionName) != null) { runByMe = nodeData.getPrimitiveByName(actionName).getExecutionREF() + " " + parameters.replace(",", " "); } else { EngineLogger.logger.debug("There is no description for action: " + actionName + ". Pioneer will use its default operation."); } String preRunByMe = ""; String newActionID = UUID.randomUUID().toString(); EngineLogger.logger.debug("Converting SALSA node type to category. nodetype: {}", nodeData.getType()); ServiceCategory theCategory = InfoParser.mapOldAndNewCategory(SalsaEntityType.fromString(nodeData.getType())); EngineLogger.logger.debug("Converted SALSA node type to category. nodetype: {} == {}", nodeData.getType(), theCategory); // if the action name is undeploy, so undeploy. Only support artifact type of script. E.g., do not support stop docker if (actionName.equals("undeploy")) { PrimitiveOperation stopAction = nodeData.getPrimitiveByName("stop"); if (stopAction != null) { // there is a stop action, add it before the runByMe LOGGER.debug("Found a stop action to run before undeploy action"); preRunByMe = stopAction.getExecutionREF(); LOGGER.debug("The command will be sent is: " + preRunByMe + " and undeploy following: " + runByMe + ". Undeploy apply for node type: " + InfoParser.mapOldAndNewCategory(SalsaEntityType.fromString(nodeData.getType()))); } // to undeploy Docker, we send the DockerID into runByMe if (theCategory == ServiceCategory.docker) { EngineLogger.logger.debug("The instance {}/{}/{} is Docker, checking docker property", serviceId, nodeId, instanceId); if (instance.getProperties() != null) { SalsaInstanceDescription_Docker vm = (SalsaInstanceDescription_Docker) instance.getProperties().getAny(); if (vm != null && vm.getInstanceId() != null) { runByMe = vm.getInstanceId(); EngineLogger.logger.debug("The instance {}/{}/{} is Docker, send runByMe as docker id: {}", serviceId, nodeId, instanceId, runByMe); } } else { EngineLogger.logger.error("Cannot get the property of docker node: {}/{}/{}", serviceId, nodeId, instanceId); } } // TODO: maybe support more things } // search for the first artifact type which is not misc, which is used to install the service SalsaArtifactType artifactTypeOfDeployment = null; for (ServiceUnit.Artifacts art : nodeData.getArtifacts()) { if (!art.getType().equals(SalsaArtifactType.misc.getString())) { artifactTypeOfDeployment = SalsaArtifactType.fromString(art.getType()); break; } } String pioneerID = PioneerManager.getPioneerIDForNode(SalsaConfiguration.getUserName(), serviceId, nodeId, instanceId, service); if (pioneerID == null) { EngineLogger.logger.error("The pioneer on node {}/{}/{}/{} is not registered to SalsaEngine, deployment aborted !", SalsaConfiguration.getUserName(), serviceId, nodeId, instanceId); return null; } EngineLogger.logger.debug("Found a pioneer to execute this action: {}", pioneerID); SalsaMsgConfigureArtifact configCommand = new SalsaMsgConfigureArtifact(newActionID, actionName, pioneerID, SalsaConfiguration.getUserName(), serviceId, topoID, nodeId, instanceId, theCategory, preRunByMe, runByMe, artifactTypeOfDeployment, ""); ActionIDManager.addAction(newActionID, configCommand); SalsaMessage msg = new SalsaMessage(SalsaMessage.MESSAGE_TYPE.salsa_reconfigure, SalsaConfiguration.getSalsaCenterEndpoint(), SalsaMessageTopic.getPioneerTopicByID(pioneerID), "", configCommand.toJson()); MessagePublishInterface publish = SalsaConfiguration.getMessageClientFactory().getMessagePublisher(); publish.pushMessage(msg); } catch (JAXBException | IOException ex) { EngineLogger.logger.error("Unable to queue action. " + ex.getMessage()); ex.printStackTrace(); throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(201).entity("ok").build(); } @Override public Response unqueueAction( String serviceId, String nodeId, int instanceId) throws SalsaException { try { EngineLogger.logger.debug("Unqueueing action: " + serviceId + "/" + nodeId + "/" + instanceId); MutualFileAccessControl.lockFile(); String salsaFile = SalsaConfiguration.getServiceStorageDir() + File.separator + serviceId + this.dataFileExtension; EngineLogger.logger.debug("Debug - Unqueueing action 1"); CloudService service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); EngineLogger.logger.debug("Debug - Unqueueing action 2"); ServiceUnit nodeData = service.getComponentById(nodeId); EngineLogger.logger.debug("Debug - Unqueueing action 3. Node: " + nodeData.getId()); ServiceInstance instance = nodeData.getInstanceById(instanceId); EngineLogger.logger.debug("Debug - Unqueueing action 4"); EngineLogger.logger.debug("Debug - Instance: " + instance.getInstanceId() + "/" + instance.getState()); instance.unqueueAction(); EngineLogger.logger.debug("Debug - Unqueueing action 5"); SalsaXmlDataProcess.writeCloudServiceToFile(service, salsaFile); } catch (JAXBException | IOException ex) { throw new ServicedataProcessingException(serviceId, ex); } finally { MutualFileAccessControl.releaseFile(); } return Response.status(201).entity("ok").build(); } protected void updateComponentStateBasedOnInstance(SalsaEntity nodeData) { Map<SalsaEntityState, Integer> rankState = new HashMap<>(); rankState.put(SalsaEntityState.UNDEPLOYED, 0); rankState.put(SalsaEntityState.ERROR, 1); rankState.put(SalsaEntityState.ALLOCATING, 2); rankState.put(SalsaEntityState.STAGING, 3); rankState.put(SalsaEntityState.STAGING_ACTION, 4); rankState.put(SalsaEntityState.CONFIGURING, 5); rankState.put(SalsaEntityState.DEPLOYED, 7); rankState.put(SalsaEntityState.INSTALLING, 8); if (nodeData.getClass().equals(CloudService.class)) { LOGGER.debug("Updating the state of the whole service !"); } List<SalsaEntity> insts = new ArrayList<>(); if (nodeData.getClass().equals(ServiceUnit.class)) { List<ServiceInstance> serviceInst = ((ServiceUnit) nodeData).getInstancesList(); for (ServiceInstance instance : serviceInst) { insts.add(instance); } } else if (nodeData.getClass().equals(ServiceTopology.class)) { List<ServiceUnit> serviceUnits = ((ServiceTopology) nodeData).getComponents(); for (ServiceUnit unit : serviceUnits) { updateComponentStateBasedOnInstance(unit); insts.add(unit); } } else if (nodeData.getClass().equals(CloudService.class)) { List<ServiceTopology> serviceTopos = ((CloudService) nodeData).getComponentTopologyList(); for (ServiceTopology topo : serviceTopos) { updateComponentStateBasedOnInstance(topo); insts.add(topo); } } int minState = 8; if (insts.isEmpty()) { nodeData.setState(SalsaEntityState.UNDEPLOYED); return; } for (SalsaEntity inst : insts) { if (minState >= rankState.get(inst.getState())) { nodeData.setState(inst.getState()); minState = rankState.get(inst.getState()); } } } @Override public String health() { try { List<String> str = SystemFunctions.getEndPoints(); String rs = ""; for (String s : str) { rs += s + ","; } return rs; } catch (MalformedObjectNameException | NullPointerException | UnknownHostException | AttributeNotFoundException | InstanceNotFoundException | MBeanException | ReflectionException e) { return "Working but cannot get the endpoint"; } } // note: this function is not care about docker, just the VM @Override public String getGangliaHostInfo(String serviceID, String nodeID, int instanceID) { String salsaFile = SalsaConfiguration.getServiceStorageDir() + "/" + serviceID + ".data"; CloudService service; try { service = SalsaXmlDataProcess.readSalsaServiceFile(salsaFile); return DynamicPlacementHelper.getGangliaVMInfo(service, nodeID, instanceID); } catch (JAXBException | IOException ex) { logger.error("Failed to get the Ganglia information for the service"); } return null; } @Override public Response logMessage(String data) { EngineLogger.logger.debug("Receive a log message from pioneer: " + data); try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("/tmp/salsa.pioneer.message.log", true)))) { out.println(data); out.close(); } catch (IOException e) { EngineLogger.logger.error(e.toString()); } return Response.status(201).entity("saved message: " + data).build(); } }