From 960e6f8e0adc6fe108f0a60a13a9a0b037505782 Mon Sep 17 00:00:00 2001 From: David Ribeiro Alves Date: Fri, 2 Mar 2012 19:15:52 +0000 Subject: [PATCH] added a computeservicecontext provider for the vbox host (byon) --- .../java/org/jclouds/virtualbox/Host.java | 40 +++++ ...VirtualBoxComputeServiceContextModule.java | 28 ++++ .../config/VirtualBoxConstants.java | 2 + .../functions/CreateAndInstallVm.java | 156 +++++++++--------- .../predicates/GuestAdditionsInstaller.java | 35 ++-- .../virtualbox/predicates/SshResponds.java | 2 + 6 files changed, 169 insertions(+), 94 deletions(-) create mode 100644 labs/virtualbox/src/main/java/org/jclouds/virtualbox/Host.java diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/Host.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/Host.java new file mode 100644 index 0000000000..77ffe2a507 --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/Host.java @@ -0,0 +1,40 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.virtualbox; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Signals the annotated target pertains to the vbox host and not to one of the nodes. + * + * @author dralves + * + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface Host { + +} diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java index dc7b885652..73aea1a23a 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxComputeServiceContextModule.java @@ -20,6 +20,9 @@ package org.jclouds.virtualbox.config; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PROVIDER; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WEBSERVER_CREDENTIAL; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WEBSERVER_IDENTITY; import java.io.InputStream; import java.net.URI; @@ -32,10 +35,13 @@ import javax.inject.Singleton; import org.eclipse.jetty.server.Server; import org.jclouds.byon.Node; +import org.jclouds.byon.config.CacheNodeStoreModule; import org.jclouds.byon.functions.NodeToNodeMetadata; import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty; import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.compute.config.ComputeServiceAdapterContextModule; import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.HardwareBuilder; @@ -47,8 +53,11 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.reference.ComputeServiceConstants.Timeouts; import org.jclouds.domain.Location; import org.jclouds.functions.IdentityFunction; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.ssh.SshClient; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.jclouds.virtualbox.Host; import org.jclouds.virtualbox.Preconfiguration; import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter; import org.jclouds.virtualbox.domain.ExecutionType; @@ -82,7 +91,9 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.inject.Injector; +import com.google.inject.Module; import com.google.inject.Provides; import com.google.inject.TypeLiteral; @@ -150,6 +161,23 @@ public class VirtualBoxComputeServiceContextModule extends return CacheBuilder.newBuilder().build(cacheLoader); } + @Provides + @Host + @Singleton + protected ComputeServiceContext provideHostController() { + String provider = "byon"; + String identity = ""; + String credential = ""; + CacheNodeStoreModule hostModule = new CacheNodeStoreModule(ImmutableMap.of( + "host", + Node.builder().id("host").name("host installing virtualbox").hostname("localhost") + .osFamily(OsFamily.LINUX.toString()).osDescription(System.getProperty("os.name")) + .osVersion(System.getProperty("os.version")).group("ssh").username(System.getProperty("user.name")) + .credentialUrl(URI.create("file://" + System.getProperty("user.home") + "/.ssh/id_rsa")).build())); + return new ComputeServiceContextFactory().createContext(provider, identity, credential, + ImmutableSet. of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule)); + } + @Provides @Singleton protected Server providesJettyServer(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java index c2ed806214..50426549c7 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java @@ -57,5 +57,7 @@ public interface VirtualBoxConstants { public static final String VIRTUALBOX_DEFAULT_DIR = System.getProperty("user.home") + File.separator +".jclouds-vbox"; + + public static final String VIRTUALBOX_PROVIDER = "virtualbox"; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java index ffc64e450c..da948ebeda 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java @@ -35,6 +35,7 @@ import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.ssh.SshClient; +import org.jclouds.virtualbox.Host; import org.jclouds.virtualbox.Preconfiguration; import org.jclouds.virtualbox.domain.ExecutionType; import org.jclouds.virtualbox.domain.IsoSpec; @@ -60,96 +61,97 @@ import com.google.inject.Inject; @Singleton public class CreateAndInstallVm implements Function { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; - - private final ComputeServiceContext context; - private final Supplier manager; - private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; - private final Predicate sshResponds; - private final ExecutionType executionType; + private final ComputeServiceContext vboxHostContext; + private final Supplier manager; + private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; - private LoadingCache preConfiguration; + private final Predicate sshResponds; + private final ExecutionType executionType; - private final Function sshClientForIMachine; + private LoadingCache preConfiguration; - private final MachineUtils machineUtils; + private final Function sshClientForIMachine; - @Inject - public CreateAndInstallVm(ComputeServiceContext context, Supplier manager, - CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, - Predicate sshResponds, Function sshClientForIMachine, - ExecutionType executionType, MachineUtils machineUtils, @Preconfiguration LoadingCache preConfiguration) { - this.context = context; - this.manager = manager; - this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; - this.sshResponds = sshResponds; - this.sshClientForIMachine = sshClientForIMachine; - this.executionType = executionType; - this.machineUtils = machineUtils; - this.preConfiguration = preConfiguration; - } + private final MachineUtils machineUtils; - @Override - public IMachine apply(MasterSpec masterSpec) { + @Inject + public CreateAndInstallVm(@Host ComputeServiceContext vboxHostContext, Supplier manager, + CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, + Predicate sshResponds, Function sshClientForIMachine, + ExecutionType executionType, MachineUtils machineUtils, + @Preconfiguration LoadingCache preConfiguration) { + this.vboxHostContext = vboxHostContext; + this.manager = manager; + this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; + this.sshResponds = sshResponds; + this.sshClientForIMachine = sshClientForIMachine; + this.executionType = executionType; + this.machineUtils = machineUtils; + this.preConfiguration = preConfiguration; + } - VmSpec vmSpec = masterSpec.getVmSpec(); - IsoSpec isoSpec = masterSpec.getIsoSpec(); - String vmName = vmSpec.getVmName(); - - final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); + @Override + public IMachine apply(MasterSpec masterSpec) { - // Launch machine and wait for it to come online - ensureMachineIsLaunched(vmName); - - URI uri = preConfiguration.getUnchecked(isoSpec); - String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL", - uri.toASCIIString()); + VmSpec vmSpec = masterSpec.getVmSpec(); + IsoSpec isoSpec = masterSpec.getIsoSpec(); + String vmName = vmSpec.getVmName(); - configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); - SshClient client = sshClientForIMachine.apply(vm); - logger.debug(">> awaiting installation to finish node(%s)", vmName); + final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); - checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", vmName); - - logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); - checkState(new GuestAdditionsInstaller(context).apply(vmName)); - - logger.debug(">> awaiting post-installation actions on vm: %s", vmName); - ListenableFuture execFuture = context.getComputeService().submitScriptOnNode(vmName, - call("cleanupUdevIfNeeded"), RunScriptOptions.NONE); - ExecResponse execResponse = Futures.getUnchecked(execFuture); - checkState(execResponse.getExitCode() == 0); - - logger.debug("<< installation of image complete. Powering down node(%s)", vmName); - ensureMachineHasPowerDown(vmName); - return vm; - } + // Launch machine and wait for it to come online + ensureMachineIsLaunched(vmName); - private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) { - Iterable> scancodelist = - transform(Splitter.on(" ").split(installationKeySequence), new StringToKeyCode()); + URI uri = preConfiguration.getUnchecked(isoSpec); + String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL", + uri.toASCIIString()); - for (List scancodes : scancodelist) { - machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes)); + configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); + SshClient client = sshClientForIMachine.apply(vm); + logger.debug(">> awaiting installation to finish node(%s)", vmName); + + checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", vmName); + + logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); + checkState(new GuestAdditionsInstaller(vboxHostContext).apply(vmName)); + + logger.debug(">> awaiting post-installation actions on vm: %s", vmName); + ListenableFuture execFuture = vboxHostContext.getComputeService().submitScriptOnNode(vmName, + call("cleanupUdevIfNeeded"), RunScriptOptions.NONE); + ExecResponse execResponse = Futures.getUnchecked(execFuture); + checkState(execResponse.getExitCode() == 0); + + logger.debug("<< installation of image complete. Powering down node(%s)", vmName); + ensureMachineHasPowerDown(vmName); + return vm; + } + + private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) { + Iterable> scancodelist = transform(Splitter.on(" ").split(installationKeySequence), + new StringToKeyCode()); + + for (List scancodes : scancodelist) { + machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes)); + } + } + + private void ensureMachineHasPowerDown(String vmName) { + machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { + @Override + public Void apply(ISession session) { + IProgress powerDownProgress = session.getConsole().powerDown(); + powerDownProgress.waitForCompletion(-1); + return null; } - } + }); + } - private void ensureMachineHasPowerDown(String vmName) { - machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { - @Override - public Void apply(ISession session) { - IProgress powerDownProgress = session.getConsole().powerDown(); - powerDownProgress.waitForCompletion(-1); - return null; - } - }); - } - - private void ensureMachineIsLaunched(String vmName) { - machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, "")); - } + private void ensureMachineIsLaunched(String vmName) { + machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, "")); + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java index 7c8d6c8613..5b8fac897f 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java @@ -21,25 +21,26 @@ import com.google.inject.Inject; @Singleton public class GuestAdditionsInstaller implements Predicate { - @Resource - @Named(ComputeServiceConstants.COMPUTE_LOGGER) - protected Logger logger = Logger.NULL; + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; - private final ComputeServiceContext context; - private String vboxVersion; + private final ComputeServiceContext vboxHostContext; + private String vboxVersion; - @Inject - public GuestAdditionsInstaller(ComputeServiceContext context) { - this.context = context; - } + @Inject + public GuestAdditionsInstaller(ComputeServiceContext vboxHostContext) { + this.vboxHostContext = vboxHostContext; + } - @Override - public boolean apply(String vmName) { - vboxVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0); - ListenableFuture execFuture = context.getComputeService().submitScriptOnNode(vmName, - new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE); - ExecResponse execResponse = Futures.getUnchecked(execFuture); - return execResponse == null ? false : execResponse.getExitStatus() == 0; - } + @Override + public boolean apply(String vmName) { + vboxVersion = Iterables.get(Splitter.on('r').split(vboxHostContext.getProviderSpecificContext().getBuildVersion()), + 0); + ListenableFuture execFuture = vboxHostContext.getComputeService().submitScriptOnNode(vmName, + new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE); + ExecResponse execResponse = Futures.getUnchecked(execFuture); + return execResponse == null ? false : execResponse.getExitStatus() == 0; + } } \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java index fe9f8c8917..0b40cdb2c4 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/SshResponds.java @@ -45,7 +45,9 @@ public class SshResponds implements Predicate { } catch (SshException e) { logger.trace("No response from ssh daemon connecting to %s: %s", client, e.getMessage()); } finally { + if (client != null) { client.disconnect(); + } } return false; }