Java tutorial
/** * This file is part of CloudML [ http://cloudml.org ] * * Copyright (C) 2012 - SINTEF ICT * Contact: Franck Chauvel <franck.chauvel@sintef.no> * * Module: root * * CloudML is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * CloudML is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with CloudML. If not, see * <http://www.gnu.org/licenses/>. */ package org.cloudml.deployer; import com.amazonaws.util.StringInputStream; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.JXPathNotFoundException; import org.cloudml.codecs.JsonCodec; import org.cloudml.connectors.Connector; import org.cloudml.connectors.ConnectorFactory; import org.cloudml.core.*; import org.cloudml.core.actions.StandardLibrary; import org.cloudml.core.collections.InternalComponentInstanceGroup; import org.cloudml.core.collections.ProvidedExecutionPlatformGroup; import org.cloudml.core.collections.RelationshipInstanceGroup; import org.cloudml.mrt.Coordinator; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Created by ferrynico on 02/12/2014. */ public class Scaler { private static final Logger journal = Logger.getLogger(Scaler.class.getName()); protected Deployment currentModel; protected Coordinator coordinator; StandardLibrary lib = new StandardLibrary(); VMInstance ci; protected CloudAppDeployer dep; public Scaler(Deployment currentModel, Coordinator coordinator, CloudAppDeployer dep) { this.currentModel = currentModel; this.coordinator = coordinator; this.dep = dep; } protected VM findVMGenerated(String fromName, String extension) { for (VM v : currentModel.getComponents().onlyVMs()) { if (v.getName().contains(fromName) && v.getName().contains(extension)) { return v; } } return null; } private VM createNewInstanceOfVMFromImage(VMInstance vmi) { VM existingVM = vmi.asExternal().asVM().getType(); //VM v=currentModel.getComponents().onlyVMs().firstNamed(existingVM.getName()+"-fromImage"); VM v = findVMGenerated(existingVM.getName(), "fromImage"); if (v == null) {//in case a type for the snapshot has already been created String name = lib.createUniqueComponentInstanceName(currentModel, existingVM); v = new VM(name + "-fromImage", existingVM.getProvider()); v.setGroupName(existingVM.getGroupName()); v.setRegion(existingVM.getRegion()); v.setImageId("tempID"); v.setLocation(existingVM.getLocation()); v.setMinRam(existingVM.getMinRam()); v.setMinCores(existingVM.getMinCores()); v.setMinStorage(existingVM.getMinStorage()); v.setSecurityGroup(existingVM.getSecurityGroup()); v.setSshKey(existingVM.getSshKey()); v.setProviderSpecificTypeName(existingVM.getProviderSpecificTypeName()); v.setPrivateKey(existingVM.getPrivateKey()); v.setProvider(existingVM.getProvider()); ProvidedExecutionPlatformGroup pepg = new ProvidedExecutionPlatformGroup(); for (ProvidedExecutionPlatform pep : existingVM.getProvidedExecutionPlatforms()) { ArrayList<Property> pg = new ArrayList<Property>(); for (Property property : pep.getOffers()) { Property prop = new Property(property.getName()); prop.setValue(property.getValue()); pg.add(prop); } ProvidedExecutionPlatform p = new ProvidedExecutionPlatform(name + "-" + pep.getName(), pg); pepg.add(p); } v.setProvidedExecutionPlatforms(pepg.toList()); currentModel.getComponents().add(v); } ci = lib.provision(currentModel, v).asExternal().asVM(); return v; } private Map<InternalComponentInstance, InternalComponentInstance> duplicateHostedGraph(Deployment d, VMInstance vmiSource, VMInstance vmiDestination) { //InternalComponentInstanceGroup icig= currentModel.getComponentInstances().onlyInternals().hostedOn(vmiSource); StandardLibrary lib = new StandardLibrary(); return lib.replicateSubGraph(d, vmiSource, vmiDestination); } protected void manageDuplicatedRelationships(RelationshipInstanceGroup rig, Set<ComponentInstance> listOfAllComponentImpacted) { if (rig != null) { dep.configureWithRelationships(rig); for (RelationshipInstance ri : rig) { listOfAllComponentImpacted.add(ri.getClientComponent()); listOfAllComponentImpacted.add(ri.getServerComponent()); } } } protected void configureBindingOfImpactedComponents(Set<ComponentInstance> listOfAllComponentImpacted, Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph) { for (InternalComponentInstance ici : duplicatedGraph.values()) { for (ProvidedPortInstance ppi : ici.getProvidedPorts()) { RelationshipInstanceGroup rig = currentModel.getRelationshipInstances().whereEitherEndIs(ppi); manageDuplicatedRelationships(rig, listOfAllComponentImpacted); } for (RequiredPortInstance rpi : ici.getRequiredPorts()) { RelationshipInstanceGroup rig = currentModel.getRelationshipInstances().whereEitherEndIs(rpi); manageDuplicatedRelationships(rig, listOfAllComponentImpacted); } } } protected void configureImpactedComponents(Set<ComponentInstance> listOfAllComponentImpacted, Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph) { for (ComponentInstance ici : listOfAllComponentImpacted) { coordinator.updateStatusInternalComponent(ici.getName(), InternalComponentInstance.State.INSTALLED.toString(), CloudAppDeployer.class.getName()); if (ici.isInternal()) { Provider p = ici.asInternal().externalHost().asVM().getType().getProvider(); Connector c2 = ConnectorFactory.createIaaSConnector(p); for (Resource r : ici.getType().getResources()) { dep.configure(c2, ci.getType(), ici.asInternal().externalHost().asVM(), r.getConfigureCommand(), false); } c2.closeConnection(); } coordinator.updateStatusInternalComponent(ici.getName(), InternalComponentInstance.State.CONFIGURED.toString(), CloudAppDeployer.class.getName()); } } protected void startImpactedComponents(Set<ComponentInstance> listOfAllComponentImpacted, Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph) { for (ComponentInstance ici : listOfAllComponentImpacted) { if (ici.isInternal()) { Provider p = ici.asInternal().externalHost().asVM().getType().getProvider(); Connector c2 = ConnectorFactory.createIaaSConnector(p); for (Resource r : ici.getType().getResources()) { dep.start(c2, ci.getType(), ici.asInternal().externalHost().asVM(), r.getStartCommand()); } c2.closeConnection(); } coordinator.updateStatusInternalComponent(ici.getName(), InternalComponentInstance.State.RUNNING.toString(), CloudAppDeployer.class.getName()); } for (InternalComponentInstance ici : duplicatedGraph.values()) { coordinator.updateStatusInternalComponent(ici.getName(), InternalComponentInstance.State.RUNNING.toString(), CloudAppDeployer.class.getName()); } } public void scaleOut(VMInstance vmi, int n) { ArrayList<VM> newbies = new ArrayList<VM>(); ArrayList<Map<InternalComponentInstance, InternalComponentInstance>> duplicatedGraphs = new ArrayList<Map<InternalComponentInstance, InternalComponentInstance>>(); ArrayList<Thread> ts = new ArrayList<Thread>(); ArrayList<VMInstance> cis = new ArrayList<VMInstance>(); for (int i = 0; i < n; i++) { VM temp = findVMGenerated(vmi.getType().getName(), "fromImage"); VM v = createNewInstanceOfVMFromImage(vmi); newbies.add(v); Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph = duplicateHostedGraph( currentModel, vmi, ci); duplicatedGraphs.add(duplicatedGraph); cis.add(ci); if (temp == null) { Connector c = ConnectorFactory.createIaaSConnector(vmi.getType().getProvider()); String ID = ""; if (!vmi.getType().getProvider().getName().toLowerCase().equals("flexiant")) ID = c.createImage(vmi); else { ID = c.createImage(vmi); restartHostedComponents(vmi); } c.closeConnection(); v.setImageId(ID); } else { v.setImageId(temp.getImageId()); } } for (int i = 0; i < n; i++) { final Map<InternalComponentInstance, InternalComponentInstance> d = duplicatedGraphs.get(i); final VM vm = newbies.get(i); final String name = vmi.getName(); final VMInstance ci = cis.get(i); ts.add(new Thread() { public void run() { //once this is done we can work in parallel Connector c2 = ConnectorFactory.createIaaSConnector(vm.getProvider()); HashMap<String, Object> result = c2.createInstance(ci); c2.closeConnection(); coordinator.updateStatusInternalComponent(ci.getName(), result.get("status").toString(), CloudAppDeployer.class.getName()); coordinator.updateStatus(name, ComponentInstance.State.RUNNING, CloudAppDeployer.class.getName()); coordinator.updateIP(ci.getName(), result.get("publicAddress").toString(), CloudAppDeployer.class.getName()); setAllEnvVarComponent(ci, currentModel); //4. configure the new VM //execute the configuration bindings Set<ComponentInstance> listOfAllComponentImpacted = new HashSet<ComponentInstance>(); configureBindingOfImpactedComponents(listOfAllComponentImpacted, d); //execute configure commands on the components configureImpactedComponents(listOfAllComponentImpacted, d); //execute start commands on the components startImpactedComponents(listOfAllComponentImpacted, d); //restart components on the VM scaled restartHostedComponents(ci); } }); ts.get(i).start(); } for (Thread t : ts) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } journal.log(Level.INFO, ">> Multiple scaling completed!"); } private void setAllEnvVarComponent(VMInstance ci, Deployment d) { Map<String, String> env = System.getenv(); String ip = ""; String port = ""; if (env.containsKey("MODACLOUDS_MONITORING_MANAGER_ENDPOINT_IP") && env.containsKey("MODACLOUDS_MONITORING_MANAGER_ENDPOINT_PORT")) { ip = env.get("MODACLOUDS_MONITORING_MANAGER_ENDPOINT_IP"); port = env.get("MODACLOUDS_MONITORING_MANAGER_ENDPOINT_PORT"); } else if (env.containsKey("MODACLOUDS_TOWER4CLOUDS_MANAGER_ENDPOINT_IP") && env.containsKey("MODACLOUDS_TOWER4CLOUDS_MANAGER_ENDPOINT_PORT")) { ip = env.get("MODACLOUDS_TOWER4CLOUDS_MANAGER_ENDPOINT_IP"); port = env.get("MODACLOUDS_TOWER4CLOUDS_MANAGER_ENDPOINT_PORT"); } else if (env.containsKey("MODACLOUDS_TOWER4CLOUDS_MANAGER_PUBLIC_ENDPOINT_IP") && env.containsKey("MODACLOUDS_TOWER4CLOUDS_MANAGER_PUBLIC_ENDPOINT_PORT")) { ip = env.get("MODACLOUDS_TOWER4CLOUDS_MANAGER_PUBLIC_ENDPOINT_IP"); port = env.get("MODACLOUDS_TOWER4CLOUDS_MANAGER_PUBLIC_ENDPOINT_PORT"); } else { try { ip = InetAddress.getLocalHost().getHostAddress(); port = "8170"; } catch (UnknownHostException e) { e.printStackTrace(); } } String cmd = ""; cmd += setEnvVarCommand(ci, "MODACLOUDS_TOWER4CLOUDS_MANAGER_IP", ip); cmd += setEnvVarCommand(ci, "MODACLOUDS_TOWER4CLOUDS_MANAGER_PORT", port); for (InternalComponentInstance ici : ci.hostedComponents()) { for (Property p : ici.getProperties()) { if (p.getName().startsWith("env:")) { cmd += prepareSetEnv(d, ici, p); } } InternalComponent ic = ici.getType(); for (Property p : ic.getProperties()) { if (p.getName().contains("env:")) { cmd += prepareSetEnv(d, ici, p); } } } setEnvVar(ci, cmd); } //TODO: All this code is replicated and should be refactored in the deployer private String setEnvVarCommand(VMInstance vmi, String varName, String value) { if (!vmi.getType().getOs().toLowerCase().contains("windows")) { //String command="echo export "+varName+"="+value+" >> ~/.bashrc"; //jc.execCommand(vmi.getId(), command, "ubuntu", vmi.getType().getPrivateKey()); return "sudo sh -c 'echo export " + varName + "=" + value + " >> /etc/environment';"; } else { //TODO: should we do something for Windows as well? } return ""; } private void setEnvVar(VMInstance vmi, String cmd) { if (!vmi.getType().getOs().toLowerCase().contains("windows")) { //String command="echo export "+varName+"="+value+" >> ~/.bashrc"; Connector jc = ConnectorFactory.createIaaSConnector(vmi.getType().getProvider()); jc.execCommand(vmi.getId(), cmd, "ubuntu", vmi.getType().getPrivateKey()); jc.closeConnection(); } else { //TODO: should we do something for Windows as well? } } private String prepareSetEnv(Deployment d, InternalComponentInstance c, Property p) { String value = ""; if (p.getValue().startsWith("$")) { if (p.getValue().equals("${this.host.id}")) { value = c.externalHost().asVM().getId(); } if (p.getValue().equals("${this.host.name}")) { value = c.externalHost().getName(); } if (p.getValue().equals("${this.host.type.name}")) { value = c.externalHost().getType().getName(); } if (p.getValue().equals("${this.provider.id}")) { value = c.externalHost().asVM().getType().getProvider().getName(); } if (p.getValue().equals("${this.name}") || p.getValue().equals("${this.id}")) { value = c.getName(); } if (p.getValue().equals("${this.type.name}")) { value = c.getType().getName(); } } else { try { JXPathContext jxpc = JXPathContext.newContext(d); Object o = jxpc.getValue(p.getValue()); value = o.toString(); } catch (NullPointerException e) { journal.log(Level.INFO, ">> Environment variable cannot be defined, xpath expression not valid"); } catch (JXPathNotFoundException e) { journal.log(Level.INFO, ">> Environment variable cannot be defined, xpath expression not valid"); } } if (!value.equals("")) { return setEnvVarCommand(c.externalHost().asVM(), p.getName().split(":")[1], value); } return ""; } /** * Method to scale out a VM within the same provider * Create a snapshot of the VM and then configure the bindings * * @param vmi an instance of VM */ public void scaleOut(VMInstance vmi) { Deployment tmp = currentModel.clone(); VM temp = findVMGenerated(vmi.getType().getName(), "fromImage"); //1. instantiate the new VM using the newly created snapshot VM v = createNewInstanceOfVMFromImage(vmi); //2. update the deployment model by cloning the PaaS and SaaS hosted on the replicated VM Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph = duplicateHostedGraph( currentModel, vmi, ci); //3. For synchronization purpose we provision once the model has been fully updated if (temp == null) { Connector c = ConnectorFactory.createIaaSConnector(vmi.getType().getProvider()); String ID = ""; if (!vmi.getType().getProvider().getName().toLowerCase().equals("flexiant")) ID = c.createImage(vmi); else { ID = c.createImage(vmi); restartHostedComponents(vmi); } c.closeConnection(); v.setImageId(ID); } else { v.setImageId(temp.getImageId()); } Connector c2 = ConnectorFactory.createIaaSConnector(v.getProvider()); HashMap<String, Object> result = c2.createInstance(ci); c2.closeConnection(); coordinator.updateStatusInternalComponent(ci.getName(), result.get("status").toString(), CloudAppDeployer.class.getName()); coordinator.updateStatus(vmi.getName(), ComponentInstance.State.RUNNING, CloudAppDeployer.class.getName()); coordinator.updateIP(ci.getName(), result.get("publicAddress").toString(), CloudAppDeployer.class.getName()); dep.setAllEnvVarComponent(currentModel); //4. configure the new VM //execute the configuration bindings Set<ComponentInstance> listOfAllComponentImpacted = new HashSet<ComponentInstance>(); configureBindingOfImpactedComponents(listOfAllComponentImpacted, duplicatedGraph); //execute configure commands on the components configureImpactedComponents(listOfAllComponentImpacted, duplicatedGraph); //execute start commands on the components startImpactedComponents(listOfAllComponentImpacted, duplicatedGraph); //restart components on the VM scaled restartHostedComponents(ci); journal.log(Level.INFO, ">> Scaling completed!"); } private ArrayList<InternalComponentInstance> allHostedComponents(ComponentInstance ci) { ArrayList<InternalComponentInstance> list = new ArrayList<InternalComponentInstance>(); InternalComponentInstanceGroup icig = ci.hostedComponents(); if (icig != null) { list.addAll(icig); for (InternalComponentInstance ici : icig) { list.addAll(allHostedComponents(ici)); } } return list; } protected void restartHostedComponents(VMInstance ci) { journal.log(Level.INFO, ">> Restarting Hosted components of: " + ci.getName()); for (InternalComponentInstance ici : allHostedComponents(ci)) { Provider p = ci.getType().getProvider(); Connector c2 = ConnectorFactory.createIaaSConnector(p); for (Resource r : ici.getType().getResources()) { dep.start(c2, ci.getType(), ci, r.getStartCommand()); } c2.closeConnection(); } } private List<VM> vmFromAProvider(Provider p) { ArrayList<VM> result = new ArrayList<VM>(); for (VM v : currentModel.getComponents().onlyVMs()) { if (v.getProvider().getName().equals(p.getName())) { result.add(v); } } return result; } private VM findSimilarVMFromProvider(VM sampleVM, Provider p) { VM selected = null; List<VM> availablesVM = vmFromAProvider(p); if (availablesVM.size() > 0) { selected = availablesVM.get(0); for (VM v : availablesVM) { if ((v.getMinRam() >= sampleVM.getMinRam()) && (v.getMinRam() < selected.getMinRam())) { selected = v; } } } return selected; } public Deployment scaleOut(ExternalComponentInstance eci, Provider provider) { if (eci.isVM()) { scaleOut(eci.asVM(), provider); return currentModel; } else { Deployment targetModel = cloneCurrentModel(); ExternalComponentInstance eci2 = targetModel.getComponentInstances().onlyExternals() .firstNamed(eci.getName()); ExternalComponent ec = eci2.getType().asExternal(); ec.setProvider(provider); eci2.setStatus(ComponentInstance.State.STOPPED); dep.deploy(targetModel); return targetModel; } } private Deployment cloneCurrentModel() { //need to clone the model JsonCodec jsonCodec = new JsonCodec(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); jsonCodec.save(currentModel, baos); Deployment targetModel = new Deployment(); try { String aString = new String(baos.toByteArray(), "UTF-8"); InputStream is = new StringInputStream(aString); targetModel = (Deployment) jsonCodec.load(is); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return targetModel; } /** * To scale our a VM on another provider (kind of bursting) * * @param vmi the vm instance to scale out * @param provider the provider where we want to burst */ public void scaleOut(VMInstance vmi, Provider provider) { Connector c = ConnectorFactory.createIaaSConnector(provider); StandardLibrary lib = new StandardLibrary(); //need to clone the model Deployment targetModel = cloneCurrentModel(); VM existingVM = findSimilarVMFromProvider(vmi.asExternal().asVM().getType(), provider); if (existingVM == null) { journal.log(Level.INFO, ">> No VM available for this provider!"); return; } //VM existingVM=vmi.asExternal().asVM().getType(); //VM v=currentModel.getComponents().onlyVMs().firstNamed(existingVM.getName()+"-scaled"); VM v = findVMGenerated(existingVM.getName(), "scaled"); if (v == null) {//in case a type for the snapshot has already been created String name = lib.createUniqueComponentInstanceName(targetModel, existingVM); v = new VM(name + "-scaled", provider); v.setGroupName(existingVM.getGroupName()); v.setRegion(existingVM.getRegion()); v.setImageId(existingVM.getImageId()); v.setLocation(existingVM.getLocation()); v.setMinRam(existingVM.getMinRam()); v.setMinCores(existingVM.getMinCores()); v.setMinStorage(existingVM.getMinStorage()); v.setSecurityGroup(existingVM.getSecurityGroup()); v.setSshKey(existingVM.getSshKey()); v.setPrivateKey(existingVM.getPrivateKey()); v.setProvider(provider); ProvidedExecutionPlatformGroup pepg = new ProvidedExecutionPlatformGroup(); for (ProvidedExecutionPlatform pep : existingVM.getProvidedExecutionPlatforms()) { ArrayList<Property> pg = new ArrayList<Property>(); for (Property property : pep.getOffers()) { Property prop = new Property(property.getName()); prop.setValue(property.getValue()); pg.add(prop); } ProvidedExecutionPlatform p = new ProvidedExecutionPlatform(name + "-" + pep.getName(), pg); pepg.add(p); } v.setProvidedExecutionPlatforms(pepg.toList()); targetModel.getComponents().add(v); } ci = lib.provision(targetModel, v).asExternal().asVM(); //2. update the deployment model by cloning the PaaS and SaaS hosted on the replicated VM Map<InternalComponentInstance, InternalComponentInstance> duplicatedGraph = duplicateHostedGraph( targetModel, vmi, ci); dep.deploy(targetModel); } }