Merge pull request #513 from dralves/jclouds-vbox

final functionality-oriented commit on jclouds vbox
This commit is contained in:
Adrian Cole 2012-03-22 11:52:44 -07:00
commit 49c873d17a
21 changed files with 391 additions and 476 deletions

View File

@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.filter;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
import java.util.Map;
@ -63,7 +64,7 @@ import com.google.inject.Singleton;
* Defines the connection between the {@link org.virtualbox_4_1.VirtualBoxManager} implementation
* and the jclouds {@link org.jclouds.compute.ComputeService}
*
* @author Mattias Holmqvist, Andrea Turli
* @author Mattias Holmqvist, Andrea Turli, David Alves
*/
@Singleton
public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IMachine, IMachine, Image, Location> {
@ -91,6 +92,10 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
public NodeAndInitialCredentials<IMachine> createNodeWithGroupEncodedIntoName(String tag, String name,
Template template) {
try {
checkState(!tag.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "tag names cannot contain \""
+ VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");
checkState(!name.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "node names cannot contain \""
+ VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");
Master master = mastersLoader.get(template.getImage());
checkState(master != null, "could not find a master for image: "+template.getClass());
NodeSpec nodeSpec = NodeSpec.builder().master(master).name(name).tag(tag).template(template).build();

View File

@ -158,7 +158,7 @@ public class VirtualBoxComputeServiceContextModule extends
bind(new TypeLiteral<Function<IMachine, SshClient>>() {
}).to(IMachineToSshClient.class);
bind(ExecutionType.class).toInstance(ExecutionType.GUI);
bind(ExecutionType.class).toInstance(ExecutionType.HEADLESS);
bind(LockType.class).toInstance(LockType.Write);
}

View File

@ -24,14 +24,16 @@ import java.io.File;
/**
* Configuration properties used for interacting with VirtualBox instances.
*
* @author Mattias Holmqvist, Andrea Turli
* @author Mattias Holmqvist, Andrea Turli, David Alves
*
*/
public interface VirtualBoxConstants {
public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds-image-";
public static final String VIRTUALBOX_NODE_NAME_SEPARATOR = "-0x0-";
public static final String VIRTUALBOX_NODE_PREFIX = "jclouds-node-";
public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds-image" + VIRTUALBOX_NODE_NAME_SEPARATOR;
public static final String VIRTUALBOX_NODE_PREFIX = "jclouds-node" + VIRTUALBOX_NODE_NAME_SEPARATOR;
public static final String VIRTUALBOX_PRECONFIGURATION_URL = "jclouds.virtualbox.preconfigurationurl";

View File

@ -29,6 +29,7 @@ import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.RunScriptOptions;
@ -39,16 +40,16 @@ import org.jclouds.virtualbox.Preconfiguration;
import org.jclouds.virtualbox.domain.IsoSpec;
import org.jclouds.virtualbox.domain.MasterSpec;
import org.jclouds.virtualbox.domain.VmSpec;
import org.jclouds.virtualbox.predicates.GuestAdditionsInstaller;
import org.jclouds.virtualbox.statements.InstallGuestAdditions;
import org.jclouds.virtualbox.util.MachineController;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.LockType;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
@ -61,32 +62,32 @@ public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
protected Logger logger = Logger.NULL;
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
private final GuestAdditionsInstaller guestAdditionsInstaller;
private final Predicate<SshClient> sshResponds;
private LoadingCache<IsoSpec, URI> preConfiguration;
private final Function<IMachine, SshClient> sshClientForIMachine;
private final MachineUtils machineUtils;
private final IMachineToNodeMetadata imachineToNodeMetadata;
private final MachineController machineController;
private final String version;
@Inject
public CreateAndInstallVm(
CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
GuestAdditionsInstaller guestAdditionsInstaller,
IMachineToNodeMetadata imachineToNodeMetadata,
Predicate<SshClient> sshResponds,
Function<IMachine, SshClient> sshClientForIMachine,
MachineUtils machineUtils,
@Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration, MachineController machineController) {
@Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration,
MachineController machineController, @Named(Constants.PROPERTY_BUILD_VERSION) String version) {
this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
this.sshResponds = sshResponds;
this.sshClientForIMachine = sshClientForIMachine;
this.machineUtils = machineUtils;
this.preConfiguration = preConfiguration;
this.guestAdditionsInstaller = guestAdditionsInstaller;
this.imachineToNodeMetadata = imachineToNodeMetadata;
this.machineController = machineController;
this.version = Iterables.get(Splitter.on('r').split(version), 0);
}
@Override
@ -118,21 +119,23 @@ public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
vmName);
logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
checkState(guestAdditionsInstaller.apply(vm));
ListenableFuture<ExecResponse> execFuture = machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(vm),
new InstallGuestAdditions(vmSpec, version), RunScriptOptions.NONE);
ExecResponse execResponse = Futures.getUnchecked(execFuture);
checkState(execResponse.getExitStatus() == 0);
logger.debug(">> awaiting post-installation actions on vm: %s", vmName);
NodeMetadata vmMetadata = imachineToNodeMetadata.apply(vm);
ListenableFuture<ExecResponse> execFuture =
machineUtils.runScriptOnNode(vmMetadata, call("cleanupUdevIfNeeded"), RunScriptOptions.NONE);
execFuture = machineUtils.runScriptOnNode(vmMetadata, call("cleanupUdevIfNeeded"), RunScriptOptions.NONE);
ExecResponse execResponse = Futures.getUnchecked(execFuture);
execResponse = Futures.getUnchecked(execFuture);
checkState(execResponse.getExitStatus() == 0);
logger.debug(
"<< installation of image complete. Powering down node(%s)",
vmName);
logger.debug("<< installation of image complete. Powering down node(%s)", vmName);
machineController.ensureMachineHasPowerDown(vmName);
return vm;
@ -144,8 +147,7 @@ public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
.split(installationKeySequence), new StringToKeyCode());
for (List<Integer> scancodes : scancodelist) {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared,
new SendScancodes(scancodes));
machineUtils.sharedLockMachineAndApplyToSession(vmName,new SendScancodes(scancodes));
}
}

View File

@ -22,6 +22,7 @@ package org.jclouds.virtualbox.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule.machineToNodeState;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.*;
import javax.annotation.Resource;
import javax.inject.Named;
@ -54,8 +55,18 @@ public class IMachineToNodeMetadata implements Function<IMachine, NodeMetadata>
@Override
public NodeMetadata apply(@Nullable IMachine vm) {
String group = "";
String name = "";
String[] encodedInVmName = vm.getName().split(VIRTUALBOX_NODE_NAME_SEPARATOR);
if (vm.getName().startsWith(VIRTUALBOX_NODE_PREFIX)){
group = encodedInVmName[2];
name = encodedInVmName[3];
} else {
name = encodedInVmName[1];
}
NodeMetadataBuilder nodeMetadataBuilder = new NodeMetadataBuilder();
nodeMetadataBuilder.name(vm.getName()).ids(vm.getName());
nodeMetadataBuilder.name(name).ids(vm.getName()).group(group);
// TODO Set up location properly
LocationBuilder locationBuilder = new LocationBuilder();
@ -63,7 +74,6 @@ public class IMachineToNodeMetadata implements Function<IMachine, NodeMetadata>
locationBuilder.id("");
locationBuilder.scope(LocationScope.HOST);
nodeMetadataBuilder.location(locationBuilder.build());
nodeMetadataBuilder.hostname(vm.getName());
MachineState vmState = vm.getState();
@ -74,8 +84,6 @@ public class IMachineToNodeMetadata implements Function<IMachine, NodeMetadata>
logger.debug("Setting virtualbox node to: " + nodeState + " from machine state: " + vmState);
// hardcoded set-up that works only for nat+host-only
// nat adapter
INetworkAdapter natAdapter = vm.getNetworkAdapter(0l);
checkNotNull(natAdapter, "slot 0 networkadapter");
@ -83,22 +91,29 @@ public class IMachineToNodeMetadata implements Function<IMachine, NodeMetadata>
"expecting slot 0 to be a NAT attachment type (was: " + natAdapter.getAttachmentType() + ")");
int ipTermination = 0;
int inPort = 0;
String hostAddress = "";
nodeMetadataBuilder.publicAddresses(ImmutableSet.of(natAdapter.getNatDriver().getHostIP()));
for (String nameProtocolnumberAddressInboudportGuestTargetport : natAdapter.getNatDriver().getRedirects()) {
Iterable<String> stuff = Splitter.on(',').split(nameProtocolnumberAddressInboudportGuestTargetport);
String protocolNumber = Iterables.get(stuff, 1);
hostAddress = Iterables.get(stuff, 2);
String inboundPort = Iterables.get(stuff, 3);
String targetPort = Iterables.get(stuff, 5);
if ("1".equals(protocolNumber) && "22".equals(targetPort)) {
int inPort = Integer.parseInt(inboundPort);
inPort = Integer.parseInt(inboundPort);
ipTermination = inPort % NodeCreator.NODE_PORT_INIT + 2;
// nodeMetadataBuilder.publicAddresses(ImmutableSet.of(hostAddress)).loginPort(inPort);
}
}
// only masters use 2222 port
if (inPort == MastersLoadingCache.MASTER_PORT) {
nodeMetadataBuilder.publicAddresses(ImmutableSet.of(hostAddress)).loginPort(inPort);
} else {
nodeMetadataBuilder.privateAddresses(ImmutableSet.of((NodeCreator.VMS_NETWORK + ipTermination) + ""));
nodeMetadataBuilder.publicAddresses(ImmutableSet.of((NodeCreator.VMS_NETWORK + ipTermination) + ""));
}
LoginCredentials loginCredentials = new LoginCredentials("toor", "password", null, true);
nodeMetadataBuilder.credentials(loginCredentials);

View File

@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
import static org.jclouds.virtualbox.util.MachineUtils.machineNotFoundException;
@ -132,6 +133,14 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
return masters.get(key.getId());
}
// the yaml image
YamlImage yamlImage = imageMapping.get(key.getId());
checkNotNull(yamlImage, "could not find yaml image for image: " + key);
checkState(!yamlImage.id.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "master image names cannot contain \""
+ VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");
String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
String guestAdditionsIso = String.format("%s/%s", isosDir, guestAdditionsFileName);
String guestAdditionsUri = "http://download.virtualbox.org/virtualbox/" + version + "/" + guestAdditionsFileName;
@ -140,11 +149,6 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
}
checkState(new File(guestAdditionsIso).exists(), "guest additions iso does not exist at: " + guestAdditionsIso);
// the yaml image
YamlImage yamlImage = imageMapping.get(key.getId());
checkNotNull(yamlImage, "could not find yaml image for image: " + key);
// check if the iso is here, download if not
String localIsoUrl = getFilePathOrDownload(yamlImage.iso);
@ -156,7 +160,7 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
.build();
StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
.attachISO(0, 0, localIsoUrl).attachHardDisk(hardDisk).attachISO(1, 1, guestAdditionsIso).build();
.attachISO(0, 0, localIsoUrl).attachHardDisk(hardDisk).attachISO(1, 0, guestAdditionsIso).build();
VmSpec vmSpecification = VmSpec.builder().id(yamlImage.id).name(vmName).memoryMB(512).osTypeId("")
.controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();

View File

@ -1,85 +0,0 @@
/*
* 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.functions;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.LockType;
import org.virtualbox_4_1.VBoxException;
import org.virtualbox_4_1.VirtualBoxManager;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.inject.Inject;
@Singleton
public class MutableMachine implements Function<String, ISession> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final Supplier<VirtualBoxManager> manager;
private final LockType lockType;
@Inject
public MutableMachine(Supplier<VirtualBoxManager> manager,
LockType lockType) {
this.manager = manager;
this.lockType = lockType;
}
@Override
public ISession apply(String machineId) {
return lockSessionOnMachineAndReturn(manager.get(), lockType, machineId);
}
/**
* Locks the machine and executes the given function using the current session.
* Since the machine is locked it is possible to perform some modifications to the IMachine.
* <p/>
* Unlocks the machine before returning.
*
* @param manager the VirtualBoxManager
* @param type the kind of lock to use when initially locking the machine.
* @param machineId the id of the machine
* @return the ISession bounded to the machine locked.
*/
public static ISession lockSessionOnMachineAndReturn(VirtualBoxManager manager, LockType type, String machineId) {
try {
ISession session = manager.getSessionObject();
IMachine immutableMachine = manager.getVBox().findMachine(machineId);
immutableMachine.lockMachine(session, type);
return session;
} catch (VBoxException e) {
throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId,
type, e.getMessage()), e);
}
}
}

View File

@ -21,6 +21,7 @@ package org.jclouds.virtualbox.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
import java.util.concurrent.atomic.AtomicInteger;
@ -45,6 +46,7 @@ import org.jclouds.virtualbox.domain.NodeSpec;
import org.jclouds.virtualbox.domain.VmSpec;
import org.jclouds.virtualbox.statements.DeleteGShadowLock;
import org.jclouds.virtualbox.statements.SetIpAddress;
import org.jclouds.virtualbox.util.MachineController;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.CleanupMode;
import org.virtualbox_4_1.IMachine;
@ -64,7 +66,7 @@ import com.google.common.collect.Iterables;
* Creates nodes, by cloning a master vm and based on the provided {@link NodeSpec}. Must be
* synchronized mainly because of snapshot creation (must be synchronized on a per-master-basis).
*
* @author dralves
* @author David Alves
*
*/
@Singleton
@ -82,32 +84,22 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
// TODO parameterize
public static final boolean USE_LINKED = true;
// TODO parameterize
public static final ExecutionType EXECUTION_TYPE = ExecutionType.HEADLESS;
private final Supplier<VirtualBoxManager> manager;
private final Function<CloneSpec, IMachine> cloner;
private final AtomicInteger nodePorts;
private final AtomicInteger nodeIps;
private MachineUtils machineUtils;
private Function<IMachine, NodeMetadata> imachineToNodeMetadata;
private final RunScriptOnNode.Factory scriptRunnerFactory;
private final Supplier<NodeMetadata> hostSupplier;
private final MachineUtils machineUtils;
private final MachineController machineController;
@Inject
public NodeCreator(Supplier<VirtualBoxManager> manager, Function<CloneSpec, IMachine> cloner,
MachineUtils machineUtils, Function<IMachine, NodeMetadata> imachineToNodeMetadata,
RunScriptOnNode.Factory scriptRunnerFactory, Supplier<NodeMetadata> hostSupplier) {
MachineUtils machineUtils, RunScriptOnNode.Factory scriptRunnerFactory, MachineController machineController) {
this.manager = manager;
this.cloner = cloner;
this.nodePorts = new AtomicInteger(NODE_PORT_INIT);
this.nodeIps = new AtomicInteger(2);
this.machineUtils = machineUtils;
this.imachineToNodeMetadata = imachineToNodeMetadata;
this.scriptRunnerFactory = scriptRunnerFactory;
this.hostSupplier = hostSupplier;
this.machineController = machineController;
}
@Override
@ -131,8 +123,8 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
}
String masterNameWithoutPrefix = master.getSpec().getVmSpec().getVmName().replace(VIRTUALBOX_IMAGE_PREFIX, "");
String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + "-" + nodeSpec.getTag() + "-"
+ nodeSpec.getName();
String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + VIRTUALBOX_NODE_NAME_SEPARATOR
+ nodeSpec.getTag() + VIRTUALBOX_NODE_NAME_SEPARATOR + nodeSpec.getName();
VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full)
.forceOverwrite(true).build();
@ -155,7 +147,7 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
IMachine cloned = cloner.apply(cloneSpec);
new LaunchMachineIfNotAlreadyRunning(manager.get(), EXECUTION_TYPE, "").apply(cloned);
machineController.ensureMachineIsLaunched(cloneVmSpec.getVmName());
// IMachineToNodeMetadata produces the final ip's but these need to be set before so we build a
// NodeMetadata just for the sake of running the gshadow and setip scripts

View File

@ -1,72 +0,0 @@
/**
* 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.predicates;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.virtualbox.functions.IMachineToNodeMetadata;
import org.jclouds.virtualbox.statements.InstallGuestAdditions;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.VirtualBoxManager;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
@Singleton
public class GuestAdditionsInstaller implements Predicate<IMachine> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final IMachineToNodeMetadata imachineToNodeMetadata;
private final MachineUtils machineUtils;
private final Supplier<VirtualBoxManager> manager;
@Inject
public GuestAdditionsInstaller(Supplier<VirtualBoxManager> manager, MachineUtils machineUtils,
IMachineToNodeMetadata imachineToNodeMetadata) {
this.machineUtils = machineUtils;
this.imachineToNodeMetadata = imachineToNodeMetadata;
this.manager = manager;
}
@Override
public boolean apply(IMachine machine) {
String vboxVersion = Iterables.get(Splitter.on('r').split(manager.get().getVBox().getVersion()), 0);
ListenableFuture<ExecResponse> execFuture = machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(machine),
new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE);
ExecResponse execResponse = Futures.getUnchecked(execFuture);
return execResponse == null ? false : execResponse.getExitStatus() == 0;
}
}

View File

@ -25,34 +25,82 @@ import static org.jclouds.scriptbuilder.domain.Statements.exec;
import static org.jclouds.scriptbuilder.domain.Statements.saveHttpResponseTo;
import java.net.URI;
import java.util.List;
import javax.annotation.Resource;
import javax.inject.Named;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.scriptbuilder.domain.StatementList;
import org.jclouds.virtualbox.domain.IsoImage;
import org.jclouds.virtualbox.domain.StorageController;
import org.jclouds.virtualbox.domain.VmSpec;
import org.testng.collections.Lists;
public class InstallGuestAdditions extends StatementList {
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
public InstallGuestAdditions(String vboxVersion) {
this(vboxVersion, "/mnt", "VBoxGuestAdditions_" + vboxVersion + ".iso");
/**
* Mounts the DVD with guest additions that was downloaded and attached as removeable storage. If no
* guest additions is attached to the vmspec then it is downloaded.
*
* @author David Alves
*
*/
public class InstallGuestAdditions implements Statement {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final StatementList statements;
public InstallGuestAdditions(VmSpec vmSpecification, String vboxVersion) {
this.statements = new StatementList(getStatements(vmSpecification, vboxVersion));
}
public InstallGuestAdditions(String vboxVersion, String mountPoint, String vboxGuestAdditionsIso) {
this(URI.create("http://download.virtualbox.org/virtualbox/" + vboxVersion + "/" + vboxGuestAdditionsIso),
mountPoint, vboxGuestAdditionsIso);
private List<Statement> getStatements(VmSpec vmSpecification, String vboxVersion) {
List<Statement> statements = Lists.newArrayList();
statements.add(call("installModuleAssistantIfNeeded"));
String mountPoint = "/mnt";
if (Iterables.tryFind(vmSpecification.getControllers(), new Predicate<StorageController>() {
@Override
public boolean apply(StorageController input) {
if (!input.getIsoImages().isEmpty()) {
for (IsoImage iso : input.getIsoImages()) {
if (iso.getSourcePath().contains("VBoxGuestAdditions_")) {
return true;
}
public InstallGuestAdditions(URI download, String mountPoint, String vboxGuestAdditionsIso) {
super(call("setupPublicCurl"), //
saveHttpResponseTo(download, "{tmp}{fs}", vboxGuestAdditionsIso),//
exec(String.format("mount -o loop {tmp}{fs}%s %s", vboxGuestAdditionsIso, mountPoint)),
call("installModuleAssistantIfNeeded"), //
exec(String.format("%s%s", mountPoint, "/VBoxLinuxAdditions.run")), //
exec(String.format("umount %s", mountPoint)));
}
}
return false;
}
}).isPresent()) {
statements.add(exec("mount -t iso9660 /dev/sr1 " + mountPoint));
} else {
String vboxGuestAdditionsIso = "VBoxGuestAdditions_" + vboxVersion + ".iso";
URI download = URI.create("http://download.virtualbox.org/virtualbox/" + vboxVersion + "/"
+ vboxGuestAdditionsIso);
statements.add(call("setupPublicCurl"));
statements.add(saveHttpResponseTo(download, "{tmp}{fs}", vboxGuestAdditionsIso));//
statements.add(exec(String.format("mount -o loop {tmp}{fs}%s %s", vboxGuestAdditionsIso, mountPoint)));
}
statements.add(exec(String.format("%s%s", mountPoint, "/VBoxLinuxAdditions.run"))); //
statements.add(exec(String.format("umount %s", mountPoint)));
return statements;
}
@Override
public String render(OsFamily family) {
if (checkNotNull(family, "family") == OsFamily.WINDOWS)
throw new UnsupportedOperationException("windows not yet implemented");
return super.render(family);
return statements.render(family);
}
@Override
public Iterable<String> functionDependencies(OsFamily family) {
return statements.functionDependencies(family);
}
}

View File

@ -28,7 +28,6 @@ import org.jclouds.virtualbox.domain.ExecutionType;
import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning;
import org.virtualbox_4_1.IProgress;
import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.LockType;
import org.virtualbox_4_1.VirtualBoxManager;
import org.virtualbox_4_1.jaxws.MachineState;
@ -68,7 +67,7 @@ public class MachineController {
public void ensureMachineHasPowerDown(String vmName) {
while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.POWERED_OFF)) {
try {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function<ISession, Void>() {
machineUtils.sharedLockMachineAndApplyToSession(vmName, new Function<ISession, Void>() {
@Override
public Void apply(ISession session) {
IProgress powerDownProgress = session.getConsole().powerDown();

View File

@ -18,12 +18,6 @@
*/
package org.jclouds.virtualbox.util;
import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
import static org.jclouds.scriptbuilder.domain.Statements.call;
import static org.jclouds.scriptbuilder.domain.Statements.findPid;
import static org.jclouds.scriptbuilder.domain.Statements.kill;
import static org.jclouds.scriptbuilder.domain.Statements.newStatementList;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
@ -37,11 +31,9 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.scriptbuilder.domain.Statement;
import org.jclouds.util.Throwables2;
import org.jclouds.virtualbox.functions.MutableMachine;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.LockType;
import org.virtualbox_4_1.SessionState;
import org.virtualbox_4_1.VBoxException;
import org.virtualbox_4_1.VirtualBoxManager;
@ -53,7 +45,7 @@ import com.google.inject.Inject;
/**
* Utilities for executing functions on a VirtualBox machine.
*
* @author Adrian Cole, Mattias Holmqvist, Andrea Turli
* @author Adrian Cole, Mattias Holmqvist, Andrea Turli, David Alves
*/
@Singleton
@ -65,15 +57,12 @@ public class MachineUtils {
private final Supplier<VirtualBoxManager> manager;
private final Factory scriptRunner;
private final Supplier<NodeMetadata> host;
@Inject
public MachineUtils(Supplier<VirtualBoxManager> manager, RunScriptOnNode.Factory scriptRunner,
Supplier<NodeMetadata> host) {
public MachineUtils(Supplier<VirtualBoxManager> manager, RunScriptOnNode.Factory scriptRunner) {
super();
this.manager = manager;
this.scriptRunner = scriptRunner;
this.host = host;
}
public ListenableFuture<ExecResponse> runScriptOnNode(NodeMetadata metadata, Statement statement,
@ -109,12 +98,78 @@ public class MachineUtils {
});
}
/**
* Locks the machine and executes the given function using the machine matching the given id. The
* machine is write locked and modifications to the session that reflect on the machine can be
* done safely.
* <p/>
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public <T> T writeLockMachineAndApplyToSession(final String machineId, final Function<ISession, T> function) {
return lockSessionOnMachineAndApply(machineId, LockType.Write, function);
}
/**
* Locks the machine and executes the given function using the machine matching the given id. The
* machine is read locked, which means that settings can be read safely (but not changed) by
* function.
* <p/>
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public <T> T sharedLockMachineAndApply(final String machineId, final Function<IMachine, T> function) {
return lockSessionOnMachineAndApply(machineId, LockType.Shared, new Function<ISession, T>() {
@Override
public T apply(ISession session) {
return function.apply(session.getMachine());
}
@Override
public String toString() {
return function.toString();
}
});
}
/**
* Locks the machine and executes the given function to the session using the machine matching
* the given id. The machine is read locked, which means that settings can be read safely (but
* not changed) by function.
* <p/>
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public <T> T sharedLockMachineAndApplyToSession(final String machineId, final Function<ISession, T> function) {
return lockSessionOnMachineAndApply(machineId, LockType.Shared, function);
}
/**
* Locks the machine and executes the given function using the current session. Since the machine
* is locked it is possible to perform some modifications to the IMachine.
* <p/>
* Unlocks the machine before returning.
*
* Tries to obtain a lock 5 times before giving up waiting 1 sec between tries. When no machine
* is found null is returned.
*
* @param type
* the kind of lock to use when initially locking the machine.
* @param machineId
@ -123,79 +178,47 @@ public class MachineUtils {
* the function to execute
* @return the result from applying the function to the session.
*/
public <T> T lockSessionOnMachineAndApply(String machineId, LockType type, Function<ISession, T> function) {
private <T> T lockSessionOnMachineAndApply(String machineId, LockType type, Function<ISession, T> function) {
int retries = 5;
int count = 0;
ISession session;
long time = System.currentTimeMillis();
while (true) {
try {
ISession session = lockSessionOnMachine(type, machineId);
IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
session = manager.get().getSessionObject();
immutableMachine.lockMachine(session, type);
break;
} catch (VBoxException e) {
VBoxException vbex = Throwables2.getFirstThrowableOfType(e, VBoxException.class);
if (vbex != null && machineNotFoundException(vbex)) {
return null;
}
count++;
logger.warn(e, "Could not lock machine (try %d of %d). Error: %s", count, retries, e.getMessage());
if (count == retries) {
throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId, type,
e.getMessage()), e);
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e1) {
}
}
}
try {
return function.apply(session);
} finally {
session.unlockMachine();
}
} catch (VBoxException e) {
throw new RuntimeException(String.format("error applying %s to %s with %s lock: %s", function, machineId,
type, e.getMessage()), e);
} finally {
session.unlockMachine();
}
}
private ISession lockSessionOnMachine(LockType type, String machineId) {
return new MutableMachine(manager, type).apply(machineId);
}
private void unlockMachine(final String machineId) {
IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
if (immutableMachine.getSessionState().equals(SessionState.Locked)) {
Statement kill = newStatementList(call("default"), findPid(immutableMachine.getSessionPid().toString()),
kill());
scriptRunner.create(host.get(), kill, runAsRoot(false).wrapInInitScript(false)).init().call();
}
}
/**
* Unlocks the machine and executes the given function using the machine matching the given id.
* Since the machine is unlocked it is possible to delete the IMachine.
* <p/>
* <p/>
* <h3>Note!</h3> Currently, this can only unlock the machine, if the lock was created in the
* current session.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public <T> T unlockMachineAndApply(final String machineId, final Function<IMachine, T> function) {
try {
unlockMachine(machineId);
IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
return function.apply(immutableMachine);
} catch (VBoxException e) {
throw new RuntimeException(String.format("error applying %s to %s: %s", function, machineId, e.getMessage()),
e);
}
}
/**
* Unlocks the machine and executes the given function, if the machine is registered. Since the
* machine is unlocked it is possible to delete the machine.
* <p/>
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the session.
*/
public <T> T unlockMachineAndApplyOrReturnNullIfNotRegistered(String machineId, Function<IMachine, T> function) {
try {
return unlockMachineAndApply(machineId, function);
} catch (RuntimeException e) {
VBoxException vbex = Throwables2.getFirstThrowableOfType(e, VBoxException.class);
if (vbex != null && vbex.getMessage().indexOf("not find a registered") == -1)
throw e;
return null;
void print() {
for (StackTraceElement element : Thread.currentThread().getStackTrace()){
System.err.println(element.toString());
}
}

View File

@ -29,7 +29,6 @@ import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.Constants;
import org.jclouds.compute.BaseVersionedServiceLiveTest;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.ComputeServiceContextFactory;
@ -63,7 +62,7 @@ import com.google.inject.Module;
/**
* Tests behavior of {@code VirtualBoxClient}
*
* @author Adrian Cole
* @author Adrian Cole, David Alves
*/
@Test(groups = "live", singleThreaded = true, testName = "BaseVirtualBoxClientLiveTest")
public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
@ -149,8 +148,8 @@ public class BaseVirtualBoxClientLiveTest extends BaseVersionedServiceLiveTest {
protected void undoVm(VmSpec vmSpecification) {
machineUtils.unlockMachineAndApplyOrReturnNullIfNotRegistered(vmSpecification.getVmId(),
new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpecification));
machineUtils.writeLockMachineAndApply(vmSpecification.getVmId(), new UnregisterMachineIfExistsAndDeleteItsMedia(
vmSpecification));
}

View File

@ -19,6 +19,7 @@
package org.jclouds.virtualbox.compute;
import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertEquals;
import java.util.Set;
@ -72,6 +73,7 @@ public class VirtualBoxExperimentLiveTest {
TemplateOptions.Builder.runScript(AdminAccess.standard()));
assertEquals(numNodes, nodes.size(), "wrong number of nodes");
for (NodeMetadata node : nodes) {
assertTrue(node.getGroup().equals("test-launch-cluster"));
logger.debug("Created Node: %s", node);
SshClient client = context.utils().sshForNode().apply(node);
client.connect();

View File

@ -35,22 +35,22 @@ import com.google.common.io.Resources;
@Test(groups = "unit")
public class InstallGuestAdditionsTest {
@Test(enabled = false)
public void testUnixByItself() throws IOException {
InstallGuestAdditions statement = new InstallGuestAdditions("4.1.6");
assertEquals(statement.render(OsFamily.UNIX),
CharStreams.toString(Resources.newReaderSupplier(
Resources.getResource("test_guest_additions_installer." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)));
}
@Test(enabled = false)
public void testUnixInInitScript() throws IOException {
Statement statement = InitScript.builder().name("install_guest_additions")
.run(new InstallGuestAdditions("4.1.6")).build();
assertEquals(statement.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(
Resources.getResource("test_guest_additions_installer_init." + ShellToken.SH.to(OsFamily.UNIX)),
Charsets.UTF_8)));
}
// @Test(enabled = false)
// public void testUnixByItself() throws IOException {
// InstallGuestAdditions statement = new InstallGuestAdditions("4.1.6");
// assertEquals(statement.render(OsFamily.UNIX),
// CharStreams.toString(Resources.newReaderSupplier(
// Resources.getResource("test_guest_additions_installer." + ShellToken.SH.to(OsFamily.UNIX)),
// Charsets.UTF_8)));
// }
//
// @Test(enabled = false)
// public void testUnixInInitScript() throws IOException {
// Statement statement = InitScript.builder().name("install_guest_additions")
// .run(new InstallGuestAdditions("4.1.6")).build();
//
// assertEquals(statement.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(
// Resources.getResource("test_guest_additions_installer_init." + ShellToken.SH.to(OsFamily.UNIX)),
// Charsets.UTF_8)));
// }
}

View File

@ -142,7 +142,7 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
String vboxVersion = Iterables.get(
Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0);
assertEquals(vboxVersion, machineUtils.lockSessionOnMachineAndApply(machine.getName(), LockType.Shared,
assertEquals(vboxVersion, machineUtils.sharedLockMachineAndApplyToSession(machine.getName(),
new Function<ISession, String>() {
@Override
public String apply(ISession session) {

View File

@ -24,6 +24,9 @@ import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
import org.jclouds.compute.domain.NodeMetadata;
import org.testng.annotations.Test;
@ -38,22 +41,25 @@ import com.google.common.collect.Iterables;
public class IMachineToNodeMetadataTest {
private static final String MASTER_NAME = "mock-image-of-a-server";
@Test
public void testCreate() throws Exception {
public void testCreateFromMaster() throws Exception {
IMachine vm = createNiceMock(IMachine.class);
expect(vm.getName()).andReturn("mocked-vm").anyTimes();
expect(vm.getName()).andReturn(VIRTUALBOX_IMAGE_PREFIX + MASTER_NAME).anyTimes();
expect(vm.getState()).andReturn(MachineState.PoweredOff).anyTimes();
INetworkAdapter nat = createNiceMock(INetworkAdapter.class);
INATEngine natEng = createNiceMock(INATEngine.class);
expect(vm.getNetworkAdapter(eq(0l))).andReturn(nat).once();
expect(vm.getNetworkAdapter(eq(1l))).andReturn(null).once();
expect(nat.getAttachmentType()).andReturn(NetworkAttachmentType.NAT).once();
expect(nat.getNatDriver()).andReturn(natEng).anyTimes();
expect(natEng.getHostIP()).andReturn("127.0.0.1").once();
expect(natEng.getRedirects()).andReturn(ImmutableList.of("0,1,127.0.0.1,3000,,22"));
expect(natEng.getRedirects()).andReturn(ImmutableList.of("0,1,127.0.0.1,2222,,22"));
INetworkAdapter hostOnly = createNiceMock(INetworkAdapter.class);
@ -61,7 +67,45 @@ public class IMachineToNodeMetadataTest {
NodeMetadata node = new IMachineToNodeMetadata().apply(vm);
assertEquals("mocked-vm", node.getName());
assertEquals(MASTER_NAME, node.getName());
assertEquals(0, node.getPrivateAddresses().size());
assertEquals(1, node.getPublicAddresses().size());
assertEquals("127.0.0.1", Iterables.get(node.getPublicAddresses(), 0));
assertEquals(MastersLoadingCache.MASTER_PORT, node.getLoginPort());
assertEquals("", node.getGroup());
}
@Test
public void testCreateFromNode() throws Exception {
IMachine vm = createNiceMock(IMachine.class);
String group = "my-cluster-group";
String name = "a-name-with-a-code-338";
expect(vm.getName()).andReturn(
VIRTUALBOX_NODE_PREFIX + MASTER_NAME + VIRTUALBOX_NODE_NAME_SEPARATOR + group
+ VIRTUALBOX_NODE_NAME_SEPARATOR + name).anyTimes();
expect(vm.getState()).andReturn(MachineState.PoweredOff).anyTimes();
INetworkAdapter nat = createNiceMock(INetworkAdapter.class);
INATEngine natEng = createNiceMock(INATEngine.class);
INetworkAdapter hostOnly = createNiceMock(INetworkAdapter.class);
expect(vm.getNetworkAdapter(eq(0l))).andReturn(nat).once();
expect(vm.getNetworkAdapter(eq(1l))).andReturn(hostOnly).once();
expect(nat.getAttachmentType()).andReturn(NetworkAttachmentType.NAT).once();
expect(nat.getNatDriver()).andReturn(natEng).anyTimes();
expect(natEng.getHostIP()).andReturn("127.0.0.1").once();
expect(natEng.getRedirects()).andReturn(ImmutableList.of("0,1,127.0.0.1,3000,,22"));
replay(vm, nat, natEng, hostOnly);
NodeMetadata node = new IMachineToNodeMetadata().apply(vm);
assertEquals(name, node.getName());
assertEquals(group, node.getGroup());
assertEquals(1, node.getPrivateAddresses().size());
assertEquals((NodeCreator.VMS_NETWORK + 2), Iterables.get(node.getPrivateAddresses(), 0));
assertEquals(1, node.getPublicAddresses().size());

View File

@ -41,7 +41,6 @@ import org.testng.annotations.Test;
import org.virtualbox_4_1.CleanupMode;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.LockType;
import org.virtualbox_4_1.NetworkAttachmentType;
import org.virtualbox_4_1.StorageBus;
@ -74,7 +73,7 @@ public class GuestAdditionsInstallerLiveTest extends
.attachISO(0, 0, operatingSystemIso)
.attachHardDisk(
HardDisk.builder().diskpath(adminDisk(sourceName)).controllerPort(0).deviceSlot(1)
.autoDelete(true).build()).attachISO(1, 1, guestAdditionsIso).build();
.autoDelete(true).build()).attachISO(1, 0, guestAdditionsIso).build();
VmSpec sourceVmSpec = VmSpec.builder().id(sourceName).name(sourceName)
.osTypeId("").memoryMB(512).cleanUpMode(CleanupMode.Full)
@ -111,8 +110,8 @@ public class GuestAdditionsInstallerLiveTest extends
machineUtils.applyForMachine(machine.getName(),
new LaunchMachineIfNotAlreadyRunning(manager.get(),
ExecutionType.GUI, ""));
assertTrue(machineUtils.lockSessionOnMachineAndApply(
machine.getName(), LockType.Shared,
assertTrue(machineUtils.sharedLockMachineAndApplyToSession(
machine.getName(),
new Function<ISession, Boolean>() {
@Override
public Boolean apply(ISession session) {

View File

@ -1,74 +0,0 @@
package org.jclouds.virtualbox.statements;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.callables.RunScriptOnNode.Factory;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
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.domain.ExecutionType;
import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
import org.jclouds.virtualbox.functions.IMachineToNodeMetadata;
import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.VirtualBoxManager;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
@Singleton
public class GuestAdditionsInstaller implements Function<String, IMachine> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final ComputeServiceContext context;
private final Supplier<VirtualBoxManager> manager;
private final ExecutionType executionType;
private final MachineUtils machineUtils;
// TODO remove this hardcoded value
private String vboxVersion = "4.1.6";
@Inject
public GuestAdditionsInstaller(ComputeServiceContext context, Supplier<VirtualBoxManager> manager,
CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists,
Predicate<SshClient> installGuestAdditionsViaSshResponds, Function<IMachine, SshClient> sshClientForIMachine,
ExecutionType executionType, MachineUtils machineUtils, Factory runScriptOnNodeFactory,
Supplier<NodeMetadata> guest) {
this.context = context;
this.manager = manager;
this.executionType = executionType;
this.machineUtils = machineUtils;
}
@Override
public IMachine apply(String vmName) {
IMachine vm = manager.get().getVBox().findMachine(vmName);
ensureMachineIsLaunched(vmName);
NodeMetadata vmMetadata = new IMachineToNodeMetadata().apply(vm);
ListenableFuture<ExecResponse> execFuture = context.getComputeService().submitScriptOnNode(vmMetadata.getId(),
new InstallGuestAdditions(vboxVersion), RunScriptOptions.NONE);
Futures.getUnchecked(execFuture);
return vm;
}
private void ensureMachineIsLaunched(String vmName) {
machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, ""));
}
}

View File

@ -1,55 +0,0 @@
/**
* 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.statements;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.CaseFormat;
import com.google.inject.Injector;
/**
* @author Andrea Turli
*/
@Test(groups = "live", singleThreaded = true, testName = "InstallGuestAdditionsLiveTest")
public class InstallGuestAdditionsLiveTest extends BaseVirtualBoxClientLiveTest {
private String vmName;
@Override
@BeforeClass(groups = "live")
public void setupClient() {
super.setupClient();
vmName = VIRTUALBOX_IMAGE_PREFIX
+ CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass()
.getSimpleName());
vmName = "jclouds-image-create-and-install-vm-live-test";
}
public void testInstallGuestAdditionsOnTheMachine() throws Exception {
Injector injector = context.utils().injector();
injector.getInstance(GuestAdditionsInstaller.class).apply(vmName);
}
}

View File

@ -0,0 +1,67 @@
/**
* 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.statements;
import static junit.framework.Assert.assertEquals;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
import org.jclouds.virtualbox.domain.StorageController;
import org.jclouds.virtualbox.domain.VmSpec;
import org.testng.annotations.Test;
import org.virtualbox_4_1.CleanupMode;
import org.virtualbox_4_1.StorageBus;
/**
* @author Andrea Turli, David Alves
*/
@Test(testName = "InstallGuestAdditionsTest")
public class InstallGuestAdditionsTest extends BaseVirtualBoxClientLiveTest {
public void testIsoPresent() {
StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
.attachISO(1, 0, "VBoxGuestAdditions_").build();
VmSpec vmSpecification = VmSpec.builder().id("").name("").memoryMB(512).osTypeId("").controller(ideController)
.forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
InstallGuestAdditions installer = new InstallGuestAdditions(vmSpecification, "4.1.8");
String scripts = installer.render(OsFamily.UNIX);
assertEquals("installModuleAssistantIfNeeded || return 1\n" + "mount -t iso9660 /dev/sr1 /mnt\n"
+ "/mnt/VBoxLinuxAdditions.run\n" + "umount /mnt\n", scripts);
}
public void testIsoNotPresent() {
StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE).build();
VmSpec vmSpecification = VmSpec.builder().id("").name("").memoryMB(512).osTypeId("").controller(ideController)
.forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
InstallGuestAdditions installer = new InstallGuestAdditions(vmSpecification, "4.1.8");
String scripts = installer.render(OsFamily.UNIX);
assertEquals(
"installModuleAssistantIfNeeded || return 1\n"
+ "setupPublicCurl || return 1\n"
+ "(mkdir -p /tmp/ && cd /tmp/ && [ ! -f VBoxGuestAdditions_4.1.8.iso ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET http://download.virtualbox.org/virtualbox/4.1.8/VBoxGuestAdditions_4.1.8.iso >VBoxGuestAdditions_4.1.8.iso)\n"
+ "mount -o loop /tmp/VBoxGuestAdditions_4.1.8.iso /mnt\n"
+ "/mnt/VBoxLinuxAdditions.run\n" + "umount /mnt\n", scripts);
}
}