mirror of https://github.com/apache/jclouds.git
added a computeservicecontext provider for the vbox host (byon)
This commit is contained in:
parent
e00181d74e
commit
960e6f8e0a
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -57,5 +57,7 @@ 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";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 Supplier<VirtualBoxManager> manager;
|
|
||||||
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
|
|
||||||
|
|
||||||
private final Predicate<SshClient> sshResponds;
|
private final ComputeServiceContext vboxHostContext;
|
||||||
private final ExecutionType executionType;
|
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
|
private final MachineUtils machineUtils;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Inject
|
||||||
public IMachine apply(MasterSpec masterSpec) {
|
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();
|
@Override
|
||||||
IsoSpec isoSpec = masterSpec.getIsoSpec();
|
public IMachine apply(MasterSpec masterSpec) {
|
||||||
String vmName = vmSpec.getVmName();
|
|
||||||
|
|
||||||
final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec);
|
|
||||||
|
|
||||||
// Launch machine and wait for it to come online
|
VmSpec vmSpec = masterSpec.getVmSpec();
|
||||||
ensureMachineIsLaunched(vmName);
|
IsoSpec isoSpec = masterSpec.getIsoSpec();
|
||||||
|
String vmName = vmSpec.getVmName();
|
||||||
URI uri = preConfiguration.getUnchecked(isoSpec);
|
|
||||||
String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL",
|
|
||||||
uri.toASCIIString());
|
|
||||||
|
|
||||||
configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence);
|
final IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec);
|
||||||
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);
|
// Launch machine and wait for it to come online
|
||||||
|
ensureMachineIsLaunched(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) {
|
URI uri = preConfiguration.getUnchecked(isoSpec);
|
||||||
Iterable<List<Integer>> scancodelist =
|
String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL",
|
||||||
transform(Splitter.on(" ").split(installationKeySequence), new StringToKeyCode());
|
uri.toASCIIString());
|
||||||
|
|
||||||
for (List<Integer> scancodes : scancodelist) {
|
configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence);
|
||||||
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new SendScancodes(scancodes));
|
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) {
|
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, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue