/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package at.ac.tuwien.dsg.cloud.elise.extensions.salsainfocollector;
import at.ac.tuwien.dsg.cloud.elise.collectorinterfaces.UnitInstanceCollector;
import at.ac.tuwien.dsg.cloud.elise.model.generic.executionmodels.RestExecution;
import at.ac.tuwien.dsg.cloud.elise.model.generic.Capability;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.LocalIdentification;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.UnitInstance;
import at.ac.tuwien.dsg.cloud.elise.model.runtime.State;
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.SalsaEntityState;
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.common.processing.SalsaXmlDataProcess;
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.exceptions.general.EngineConnectionException;
import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_Docker;
import at.ac.tuwien.dsg.cloud.salsa.tosca.extension.SalsaInstanceDescription_VM;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Duc-Hung LE
*/
public class CollectorForSalsa extends UnitInstanceCollector {
static Logger logger = LoggerFactory.getLogger("SALSACollector");
String salsaEndpoint = "http://localhost:8080/salsa-engine";
public CollectorForSalsa() {
this.salsaEndpoint = readAdaptorConfig("endpoint");
}
@Override
public Set<UnitInstance> collectAllInstance() {
logger.debug("SALSA collector start to collect unit instance from SALSA !");
Set<UnitInstance> instances = new HashSet<>();
SalsaCenterConnector centerCon;
try {
centerCon = new SalsaCenterConnector(this.salsaEndpoint, "/tmp", logger);
} catch (EngineConnectionException ex) {
logger.error("Cannot connect to local SALSA engine at: " + this.salsaEndpoint, ex);
return null;
}
centerCon.getServiceListJson();
Gson gson = new Gson();
logger.debug("Collecting....");
ServiceJsonList jsonList = (ServiceJsonList) gson.fromJson(centerCon.getServiceListJson(), ServiceJsonList.class);
logger.debug("List all salsa service: " + jsonList.toString());
List<String> listOfServices = new ArrayList(Arrays.asList(jsonList.toString().split(" ")));
for (String s : listOfServices) {
logger.debug("Checking instance on service : " + s);
CloudService service;
try {
String url = this.salsaEndpoint + "/rest/services/" + s;
logger.debug("QUERY URL: {}", url);
String xml = RestfulUtils.queryDataToCenter(url, RestfulUtils.HttpVerb.GET, "", "", MediaType.TEXT_XML);
service = SalsaXmlDataProcess.readSalsaServiceXml(xml);
logger.debug("Query done");
} catch (JAXBException | IOException ex) {
logger.error("Cannot get the service data. Service ID: {}", s, ex);
continue;
}
for (ServiceUnit unit : service.getAllComponent()) {
ServiceTopology topo = service.getTopologyOfNode(unit.getId());
for (ServiceInstance ins : unit.getInstancesList()) {
logger.debug("SALSA Unit Instance: " + unit.getId() + "/" + ins.getInstanceId());
UnitInstance unitInst = new UnitInstance(unit.getId(), null, convertSalsaState(ins.getState()));
unitInst.hasExtra("salsaID", service.getId() + "/" + topo.getId() + "/" + unit.getId() + "/" + ins.getInstanceId());
unitInst.hasExtra("salsaState", ins.getState().getNodeStateString());
// the ID in ELISE will be given by SALSA, that why we do integration
unitInst.setId(ins.getUuid().toString());
if (unit.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) {
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());
}
VMInfo.setState(vmDescription.getState());
unitInst.setDomainInfo(VMInfo.toJson());
} else if (unit.getType().equals(SalsaEntityType.DOCKER.getEntityTypeString())) {
unitInst.setCategory(ServiceCategory.AppContainer);
// 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.setDomainInfo(dockerInfo.toJson());
}
} else if (unit.getType().equals(SalsaEntityType.TOMCAT.getEntityTypeString())) {
unitInst.setCategory(ServiceCategory.WebContainer);
} else if (unit.getId().toLowerCase().startsWith("sensor")) {
unitInst.setCategory(ServiceCategory.Sensor);
} else if (unit.getType().equals(SalsaEntityType.SOFTWARE.getEntityTypeString())) {
// get basic information of app
// TODO: fix service type
SystemServiceInfo systemServiceInfo = new SystemServiceInfo("unknownPID", "unknownName");
systemServiceInfo.setStatus("unknown status");
unitInst.setDomainInfo(systemServiceInfo.toJson());
} else {
unitInst.setCategory(ServiceCategory.SystemService);
}
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"));
}
/**
* Collect relationship between instances
*/
logger.debug("Adding unit instance, ID: " + unitInst.getId());
boolean adding = instances.add(unitInst);
logger.debug("Added result: " + adding + ", array now have : " + instances.size());
}
}
}
return instances;
}
@Override
public UnitInstance collectInstanceByID(ServiceCategory instanceType, String domainID) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public LocalIdentification identify(UnitInstance instance) {
logger.debug("Identify for unit instance: \n" + instance.getName());
String structureID = instance.getExtra().get("salsaID");
LocalIdentification id = new LocalIdentification(instance.getCategory(), "SALSA");
id.hasIdentification("id", structureID);
// VirtualMachineInfo vminfo = (VirtualMachineInfo) instance.findDomainInfoByCategory(ServiceCategory.VirtualMachine);
// String ip = vminfo.getPrivateIp();
// rtGovOpsPortmap
// DockerInfo dockerMeta = (DockerInfo) instance.findDomainInfoByCategory(ServiceCategory.AppContainer);
//
// if ((instance.getCategory().equals(ServiceCategory.Sensor) || instance.getCategory().equals(ServiceCategory.Gateway)) && dockerMeta != null) {
// //2812:2826 5683:5697 80:9094
// logger.debug("Found a docker metadata, trying to attract GovOps ID");
// // get portmap, padding a space for something
// String fullportmap = dockerMeta.getPortmap() + " ";
// logger.debug("Get full portmap string: " + fullportmap);
// // fix for generating rtGovOps ID
// int startIndex = fullportmap.indexOf("80:") + 3;
// String portmap = fullportmap.substring(startIndex, fullportmap.indexOf(" ", startIndex));
// String rtGovOpsPortmap = ip + ":" + portmap;
// logger.debug("rtGovOps ID: " + rtGovOpsPortmap);
// id.hasIdentification("rtGovOpsID", rtGovOpsPortmap);
// }
// id.hasIdentification("ip", ip);
return id;
}
@Override
public String getName() {
return "SalsaCollector";
}
public class ServiceJsonList {
List<ServiceInfo> services = new ArrayList<>();
public ServiceJsonList() {
}
public class ServiceInfo {
String serviceName;
String serviceId;
String deployTime;
public ServiceInfo(String name, String id, String deploytime) {
this.serviceName = name;
this.serviceId = id;
this.deployTime = deploytime;
}
public String getServiceName() {
return this.serviceName;
}
public String getServiceId() {
return this.serviceId;
}
public String getDeployTime() {
return this.deployTime;
}
}
public List<ServiceInfo> getServicesList() {
return this.services;
}
@Override
public String toString() {
String re = "";
for (ServiceInfo si : this.services) {
re = re + si.getServiceId() + " ";
}
return re.trim();
}
}
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();
}
private State convertSalsaState(SalsaEntityState salsastate) {
switch (salsastate) {
case ALLOCATING:
case CONFIGURING:
case INSTALLING:
case STAGING:
case STAGING_ACTION:
return State.DEPLOYING;
case DEPLOYED:
return State.FINAL;
case ERROR:
return State.ERROR;
case UNDEPLOYED:
return State.UNDEPLOYING;
}
return State.UNDEPLOYING;
}
}