Package org.apache.ace.nodelauncher.amazon.impl

Source Code of org.apache.ace.nodelauncher.amazon.impl.AmazonNodeLauncher

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.apache.ace.nodelauncher.amazon.impl;

import static org.jclouds.compute.predicates.NodePredicates.runningInGroup;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Dictionary;
import java.util.Properties;
import java.util.Set;

import org.apache.ace.nodelauncher.NodeLauncher;
import org.apache.ace.nodelauncher.NodeLauncherConfig;
import org.apache.ace.nodelauncher.amazon.JcloudsNodeLauncherConfig;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.domain.ComputeMetadata;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.TemplateBuilder;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.compute.options.EC2TemplateOptions;
import org.jclouds.ec2.domain.InstanceType;
import org.jclouds.scriptbuilder.domain.Statements;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;

import com.google.common.io.Files;
/**
* Simple NodeLauncher implementation that launches nodes based on a given AMI in Amazon EC2.
* We expect the AMI we launch to have a java on its path, at least after bootstrap.<br><br>
* <p/>
* This service is configured using Config Admin; see the constants in the class for more information
* about this.<br><br>
* <p/>
* After the node has been started up, this service will install a management agent on it. For this
* to work, there should be an ace-launcher in the OBR of the server the node should connect to.
*/
public class AmazonNodeLauncher implements NodeLauncher, ManagedService {
    public static final String PID = "org.apache.ace.nodelauncher.amazon";

    /**
     * Configuration key: The ACE server the newly started nodes should connect to.
     */
    public static final String SERVER = "server";

    /**
     * Configuration key: The ID of the AMI to use. Note that this AMI should be available
     * in the location ("availability zone") you configure.
     */
    public static final String AMI_ID = "amiId";

    /**
     * Configuration key: The ID of the AMI owner to use. You need this when you want to use your own AMIs.
     */
    public static final String AMI_OWNER_ID = "amiOwnerId";

    /**
     * Configuration key: The location where the node should be started; this is an Amazon "availability zone",
     * something like "eu-west-1".
     */
    public static final String LOCATION = "location";

    /**
     * Configuration key: the Amazon access key ID.
     */
    public static final String ACCESS_KEY_ID = "accessKeyid";

    /**
     * Configuration key: The secret key that goes with your access key.
     */
    public static final String SECRET_ACCESS_KEY = "secretAccessKey";

    /**
     * Configuration key: The (optional) name of an existing keypair to use when creating a new node. If you
     * do not specify it, a new keypair will be created. Specifying an existing keypair makes it easier to
     * for example log into each node with SSH using your existing keypair.
     */
    public static final String KEYPAIR = "keypair";

    /**
     * Configuration key: A prefix to use for all nodes launched by this service. You can use this (a) allow
     * multiple nodes with the same ID, but launcher from different NodeLauncher services, or (b) to more
     * easily identify your nodes in the AWS management console.
     */
    public static final String TAG_PREFIX = "tagPrefix";

    /**
     * Configuration key: A piece of shell script that is run <em>before</em> the management agent is started.
     */
    public static final String NODE_BOOTSTRAP = "nodeBootstrap";

    /**
     * Configuration key: A set of VM options to pass to the JVM when starting the management agent, as a single string.
     */
    public static final String VM_OPTIONS = "vmOptions";

    /**
     * Configuration key: Any command line arguments you want to pass to the launcher; see the ace-launcher for
     * the possible options.
     */
    public static final String LAUNCHER_ARGUMENTS = "launcherArguments";

    /**
     * Configuration key: Extra ports to open on the nodes, besides the default ones (see DEFAULT_PORTS).
     */
    public static final String EXTRA_PORTS = "extraPorts";

    /**
     * Configuration key: Should we run the process as root?
     */
    public static final String RUN_AS_ROOT = "runAsRoot";

    /**
     * Configuration key: The hardware ID to use for the node.
     */
    public static final String HARDWARE_ID = "hardwareId";

    /**
     * Default set of ports to open on a node.
     */
    public static final int[] DEFAULT_PORTS = new int[]{22, 80, 8080};

    /**
     * Configuration key: The (optional) name of the JAR to launch to
     * bootstrap the OSGi framework (or whatever you want to run on the
     * node).
     */
    public static final String ACE_LAUNCHER = "aceLauncher";

    /**
     * Configuration key: An additional list of artifacts that must be
     * downloaded from the OBR when bootstrapping.
     */
    public static final String ADDITIONAL_OBR_DOWNLOADS = "additionalObrDownloads";

    /**
     * Configuration key: An additional list of URLs that must be downloaded when
     * bootstrapping.
     */
    public static final String EXTERNAL_DOWNLOAD_URLS = "externalDownloadUrls";

    /**
     * Configuration key: The (optional) ssh user to use when connecting to the node. Uses jclouds defaults by default,
     * which is root and ec2-user.
     */
    public static final String SSH_USER = "sshUser";

    /**
     * Configuration key: The (optional) private key file, which you must install on
     * the ACE server locally if you want it to be used when creating new nodes.
     */
    private static final String PRIVATE_KEY_FILE = "privateKeyFile";

    /**
     * Default configuration object. Properties are read from the nodelauncher configuration file.
     */
    private JcloudsNodeLauncherConfig m_defaultNodeConfig;

    /**
     * Current configuration object. This reflects settings done from the UI for example.
     * This instance is set when a node is started.
     */
    private JcloudsNodeLauncherConfig m_currentConfig;


    public void start(String id) throws Exception {

        start(id, m_defaultNodeConfig);
    }

    public void start(String id, NodeLauncherConfig cfg) throws Exception {
        JcloudsNodeLauncherConfig config = (JcloudsNodeLauncherConfig) cfg;
        m_currentConfig = config;

        ComputeService computeService = config.getComputeService();
        TemplateBuilder template = computeService.templateBuilder()
                .imageId(config.getImageId())
                .hardwareId(config.getHardwareId())
                .locationId(config.getLocation());

        int[] extraPorts = parseExtraPorts(config.getExtraPorts());
        int[] inboundPorts = mergePorts(DEFAULT_PORTS, extraPorts);

        TemplateOptions options = new EC2TemplateOptions()
                .as(EC2TemplateOptions.class).inboundPorts(inboundPorts)
                .blockOnComplete(false)
                .runAsRoot(config.isRunAsRoot());

        if (useConfiguredKeyPair(config)) {
            options.as(EC2TemplateOptions.class).keyPair(config.getKeyPair());
        }

        template.options(options);

        Set<? extends NodeMetadata> tag = computeService.createNodesInGroup(config.getTagPrefix() + id, 1, template.build());
        if (!useConfiguredPrivateKey(config)) {
            System.out.println("In case you need it, this is the key to ssh to " + id + ":\n" + tag.iterator().next().getCredentials().credential);
        }

        LoginCredentials.Builder loginBuilder = LoginCredentials.builder();

        if (config.getSshUser() != null && config.getSshUser().length() > 0) {
            loginBuilder.user(config.getSshUser());
        } else {
            loginBuilder.user("ec2-user");
        }

        if (useConfiguredPrivateKey(config)) {
            loginBuilder.privateKey(Files.toString(new File(config.getPrivateKeyFile()), Charset.defaultCharset()));
        }

        computeService.runScriptOnNodesMatching(runningInGroup(config.getTagPrefix() + id),
                Statements.exec(buildStartupScript(id, config)),
                RunScriptOptions.Builder.blockOnComplete(false).overrideLoginCredentials(loginBuilder.build()));
    }

    private boolean useConfiguredPrivateKey(JcloudsNodeLauncherConfig config) {
        return config.getPrivateKeyFile() != null && config.getPrivateKeyFile().length() > 0;
    }

    private boolean useConfiguredKeyPair(JcloudsNodeLauncherConfig config) {
        return config.getKeyPair() != null && config.getKeyPair().length() > 0;
    }

    public int[] mergePorts(int[] first, int[] last) {
        int[] result = new int[first.length + last.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = (i < first.length) ? first[i] : last[i - first.length];
        }
        return result;
    }

    public int[] parseExtraPorts(String[] extraPorts) {
        if(extraPorts == null || extraPorts.length == 0) {
            return new int[0];
        }

        int[] result = new int[extraPorts.length];

        for (int i = 0; i < extraPorts.length; i++) {
            result[i] = Integer.parseInt(extraPorts[i].trim());
        }

        return result;
    }

    private String buildStartupScript(String id, JcloudsNodeLauncherConfig config) throws MalformedURLException {
        StringBuilder script = new StringBuilder("cd ~; ");
        if (config.getNodeBootstrap() != null && config.getNodeBootstrap().length() > 0) {
            script.append(config.getNodeBootstrap()).append(" ; ");
        }

        script.append("wget ").append(new URL(config.getServer(), "/obr/" + config.getAceLauncher())).append(" ;");
        if (config.getAdditionalObrDownloads().length() > 0) {
            for (String additonalDownload : config.getAdditionalObrDownloads().split(",")) {
                script.append("wget ").append(new URL(config.getServer(), "/obr/" + additonalDownload.trim())).append(" ;");
            }
        }

        if (config.getExternalDownloadUrls().length() > 0) {
            for (String additonalDownload : config.getExternalDownloadUrls().split(",")) {
                script.append("wget ").append(additonalDownload.trim()).append(" ;");
            }
        }
        script.append("nohup java -jar ").append(config.getAceLauncher()).append(" ");
        script.append("discovery=").append(config.getServer().toExternalForm()).append(" ");
        script.append("identification=").append(id).append(" ");
        script.append(config.getVmOptions()).append(" ");
        script.append(config.getLauncherArguments());
        return script.toString();
    }

    public void stop(String id) {

        getActiveConfig().getComputeService().destroyNodesMatching(runningInGroup(getActiveConfig().getTagPrefix() + id));
    }

    public Properties getProperties(String id) throws Exception {
        Properties result = new Properties();

        JcloudsNodeLauncherConfig config = getActiveConfig();
        NodeMetadata nodeMetadata = getNodeMetadataForRunningNodeWithTag(config.getTagPrefix() + id, config);
        if (nodeMetadata == null) {
            return null;
        }
        result.put("id", id);
        result.put("node-id", nodeMetadata.getId());
        result.put("ip", nodeMetadata.getPublicAddresses().iterator().next());

        return result;
    }

    private JcloudsNodeLauncherConfig getActiveConfig() {
        return m_currentConfig != null ? m_currentConfig : m_defaultNodeConfig;
    }

    private NodeMetadata getNodeMetadataForRunningNodeWithTag(String tag, JcloudsNodeLauncherConfig config) {

        for (ComputeMetadata node : config.getComputeService().listNodes()) {
            NodeMetadata candidate = config.getComputeService().getNodeMetadata(node.getId());
            if (tag.equals(candidate.getGroup()) && candidate.getStatus().equals(NodeMetadata.Status.RUNNING)) {
                return candidate;
            }
        }
        return null;
    }

    public void updated(@SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
        if (properties != null) {
            URL server;
            try {
                server = new URL(getConfigProperty(properties, SERVER));
            } catch (MalformedURLException e) {
                throw new ConfigurationException(SERVER, getConfigProperty(properties, SERVER) + " is not a valid URL.", e);
            }
            String amiId = getConfigProperty(properties, AMI_ID);
            String amiOwnerId = getConfigProperty(properties, AMI_OWNER_ID, "");
            String location = getConfigProperty(properties, LOCATION);
            String hardwareId = getConfigProperty(properties, HARDWARE_ID, InstanceType.C1_MEDIUM);
            String accessKeyId = getConfigProperty(properties, ACCESS_KEY_ID);
            String secretAccessKey = getConfigProperty(properties, SECRET_ACCESS_KEY);
            String keyPair = getConfigProperty(properties, KEYPAIR, "");
            String privateKeyFile = getConfigProperty(properties, PRIVATE_KEY_FILE, "");
            String vmOptions = getConfigProperty(properties, VM_OPTIONS, "");
            String nodeBootstrap = getConfigProperty(properties, NODE_BOOTSTRAP, "");
            String tagPrefix = getConfigProperty(properties, TAG_PREFIX, "");
            String launcherArguments = getConfigProperty(properties, LAUNCHER_ARGUMENTS, "");
            String extraPorts = getConfigProperty(properties, EXTRA_PORTS, "");
            String runAsRoot = getConfigProperty(properties, RUN_AS_ROOT, "false");
            String aceLauncher = getConfigProperty(properties, ACE_LAUNCHER, "ace-launcher.jar");
            String additionalObrDownloads = getConfigProperty(properties, ADDITIONAL_OBR_DOWNLOADS, "");
            String externalDownloadUrls = getConfigProperty(properties, EXTERNAL_DOWNLOAD_URLS, "");
            String sshUser = getConfigProperty(properties, SSH_USER, "ec2-user");

            m_defaultNodeConfig = new JcloudsNodeLauncherConfig()
                    .setAccessKeyId(accessKeyId)
                    .setSecretAccessKey(secretAccessKey)
                    .setServer(server)
                    .setImageId(amiId)
                    .setImageOwnerId(amiOwnerId)
                    .setLocation(location)
                    .setHardwareId(hardwareId)
                    .setKeyPair(keyPair)
                    .setPrivateKeyFile(privateKeyFile)
                    .setTagPrefix(tagPrefix)
                    .setVmOptions(vmOptions)
                    .setLauncherArguments(launcherArguments)
                    .setExtraPorts(extraPorts)
                    .setRunAsRoot(Boolean.parseBoolean(runAsRoot))
                    .setAccessKeyId(accessKeyId)
                    .setSecretAccessKey(secretAccessKey)
                    .setNodeBootstrap(nodeBootstrap)
                    .setAceLauncher(aceLauncher)
                    .setAdditionalObrDownloads(additionalObrDownloads)
                    .setExternalDownloadUrls(externalDownloadUrls)
                    .setSshUser(sshUser);

            m_defaultNodeConfig.createComputeServiceContext();
        }
    }


    private String getConfigProperty(@SuppressWarnings("rawtypes") Dictionary settings, String id) throws ConfigurationException {
        return getConfigProperty(settings, id, null);
    }

    private String getConfigProperty(@SuppressWarnings("rawtypes") Dictionary settings, String id, String defaultValue) throws ConfigurationException {
        String result = (String) settings.get(id);
        if (result == null) {
            if (defaultValue == null) {
                throw new ConfigurationException(id, "key missing");
            } else {
                return defaultValue;
            }
        }
        return result;
    }

    public void stop() {
        if (m_currentConfig != null) {
            m_currentConfig.close();
        }
    }

    public NodeLauncherConfig getDefaultConfig() {
        return m_defaultNodeConfig;
    }

    public JcloudsNodeLauncherConfig getCurrentConfig() {
        return m_currentConfig;
    }
}
TOP

Related Classes of org.apache.ace.nodelauncher.amazon.impl.AmazonNodeLauncher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.