package hudson.plugins.virtualbox; import hudson.model.Descriptor; import hudson.model.TaskListener; import hudson.slaves.ComputerLauncher; import hudson.slaves.SlaveComputer; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** * {@link ComputerLauncher} for VirtualBox that waits for the instance to really come up before processing to * the real user-specified {@link ComputerLauncher}. * <p> * TODO check relaunch during launch * </p> * * @author Evgeny Mandrikov */ public class VirtualBoxComputerLauncher extends ComputerLauncher { private static final Logger LOG = Logger.getLogger(VirtualBoxComputerLauncher.class.getName()); private static final int SECOND = 1000; private ComputerLauncher delegate; /** * @param delegate real user-specified {@link ComputerLauncher}. */ public VirtualBoxComputerLauncher(ComputerLauncher delegate) { this.delegate = delegate; } @Override public void launch(SlaveComputer computer, TaskListener listener) throws IOException, InterruptedException { VirtualBoxSlave slave = ((VirtualBoxComputer) computer).getNode(); try { // Connect to VirtualBox host VirtualBoxMachine machine = VirtualBoxPlugin.getVirtualBoxMachine(slave.getHostName(), slave.getVirtualMachineName()); if (machine == null) { listener.fatalError("Unable to find specified machine"); return; } // TODO check virtual machine state - if started, then do nothing // if no, then start log(listener, Messages.VirtualBoxLauncher_startVM(machine)); long result = VirtualBoxUtils.startVm(machine, slave.getVirtualMachineType()); if (result != 0) { listener.fatalError("Unable to launch"); return; } // TODO result may be != 0 } catch (Throwable e) { listener.fatalError(e.getMessage(), e); e.printStackTrace(listener.getLogger()); LOG.log(Level.WARNING, e.getMessage(), e); return; } // Stage 2 of the launch. Called after the VirtualBox instance comes up. boolean successful = false; int attempt = 0; while (!successful) { attempt++; log(listener, "Sleep before stage 2 launcher, attempt " + attempt); Thread.sleep(10 * SECOND); successful = delegateLaunch(computer, listener); if (!successful && attempt > 10) { log(listener, "Maximum number of attempts reached"); return; } } } /** * @param computer {@link hudson.model.Computer} for which agent should be launched * @param listener The progress of the launch, as well as any error, should be sent to this listener. * @return true, if successfully launched, otherwise false */ protected boolean delegateLaunch(SlaveComputer computer, TaskListener listener) { try { log(listener, "Starting stage 2 launcher (" + delegate.getClass().getSimpleName() + ")"); getCore().launch(computer, listener); log(listener, "Stage 2 launcher completed"); return true; } catch (IOException e) { log(listener, "Unable to launch: " + e.getMessage()); return false; } catch (InterruptedException e) { log(listener, "Unable to launch: " + e.getMessage()); return false; } } private static void log(TaskListener listener, String message) { listener.getLogger().println("[VirtualBox] " + message); } @Override public void beforeDisconnect(SlaveComputer computer, TaskListener listener) { log(listener, "Starting stage 2 beforeDisconnect"); getCore().beforeDisconnect(computer, listener); log(listener, "Stage 2 beforeDisconnect completed"); } @Override public void afterDisconnect(SlaveComputer computer, TaskListener listener) { VirtualBoxSlave slave = ((VirtualBoxComputer) computer).getNode(); // Stage 2 of the afterDisconnect log(listener, "Starting stage 2 afterDisconnect"); getCore().afterDisconnect(computer, listener); log(listener, "Stage 2 afterDisconnect completed"); try { // Connect to VirtualBox host VirtualBoxMachine machine = VirtualBoxPlugin.getVirtualBoxMachine(slave.getHostName(), slave.getVirtualMachineName()); if (machine == null) { listener.fatalError("Unable to find specified machine"); } // TODO check virtual machine state - if stopped, then do nothing // if no, then stop log(listener, Messages.VirtualBoxLauncher_stopVM(machine)); long result = VirtualBoxUtils.stopVm(machine); if (result != 0) { listener.fatalError("Unable to stop"); return; } } catch (Throwable e) { listener.fatalError(e.getMessage(), e); } } /** * @return delegation target */ public ComputerLauncher getCore() { return delegate; } @Override public Descriptor<ComputerLauncher> getDescriptor() { // Don't allow creation of launcher from UI throw new UnsupportedOperationException(); } }