package dk.kaspergsm.stormdeploy.commands; import static org.jclouds.scriptbuilder.domain.Statements.exec; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.RunNodesException; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadata.Status; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.StatementList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.io.Files; import dk.kaspergsm.stormdeploy.Tools; import dk.kaspergsm.stormdeploy.configurations.NodeConfiguration; import dk.kaspergsm.stormdeploy.userprovided.Configuration; import dk.kaspergsm.stormdeploy.userprovided.Credential; public class ScaleOutCluster { private static Logger log = LoggerFactory.getLogger(ScaleOutCluster.class); /** * Assumes we are currently attached to the cluster to extend */ @SuppressWarnings("unchecked") public static void AddWorkers(int numInstances, String clustername, String instanceType, Configuration config, Credential credentials, ComputeServiceContext computeContext) { /** * Parse current running nodes for cluster */ ArrayList<NodeMetadata> existingZookeeper = new ArrayList<NodeMetadata>(); List<NodeMetadata> existingWorkers = new ArrayList<NodeMetadata>(); ArrayList<NodeMetadata> existingDRPC = new ArrayList<NodeMetadata>(); NodeMetadata nimbus = null, ui = null; String image = null, region = null; for (NodeMetadata n : (Set<NodeMetadata>) computeContext.getComputeService().listNodes()) { if (n.getStatus() != Status.TERMINATED && n.getGroup() != null && n.getGroup().toLowerCase().equals(clustername.toLowerCase()) && n.getUserMetadata().containsKey("daemons")) { String daemons = n.getUserMetadata().get("daemons").replace("[", "").replace("]", ""); for (String daemon : daemons.split(",")) { if (daemon.trim().toLowerCase().equals("master")) nimbus = n; if (daemon.trim().toLowerCase().equals("ui")) ui = n; if (daemon.trim().toLowerCase().equals("worker")) existingWorkers.add(n); if (daemon.trim().toLowerCase().equals("zk")) existingZookeeper.add(n); if (daemon.trim().toLowerCase().equals("drpc")) existingDRPC.add(n); } if (image == null) image = n.getImageId(); if (region == null) region = n.getLocation().getParent().getId(); } } /** * Start new workernodes */ Set<NodeMetadata> newWorkerNodes = startWorkerNodesNow( clustername, NodeConfiguration.getCommands( clustername, credentials, config, getInstancesPrivateIp(existingZookeeper), getInstancesPrivateIp(existingDRPC), nimbus == null ? null : nimbus.getPrivateAddresses().iterator().next(), ui == null ? null : ui.getPrivateAddresses().iterator().next()), instanceType, numInstances, image, region, computeContext, config); /** * Update attachment */ Attach.attach(clustername, config.getStormVersion(), computeContext); /** * Print final info */ log.info("User: " + config.getImageUsername()); log.info("Started:"); for (NodeMetadata n : newWorkerNodes) log.info("\t" + Tools.getInstanceIp(n) + "\t" + "[WORKER]"); /** * Close application now */ System.exit(0); } private static List<String> getInstancesPrivateIp(List<NodeMetadata> nodes) { List<String> newNodes = new ArrayList<String>(); for (NodeMetadata n : nodes) newNodes.add(n.getPrivateAddresses().iterator().next()); return newNodes; } @SuppressWarnings("unchecked") private static Set<NodeMetadata> startWorkerNodesNow(String clustername, List<Statement> commands, String instanceType, int numInstances, String image, String region, ComputeServiceContext computeContext, Configuration config) { try { // Create initScript String installDir = config.getInstallDir(); ArrayList<Statement> initScript = new ArrayList<Statement>(); initScript.add(exec("mkdir -p "+ installDir)); if (!"~/".equals(installDir)) { initScript.add(exec("chown " + config.getImageUsername() + " " + installDir)); } initScript.add(exec("echo WORKER > " + installDir + "daemons")); initScript.add(exec("echo WORKER > " + installDir + "daemons")); initScript.add(exec("echo \"" + instanceType + "\" > "+ installDir +".instance-type")); initScript.addAll(commands); log.info("Starting " + numInstances + " instance(s) of type " + instanceType); TemplateBuilder templateBuilder = computeContext.getComputeService().templateBuilder() .hardwareId(instanceType) .imageId(image) .locationId(region) .options(new TemplateOptions() .nameTask("Setup") .blockOnComplete(true) .wrapInInitScript(true) .inboundPorts(22, 6627, 8080) // 22 = SSH, 6627 = Thrift, 8080 = UI .overrideLoginUser(config.getImageUsername()) .userMetadata("daemons", "[WORKER]") .runScript(new StatementList(initScript)) .overrideLoginCredentials(Tools.getPrivateKeyCredentials(config.getImageUsername(), config.getSSHKeyName())) .authorizePublicKey(Files.toString(new File(System.getProperty("user.home") + File.separator + ".ssh" + File.separator + config.getSSHKeyName() + ".pub"), Charsets.UTF_8))); return (Set<NodeMetadata>) computeContext.getComputeService().createNodesInGroup(clustername, numInstances, templateBuilder.build()); } catch (IllegalArgumentException ex) { log.error("Error when starting instance(s)", ex); } catch (RunNodesException ex) { log.error("Error when starting instance(s)", ex); } catch (IOException ex) { log.error("Error when starting instance(s)", ex); } return null; } }