/* * 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.genericCapability; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.base.AppCapabilityBase; import at.ac.tuwien.dsg.cloud.salsa.engine.impl.base.VMCapabilityBase; 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.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.interfaces.SalsaException; import at.ac.tuwien.dsg.cloud.salsa.engine.capabilityinterface.UnitCapabilityInterface; import at.ac.tuwien.dsg.cloud.salsa.engine.exceptions.EngineConnectionException; import at.ac.tuwien.dsg.cloud.salsa.engine.exceptions.VMProvisionException; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.EngineLogger; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.PioneerManager; import at.ac.tuwien.dsg.cloud.salsa.engine.utils.SalsaConfiguration; import java.util.List; /** * This class generalize the capability of application and VM into service unit. The states control is enabled here. * * @author Duc-Hung Le */ public class GenericUnitCapability implements UnitCapabilityInterface { SalsaCenterConnector centerCon; UnitCapabilityInterface lowerCapaApp = new AppCapabilityBase(); UnitCapabilityInterface lowerCapaVM = new VMCapabilityBase(); { try { centerCon = new SalsaCenterConnector(SalsaConfiguration.getSalsaCenterEndpointLocalhost(), "/tmp", EngineLogger.logger); } catch (EngineConnectionException ex) { EngineLogger.logger.error("Cannot connect to SALSA service in localhost: " + SalsaConfiguration.getSalsaCenterEndpointLocalhost() + ". This is a fatal error !"); } } static final long cooldown = 2000; static long lastDeploymentTime = (new java.util.Date()).getTime(); // This assume that a docker container will always created at least 2 seconds after previous one, on a same VM private void waitForCooledDown() { long currentTime = (new java.util.Date()).getTime(); long between = currentTime - lastDeploymentTime; if (between < cooldown) { EngineLogger.logger.debug("Waiting a "+(cooldown-between)+" miliseconds to reduce the cloud failure when create many VM at a time"); try { Thread.sleep(cooldown-between); } catch (InterruptedException ex) { lastDeploymentTime = currentTime; } } lastDeploymentTime = currentTime; } @Override public ServiceInstance deploy(String serviceId, String nodeId, int instanceId) throws SalsaException { waitForCooledDown(); EngineLogger.logger.info("Start generic unit deployment for node: {}/{}/{}", serviceId, nodeId, instanceId); CloudService newservice = centerCon.getUpdateCloudServiceRuntime(serviceId); String topologyId = newservice.getTopologyOfNode(nodeId).getId(); ServiceUnit node = newservice.getComponentById(topologyId, nodeId); EngineLogger.logger.debug("Node type: " + node.getType() + ". String: " + SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString()); ServiceInstance repData = new ServiceInstance(instanceId, null); repData.setState(SalsaEntityState.ALLOCATING); repData.setExtra("Waiting for dependencies"); repData.setHostedId_Integer(2147483647); java.util.Date date = new java.util.Date(); EngineLogger.logger.debug("TIMESTAMP - Node: " + nodeId + "/" + instanceId + ", state: Allocating(manual)" + ", Time: " + date.getTime()); if (node.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) { if (node.getInstanceNumber() >= node.getMax()) { EngineLogger.logger.error("Not enough cloud resource quota for the node: " + nodeId + ". Quit !"); // out of quota throw new VMProvisionException(VMProvisionException.VMProvisionError.QUOTA_LIMITED, "Not enough quota as described to deploy the node: " + serviceId + nodeId + instanceId + ". This node can have maxinum " + node.getMax() + " instances"); } else { centerCon.addInstanceUnitMetaData(serviceId, topologyId, nodeId, repData); lowerCapaVM.deploy(serviceId, nodeId, instanceId); centerCon.updateNodeState(serviceId, topologyId, nodeId, instanceId, SalsaEntityState.CONFIGURING, "Pioneer is configuring artifacts"); EngineLogger.logger.debug("Updated VM state for node: " + nodeId + " to CONFIGURING !"); EngineLogger.logger.info("Generic unit deployment for VM node: {}/{}/{} is done", serviceId, nodeId, instanceId); return new ServiceInstance(instanceId); } } else { centerCon.addInstanceUnitMetaData(serviceId, topologyId, nodeId, repData); // app based: now select different kind of app lowerCapaApp.deploy(serviceId, nodeId, instanceId); EngineLogger.logger.info("Generic unit deployment for artifact node: {}/{}/{} is done", serviceId, nodeId, instanceId); return new ServiceInstance(instanceId); } } @Override public void remove(String serviceId, String nodeId, int instanceId) throws SalsaException { EngineLogger.logger.info("Start remove generic node: {}/{}/{}", serviceId, nodeId, instanceId); CloudService service = centerCon.getUpdateCloudServiceRuntime(serviceId); ServiceUnit node = service.getComponentById(nodeId); ServiceInstance instance = node.getInstanceById(instanceId); List<ServiceUnit> listUnit = service.getAllComponent(); // undeploy dependency chain first. It is recursive. for (ServiceUnit u : listUnit) { // all the unit of the service EngineLogger.logger.debug("Checking if this unit: " + u.getId() + " is HOSTED ON current removing node: " + nodeId); if (u.getHostedId().equals(nodeId)) { // this is hosted on the node we want to remove. remove it first EngineLogger.logger.debug("YES! Now check instance of node: " + u.getId() + " is HOSTED ON current removing node: " + nodeId); for (ServiceInstance i : u.getInstanceHostOn(instanceId)) { // the instance of above unit and hosted on current instance EngineLogger.logger.debug("Found instance need to be remove first: " + u.getId() + "/" + i.getInstanceId()); GenericUnitCapability geneCapa = new GenericUnitCapability(); geneCapa.remove(serviceId, u.getId(), i.getInstanceId()); } } for (String connectoId : u.getConnecttoId()) { // the unit u can connect to something EngineLogger.logger.debug("Checking if this unit: " + u.getId() + " is CONNECT TO current removing node: " + nodeId); if (connectoId.equals(nodeId)) { // which can be this node EngineLogger.logger.debug("YES! Now checking instance of node: " + u.getId() + " is CONNECT TO current removing node: " + nodeId); for (ServiceInstance i : u.getInstancesList()) { // remove all its instance EngineLogger.logger.debug("Found instance need to be remove first: " + u.getId() + "/" + i.getInstanceId()); GenericUnitCapability geneCapa = new GenericUnitCapability(); geneCapa.remove(serviceId, u.getId(), i.getInstanceId()); } } } } EngineLogger.logger.debug("The dependency should be cleaned for the node: " + nodeId + "/" + instanceId); // check all instance of hoston and connect to are undeployed // do similar things and check if all the list are empty boolean cleaned; do { service = centerCon.getUpdateCloudServiceRuntime(serviceId); node = service.getComponentById(nodeId); listUnit = service.getAllComponent(); cleaned = true; EngineLogger.logger.debug("Checking if dependency is really clean for node: " + nodeId + "/" + instanceId); for (ServiceUnit u : listUnit) { if (u.getHostedId().equals(nodeId)) { EngineLogger.logger.debug("Checking if dependency is really clean for node: " + nodeId + "/" + instanceId + ". Checking unit: " + u.getId() + " which number of hosted on inst: " + u.getInstanceHostOn(instanceId).size()); if (!u.getInstanceHostOn(instanceId).isEmpty()) { EngineLogger.logger.debug("Waiting for cleaning HOST ON nodes of: " + nodeId + "/" + instanceId + ". Nodes left: " + u.getInstanceHostOn(instanceId).size()); for (ServiceInstance debugI : u.getInstanceHostOn(instanceId)) { EngineLogger.logger.debug("They are: " + u.getId() + "/" + debugI.getInstanceId()); } cleaned = false; } } for (String connectoId : u.getConnecttoId()) { if (connectoId.equals(nodeId)) { if (!u.getInstancesList().isEmpty()) { EngineLogger.logger.debug("Waiting for cleaning CONNECT TO nodes of: " + nodeId + "/" + instanceId + ". Nodes left: " + u.getInstancesList().size()); cleaned = false; } } } } try { Thread.sleep(3000); } catch (InterruptedException e) { } } while (cleaned == false); EngineLogger.logger.debug("It is TRUE, the dependency is now cleaned for the node: " + nodeId + "/" + instanceId); // if the state is not DEPLOYED, just remove the metadata if (!instance.getState().equals(SalsaEntityState.DEPLOYED)) { EngineLogger.logger.warn("Removing metadata for node {},{},{}. However, the state is {}, maybe the actual instance is created but will not be removed.", serviceId, nodeId, instanceId, instance.getState()); centerCon.removeInstanceMetadata(serviceId, nodeId, instanceId); EngineLogger.logger.info("Removed generic node: {}/{}/{} with warning", serviceId, nodeId, instanceId); return; } // Call appropriate catapbility based on type if (node.getType().equals(SalsaEntityType.OPERATING_SYSTEM.getEntityTypeString())) { EngineLogger.logger.info("Node {}/{}/{} is an VM, calling VMCacapabilityBase...", serviceId, nodeId, instanceId); lowerCapaVM.remove(serviceId, nodeId, instanceId); } else { EngineLogger.logger.info("Node {}/{}/{} is an app, calling AppCapabilityBase...", serviceId, nodeId, instanceId); lowerCapaApp.remove(serviceId, nodeId, instanceId); } EngineLogger.logger.info("Removed generic node: {}/{}/{}", serviceId, nodeId, instanceId); // unregister pioneer String pioneerID = PioneerManager.getPioneerID(SalsaConfiguration.getUserName(), serviceId, nodeId, instanceId); if (pioneerID != null) { PioneerManager.removePioneer(pioneerID); EngineLogger.logger.debug("Also unregistered pioneer ID: {}", pioneerID); } } }