/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.ow2.choreos.ee.nodes.cm; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.log4j.Logger; import org.ow2.choreos.ee.config.QoSManagementConfiguration; import org.ow2.choreos.invoker.Invoker; import org.ow2.choreos.invoker.InvokerFactory; import org.ow2.choreos.nodes.datamodel.CloudNode; import org.ow2.choreos.utils.SshNotConnected; import org.ow2.choreos.utils.SshUtil; import org.ow2.choreos.utils.SshWaiter; import org.ow2.choreos.utils.TimeoutsAndTrials; /** * * @author leonardo * */ public class NodePreparer { private static final String TASK_NAME = "PREPARE_DEPLOYMENT"; // this executor is shared among multiple executions to the same node private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private CloudNode node; SshWaiter sshWaiter = new SshWaiter(); private Logger logger = Logger.getLogger(NodePreparer.class); public NodePreparer(CloudNode node) { this.node = node; } /** * * @param packageUri * @param cookbookTemplateName * ex: jar, war, cd * @return the id of the prepared instance * @throws NodeNotPreparedException */ public String prepareNodeForDeployment(String packageUri, String cookbookTemplateName) throws NodeNotPreparedException { DeploymentPreparerInvokerTask task = new DeploymentPreparerInvokerTask(packageUri, cookbookTemplateName); Future<String> future = singleThreadExecutor.submit(task); String instanceId = checkFuture(future); return instanceId; } public void prepareNodeForUndeployment(String instanceId) throws NodeNotPreparedException { UndeploymentPreparerInvokerTask task = new UndeploymentPreparerInvokerTask(instanceId); Future<String> future = singleThreadExecutor.submit(task); checkFuture(future); } private String checkFuture(Future<String> future) throws NodeNotPreparedException { try { // since the task been executed is basically the invoker invocation, // we hope do not get stuck here (invoker already has a timeout) // this is why we did not set a timeout in the "future.get()" String result = future.get(); if (result == null) return fail(); return result; } catch (InterruptedException e) { return fail(); } catch (ExecutionException e) { return fail(); } } private String fail() throws NodeNotPreparedException { NodeNotPreparedException e = new NodeNotPreparedException(node.getId()); logger.error(e.getMessage()); throw e; } private class DeploymentPreparerInvokerTask implements Callable<String> { String packageUri; String cookbookTemplateName; public DeploymentPreparerInvokerTask(String packageUri, String cookbookTemplateName) { this.packageUri = packageUri; this.cookbookTemplateName = cookbookTemplateName; } @Override public String call() throws Exception { DeploymentPrepareNodeTask task = new DeploymentPrepareNodeTask(packageUri, cookbookTemplateName); InvokerFactory<String> factory = new InvokerFactory<String>(); Invoker<String> invoker = factory.geNewInvokerInstance(TASK_NAME, task); String instanceId = invoker.invoke(); return instanceId; } } private class UndeploymentPreparerInvokerTask implements Callable<String> { private String instanceId; /** * @param instanceId */ public UndeploymentPreparerInvokerTask(String instanceId) { this.instanceId = instanceId; } @Override public String call() throws Exception { UndeploymentPrepareNodeTask task = new UndeploymentPrepareNodeTask(instanceId); InvokerFactory<String> factory = new InvokerFactory<String>(); Invoker<String> invoker = factory.geNewInvokerInstance(TASK_NAME, task); String result = invoker.invoke(); return result; } } private abstract class AbstractPreparerInvokerTask { protected SshUtil getSsh() throws SshNotConnected { int timeout = TimeoutsAndTrials.get("CONNECT_SSH_TIMEOUT"); return sshWaiter.waitSsh(node.getIp(), node.getUser(), node.getPrivateKeyFile(), timeout); } } private class DeploymentPrepareNodeTask extends AbstractPreparerInvokerTask implements Callable<String> { String packageUri; String cookbookTemplateName; public DeploymentPrepareNodeTask(String packageUri, String cookbookTemplateName) { this.packageUri = packageUri; this.cookbookTemplateName = cookbookTemplateName; } @Override public String call() throws Exception { SshUtil ssh = getSsh(); String command = getCommand(); String instanceId = ssh.runCommand(command); return instanceId; } private String getCommand() { if (Boolean.parseBoolean(QoSManagementConfiguration.get(QoSManagementConfiguration.QOS_MGMT))) return ". chef-solo/prepare_deployment.sh " + packageUri + " " + cookbookTemplateName + " qos"; else return ". chef-solo/prepare_deployment.sh " + packageUri + " " + cookbookTemplateName + " no-qos"; } } private class UndeploymentPrepareNodeTask extends AbstractPreparerInvokerTask implements Callable<String> { private String instanceId; public UndeploymentPrepareNodeTask(String instanceId) { this.instanceId = instanceId; } @Override public String call() throws Exception { SshUtil ssh; String result = ""; try { ssh = getSsh(); String command = getCommand(); result = ssh.runCommand(command); } catch (Exception e) { throw e; } return result; } private String getCommand() { return ". chef-solo/prepare_undeployment.sh " + instanceId; } } }