/*
* 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.impl.richInformationCapability;
import at.ac.tuwien.dsg.cloud.elise.master.QueryManagement.utils.EliseConfiguration;
import at.ac.tuwien.dsg.cloud.elise.master.RESTService.EliseManager;
import at.ac.tuwien.dsg.cloud.elise.model.generic.Capability;
import at.ac.tuwien.dsg.cloud.elise.model.generic.executionmodels.RestExecution;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.UnitInstance;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.GlobalIdentification;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.IDType;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.LocalIdentification;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.CloudService;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceInstance;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceTopology;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceUnit;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.enums.SalsaEntityType;
import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SalsaCenterConnector;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.IaaS.DockerInfo;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.IaaS.VirtualMachineInfo;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.application.SystemServiceInfo;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.types.ServiceCategory;
import at.ac.tuwien.dsg.cloud.salsa.engine.capabilityinterface.UnitCapabilityInterface;
import at.ac.tuwien.dsg.cloud.salsa.common.interfaces.SalsaException;
import at.ac.tuwien.dsg.cloud.salsa.engine.impl.genericCapability.GenericUnitCapability;
import at.ac.tuwien.dsg.cloud.salsa.engine.utils.EngineLogger;
import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SalsaConfiguration;
import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_Docker;
import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_SystemProcess;
import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_VM;
import java.util.Collections;
import java.util.List;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
import org.slf4j.Logger;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.State;
import at.ac.tuwien.dsg.cloud.elise.master.RESTService.EliseRepository;
import at.ac.tuwien.dsg.cloud.elise.model.extra.contract.Contract;
import at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.ServiceUnit.Artifacts;
import at.ac.tuwien.dsg.cloud.salsa.domainmodels.types.SalsaArtifactType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* This enhance the action by adding information into ELISE database
*
* @author Duc-Hung LE
*/
public class RichInformationUnitCapability implements UnitCapabilityInterface {
UnitCapabilityInterface lowerCapa = new GenericUnitCapability();
Logger logger = EngineLogger.logger;
String salsaEndpoint = SalsaConfiguration.getSalsaCenterEndpoint();
@Override
public ServiceInstance deploy(String serviceId, String nodeId, int instanceId) throws SalsaException {
logger.debug("Deploy instance {}/{}/{} before saving information to DB", serviceId, nodeId, instanceId);
lowerCapa.deploy(serviceId, nodeId, instanceId);
logger.debug("Saving information into elise DB: {}/{}/{}", serviceId, nodeId, instanceId);
// save unit to ELISE
SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger);
CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId);
ServiceUnit unit = service.getComponentById(nodeId);
ServiceTopology topo = service.getTopologyOfNode(nodeId);
ServiceInstance instance = unit.getInstanceById(instanceId);
// create unit instance and add several simple domainInfo getting from SALSA
logger.debug("Creating unit instance");
UnitInstance unitInst = makeUnitInstance(service, unit, instance);
// unitInst.hasExtra("salsaID", service.getId() + "/" + unit.getId() + "/" + instance.getInstanceId());
GlobalIdentification globalID = new GlobalIdentification();
globalID.setUuid(instance.getUuid().toString());
LocalIdentification id = new LocalIdentification(unitInst.getCategory(), "SALSA");
// logger.debug("Setting identification with salsaID=" + unitInst.getExtra().get("salsaID"));
id.hasIdentification(IDType.SALSA_SERVICE.toString(), serviceId);
id.hasIdentification(IDType.SALSA_TOPOLOGY.toString(), serviceId + "/" + topo.getId());
id.hasIdentification(IDType.SALSA_UNIT.toString(), serviceId + "/" + nodeId);
id.hasIdentification(IDType.SALSA_INSTANCE.toString(), serviceId + "/" + nodeId + "/" + instanceId);
// in the case it is a sensor (name started with sensor ??) and hosted on docker ==> we may have GovOps
if (unit.getId().toLowerCase().startsWith("sensor") && !unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
// logger.debug("Instance {} is a sensor, checking IP_PORT identification", unitInst.getExtra().get("salsaID"));
ServiceUnit hostedUnit = service.getComponentById(unit.getHostedId());
// get port map of Docker
if (hostedUnit != null && hostedUnit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) {
logger.debug("The sensor is hosted on node {}", hostedUnit.getId());
ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer());
if (hostedInstance != null && hostedInstance.getProperties() != null) {
logger.debug("debug1: marshalling docker description ........");
SalsaInstanceDescription_Docker dockerDesp = (SalsaInstanceDescription_Docker) hostedInstance.getProperties().getAny();
logger.debug("debug1: marshalling docker description ........ DONE !");
// portmap should be 2102:10.99.0.21:2102 80:10.99.0.21:9080 4567:10.99.0.21:4567
String portmap = dockerDesp.getPortmap() + " ";
logger.debug("Get port map: " + portmap);
if (!portmap.trim().isEmpty()) {
String portmap80 = portmap.substring(portmap.indexOf("80:") + 3, portmap.indexOf(" ", portmap.indexOf("80:")));
if (portmap80.trim().isEmpty()) {
logger.debug("{} will be {}", IDType.IP_PORT.toString(), portmap80);
id.hasIdentification(IDType.IP_PORT.toString(), portmap80.trim());
}
}
} else {
logger.debug("Do not find the instance or the instance properties of node {} that hosted node {}/{}", hostedUnit.getId(), unit.getId(), instance.getInstanceId());
}
} else {
logger.debug("The hostedUnit of the sensor {} is null or not a docker", unit.getId());
}
}
globalID.addLocalIdentification(id);
logger.debug("adding localIdentification for node: {}/{} with id: {}", nodeId, instanceId, globalID.toJson());
unitInst.setIdentification(globalID);
logger.debug("Prepare to connect to EliseManager service: {}", EliseConfiguration.getRESTEndpointLocal());
// TODO: add more local identification here to adapt with other management tool: SYBL, rtGovOps?
// save the UnitInstance into the graph DB
EliseManager eliseManager = (EliseManager) JAXRSClientFactory.create(EliseConfiguration.getRESTEndpointLocal(), EliseManager.class, Collections.singletonList(new JacksonJaxbJsonProvider()));
logger.debug("It may be connectted or not ! Now cheking...");
if (eliseManager != null) {
logger.debug("eliseManager is not null");
try {
eliseManager.health();
} catch (Exception e) {
logger.error(e.getMessage());
e.printStackTrace();
}
logger.debug("Yes, we can check the health");
logger.debug("Checking ELISE connectivity: [" + eliseManager.health() + "]");
} else {
logger.error("Cannot contact to ELISE");
}
logger.debug("EliseConfiguration.getRESTEndpointLocal() = " + EliseConfiguration.getRESTEndpointLocal());
EliseRepository unitInstanceDAO = (EliseRepository) JAXRSClientFactory.create(EliseConfiguration.getRESTEndpointLocal(), EliseRepository.class, Collections.singletonList(new JacksonJsonProvider()));
if (unitInstanceDAO == null){
logger.debug("unitInstanceDAO is null");
} else {
logger.debug("unitInstanceDAO is NOT null");
}
if (unitInstanceDAO != null) {
logger.debug("unitInstanceDao is not null, prepare to add");
// get the hoston and connect-to unit in the database if exist, then add to the unitInst
// persist relationship after unit
logger.debug("Adding relationship to persist data");
ServiceInstance hostedServiceInstance = service.getInstanceById(unit.getHostedId(), instance.getHostedId_Integer());
if (hostedServiceInstance != null) {
logger.debug("Found a instance of host-on relationship: instance " + unit.getHostedId() + "/" + hostedServiceInstance.getInstanceId());
UnitInstance hostedUnitInstance = unitInstanceDAO.readUnitInstance(hostedServiceInstance.getUuid().toString());
if (hostedUnitInstance != null) {
logger.debug(" --> and yes we found it in the database, phew: " + hostedUnitInstance.getUuid() + "/" + hostedUnitInstance.getName());
hostedUnitInstance.setCapabilities(null); // ortherwise salsa will persist many capabilities
unitInst.hostedOnInstance(hostedUnitInstance);
// HostOnRelationshipInstance newRela = new HostOnRelationshipInstance(unitInst, hostedUnitInstance);
// logger.debug(" --> new host-on relationship is created: " + unitInst.getId() +" and " + hostedUnitInstance.getId());
// unitInstanceDAO.addRelationshipHostOn(newRela);
} else {
logger.debug(" --> but do not find such instance in the database");
}
} else {
logger.debug("Hosted unit is null, it should be OS instance: " + unit.getType());
}
if (unit.getConnecttoId() != null && !unit.getConnecttoId().isEmpty()) {
for (String connectedID : unit.getConnecttoId()) {
ServiceUnit connectedUnit = service.getComponentById(connectedID);
if (connectedUnit != null && connectedUnit.getInstancesList().size() > 0) {
logger.debug("The unit is connect-to: " + connectedUnit.getId());
ServiceInstance connectedServiceInstance = connectedUnit.getInstancesList().get(0);
logger.debug(" --> and we will check if the instance is exist in the db, id: " + connectedServiceInstance.getUuid());
UnitInstance connectedUnitInstance = unitInstanceDAO.readUnitInstance(connectedServiceInstance.getUuid().toString());
if (connectedUnitInstance != null) {
logger.debug(" ----> yes the instance is exist, save it: " + connectedUnitInstance.getUuid());
connectedUnitInstance.setCapabilities(null);
unitInst.connectToInstance(connectedUnitInstance);
// ConnectToRelationshipInstance newRela = new ConnectToRelationshipInstance(unitInst, connectedUnitInstance, "");
// logger.debug(" --> new connectto relationship is created: " + unitInst.getId() +" and " + connectedUnitInstance.getId());
// unitInstanceDAO.addRelationshipConnectTo(newRela);
} else {
logger.debug(" ----> no, the instance is not found in database");
}
} else {
logger.debug("Some error should happen, no service unit is found with id : " + connectedID);
}
}
} else {
logger.debug(unit.getId() + " has not any connection to relationships");
}
System.out.println("Saving unit instance: "+ unitInst.toJson());
// TODO: this remove the domain info temporary, will be added later
// unitInst.setDomain(null);
unitInstanceDAO.saveUnitInstance(unitInst);
System.out.println("Save unit instance DONE !" + unitInst.getUuid());
} else {
logger.error("Cannot connect to the elise DB");
}
return instance;
}
@Override
public void remove(String serviceId, String nodeId, int instanceId) throws SalsaException {
logger.debug("Trying to remove information of {}/{}/{} from ELISE database...", serviceId, nodeId, instanceId);
EliseRepository unitInstanceDAO = (EliseRepository) JAXRSClientFactory.create(EliseConfiguration.getRESTEndpointLocal(), EliseRepository.class, Collections.singletonList(new JacksonJsonProvider()));
SalsaCenterConnector centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger);
CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId);
if (service == null) {
EngineLogger.logger.error("Cannot get service to removed: {}", serviceId);
return;
}
ServiceUnit unit = service.getComponentById(nodeId);
if (unit == null) {
EngineLogger.logger.error("Try to delete instance of unit {}/{} but get NULL data.", serviceId, nodeId);
return;
}
ServiceInstance instance = unit.getInstanceById(instanceId);
if (instance != null) {
if (unitInstanceDAO != null) {
logger.debug("Trying to delete unit with UUID: {}" + instance.getUuid());
try {
unitInstanceDAO.deleteUnitInstance(instance.getUuid().toString());
EliseManager eliseManager = ((EliseManager) JAXRSClientFactory.create(EliseConfiguration.getRESTEndpointLocal(), EliseManager.class, Collections.singletonList(new JacksonJsonProvider())));
logger.debug("Now deleting the unit identification: " + instance.getUuid().toString());
eliseManager.deleteGlobalIdentification(instance.getUuid().toString());
} catch (Exception e) {
logger.error("Msg: " + e.getMessage() + ", cause: " + e.getCause());
e.printStackTrace();
}
} else {
logger.error("Cannot connect to the database to delete instance" + instance.getUuid());
}
lowerCapa.remove(serviceId, nodeId, instanceId);
} else {
logger.error("Do not found the instance to delete: {}/{}/{}", serviceId, nodeId, instanceId);
}
}
private UnitInstance makeUnitInstance(CloudService service, ServiceUnit unit, ServiceInstance ins) {
logger.debug("Making unit instance: {}/{}/{}", service.getId(), unit.getId(), ins.getInstanceId());
UnitInstance unitInst = new UnitInstance(unit.getId(), null);
ServiceTopology topo = service.getTopologyOfNode(unit.getId());
// unitInst.hasExtra("salsaID", service.getId() + "/" + topo.getId() + "/" + unit.getId() + "/" + ins.getInstanceId());
// unitInst.hasExtra("salsaState", ins.getState().getNodeStateString());
unitInst.setState(State.valueOf(ins.getState().toString()));
unitInst.setUuid(ins.getUuid().toString());
/**
* MAKE INITIAL DOMAIN INFO
*/
if (unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
logger.debug("Making VM domain info: {}/{}/{}", service.getId(), unit.getId(), ins.getInstanceId());
unitInst.setCategory(ServiceCategory.VirtualMachine);
// get basic VM information
SalsaInstanceDescription_VM vmDescription = getVMOfUnit(service, topo, unit, ins);
VirtualMachineInfo VMInfo = new VirtualMachineInfo(vmDescription.getProvider(), vmDescription.getInstanceId(), "VM:" + vmDescription.getPrivateIp());
VMInfo.setPrivateIp(vmDescription.getPrivateIp());
VMInfo.setPublicIp(vmDescription.getPublicIp());
if (VMInfo.getPackagesDependencies() != null && VMInfo.getPackagesDependencies().getPackageDependency() != null) {
VMInfo.getPackagesDependencies().getPackageDependency().addAll(vmDescription.getPackagesDependenciesList().getPackageDependency());
}
// TODO: the domain info is NOT set, or DB persistance does not work, make it later
// unitInst.setDomain(VMInfo);
// unitInst.setDomainClazz(VMInfo.getClass());
} else if (unit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) {
logger.debug("Making App container domain info: {}/{}/{}", service.getId(), unit.getId(), ins.getInstanceId());
unitInst.setCategory(ServiceCategory.docker);
// get basic docker info
SalsaInstanceDescription_Docker dockerDescription = getDockerUnit(service, topo, unit, ins);
if (dockerDescription != null) {
logger.debug("Adding docker feature ...");
DockerInfo dockerInfo = new DockerInfo("docker", dockerDescription.getInstanceId(), dockerDescription.getDockername());
// unitInst.setDomain(dockerInfo);
// unitInst.setDomainClazz(dockerInfo.getClass());
}
} else if (unit.getType().equals(SalsaEntityType.TOMCAT.getEntityTypeString())) {
unitInst.setCategory(ServiceCategory.TomcatContainer);
} else if (unit.getId().toLowerCase().startsWith("sensor")) {
unitInst.setCategory(ServiceCategory.Sensor);
} else if (unit.getType().equals(SalsaEntityType.SERVICE.getEntityTypeString())) {
// get basic information of system service
unitInst.setCategory(ServiceCategory.SystemService);
logger.debug("Making SystemService domain info: {}/{}/{}", service.getId(), unit.getId(), ins.getInstanceId());
if (ins.getProperties() != null) {
SalsaInstanceDescription_SystemProcess sysProcess = (SalsaInstanceDescription_SystemProcess) ins.getProperties().getAny();
SystemServiceInfo systemServiceInfo = new SystemServiceInfo(sysProcess.getName(), sysProcess.getName());
// unitInst.setDomain(systemServiceInfo);
// unitInst.setDomainClazz(systemServiceInfo.getClass());
}
} else {
unitInst.setCategory(ServiceCategory.ExecutableApp);
}
unitInst.setCategory(ServiceCategory.valueOf(unit.getType().toString()));
List<at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.PrimitiveOperation> pos = ins.getPrimitive();
boolean existedDeploy = false;
boolean existedUnDeploy = false;
if (pos != null) {
for (at.ac.tuwien.dsg.cloud.salsa.common.cloudservice.model.PrimitiveOperation po : pos) {
String actionName = po.getName();
String actionREF = this.salsaEndpoint + "/services/" + service.getId() + "/nodes/" + unit.getId() + "/instances/" + ins.getInstanceId() + "/action_queue/" + actionName;
unitInst.hasCapability(new Capability(actionName, Capability.ExecutionMethod.REST, new RestExecution(actionREF, RestExecution.RestMethod.POST, "")).executedBy("SALSA"));
if (actionName.equals("deploy")) {
existedDeploy = true;
}
if (actionName.equals("undeploy")) {
existedUnDeploy = true;
}
}
}
if (!existedUnDeploy) {
String destroyInstanceStr = this.salsaEndpoint + "/services/" + service.getId() + "/topologies/" + topo.getId() + "/nodes/" + unit.getId() + "/instances/" + ins.getInstanceId();
unitInst.hasCapability(new Capability("undeploy", Capability.ExecutionMethod.REST, new RestExecution(destroyInstanceStr, RestExecution.RestMethod.DELETE, "")).executedBy("SALSA"));
}
if (!existedDeploy) {
String deploymore = this.salsaEndpoint + "/services/" + service.getId() + "/topologies/" + topo.getId() + "/nodes/" + unit.getId() + "/instance-count/{quantity}";
unitInst.hasCapability(new Capability("deploy", Capability.ExecutionMethod.REST, new RestExecution(deploymore, RestExecution.RestMethod.POST, "")).hasParameters("quantity", "1").executedBy("SALSA"));
}
logger.debug("Creating domain info done: {}/{}/{}", service.getId(), unit.getId(), ins.getInstanceId());
// add contract artifact if exist
logger.debug("Checking contract artifacts");
try {
for (Artifacts art : unit.getArtifacts()) {
if (art.getType().equals(SalsaArtifactType.contract.toString())) {
String url = art.getReference();
String contractJson = IOUtils.toString(new URL(url));
logger.debug("Contract JSON: " + contractJson);
ObjectMapper mapper = new ObjectMapper();
Contract contract = mapper.readValue(contractJson, Contract.class);
unitInst.setContract(contract);
}
}
} catch (IOException e) {
logger.error("Cannnot parse contract information. Error: " + e.getMessage());
e.printStackTrace();
}
return unitInst;
}
private SalsaInstanceDescription_Docker getDockerUnit(CloudService service, ServiceTopology topo, ServiceUnit unit, ServiceInstance instance) {
logger.debug("Getting Docker information if it is available ...");
if (unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
logger.debug(unit.getId() + " is a VM node, there will be no docker, just done!");
return null;
}
ServiceUnit hostedUnit = topo.getComponentById(unit.getHostedId());
ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer());
if (hostedUnit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) {
logger.debug("Found a docker node: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId());
if (hostedInstance.getProperties() == null || hostedInstance.getProperties().getAny() == null) {
return null;
}
return (SalsaInstanceDescription_Docker) hostedInstance.getProperties().getAny();
} else {
logger.debug(unit.getId() + " is not hosted on a docker node. Just done!");
return null;
}
}
private SalsaInstanceDescription_VM getVMOfUnit(CloudService service, ServiceTopology topo, ServiceUnit unit, ServiceInstance instance) {
logger.debug("Getting VM for node: " + service.getId() + "/" + topo.getId() + "/" + unit.getId() + "/" + instance.getInstanceId());
if (unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
logger.debug("The node is acually the OS, return !");
return (SalsaInstanceDescription_VM) instance.getProperties().getAny();
}
ServiceUnit hostedUnit = topo.getComponentById(unit.getHostedId());
ServiceInstance hostedInstance = hostedUnit.getInstanceById(instance.getHostedId_Integer());
while (!hostedUnit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
logger.debug("Not a OS node, checking which hosts this node...");
hostedUnit = topo.getComponentById(hostedUnit.getHostedId());
hostedInstance = hostedUnit.getInstanceById(hostedInstance.getHostedId_Integer());
logger.debug("And we found the host node is: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId());
}
logger.debug("IN conclude, instance is hosted on the OS node: " + hostedUnit.getId() + "/" + hostedInstance.getInstanceId());
return (SalsaInstanceDescription_VM) hostedInstance.getProperties().getAny();
}
}