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;
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.<Module> of(new SLF4JLoggingModule(), new SshjSshClientModule(), hostModule));
}
@Provides
@Singleton
protected Server providesJettyServer(@Named(VIRTUALBOX_PRECONFIGURATION_URL) String preconfigurationUrl) {

View File

@ -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";
}

View File

@ -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<MasterSpec, IMachine> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final ComputeServiceContext context;
private final Supplier<VirtualBoxManager> manager;
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final Predicate<SshClient> sshResponds;
private final ExecutionType executionType;
private final ComputeServiceContext vboxHostContext;
private final Supplier<VirtualBoxManager> manager;
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
private LoadingCache<IsoSpec, URI> preConfiguration;
private final Predicate<SshClient> sshResponds;
private final ExecutionType executionType;
private final Function<IMachine, SshClient> sshClientForIMachine;
private LoadingCache<IsoSpec, URI> preConfiguration;
private final MachineUtils machineUtils;
private final Function<IMachine, SshClient> sshClientForIMachine;
@Inject
public CreateAndInstallVm(ComputeServiceContext context, Supplier<VirtualBoxManager> manager,
CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
Predicate<SshClient> sshResponds, Function<IMachine, SshClient> sshClientForIMachine,
ExecutionType executionType, MachineUtils machineUtils, @Preconfiguration LoadingCache<IsoSpec, URI> 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<VirtualBoxManager> manager,
CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
Predicate<SshClient> sshResponds, Function<IMachine, SshClient> sshClientForIMachine,
ExecutionType executionType, MachineUtils machineUtils,
@Preconfiguration LoadingCache<IsoSpec, URI> 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<ExecResponse> 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<List<Integer>> scancodelist =
transform(Splitter.on(" ").split(installationKeySequence), new StringToKeyCode());
URI uri = preConfiguration.getUnchecked(isoSpec);
String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL",
uri.toASCIIString());
for (List<Integer> 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<ExecResponse> 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<List<Integer>> scancodelist = transform(Splitter.on(" ").split(installationKeySequence),
new StringToKeyCode());
for (List<Integer> scancodes : scancodelist) {
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) {
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 ensureMachineIsLaunched(String vmName) {
machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, ""));
}
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
public class GuestAdditionsInstaller implements Predicate<String> {
@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<ExecResponse> 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<ExecResponse> execFuture = vboxHostContext.getComputeService().submitScriptOnNode(vmName,
new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE);
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) {
logger.trace("No response from ssh daemon connecting to %s: %s", client, e.getMessage());
} finally {
if (client != null) {
client.disconnect();
}
}
return false;
}