/* 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.invoker.Invoker; import org.ow2.choreos.invoker.InvokerFactory; import org.ow2.choreos.nodes.NodeNotUpdatedException; 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, cadu, felps * */ public class NodeUpdater { public static final String CHEF_SOLO_COMMAND = "sudo chef-solo -c $HOME/chef-solo/solo.rb >> /tmp/chef-solo-update"; private static final String TASK_NAME = "NODE_UPDATE"; // this executor is shared among multiple updates to the same node private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private CloudNode node; private UpdateHandlers handlers = new UpdateHandlers(); SshWaiter sshWaiter = new SshWaiter(); private Logger logger = Logger.getLogger(NodeUpdater.class); public NodeUpdater(CloudNode node) { this.node = node; } public void addHandler(UpdateHandler handler) { handlers.addHandler(handler); } public void update() throws NodeNotUpdatedException { UpdateInvokerTask updateTask = new UpdateInvokerTask(); Future<Void> future = singleThreadExecutor.submit(updateTask); checkFuture(node, future); } private void checkFuture(CloudNode node, Future<Void> future) throws NodeNotUpdatedException { 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()" future.get(); } catch (InterruptedException e) { fail(node); } catch (ExecutionException e) { fail(node); } } private void fail(CloudNode node) throws NodeNotUpdatedException { NodeNotUpdatedException e = new NodeNotUpdatedException(node.getId()); logger.error(e.getMessage()); throw e; } private class UpdateInvokerTask implements Callable<Void> { @Override public Void call() throws Exception { ChefSoloTask task = new ChefSoloTask(); InvokerFactory<Void> factory = new InvokerFactory<Void>(); Invoker<Void> invoker = factory.geNewInvokerInstance(TASK_NAME, task); invoker.invoke(); return null; } } private class ChefSoloTask implements Callable<Void> { @Override public Void call() throws Exception { logger.debug("updating node " + node.getId()); handlers.fetchHandlers(); SshUtil ssh = getSsh(); ssh.runCommand(CHEF_SOLO_COMMAND); // if ssh did not throw an exception, then: processHandlers(); return null; } private SshUtil getSsh() throws SshNotConnected { int timeout = TimeoutsAndTrials.get("CONNECT_SSH_TIMEOUT"); return sshWaiter.waitSsh(node.getIp(), node.getUser(), node.getPrivateKeyFile(), timeout); } private void processHandlers() { for (UpdateHandler h : handlers.getHandlersForProcessing()) h.handle(); } } }