added a computeservicecontext provider for the vbox host (byon)

This commit is contained in:
David Ribeiro Alves 2012-03-02 19:15:52 +00:00
parent e00181d74e
commit 960e6f8e0a
6 changed files with 169 additions and 94 deletions

View File

@ -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 {
}

View File

@ -20,6 +20,9 @@
package org.jclouds.virtualbox.config; package org.jclouds.virtualbox.config;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; 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.io.InputStream;
import java.net.URI; import java.net.URI;
@ -32,10 +35,13 @@ import javax.inject.Singleton;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.jclouds.byon.Node; import org.jclouds.byon.Node;
import org.jclouds.byon.config.CacheNodeStoreModule;
import org.jclouds.byon.functions.NodeToNodeMetadata; import org.jclouds.byon.functions.NodeToNodeMetadata;
import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty; import org.jclouds.byon.suppliers.SupplyFromProviderURIOrNodesProperty;
import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; 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.config.ComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder; 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.compute.reference.ComputeServiceConstants.Timeouts;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.functions.IdentityFunction; import org.jclouds.functions.IdentityFunction;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.ssh.SshClient; 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.Preconfiguration;
import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter; import org.jclouds.virtualbox.compute.VirtualBoxComputeServiceAdapter;
import org.jclouds.virtualbox.domain.ExecutionType; 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.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -150,6 +161,23 @@ public class VirtualBoxComputeServiceContextModule extends
return CacheBuilder.newBuilder().build(cacheLoader); 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.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule));
}
@Provides @Provides
@Singleton @Singleton
protected Server providesJettyServer(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) { protected Server providesJettyServer(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {

View File

@ -58,4 +58,6 @@ public interface VirtualBoxConstants {
public static final String VIRTUALBOX_DEFAULT_DIR = System.getProperty("user.home") public static final String VIRTUALBOX_DEFAULT_DIR = System.getProperty("user.home")
+ File.separator +".jclouds-vbox"; + File.separator +".jclouds-vbox";
public static final String VIRTUALBOX_PROVIDER = "virtualbox";
} }

View File

@ -35,6 +35,7 @@ import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.jclouds.virtualbox.Host;
import org.jclouds.virtualbox.Preconfiguration; import org.jclouds.virtualbox.Preconfiguration;
import org.jclouds.virtualbox.domain.ExecutionType; import org.jclouds.virtualbox.domain.ExecutionType;
import org.jclouds.virtualbox.domain.IsoSpec; import org.jclouds.virtualbox.domain.IsoSpec;
@ -60,96 +61,97 @@ import com.google.inject.Inject;
@Singleton @Singleton
public class CreateAndInstallVm implements Function<MasterSpec, IMachine> { public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final ComputeServiceContext context; private final ComputeServiceContext vboxHostContext;
private final Supplier<VirtualBoxManager> manager; private final Supplier<VirtualBoxManager> manager;
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
private final Predicate<SshClient> sshResponds; private final Predicate<SshClient> sshResponds;
private final ExecutionType executionType; private final ExecutionType executionType;
private LoadingCache<IsoSpec, URI> preConfiguration; private LoadingCache<IsoSpec, URI> preConfiguration;
private final Function<IMachine, SshClient> sshClientForIMachine; private final Function<IMachine, SshClient> sshClientForIMachine;
private final MachineUtils machineUtils; private final MachineUtils machineUtils;
@Inject @Inject
public CreateAndInstallVm(ComputeServiceContext context, Supplier<VirtualBoxManager> manager, public CreateAndInstallVm(@Host ComputeServiceContext vboxHostContext, Supplier<VirtualBoxManager> manager,
CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
Predicate<SshClient> sshResponds, Function<IMachine, SshClient> sshClientForIMachine, Predicate<SshClient> sshResponds, Function<IMachine, SshClient> sshClientForIMachine,
ExecutionType executionType, MachineUtils machineUtils, @Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration) { ExecutionType executionType, MachineUtils machineUtils,
this.context = context; @Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration) {
this.manager = manager; this.vboxHostContext = vboxHostContext;
this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; this.manager = manager;
this.sshResponds = sshResponds; this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
this.sshClientForIMachine = sshClientForIMachine; this.sshResponds = sshResponds;
this.executionType = executionType; this.sshClientForIMachine = sshClientForIMachine;
this.machineUtils = machineUtils; this.executionType = executionType;
this.preConfiguration = preConfiguration; this.machineUtils = machineUtils;
} this.preConfiguration = preConfiguration;
}
@Override @Override
public IMachine apply(MasterSpec masterSpec) { public IMachine apply(MasterSpec masterSpec) {
VmSpec vmSpec = masterSpec.getVmSpec(); VmSpec vmSpec = masterSpec.getVmSpec();
IsoSpec isoSpec = masterSpec.getIsoSpec(); IsoSpec isoSpec = masterSpec.getIsoSpec();
String vmName = vmSpec.getVmName(); String vmName = vmSpec.getVmName();
final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec);
// Launch machine and wait for it to come online // Launch machine and wait for it to come online
ensureMachineIsLaunched(vmName); ensureMachineIsLaunched(vmName);
URI uri = preConfiguration.getUnchecked(isoSpec); URI uri = preConfiguration.getUnchecked(isoSpec);
String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL", String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL",
uri.toASCIIString()); uri.toASCIIString());
configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence);
SshClient client = sshClientForIMachine.apply(vm); SshClient client = sshClientForIMachine.apply(vm);
logger.debug(">> awaiting installation to finish node(%s)", vmName); 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); 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); logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
checkState(new GuestAdditionsInstaller(context).apply(vmName)); checkState(new GuestAdditionsInstaller(vboxHostContext).apply(vmName));
logger.debug(">> awaiting post-installation actions on vm: %s", vmName); logger.debug(">> awaiting post-installation actions on vm: %s", vmName);
ListenableFuture<ExecResponse> execFuture = context.getComputeService().submitScriptOnNode(vmName, ListenableFuture<ExecResponse> execFuture = vboxHostContext.getComputeService().submitScriptOnNode(vmName,
call("cleanupUdevIfNeeded"), RunScriptOptions.NONE); call("cleanupUdevIfNeeded"), RunScriptOptions.NONE);
ExecResponse execResponse = Futures.getUnchecked(execFuture); ExecResponse execResponse = Futures.getUnchecked(execFuture);
checkState(execResponse.getExitCode() == 0); checkState(execResponse.getExitCode() == 0);
logger.debug("<< installation of image complete. Powering down node(%s)", vmName); logger.debug("<< installation of image complete. Powering down node(%s)", vmName);
ensureMachineHasPowerDown(vmName); ensureMachineHasPowerDown(vmName);
return vm; return vm;
} }
private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) { private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) {
Iterable<List<Integer>> scancodelist = Iterable<List<Integer>> scancodelist = transform(Splitter.on(" ").split(installationKeySequence),
transform(Splitter.on(" ").split(installationKeySequence), new StringToKeyCode()); new StringToKeyCode());
for (List<Integer> scancodes : scancodelist) { for (List<Integer> scancodes : scancodelist) {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes)); machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes));
}
}
private void ensureMachineHasPowerDown(String vmName) {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function<ISession, Void>() {
@Override
public Void apply(ISession session) {
IProgress powerDownProgress = session.getConsole().powerDown();
powerDownProgress.waitForCompletion(-1);
return null;
} }
} });
}
private void ensureMachineHasPowerDown(String vmName) { private void ensureMachineIsLaunched(String vmName) {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function<ISession, Void>() { machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, ""));
@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, ""));
}
} }

View File

@ -21,25 +21,26 @@ import com.google.inject.Inject;
@Singleton @Singleton
public class GuestAdditionsInstaller implements Predicate<String> { public class GuestAdditionsInstaller implements Predicate<String> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
private final ComputeServiceContext context; private final ComputeServiceContext vboxHostContext;
private String vboxVersion; private String vboxVersion;
@Inject @Inject
public GuestAdditionsInstaller(ComputeServiceContext context) { public GuestAdditionsInstaller(ComputeServiceContext vboxHostContext) {
this.context = context; this.vboxHostContext = vboxHostContext;
} }
@Override @Override
public boolean apply(String vmName) { public boolean apply(String vmName) {
vboxVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0); vboxVersion = Iterables.get(Splitter.on('r').split(vboxHostContext.getProviderSpecificContext().getBuildVersion()),
ListenableFuture<ExecResponse> execFuture = context.getComputeService().submitScriptOnNode(vmName, 0);
new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE); ListenableFuture<ExecResponse> execFuture = vboxHostContext.getComputeService().submitScriptOnNode(vmName,
ExecResponse execResponse = Futures.getUnchecked(execFuture); new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE);
return execResponse == null ? false : execResponse.getExitStatus() == 0; ExecResponse execResponse = Futures.getUnchecked(execFuture);
} return execResponse == null ? false : execResponse.getExitStatus() == 0;
}
} }

View File

@ -45,7 +45,9 @@ public class SshResponds implements Predicate<SshClient> {
} catch (SshException e) { } catch (SshException e) {
logger.trace("No response from ssh daemon connecting to %s: %s", client, e.getMessage()); logger.trace("No response from ssh daemon connecting to %s: %s", client, e.getMessage());
} finally { } finally {
if (client != null) {
client.disconnect(); client.disconnect();
}
} }
return false; return false;
} }