org.cloudml.deployer.Scaler.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudml.deployer.Scaler.java

Source

/**
 * 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);
    }

}