diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java index 5f949eca14..ae836e0fc4 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/config/VirtualBoxConstants.java @@ -27,7 +27,7 @@ package org.jclouds.virtualbox.config; */ public interface VirtualBoxConstants { - public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds#image#"; + public static final String VIRTUALBOX_IMAGE_PREFIX = "jclouds-image-"; public static final String VIRTUALBOX_PRECONFIGURATION_URL = "jclouds.virtualbox.preconfigurationurl"; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java index fe36700e8c..e93ff50539 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndInstallVm.java @@ -24,6 +24,8 @@ import com.google.common.base.Splitter; import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; import com.google.inject.Inject; + +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.ssh.SshClient; @@ -32,6 +34,7 @@ import org.jclouds.virtualbox.domain.ExecutionType; 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.util.MachineUtils; import org.virtualbox_4_1.*; @@ -50,7 +53,8 @@ public class CreateAndInstallVm implements Function { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - + + private final ComputeServiceContext context; private final Supplier manager; private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists; @@ -64,10 +68,11 @@ public class CreateAndInstallVm implements Function { private final MachineUtils machineUtils; @Inject - public CreateAndInstallVm(Supplier manager, + public CreateAndInstallVm(ComputeServiceContext context, Supplier manager, CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, Predicate sshResponds, Function sshClientForIMachine, ExecutionType executionType, MachineUtils machineUtils, @Preconfiguration LoadingCache preConfiguration) { + this.context = context; this.manager = manager; this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists; this.sshResponds = sshResponds; @@ -75,7 +80,6 @@ public class CreateAndInstallVm implements Function { this.executionType = executionType; this.machineUtils = machineUtils; this.preConfiguration = preConfiguration; - } @Override @@ -99,6 +103,9 @@ public class CreateAndInstallVm implements Function { 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(context).apply(vmName)); logger.debug("<< installation of image complete. Powering down node(%s)", vmName); ensureMachineHasPowerDown(vmName); diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java index 1976e64e00..16fa75c6a4 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadata.java @@ -35,6 +35,7 @@ import org.jclouds.compute.domain.Processor; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.LocationBuilder; import org.jclouds.domain.LocationScope; +import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; import org.jclouds.logging.Logger; import org.virtualbox_4_1.IMachine; @@ -104,9 +105,9 @@ public class IMachineToNodeMetadata implements Function } } - // nodeMetadataBuilder.imageId(""); - // nodeMetadataBuilder.group(""); - + LoginCredentials loginCredentials = new LoginCredentials("toor", "password", null, true); + nodeMetadataBuilder.credentials(loginCredentials); + return nodeMetadataBuilder.build(); } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java new file mode 100644 index 0000000000..05afd85a3a --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstaller.java @@ -0,0 +1,67 @@ +package org.jclouds.virtualbox.predicates; + +import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; + +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.RunScriptData; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.scriptbuilder.domain.StatementList; +import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.virtualbox.statements.InstallGuestAdditions; + +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.inject.Inject; + +@Singleton +public class GuestAdditionsInstaller implements Predicate { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final ComputeServiceContext context; + private String vboxVersion; + + @Inject + public GuestAdditionsInstaller(ComputeServiceContext context) { + this.context = context; + } + + @Override + public boolean apply(String vmName) { + StatementList statementList = prepareStatementList(); + + ListenableFuture execFuture = context.getComputeService().submitScriptOnNode(vmName, statementList, + runAsRoot(true).wrapInInitScript(false)); + ExecResponse execResponse = null; + try { + execResponse = execFuture.get(); + } catch (InterruptedException e) { + Throwables.propagate(e); + } catch (ExecutionException e) { + Throwables.propagate(e); + } + return execResponse == null ? false : execResponse.getExitCode() == 0; + } + + private StatementList prepareStatementList() { + vboxVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0); + InstallGuestAdditions installGuestAdditions = new InstallGuestAdditions(vboxVersion); + StatementList statementList = new StatementList(Statements.exec(RunScriptData.aptInstallLazyUpgrade("curl")), + installGuestAdditions); + return statementList; + } + +} \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java index 0a4e8f5023..8ba07609ea 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/statements/InstallGuestAdditions.java @@ -34,13 +34,13 @@ import org.jclouds.scriptbuilder.domain.Statement; import com.google.common.collect.ImmutableMultimap; public class InstallGuestAdditions implements Statement { - + private final String vboxVersion; private final String mountPoint; - + public InstallGuestAdditions(String vboxVersion) { this(vboxVersion, "/mnt"); - } + } public InstallGuestAdditions(String vboxVersion, String mountPoint) { this.vboxVersion = checkNotNull(vboxVersion, "vboxVersion"); @@ -57,17 +57,18 @@ public class InstallGuestAdditions implements Statement { checkNotNull(family, "family"); if (family == OsFamily.WINDOWS) throw new UnsupportedOperationException("windows not yet implemented"); - + String vboxGuestAdditionsIso = "VBoxGuestAdditions_" + vboxVersion + ".iso"; ScriptBuilder scriptBuilder = new ScriptBuilder() .addStatement( - new SaveHttpResponseTo("{tmp}", vboxGuestAdditionsIso, "GET", URI + new SaveHttpResponseTo("{tmp}{fs}", vboxGuestAdditionsIso, "GET", URI .create("http://download.virtualbox.org/virtualbox/" + vboxVersion + "/" + vboxGuestAdditionsIso), ImmutableMultimap. of())) - .addStatement(exec(String.format("mount -o loop {tmp}{fs}%s %s", vboxGuestAdditionsIso, mountPoint))) - .addStatement(call("installGuestAdditions")) - .addStatement(exec(String.format("sh %s%s", mountPoint, "/VBoxLinuxAdditions.run"))) - .addStatement(exec(String.format("umount %s", mountPoint))); + .addStatement(exec(String.format("mount -o loop {tmp}{fs}%s %s", vboxGuestAdditionsIso, mountPoint))) + .addStatement(call("installModuleAssistantIfNeeded")) + .addStatement(exec(String.format("%s%s", mountPoint, "/VBoxLinuxAdditions.run"))) + .addStatement(exec(String.format("umount %s", mountPoint))); + return scriptBuilder.render(family); } diff --git a/labs/virtualbox/src/main/resources/functions/installGuestAdditions.sh b/labs/virtualbox/src/main/resources/functions/installGuestAdditions.sh index e1bd00e9a4..205aa45d92 100644 --- a/labs/virtualbox/src/main/resources/functions/installGuestAdditions.sh +++ b/labs/virtualbox/src/main/resources/functions/installGuestAdditions.sh @@ -1,14 +1,10 @@ function installGuestAdditions { unset OSNAME; - [ $# -eq 1 ] || { - abort "installGuestAdditions requires virtual machine name parameter" - return 1 - } local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift if [ $OSNAME = 'Ubuntu' ] then - echo "OS Name is Ubuntu" - `apt-get install build-essential module-assistant && m-a prepare -i` + echo "OS is Ubuntu" + apt-get -f -y -qq --force-yes install build-essential module-assistant && m-a prepare -i fi return 0 } \ No newline at end of file diff --git a/labs/virtualbox/src/main/resources/functions/installModuleAssistantIfNeeded.sh b/labs/virtualbox/src/main/resources/functions/installModuleAssistantIfNeeded.sh new file mode 100644 index 0000000000..4852add51e --- /dev/null +++ b/labs/virtualbox/src/main/resources/functions/installModuleAssistantIfNeeded.sh @@ -0,0 +1,10 @@ +function installModuleAssistantIfNeeded { + unset OSNAME; + local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift + if [ $OSNAME = 'Ubuntu' ] + then + echo "OS is Ubuntu" + apt-get -f -y -qq --force-yes install build-essential module-assistant; + m-a prepare -i + fi +} \ No newline at end of file diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java index 8aace24860..68df010720 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/domain/InstallGuestAdditionsTest.java @@ -37,6 +37,6 @@ public class InstallGuestAdditionsTest { public void testUnix() throws IOException { InstallGuestAdditions statement = new InstallGuestAdditions("4.1.6"); assertEquals(statement.render(OsFamily.UNIX), CharStreams.toString(Resources.newReaderSupplier(Resources - .getResource("test_install_guest_additions." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); + .getResource("test_guest_additions_installer." + ShellToken.SH.to(OsFamily.UNIX)), Charsets.UTF_8))); } } \ No newline at end of file diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java index 92f533a498..c67956d000 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java @@ -23,11 +23,16 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE; import static org.testng.Assert.assertEquals; -import com.google.inject.Injector; - import org.jclouds.config.ValueOfConfigurationKeyOrNull; import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; -import org.jclouds.virtualbox.domain.*; +import org.jclouds.virtualbox.domain.HardDisk; +import org.jclouds.virtualbox.domain.IsoSpec; +import org.jclouds.virtualbox.domain.MasterSpec; +import org.jclouds.virtualbox.domain.NetworkSpec; +import org.jclouds.virtualbox.domain.StorageController; +import org.jclouds.virtualbox.domain.VmSpec; +import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists; +import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.virtualbox_4_1.CleanupMode; @@ -38,6 +43,7 @@ import org.virtualbox_4_1.StorageBus; import com.google.common.base.CaseFormat; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; /** * @author Andrea Turli diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java index 9e2b36d763..362868c1a9 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CreateAndInstallVmLiveTest.java @@ -76,7 +76,7 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest { .attachISO(0, 0, operatingSystemIso) .attachHardDisk(hardDisk) .attachISO(1, 1, guestAdditionsIso).build(); - vmSpecification = VmSpec.builder().id("jclouds#image#create-and-install-vm-test").name(vmName).memoryMB(512).osTypeId("") + vmSpecification = VmSpec.builder().id("jclouds-image-create-and-install-vm-test").name(vmName).memoryMB(512).osTypeId("") .controller(ideController) .forceOverwrite(true) .cleanUpMode(CleanupMode.Full).build(); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java new file mode 100644 index 0000000000..4abc0fea20 --- /dev/null +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java @@ -0,0 +1,143 @@ +/** + * 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 static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE; +import static org.testng.Assert.assertTrue; + +import org.jclouds.config.ValueOfConfigurationKeyOrNull; +import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; +import org.jclouds.virtualbox.domain.HardDisk; +import org.jclouds.virtualbox.domain.IsoSpec; +import org.jclouds.virtualbox.domain.MasterSpec; +import org.jclouds.virtualbox.domain.NatAdapter; +import org.jclouds.virtualbox.domain.NetworkSpec; +import org.jclouds.virtualbox.domain.StorageController; +import org.jclouds.virtualbox.domain.VmSpec; +import org.jclouds.virtualbox.functions.CreateAndInstallVm; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.virtualbox_4_1.CleanupMode; +import org.virtualbox_4_1.IMachine; +import org.virtualbox_4_1.IProgress; +import org.virtualbox_4_1.ISession; +import org.virtualbox_4_1.LockType; +import org.virtualbox_4_1.StorageBus; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Injector; + +/** + * @author Andrea Turli + */ +@Test(groups = "live", singleThreaded = true, testName = "GuestAdditionsInstallerLiveTest") +public class GuestAdditionsInstallerLiveTest extends + BaseVirtualBoxClientLiveTest { + + private MasterSpec sourceMachineSpec; + + @Override + @BeforeClass(groups = "live") + public void setupClient() { + super.setupClient(); + String sourceName = VIRTUALBOX_IMAGE_PREFIX + + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass() + .getSimpleName()); + + StorageController ideController = StorageController + .builder() + .name("IDE Controller") + .bus(StorageBus.IDE) + .attachISO(0, 0, operatingSystemIso) + .attachHardDisk( + HardDisk.builder().diskpath(adminDisk).controllerPort(0) + .deviceSlot(1).autoDelete(true).build()) + .attachISO(1, 1, guestAdditionsIso).build(); + + VmSpec sourceVmSpec = VmSpec.builder().id(sourceName).name(sourceName) + .osTypeId("").memoryMB(512).cleanUpMode(CleanupMode.Full) + .controller(ideController).forceOverwrite(true).build(); + + Injector injector = context.utils().injector(); + Function configProperties = injector + .getInstance(ValueOfConfigurationKeyOrNull.class); + IsoSpec isoSpec = IsoSpec + .builder() + .sourcePath(operatingSystemIso) + .installationScript( + configProperties.apply(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE) + .replace("HOSTNAME", sourceVmSpec.getVmName())).build(); + + NetworkSpec networkSpec = //NetworkSpec.builder().build(); + NetworkSpec.builder() + .natNetworkAdapter(0, NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build()) + .build(); + sourceMachineSpec = MasterSpec.builder().iso(isoSpec).vm(sourceVmSpec).network(networkSpec).build(); + + } + + @Test + public void testGuestAdditionsAreInstalled() throws Exception { + try { + IMachine machine = getVmWithGuestAdditionsInstalled(); + assertTrue(machineUtils.lockSessionOnMachineAndApply(machine.getName(), LockType.Shared, new Function() { + @Override + public Boolean apply(ISession session) { + return session.getMachine().getGuestPropertyValue("/VirtualBox/GuestAdd/Version") != null; + } + })); + } finally { + for (VmSpec spec : ImmutableSet.of( + sourceMachineSpec.getVmSpec())) { + ensureMachineHasPowerDown(spec.getVmName()); + undoVm(spec); + } + } + + } + + private IMachine getVmWithGuestAdditionsInstalled() { + try { + Injector injector = context.utils().injector(); + return injector.getInstance( + CreateAndInstallVm.class).apply( + sourceMachineSpec); + } catch (IllegalStateException e) { + // already created + return manager.get().getVBox() + .findMachine(sourceMachineSpec.getVmSpec().getVmId()); + } + } + + private void ensureMachineHasPowerDown(String vmName) { + machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { + @Override + public Void apply(ISession session) { + IProgress powerDownProgress = session.getConsole().powerDown(); + powerDownProgress.waitForCompletion(-1); + return null; + } + }); + } +} \ No newline at end of file diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/GuestAdditionsInstaller.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/GuestAdditionsInstaller.java new file mode 100644 index 0000000000..77e37c309c --- /dev/null +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/GuestAdditionsInstaller.java @@ -0,0 +1,92 @@ +package org.jclouds.virtualbox.statements; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; + +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.RunScriptData; +import org.jclouds.compute.callables.RunScriptOnNode.Factory; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.scriptbuilder.domain.StatementList; +import org.jclouds.scriptbuilder.domain.Statements; +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.base.Throwables; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.inject.Inject; + +@Singleton +public class GuestAdditionsInstaller implements Function { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final ComputeServiceContext context; + + private final Supplier 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 manager, + CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists, + Predicate installGuestAdditionsViaSshResponds, Function sshClientForIMachine, + ExecutionType executionType, MachineUtils machineUtils, Factory runScriptOnNodeFactory, + Supplier 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); + + InstallGuestAdditions installGuestAdditions = new InstallGuestAdditions(vboxVersion); + StatementList statementList = new StatementList(Statements.exec(RunScriptData.aptInstallLazyUpgrade("curl")), + installGuestAdditions); + + NodeMetadata vmMetadata = new IMachineToNodeMetadata().apply(vm); + + ListenableFuture execFuture = context.getComputeService().submitScriptOnNode(vmMetadata.getId(), statementList, + runAsRoot(true).wrapInInitScript(false)); + try { + execFuture.get(); + } catch (InterruptedException e) { + Throwables.propagate(e); + } catch (ExecutionException e) { + Throwables.propagate(e); + } + return vm; + } + + private void ensureMachineIsLaunched(String vmName) { + machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), executionType, "")); + } + +} \ No newline at end of file diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsTest.java new file mode 100644 index 0000000000..1fb1bd419f --- /dev/null +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsTest.java @@ -0,0 +1,55 @@ +/** + * 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 = "InstallGuestAdditionsTest") +public class InstallGuestAdditionsTest 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); + } + +} \ No newline at end of file diff --git a/labs/virtualbox/src/test/resources/test_guest_additions_installer.sh b/labs/virtualbox/src/test/resources/test_guest_additions_installer.sh new file mode 100644 index 0000000000..8af63cbff9 --- /dev/null +++ b/labs/virtualbox/src/test/resources/test_guest_additions_installer.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set +u +shopt -s xpg_echo +shopt -s expand_aliases +unset PATH JAVA_HOME LD_LIBRARY_PATH +function abort { + echo "aborting: $@" 1>&2 + exit 1 +} +function installModuleAssistantIfNeeded { + unset OSNAME; + local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift + if [ $OSNAME = 'Ubuntu' ] + then + echo "OS is Ubuntu" + apt-get -f -y -qq --force-yes install build-essential module-assistant && m-a prepare -i + fi + return 0 +} +export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin +(mkdir -p /tmp/ && cd /tmp/ && [ ! -f VBoxGuestAdditions_4.1.6.iso ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET http://download.virtualbox.org/virtualbox/4.1.6/VBoxGuestAdditions_4.1.6.iso >VBoxGuestAdditions_4.1.6.iso) +mount -o loop /tmp/VBoxGuestAdditions_4.1.6.iso /mnt +installModuleAssistantIfNeeded || exit 1 +/mnt/VBoxLinuxAdditions.run +umount /mnt +exit 0 diff --git a/labs/virtualbox/src/test/resources/test_install_guest_additions.sh b/labs/virtualbox/src/test/resources/test_install_guest_additions.sh deleted file mode 100644 index fe2736bff5..0000000000 --- a/labs/virtualbox/src/test/resources/test_install_guest_additions.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -set +u -shopt -s xpg_echo -shopt -s expand_aliases -unset PATH JAVA_HOME LD_LIBRARY_PATH -function abort { - echo "aborting: $@" 1>&2 - exit 1 -} -function installGuestAdditions { - unset OSNAME; - [ $# -eq 1 ] || { - abort "installGuestAdditions requires virtual machine name parameter" - return 1 - } - local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift - if [ $OSNAME = 'Ubuntu' ] - then - echo "OS Name is Ubuntu" - `apt-get install build-essential module-assistant && m-a prepare -i` - fi - return 0 -} -export PATH=/usr/ucb/bin:/bin:/sbin:/usr/bin:/usr/sbin -(mkdir -p /tmp && cd /tmp && [ ! -f VBoxGuestAdditions_4.1.6.iso ] && curl -q -s -S -L --connect-timeout 10 --max-time 600 --retry 20 -C - -X GET http://download.virtualbox.org/virtualbox/4.1.6/VBoxGuestAdditions_4.1.6.iso >VBoxGuestAdditions_4.1.6.iso) -mount -o loop /tmp/VBoxGuestAdditions_4.1.6.iso /mnt -installGuestAdditions || exit 1 -sh /mnt/VBoxLinuxAdditions.run -umount /mnt -exit 0 diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/statements/GuestAdditionsInstallation.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/statements/GuestAdditionsInstallation.java new file mode 100644 index 0000000000..28af1a9f23 --- /dev/null +++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/statements/GuestAdditionsInstallation.java @@ -0,0 +1,74 @@ +/** + * 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 com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.scriptbuilder.domain.Statements.exec; + +import java.net.URI; +import java.util.Collections; + +import org.jclouds.scriptbuilder.ScriptBuilder; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.SaveHttpResponseTo; +import org.jclouds.scriptbuilder.domain.Statement; + +import com.google.common.collect.ImmutableMultimap; + +public class GuestAdditionsInstallation implements Statement { + + private final String vboxVersion; + private final String mountPoint; + + public GuestAdditionsInstallation(String vboxVersion) { + this(vboxVersion, "/mnt"); + } + + public GuestAdditionsInstallation(String vboxVersion, String mountPoint) { + this.vboxVersion = checkNotNull(vboxVersion, "vboxVersion"); + this.mountPoint = checkNotNull(mountPoint, "mountPoint"); + } + + @Override + public Iterable functionDependencies(OsFamily family) { + return Collections.emptyList(); + } + + @Override + public String render(OsFamily family) { + checkNotNull(family, "family"); + if (family == OsFamily.WINDOWS) + throw new UnsupportedOperationException("windows not yet implemented"); + + String vboxGuestAdditionsIso = "VBoxGuestAdditions_" + vboxVersion + ".iso"; + ScriptBuilder scriptBuilder = new ScriptBuilder() + .addStatement( + new SaveHttpResponseTo("{tmp}", vboxGuestAdditionsIso, "GET", URI + .create("http://download.virtualbox.org/virtualbox/" + vboxVersion + "/" + + vboxGuestAdditionsIso), ImmutableMultimap. of())) + .addStatement(exec(String.format("mount -o loop {tmp}{fs}%s %s", vboxGuestAdditionsIso, mountPoint))) + .addStatement(call("installGuestAdditions")) + .addStatement(exec(String.format("sh %s%s", mountPoint, "/VBoxLinuxAdditions.run"))) + .addStatement(exec(String.format("umount %s", mountPoint))); + return scriptBuilder.render(family); + } + +}