From fa93b39c3d7882dc2ade35a412500075b4287873 Mon Sep 17 00:00:00 2001 From: Andrea Turli Date: Mon, 17 Sep 2012 21:21:20 +0200 Subject: [PATCH] issue 384: improved ISession management; managed to start VBoxExperimentLiveTest with 30 guests; added ubuntu 10.04.4 support; paramentrized guest memory --- labs/virtualbox/pom.xml | 2 +- .../virtualbox/VirtualBoxApiMetadata.java | 17 +- .../VirtualBoxComputeServiceAdapter.java | 31 +- ...VirtualBoxComputeServiceContextModule.java | 1 + .../config/VirtualBoxConstants.java | 8 +- .../jclouds/virtualbox/domain/MasterSpec.java | 18 +- .../domain/NetworkInterfaceCard.java | 28 +- .../org/jclouds/virtualbox/domain/VmSpec.java | 22 +- .../jclouds/virtualbox/domain/YamlImage.java | 6 +- .../AttachBridgedAdapterToMachine.java | 2 +- .../functions/AttachHostOnlyAdapter.java | 2 +- ...NATAdapterToMachineIfNotAlreadyExists.java | 2 +- ...MachineFromIMachineIfNotAlreadyExists.java | 13 +- .../functions/CreateAndInstallVm.java | 72 ++-- ...isterMachineFromIsoIfNotAlreadyExists.java | 15 +- .../functions/IMachineToNodeMetadata.java | 30 +- .../functions/IMachineToSshClient.java | 83 ++--- .../functions/IpAddressesLoadingCache.java | 49 +-- .../LaunchMachineIfNotAlreadyRunning.java | 5 - .../functions/MastersLoadingCache.java | 30 +- .../virtualbox/functions/NodeCreator.java | 230 +++--------- .../virtualbox/functions/SendScancodes.java | 15 +- .../TakeSnapshotIfNotAlreadyAttached.java | 5 +- .../admin/StartVBoxIfNotAlreadyRunning.java | 36 +- ...MachineIfExistsAndForceDeleteItsMedia.java | 2 +- .../statements/InstallGuestAdditions.java | 7 +- .../virtualbox/util/MachineController.java | 129 +++++-- .../util/MachineNameOrIdAndNicSlot.java | 132 +++++++ .../jclouds/virtualbox/util/MachineUtils.java | 59 +-- .../jclouds/virtualbox/util/NetworkUtils.java | 339 ++++++++++++++++++ .../src/main/resources/default-images.yaml | 140 +++++++- .../BaseVirtualBoxClientLiveTest.java | 11 +- .../compute/VirtualBoxExperimentLiveTest.java | 5 +- .../functions/IMachineToNodeMetadataTest.java | 16 +- .../admin/ImageFromYamlStringTest.java | 8 +- .../GuestAdditionsInstallerLiveTest.java | 9 +- .../InstallGuestAdditionsLiveTest.java | 15 +- .../virtualbox/util/MachineUtilsLiveTest.java | 146 +++++--- .../src/test/resources/default-images.yaml | 139 ++++++- .../virtualbox/src/test/resources/logback.xml | 5 + 40 files changed, 1330 insertions(+), 554 deletions(-) create mode 100644 labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineNameOrIdAndNicSlot.java create mode 100644 labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/NetworkUtils.java diff --git a/labs/virtualbox/pom.xml b/labs/virtualbox/pom.xml index 39a2bbb695..bb66b00c1c 100644 --- a/labs/virtualbox/pom.xml +++ b/labs/virtualbox/pom.xml @@ -38,7 +38,7 @@ http://localhost:18083/ 4.1.4 4.1.20r80170 - administrator + ${user.name} CHANGE_ME osFamily=UBUNTU,osVersionMatches=12.04.1,os64Bit=true,osArchMatches=amd64,loginUser=toor:password,authenticateSudo=true org.jclouds.virtualbox*;version="${project.version}" diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java index 6fd60045c8..c65f208341 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/VirtualBoxApiMetadata.java @@ -20,12 +20,12 @@ package org.jclouds.virtualbox; import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_CREDENTIAL; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_IDENTITY; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGES_DESCRIPTOR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_MEMORY; + import java.io.File; import java.net.URI; @@ -74,22 +74,17 @@ public class VirtualBoxApiMetadata extends BaseApiMetadata { + "initrd=/install/initrd.gz -- "); String workingDir = System.getProperty("test.virtualbox.workingDir", VIRTUALBOX_DEFAULT_DIR); - properties.put(VIRTUALBOX_WORKINGDIR, workingDir); + String ram = System.getProperty(VIRTUALBOX_GUEST_MEMORY, "1024"); + properties.put(VIRTUALBOX_GUEST_MEMORY, ram); + String yamlDescriptor = System.getProperty("test.virtualbox.image.descriptor.yaml", VIRTUALBOX_WORKINGDIR + File.separator + "images.yaml"); properties.put(VIRTUALBOX_IMAGES_DESCRIPTOR, yamlDescriptor); - properties.put(VIRTUALBOX_PRECONFIGURATION_URL, "http://10.0.2.2:23232/preseed.cfg"); - - properties.put(VIRTUALBOX_GUEST_IDENTITY, "toor"); - properties.put(VIRTUALBOX_GUEST_CREDENTIAL, "password"); - properties.setProperty(TEMPLATE, - String.format("osFamily=UBUNTU,osVersionMatches=12.04.1,os64Bit=true,osArchMatches=amd64,loginUser=%s:%s,authenticateSudo=true", - properties.getProperty(VIRTUALBOX_GUEST_IDENTITY), - properties.getProperty(VIRTUALBOX_GUEST_CREDENTIAL))); + properties.setProperty(TEMPLATE, "osFamily=UBUNTU,osVersionMatches=12.04.1,os64Bit=true,osArchMatches=amd64"); return properties; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java index 2bba7189bd..b061582810 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/compute/VirtualBoxComputeServiceAdapter.java @@ -28,6 +28,7 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_ import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Inject; @@ -46,6 +47,7 @@ import org.jclouds.virtualbox.domain.Master; import org.jclouds.virtualbox.domain.NodeSpec; import org.jclouds.virtualbox.domain.YamlImage; import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndForceDeleteItsMedia; +import org.jclouds.virtualbox.util.MachineController; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.IProgress; import org.virtualbox_4_1.ISession; @@ -62,6 +64,7 @@ import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Singleton; /** @@ -82,17 +85,20 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter mastersLoader; private final Function> cloneCreator; private final Function imachineToImage; + private final MachineController machineController; @Inject public VirtualBoxComputeServiceAdapter(Supplier manager, Supplier> imagesMapper, LoadingCache mastersLoader, Function> cloneCreator, - Function imachineToImage) { + Function imachineToImage, + MachineController machineController) { this.manager = checkNotNull(manager, "manager"); this.imagesToYamlImages = imagesMapper.get(); this.mastersLoader = mastersLoader; this.cloneCreator = cloneCreator; this.imachineToImage = imachineToImage; + this.machineController = machineController; } @Override @@ -199,7 +205,11 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter toPortableImageStatus() { return toPortableImageStatus; } + } 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 e0c8442435..a3f5d8804c 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 @@ -50,6 +50,8 @@ public interface VirtualBoxConstants { public static final String VIRTUALBOX_MACHINE_CREDENTIAL = "jclouds.virtualbox.credential"; public static final String VIRTUALBOX_MACHINE_LOCATION = "jclouds.virtualbox.location"; + + public static final String VIRTUALBOX_GUEST_MEMORY = "jclouds.virtualbox.guest.memory"; public static final String VIRTUALBOX_HOST_ID = "jclouds.virtualbox.hostid"; @@ -62,7 +64,7 @@ public interface VirtualBoxConstants { public static final String VIRTUALBOX_PROVIDER = "virtualbox"; - public static final String VIRTUALBOX_GUEST_IDENTITY = "jclouds.virtualbox.guest.identity"; - public static final String VIRTUALBOX_GUEST_CREDENTIAL = "jclouds.virtualbox.guest.credential"; - + public static final String GUEST_OS_PASSWORD = "guestPassword"; + + public static final String GUEST_OS_USER = "guestUser"; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/MasterSpec.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/MasterSpec.java index f9b5063b1a..6574a957ea 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/MasterSpec.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/MasterSpec.java @@ -21,6 +21,8 @@ package org.jclouds.virtualbox.domain; import static com.google.common.base.Preconditions.checkNotNull; +import org.jclouds.domain.LoginCredentials; + import com.google.common.base.Objects; /** @@ -32,6 +34,7 @@ public class MasterSpec { private VmSpec vmSpec; private IsoSpec isoSpec; private NetworkSpec networkSpec; + private LoginCredentials loginCredentials; public static Builder builder() { return new Builder(); @@ -42,6 +45,7 @@ public class MasterSpec { private VmSpec vmSpec; private IsoSpec isoSpec; private NetworkSpec networkSpec; + private LoginCredentials loginCredentials; public Builder vm(VmSpec vmSpec) { this.vmSpec = vmSpec; @@ -58,19 +62,25 @@ public class MasterSpec { return this; } + public Builder credentials(LoginCredentials loginCredentials) { + this.loginCredentials = loginCredentials; + return this; + } + public MasterSpec build() { - return new MasterSpec(vmSpec, isoSpec, networkSpec); + return new MasterSpec(vmSpec, isoSpec, networkSpec, loginCredentials); } } - private MasterSpec(VmSpec vmSpec, IsoSpec isoSpec, NetworkSpec networkSpec) { + private MasterSpec(VmSpec vmSpec, IsoSpec isoSpec, NetworkSpec networkSpec, LoginCredentials loginCredentials) { checkNotNull(vmSpec, "vmSpec"); checkNotNull(isoSpec, "isoSpec"); checkNotNull(networkSpec, "networkSpec"); this.vmSpec = vmSpec; this.isoSpec = isoSpec; this.networkSpec = networkSpec; + this.loginCredentials = loginCredentials; } public VmSpec getVmSpec() { @@ -84,6 +94,10 @@ public class MasterSpec { public NetworkSpec getNetworkSpec() { return networkSpec; } + + public LoginCredentials getLoginCredentials() { + return loginCredentials; + } @Override public boolean equals(Object o) { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkInterfaceCard.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkInterfaceCard.java index 57f565e802..816626cf13 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkInterfaceCard.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/NetworkInterfaceCard.java @@ -28,12 +28,13 @@ public class NetworkInterfaceCard { private final long slot; private final NetworkAdapter networkAdapter; private final String hostInterfaceName; - + private final boolean enabled; - public NetworkInterfaceCard(long slot, NetworkAdapter networkAdapter, String hostInterfaceName) { + public NetworkInterfaceCard(long slot, NetworkAdapter networkAdapter, String hostInterfaceName, boolean enabled) { this.slot = checkNotNull(slot, "slot"); this.networkAdapter = checkNotNull(networkAdapter, "networkAdapter"); this.hostInterfaceName = hostInterfaceName; + this.enabled = enabled; } public static Builder builder() { @@ -45,6 +46,7 @@ public class NetworkInterfaceCard { private long slot = 0L; private NetworkAdapter networkAdapter; private String hostInterfaceName; + private boolean enabled = true; public Builder slot(long slot) { checkArgument(slot >= 0 && slot < 4, "must be 0, 1, 2, 3: %s", slot); @@ -62,10 +64,16 @@ public class NetworkInterfaceCard { String hostInterfaceName) { this.hostInterfaceName = hostInterfaceName; return this; - } + } + + public Builder enabled( + boolean enabled) { + this.enabled = enabled; + return this; + } public NetworkInterfaceCard build() { - return new NetworkInterfaceCard(slot, networkAdapter, hostInterfaceName); + return new NetworkInterfaceCard(slot, networkAdapter, hostInterfaceName, enabled); } } @@ -79,7 +87,11 @@ public class NetworkInterfaceCard { public String getHostInterfaceName() { return hostInterfaceName; - } + } + + public boolean isEnabled() { + return enabled; + } @Override public boolean equals(Object o) { @@ -89,14 +101,15 @@ public class NetworkInterfaceCard { NetworkInterfaceCard other = (NetworkInterfaceCard) o; return Objects.equal(slot, other.slot) && - Objects.equal(networkAdapter, other.networkAdapter); + Objects.equal(networkAdapter, other.networkAdapter) + && Objects.equal(enabled, other.enabled); } return false; } @Override public int hashCode() { - return Objects.hashCode(slot, networkAdapter); + return Objects.hashCode(slot, networkAdapter, enabled); } @Override @@ -104,6 +117,7 @@ public class NetworkInterfaceCard { return "NetworkInterfaceCard{slot="+ slot + ", networkAdapter=" + networkAdapter + + ", enabled=" + enabled + '}'; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java index 102fb76d3e..7ae072600b 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/VmSpec.java @@ -42,7 +42,7 @@ public class VmSpec { private final Set controllers; private final CleanupMode cleanupMode; - public VmSpec(String vmId, String vmName, String osTypeId, long memory, boolean forceOverwrite, + public VmSpec(String vmId, String vmName, String osTypeId, long memory, String guestUser, String guestPassword, boolean forceOverwrite, Set controllers, CleanupMode cleanupMode) { this.vmId = checkNotNull(vmId, "vmId"); this.vmName = checkNotNull(vmName, "vmName"); @@ -67,6 +67,8 @@ public class VmSpec { private String osTypeId = ""; private boolean forceOverwrite = true; private long memory; + private String guestUser; + private String guestPassword; private CleanupMode cleanUpMode; public Builder controller(StorageController controller) { @@ -103,13 +105,24 @@ public class VmSpec { this.cleanUpMode = cleanupMode; return this; } + + public Builder guestUser(String guestUser) { + this.guestUser = guestUser; + return this; + } + + public Builder guestPassword(String guestPassword) { + this.guestPassword = guestPassword; + return this; + } public VmSpec build() { checkNotNull(name, "name"); checkNotNull(id, "id"); checkArgument(memory > 0, "Memory must be set"); - return new VmSpec(id, name, osTypeId, memory, forceOverwrite, controllers, cleanUpMode); + return new VmSpec(id, name, osTypeId, memory, guestUser, guestPassword, forceOverwrite, controllers, cleanUpMode); } + } public String getVmId() { @@ -162,7 +175,8 @@ public class VmSpec { @Override public String toString() { return "VmSpecification{" + "vmName='" + vmName + '\'' + ", osTypeId='" + osTypeId + '\'' + ", memory='" + memory - + '\'' + ", vmId='" + vmId + '\'' + ", forceOverwrite=" + forceOverwrite + ", controllers=" - + controllers + ", cleanupMode=" + cleanupMode + '}'; + + '\'' + ", vmId='" + vmId + '\'' + '\'' + + ", forceOverwrite=" + forceOverwrite + ", controllers=" + + controllers + ", cleanupMode=" + cleanupMode + '}'; } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java index 8a45140ffa..b3198f846d 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/domain/YamlImage.java @@ -27,6 +27,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.ImageBuilder; import org.jclouds.compute.domain.OperatingSystem; import org.jclouds.compute.domain.OsFamily; +import org.jclouds.domain.LoginCredentials; import com.google.common.base.Function; import com.google.common.collect.Lists; @@ -137,13 +138,16 @@ public class YamlImage { public Image apply(YamlImage arg0) { if (arg0 == null) return null; + OsFamily family = parseOsFamilyOrUnrecognized(arg0.os_family); OperatingSystem operatingSystem = OperatingSystem.builder().description(arg0.os_description).family(family) .version(arg0.os_version).is64Bit(arg0.os_64bit).arch(arg0.os_arch).build(); return new ImageBuilder().id(arg0.id).name(arg0.name).description(arg0.description) - .operatingSystem(operatingSystem).status(Image.Status.AVAILABLE).build(); + .operatingSystem(operatingSystem).status(Image.Status.AVAILABLE) + .defaultCredentials(new LoginCredentials(arg0.username, arg0.credential, null, true)) + .build(); } }; diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java index 323229f70c..aa27c7c405 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachBridgedAdapterToMachine.java @@ -46,7 +46,7 @@ public class AttachBridgedAdapterToMachine implements Function { iNetworkAdapter.setAdapterType(Am79C973); iNetworkAdapter.setMACAddress(networkInterfaceCard.getNetworkAdapter().getMacAddress()); iNetworkAdapter.setBridgedInterface(networkInterfaceCard.getHostInterfaceName()); - iNetworkAdapter.setEnabled(true); + iNetworkAdapter.setEnabled(networkInterfaceCard.isEnabled()); machine.saveSettings(); return null; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java index 016b942a9f..9eb16c0ef4 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachHostOnlyAdapter.java @@ -45,7 +45,7 @@ public class AttachHostOnlyAdapter implements Function { iNetworkAdapter.setAdapterType(Am79C973); iNetworkAdapter.setMACAddress(networkInterfaceCard.getNetworkAdapter().getMacAddress()); iNetworkAdapter.setHostOnlyInterface(networkInterfaceCard.getHostInterfaceName()); - iNetworkAdapter.setEnabled(true); + iNetworkAdapter.setEnabled(networkInterfaceCard.isEnabled()); machine.saveSettings(); return null; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java index 9dddd714ee..d390b14248 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/AttachNATAdapterToMachineIfNotAlreadyExists.java @@ -55,7 +55,7 @@ public class AttachNATAdapterToMachineIfNotAlreadyExists implements Function manager, @Named(VirtualBoxConstants.VIRTUALBOX_WORKINGDIR) String workingDir, MachineUtils machineUtils) { @@ -125,6 +125,13 @@ public class CloneAndRegisterMachineFromIMachineIfNotAlreadyExists implements Fu for (NetworkInterfaceCard networkInterfaceCard : networkSpec.getNetworkInterfaceCards()) { new AttachNicToMachine(vmSpec.getVmName(), machineUtils).apply(networkInterfaceCard); } + + // set only once the creds for this machine, same coming from its master + logger.debug(">> storing guest credentials on vm %s as extra data", clonedMachine.getName()); + String masterUsername = master.getExtraData(GUEST_OS_USER); + String masterPassword = master.getExtraData(GUEST_OS_PASSWORD); + clonedMachine.setExtraData(GUEST_OS_USER, masterUsername); + clonedMachine.setExtraData(GUEST_OS_PASSWORD, masterPassword); return clonedMachine; } 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 ff013ede56..19535d0d6f 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 @@ -21,9 +21,12 @@ package org.jclouds.virtualbox.functions; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.transform; import static org.jclouds.scriptbuilder.domain.Statements.call; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_PASSWORD; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_USER; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_PRECONFIGURATION_URL; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Named; @@ -49,10 +52,10 @@ import org.virtualbox_4_1.IMediumAttachment; import com.google.common.base.Function; 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.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Inject; @Singleton @@ -70,7 +73,7 @@ public class CreateAndInstallVm implements Function { private final MachineController machineController; private final String version; private final String preconfigurationUrl; - + @Inject public CreateAndInstallVm( CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists, @@ -92,49 +95,60 @@ public class CreateAndInstallVm implements Function { public IMachine apply(MasterSpec masterSpec) { VmSpec vmSpec = masterSpec.getVmSpec(); IsoSpec isoSpec = masterSpec.getIsoSpec(); - String vmName = vmSpec.getVmName(); - IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); + String masterName = vmSpec.getVmName(); + IMachine masterMachine = createAndRegisterMachineFromIsoIfNotAlreadyExists.apply(masterSpec); // Launch machine and wait for it to come online - machineController.ensureMachineIsLaunched(vmName); + machineController.ensureMachineIsLaunched(masterName); String installationKeySequence = isoSpec.getInstallationKeySequence().replace("PRECONFIGURATION_URL", preconfigurationUrl); - configureOsInstallationWithKeyboardSequence(vmName, installationKeySequence); + + configureOsInstallationWithKeyboardSequence(masterName, installationKeySequence); - // the OS installation is a long process: let's delay the check for ssh of 30 sec - try { - Thread.sleep(30000l); - } catch (InterruptedException e) { - Throwables.propagate(e); - } + // the OS installation is a long process: let's delay the check for ssh of 40 sec + Uninterruptibles.sleepUninterruptibly(40, TimeUnit.SECONDS); + + masterMachine.setExtraData(GUEST_OS_USER, masterSpec.getLoginCredentials().getUser()); + masterMachine.setExtraData(GUEST_OS_PASSWORD, masterSpec.getLoginCredentials().getPassword()); - 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); - NodeMetadata nodeMetadata = imachineToNodeMetadata.apply(vm); + SshClient client = sshClientForIMachine.apply(masterMachine); + logger.debug(">> awaiting installation to finish node(%s)", masterName); + checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", masterName); + NodeMetadata nodeMetadata = imachineToNodeMetadata.apply(masterMachine); - logger.debug(">> awaiting post-installation actions on vm: %s", vmName); + logger.debug(">> awaiting post-installation actions on vm: %s", masterName); ListenableFuture execCleanup = machineUtils.runScriptOnNode(nodeMetadata, call("cleanupUdevIfNeeded"), RunScriptOptions.NONE); ExecResponse cleanupResponse = Futures.getUnchecked(execCleanup); checkState(cleanupResponse.getExitStatus() == 0); - logger.debug(">> awaiting installation of guest additions on vm: %s", vmName); + logger.debug(">> awaiting installation of guest additions on vm: %s", masterName); ListenableFuture execInstallGA = machineUtils.runScriptOnNode(nodeMetadata, new InstallGuestAdditions(vmSpec, version), RunScriptOptions.NONE); ExecResponse gaInstallationResponse = Futures.getUnchecked(execInstallGA); checkState(gaInstallationResponse.getExitStatus() == 0); - machineController.ensureMachineIsShutdown(vmName); - Iterable mediumAttachments = Iterables.filter(vm.getMediumAttachmentsOfController("IDE Controller"), - new Predicate() { - public boolean apply(IMediumAttachment in) { - return in.getMedium() != null && in.getMedium().getDeviceType().equals(DeviceType.DVD); - } - }); + + machineController.ensureMachineIsShutdown(masterName); + + // detach DVD and ISOs, if needed + Iterable mediumAttachments = Iterables.filter( + masterMachine.getMediumAttachmentsOfController("IDE Controller"), + new Predicate() { + public boolean apply(IMediumAttachment in) { + return in.getMedium() != null + && in.getMedium().getDeviceType() + .equals(DeviceType.DVD); + } + }); for (IMediumAttachment iMediumAttachment : mediumAttachments) { - machineUtils.writeLockMachineAndApply(vm.getName(), new DetachDistroMediumFromMachine( - iMediumAttachment.getController(), iMediumAttachment.getPort(), iMediumAttachment.getDevice())); - } - return vm; + logger.debug("Detach %s from (%s)", iMediumAttachment.getMedium() + .getName(), masterMachine.getName()); + machineUtils.writeLockMachineAndApply( + masterMachine.getName(), + new DetachDistroMediumFromMachine(iMediumAttachment + .getController(), iMediumAttachment.getPort(), + iMediumAttachment.getDevice())); + } + return masterMachine; } private void configureOsInstallationWithKeyboardSequence(String vmName, String installationKeySequence) { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java index fb8dc97e7b..bd710c4a82 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CreateAndRegisterMachineFromIsoIfNotAlreadyExists.java @@ -77,30 +77,31 @@ public class CreateAndRegisterMachineFromIsoIfNotAlreadyExists implements Functi } @Override - public IMachine apply(MasterSpec launchSpecification) { + public IMachine apply(MasterSpec masterSpec) { final IVirtualBox vBox = manager.get().getVBox(); - String vmName = launchSpecification.getVmSpec().getVmName(); - String vmId = launchSpecification.getVmSpec().getVmId(); + String vmName = masterSpec.getVmSpec().getVmName(); + String vmId = masterSpec.getVmSpec().getVmId(); try { vBox.findMachine(vmId); throw new IllegalStateException("Machine " + vmName + " is already registered."); } catch (VBoxException e) { if (machineNotFoundException(e)) - return createMachine(vBox, launchSpecification); + return createMachine(vBox, masterSpec); else throw e; } } - private IMachine createMachine(IVirtualBox vBox, MasterSpec machineSpec) { - VmSpec vmSpec = machineSpec.getVmSpec(); + private IMachine createMachine(IVirtualBox vBox, MasterSpec masterSpec) { + VmSpec vmSpec = masterSpec.getVmSpec(); String settingsFile = vBox.composeMachineFilename(vmSpec.getVmName(), workingDir); IMachine newMachine = vBox.createMachine(settingsFile, vmSpec.getVmName(), vmSpec.getOsTypeId(), vmSpec.getVmId(), vmSpec.isForceOverwrite()); + manager.get().getVBox().registerMachine(newMachine); - ensureConfiguration(machineSpec); + ensureConfiguration(masterSpec); return newMachine; } 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 499fca7da1..b15b040eb7 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 @@ -19,6 +19,8 @@ package org.jclouds.virtualbox.functions; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_PASSWORD; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_USER; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX; @@ -37,7 +39,7 @@ import org.jclouds.domain.LocationScope; import org.jclouds.domain.LoginCredentials; import org.jclouds.javax.annotation.Nullable; import org.jclouds.logging.Logger; -import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.testng.collections.Lists; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.INetworkAdapter; @@ -58,12 +60,13 @@ public class IMachineToNodeMetadata implements Function protected Logger logger = Logger.NULL; private final Map toPortableNodeStatus; - private final MachineUtils machineUtils; + private final NetworkUtils networkUtils; @Inject - public IMachineToNodeMetadata(Map toPortableNodeStatus, MachineUtils machineUtils) { + public IMachineToNodeMetadata(Map toPortableNodeStatus, + NetworkUtils networkUtils) { this.toPortableNodeStatus = toPortableNodeStatus; - this.machineUtils = machineUtils; + this.networkUtils = networkUtils; } @Override @@ -95,8 +98,10 @@ public class IMachineToNodeMetadata implements Function nodeState = Status.UNRECOGNIZED; nodeMetadataBuilder.status(nodeState); nodeMetadataBuilder = getIpAddresses(vm, nodeMetadataBuilder); - - LoginCredentials loginCredentials = new LoginCredentials("toor", "password", null, true); + + String guestOsUser = vm.getExtraData(GUEST_OS_USER); + String guestOsPassword = vm.getExtraData(GUEST_OS_PASSWORD); + LoginCredentials loginCredentials = new LoginCredentials(guestOsUser, guestOsPassword, null, true); nodeMetadataBuilder.credentials(loginCredentials); return nodeMetadataBuilder.build(); @@ -104,7 +109,7 @@ public class IMachineToNodeMetadata implements Function private NodeMetadataBuilder getIpAddresses(IMachine vm, NodeMetadataBuilder nodeMetadataBuilder) { List publicIpAddresses = Lists.newArrayList(); - + List privateIpAddresses = Lists.newArrayList(); for(long slot = 0; slot < 4; slot ++) { INetworkAdapter adapter = vm.getNetworkAdapter(slot); if(adapter != null) { @@ -123,16 +128,15 @@ public class IMachineToNodeMetadata implements Function publicIpAddresses.add(hostAddress); nodeMetadataBuilder.loginPort(inPort); } - //privateIpAddresses.add((NodeCreator.VMS_NETWORK + ipTermination) + ""); } - // TODO this could be a public and private address } else if (adapter.getAttachmentType() == NetworkAttachmentType.Bridged) { - String clientIpAddress = machineUtils.getIpAddressFromFirstNIC(vm.getName()); - //privateIpAddresses.add(clientIpAddress); - publicIpAddresses.add(clientIpAddress); + // TODO quick test first + String clientIpAddress = networkUtils.getIpAddressFromNicSlot(vm.getName(), adapter.getSlot()); + privateIpAddresses.add(clientIpAddress); } else if (adapter.getAttachmentType() == NetworkAttachmentType.HostOnly) { - String clientIpAddress = machineUtils.getIpAddressFromFirstNIC(vm.getName()); + // TODO quick test first + String clientIpAddress = networkUtils.getIpAddressFromNicSlot(vm.getName(), adapter.getSlot()); publicIpAddresses.add(clientIpAddress); } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java index adb15aa207..0b25dbbe2a 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IMachineToSshClient.java @@ -19,36 +19,24 @@ 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.VirtualBoxConstants.VIRTUALBOX_GUEST_CREDENTIAL; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_IDENTITY; - -import java.util.List; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_PASSWORD; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_USER; import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Singleton; -import org.jclouds.compute.callables.RunScriptOnNode; -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.domain.LoginCredentials; import org.jclouds.logging.Logger; import org.jclouds.ssh.SshClient; -import org.jclouds.virtualbox.VirtualBoxApiMetadata; -import org.jclouds.virtualbox.domain.BridgedIf; -import org.jclouds.virtualbox.statements.GetIPAddressFromMAC; -import org.jclouds.virtualbox.statements.ScanNetworkWithPing; -import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.INetworkAdapter; import org.virtualbox_4_1.NetworkAttachmentType; import com.google.common.base.Function; import com.google.common.base.Splitter; -import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.common.net.HostAndPort; import com.google.inject.Inject; @@ -61,36 +49,30 @@ public class IMachineToSshClient implements Function { protected Logger logger = Logger.NULL; private final SshClient.Factory sshClientFactory; - private final RunScriptOnNode.Factory scriptRunnerFactory; - private final Supplier hostSupplier; - private final MachineUtils machineUtils; - + private final NetworkUtils networkUtils; + @Inject public IMachineToSshClient(SshClient.Factory sshClientFactory, - RunScriptOnNode.Factory scriptRunnerFactory, - Supplier hostSupplier, MachineUtils machineUtils) { + NetworkUtils networkUtils) { this.sshClientFactory = sshClientFactory; - this.scriptRunnerFactory = scriptRunnerFactory; - this.hostSupplier = hostSupplier; - this.machineUtils = machineUtils; + this.networkUtils = networkUtils; } @Override - public SshClient apply(final IMachine vm) { - INetworkAdapter networkAdapter = vm.getNetworkAdapter(0L); + public SshClient apply(final IMachine vm) { + INetworkAdapter networkAdapter = vm.getNetworkAdapter(0L); - SshClient client = null; - checkNotNull(networkAdapter); + SshClient client = null; + checkNotNull(networkAdapter); - String clientIpAddress = null; - String sshPort = "22"; + String clientIpAddress = null; + String sshPort = "22"; + String guestIdentity = vm.getExtraData(GUEST_OS_USER); + String guestCredential = vm.getExtraData(GUEST_OS_PASSWORD); - String guestIdentity = VirtualBoxApiMetadata.defaultProperties().getProperty(VIRTUALBOX_GUEST_IDENTITY); - String guestCredential = VirtualBoxApiMetadata.defaultProperties().getProperty(VIRTUALBOX_GUEST_CREDENTIAL); - LoginCredentials loginCredentials = LoginCredentials.builder() - .user(guestIdentity) - .password(guestCredential).authenticateSudo(true) - .build(); + LoginCredentials loginCredentials = LoginCredentials.builder() + .user(guestIdentity).password(guestCredential) + .authenticateSudo(true).build(); if (networkAdapter.getAttachmentType() .equals(NetworkAttachmentType.NAT)) { @@ -109,11 +91,10 @@ public class IMachineToSshClient implements Function { } } else if (networkAdapter.getAttachmentType().equals( NetworkAttachmentType.Bridged)) { - String network = "1.1.1.1"; - clientIpAddress = getIpAddressFromBridgedNIC(networkAdapter, network); + clientIpAddress = networkUtils.getIpAddressFromNicSlot(vm.getName(), networkAdapter.getSlot()); } else if (networkAdapter.getAttachmentType().equals( NetworkAttachmentType.HostOnly)) { - clientIpAddress = machineUtils.getIpAddressFromFirstNIC(vm.getName()); + clientIpAddress = networkUtils.getIpAddressFromNicSlot(vm.getName(), networkAdapter.getSlot()); } checkNotNull(clientIpAddress, "clientIpAddress"); @@ -124,28 +105,4 @@ public class IMachineToSshClient implements Function { return client; } - private String getIpAddressFromBridgedNIC(INetworkAdapter networkAdapter, - String network) { - // RetrieveActiveBridgedInterfaces - List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory).apply(hostSupplier.get()); - BridgedIf activeBridgedIf = checkNotNull(Iterables.get(activeBridgedInterfaces, 0), "activeBridgedInterfaces"); - network = activeBridgedIf.getIpAddress(); - - // scan ip - RunScriptOnNode ipScanRunScript = scriptRunnerFactory.create( - hostSupplier.get(), new ScanNetworkWithPing(network), - RunScriptOptions.NONE); - ExecResponse execResponse = ipScanRunScript.init().call(); - checkState(execResponse.getExitStatus() == 0); - - // retrieve ip from mac - RunScriptOnNode getIpFromMACAddressRunScript = scriptRunnerFactory - .create(hostSupplier.get(), new GetIPAddressFromMAC( - networkAdapter.getMACAddress()), - RunScriptOptions.NONE); - ExecResponse ipExecResponse = getIpFromMACAddressRunScript.init() - .call(); - checkState(ipExecResponse.getExitStatus() == 0); - return checkNotNull(ipExecResponse.getOutput(), "ipAddress"); - } } \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IpAddressesLoadingCache.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IpAddressesLoadingCache.java index c45112a6b7..03781f85b1 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IpAddressesLoadingCache.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/IpAddressesLoadingCache.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Inject; @@ -31,30 +32,33 @@ import javax.inject.Singleton; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; -import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.MachineNameOrIdAndNicSlot; +import org.jclouds.virtualbox.util.NetworkUtils; import org.virtualbox_4_1.VirtualBoxManager; +import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.cache.AbstractLoadingCache; import com.google.common.cache.LoadingCache; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.Uninterruptibles; /** * A {@link LoadingCache} for ip addresses. If the requested ip address has been * previously extracted this returns it, if not it calls vbox api. * - * @author andrea turli + * @author Andrea Turli * */ @Singleton public class IpAddressesLoadingCache extends - AbstractLoadingCache { + AbstractLoadingCache { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; - private final Map masters = Maps.newHashMap(); + private final Map masters = Maps.newHashMap(); private final Supplier manager; @Inject @@ -63,27 +67,23 @@ public class IpAddressesLoadingCache extends } @Override - public synchronized String get(String idOrName) throws ExecutionException { - if (masters.containsKey(idOrName)) { - return masters.get(idOrName); + public synchronized String get(MachineNameOrIdAndNicSlot machineNameOrIdAndNicPort) throws ExecutionException { + if (masters.containsKey(machineNameOrIdAndNicPort)) { + return masters.get(machineNameOrIdAndNicPort); } - - String currentIp = "", previousIp = ""; - int count = 0; - while (count < 3) { - currentIp = ""; - while (!MachineUtils.isIpv4(currentIp)) { - currentIp = manager.get().getVBox().findMachine(idOrName) - .getGuestPropertyValue("/VirtualBox/GuestInfo/Net/0/V4/IP"); + String query = String.format("/VirtualBox/GuestInfo/Net/%s/V4/IP", machineNameOrIdAndNicPort.getSlotText()); + String currentIp = ""; + while (!NetworkUtils.isIpv4(currentIp)) { + currentIp = manager.get().getVBox().findMachine(machineNameOrIdAndNicPort.getMachineNameOrId()) + .getGuestPropertyValue(query); + if(!Strings.nullToEmpty(currentIp).isEmpty()) + logger.debug("Found IP address %s for '%s' at slot %s", currentIp, + machineNameOrIdAndNicPort.getMachineNameOrId(), + machineNameOrIdAndNicPort.getSlotText()); + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); } - if (previousIp.equals(currentIp)) { - count++; - } - previousIp = currentIp; - } - - masters.put(idOrName, currentIp); + masters.put(machineNameOrIdAndNicPort, currentIp); return currentIp; } @@ -92,4 +92,9 @@ public class IpAddressesLoadingCache extends return masters.get((String) key); } + @Override + public void invalidate(Object key) { + masters.remove(key); + } + } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java index d6445370b2..751afbbf4b 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/LaunchMachineIfNotAlreadyRunning.java @@ -75,11 +75,6 @@ public class LaunchMachineIfNotAlreadyRunning implements Function { - // TODO parameterize - public static final int MASTER_PORT = 2222; - @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @@ -197,18 +197,17 @@ public class MastersLoadingCache extends AbstractLoadingCache { server.start(preconfigurationUrl, currentImage.preseed_cfg); } } catch (URISyntaxException e1) { + logger.error("Cannot start the preseed server", e); throw e; } MasterSpec masterSpec = buildMasterSpecFromYaml(currentImage, vmName); - - // create the master machine if it can't be found masterMachine = masterCreatorAndInstaller.apply(masterSpec); - // build the master master = Master.builder().machine(masterMachine) .spec(masterSpec).build(); } else { + logger.error("Problem during master creation", e); throw e; } } finally { @@ -241,15 +240,16 @@ public class MastersLoadingCache extends AbstractLoadingCache { .attachISO(0, 0, localIsoUrl).attachHardDisk(hardDisk) .attachISO(1, 0, guestAdditionsIso).build(); - VmSpec vmSpecification = VmSpec.builder().id(currentImage.id) - .name(vmName).memoryMB(512).osTypeId("") + VmSpec vmSpecification = VmSpec.builder().id(currentImage.id) + .name(vmName).memoryMB(512).osTypeId(getOsTypeId(currentImage.os_family, currentImage.os_64bit)) .controller(ideController).forceOverwrite(true) + .guestUser(currentImage.username).guestPassword(currentImage.credential) .cleanUpMode(CleanupMode.Full).build(); NetworkAdapter networkAdapter = NetworkAdapter .builder() .networkAttachmentType(NetworkAttachmentType.NAT) - .tcpRedirectRule(providerSupplier.get().getHost(), MASTER_PORT, + .tcpRedirectRule(providerSupplier.get().getHost(), NetworkUtils.MASTER_PORT, "", 22).build(); NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard @@ -257,7 +257,7 @@ public class MastersLoadingCache extends AbstractLoadingCache { NetworkSpec networkSpec = NetworkSpec.builder() .addNIC(networkInterfaceCard).build(); - + return MasterSpec .builder() .vm(vmSpecification) @@ -267,10 +267,12 @@ public class MastersLoadingCache extends AbstractLoadingCache { .installationScript( installationKeySequence.replace("HOSTNAME", vmSpecification.getVmName())).build()) - .network(networkSpec).build(); + .network(networkSpec) + .credentials(new LoginCredentials(currentImage.username, currentImage.credential, null, true)) + .build(); } - @Override + @Override public synchronized Master getIfPresent(Object key) { checkArgument(key instanceof Image, "this cache is for entries who's keys are Images"); @@ -315,4 +317,8 @@ public class MastersLoadingCache extends AbstractLoadingCache { return file.getAbsolutePath(); } + private String getOsTypeId(String os_family, boolean os_64bit) { + String osFamily = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, os_family); + return os_64bit ? osFamily + "_64" : osFamily; + } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java index e9f6f289a9..748bee31fe 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/NodeCreator.java @@ -21,52 +21,42 @@ 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.compute.options.RunScriptOptions.Builder.runAsRoot; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_CREDENTIAL; -import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_IDENTITY; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_PASSWORD; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.GUEST_OS_USER; +import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_GUEST_MEMORY; 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.net.URI; -import java.util.List; - +import javax.annotation.Resource; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; import org.jclouds.compute.callables.RunScriptOnNode; -import org.jclouds.compute.callables.RunScriptOnNode.Factory; -import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.LoginCredentials; -import org.jclouds.javax.annotation.Nullable; -import org.jclouds.location.Provider; -import org.jclouds.rest.annotations.Credential; -import org.jclouds.rest.annotations.Identity; -import org.jclouds.scriptbuilder.domain.Statements; -import org.jclouds.virtualbox.VirtualBoxApiMetadata; +import org.jclouds.logging.Logger; import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule; import org.jclouds.virtualbox.domain.CloneSpec; import org.jclouds.virtualbox.domain.Master; -import org.jclouds.virtualbox.domain.NetworkAdapter; import org.jclouds.virtualbox.domain.NetworkInterfaceCard; import org.jclouds.virtualbox.domain.NetworkSpec; import org.jclouds.virtualbox.domain.NodeSpec; import org.jclouds.virtualbox.domain.VmSpec; import org.jclouds.virtualbox.statements.DeleteGShadowLock; -import org.jclouds.virtualbox.statements.EnableNetworkInterface; import org.jclouds.virtualbox.util.MachineController; import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.virtualbox_4_1.CleanupMode; -import org.virtualbox_4_1.HostNetworkInterfaceType; -import org.virtualbox_4_1.IDHCPServer; -import org.virtualbox_4_1.IHostNetworkInterface; 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.NetworkAttachmentType; import org.virtualbox_4_1.VirtualBoxManager; @@ -74,7 +64,6 @@ import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -87,67 +76,65 @@ import com.google.common.collect.Iterables; */ @Singleton public class NodeCreator implements Function> { - + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + private final Supplier manager; private final Function cloner; private final MachineUtils machineUtils; private final MachineController machineController; - private final Factory runScriptOnNodeFactory; - private final Supplier host; - private final Supplier providerSupplier; - private final String username; - private final String password; - private int ram = 512; - private final String guestIdentity = VirtualBoxApiMetadata.defaultProperties().getProperty(VIRTUALBOX_GUEST_IDENTITY); - private final String guestCredential = VirtualBoxApiMetadata.defaultProperties().getProperty(VIRTUALBOX_GUEST_CREDENTIAL); + private final NetworkUtils networkUtils; + private final int ram; + @Inject - public NodeCreator(Supplier manager, Function cloner, Factory runScriptOnNodeFactory, + public NodeCreator(Supplier manager, Function cloner, MachineUtils machineUtils, RunScriptOnNode.Factory scriptRunnerFactory, MachineController machineController, - Supplier host, - @Provider Supplier providerSupplier, - @Nullable @Identity String identity, - @Nullable @Credential String credential) { + NetworkUtils networkUtils, + @Named(VIRTUALBOX_GUEST_MEMORY) String ram) { this.manager = manager; this.cloner = cloner; - this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory"); + this.networkUtils = networkUtils; this.machineUtils = machineUtils; this.machineController = machineController; - this.host = checkNotNull(host, "host"); - this.providerSupplier = checkNotNull(providerSupplier, - "endpoint to virtualbox websrvd is needed"); - this.username = identity; - this.password = credential; + this.ram = Integer.valueOf(ram); } @Override public synchronized NodeAndInitialCredentials apply(NodeSpec nodeSpec) { checkNotNull(nodeSpec, "NodeSpec"); Master master = checkNotNull(nodeSpec.getMaster(), "Master"); + if (master.getMachine().getCurrentSnapshot() != null) { ISession session; try { - session = manager.get().openMachineSession(master.getMachine()); + session = manager.get().getSessionObject(); + master.getMachine().lockMachine(session, LockType.Write); + IProgress progress = session.getConsole().deleteSnapshot(master.getMachine().getCurrentSnapshot().getId()); + progress.waitForCompletion(-1); + session.unlockMachine(); } catch (Exception e) { throw new RuntimeException("error opening vbox machine session: " + e.getMessage(), e); } - IProgress progress = session.getConsole().deleteSnapshot(master.getMachine().getCurrentSnapshot().getId()); - progress.waitForCompletion(-1); - session.unlockMachine(); + logger.debug("Deleted an existing snapshot from %s", master.getMachine().getName()); } String masterNameWithoutPrefix = master.getMachine().getName().replace(VIRTUALBOX_IMAGE_PREFIX, ""); String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + VIRTUALBOX_NODE_NAME_SEPARATOR + nodeSpec.getTag() + VIRTUALBOX_NODE_NAME_SEPARATOR + nodeSpec.getName(); - if (nodeSpec.getTemplate() != null && nodeSpec.getTemplate().getHardware() != null - && nodeSpec.getTemplate().getHardware().getRam() > 0) { - ram = nodeSpec.getTemplate().getHardware().getRam(); - } + + IMachine masterMachine = master.getMachine(); + String username = masterMachine.getExtraData(GUEST_OS_USER); + String password = masterMachine.getExtraData(GUEST_OS_PASSWORD); + VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(ram) + .guestUser(username).guestPassword(password) .cleanUpMode(CleanupMode.Full) - .forceOverwrite(true).build(); - + .forceOverwrite(true).build(); + // case 'vbox host is localhost': NAT + HOST-ONLY - NetworkSpec networkSpec = createNetworkSpecWhenVboxIsLocalhost(); + NetworkSpec networkSpec = networkUtils.createNetworkSpecWhenVboxIsLocalhost(); Optional optionalNatIfaceCard = Iterables.tryFind( networkSpec.getNetworkInterfaceCards(), new Predicate() { @@ -161,9 +148,10 @@ public class NodeCreator implements Function(cloned, - cloneName, LoginCredentials.builder() - .user(guestIdentity) - .password(guestCredential) - .authenticateSudo(true) - .build()); + cloneName, credentials); } private NodeMetadata buildPartialNodeMetadata(IMachine clone) { NodeMetadataBuilder nodeMetadataBuilder = new NodeMetadataBuilder(); nodeMetadataBuilder.id(clone.getName()); nodeMetadataBuilder.status(VirtualBoxComputeServiceContextModule.toPortableNodeStatus.get(clone.getState())); - nodeMetadataBuilder.publicAddresses(ImmutableSet.of(machineUtils.getIpAddressFromFirstNIC(clone.getName()))); - LoginCredentials loginCredentials = new LoginCredentials(guestIdentity, guestCredential, null, true); + long slot = findSlotForNetworkAttachment(clone, NetworkAttachmentType.HostOnly); + nodeMetadataBuilder.publicAddresses(ImmutableSet.of(networkUtils.getIpAddressFromNicSlot(clone.getName(), slot))); + String guestOsUser = clone.getExtraData(GUEST_OS_USER); + String guestOsPassword = clone.getExtraData(GUEST_OS_PASSWORD); + LoginCredentials loginCredentials = new LoginCredentials(guestOsUser, guestOsPassword, null, true); nodeMetadataBuilder.credentials(loginCredentials); return nodeMetadataBuilder.build(); } - private NetworkSpec createNetworkSpecWhenVboxIsLocalhost() { - NetworkAdapter natAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT) - .build(); - NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder() - .addNetworkAdapter(natAdapter) - .slot(1L) - .build(); - NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder() - .networkAttachmentType(NetworkAttachmentType.HostOnly) - .build(); - // create new hostOnly interface if needed, otherwise use the one already there with dhcp enabled ... - String hostOnlyIfName = getHostOnlyIfOrCreate(); - NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter) - .addHostInterfaceName(hostOnlyIfName).slot(0L).build(); - return createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard); - } - - private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard, - NetworkInterfaceCard hostOnlyIfaceCard) { - return NetworkSpec.builder() - .addNIC(natIfaceCard) - .addNIC(hostOnlyIfaceCard) - .build(); - } - - private String getHostOnlyIfOrCreate() { - IHostNetworkInterface availableHostInterfaceIf = returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(manager - .get().getVBox().getHost().getNetworkInterfaces()); - if (availableHostInterfaceIf==null) { - final String hostOnlyIfName = createHostOnlyIf(); - assignDHCPtoHostOnlyInterface(hostOnlyIfName); - return hostOnlyIfName; - } else { - return availableHostInterfaceIf.getName(); + private long findSlotForNetworkAttachment(IMachine clone, NetworkAttachmentType networkAttachmentType) { + long slot = -1, i = 0; + while (slot == -1 && i < 4) { + if(clone.getNetworkAdapter(i).getAttachmentType().equals(networkAttachmentType)) + slot = i; + i++; } + checkState(slot!=-1); + return slot; } - private void assignDHCPtoHostOnlyInterface(final String hostOnlyIfName) { - List availableNetworkInterfaces = manager.get().getVBox().getHost() - .getNetworkInterfaces(); - - IHostNetworkInterface iHostNetworkInterfaceWithHostOnlyIfName = Iterables.getOnlyElement(Iterables.filter(availableNetworkInterfaces, new Predicate() { - - @Override - public boolean apply(IHostNetworkInterface iHostNetworkInterface) { - return iHostNetworkInterface.getName().equals(hostOnlyIfName); - } - })); - - String hostOnlyIfIpAddress = iHostNetworkInterfaceWithHostOnlyIfName.getIPAddress(); - String dhcpIpAddress = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".254"; - String dhcpNetmask = "255.255.255.0"; - String dhcpLowerIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".2"; - String dhcpUpperIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".253"; - NodeMetadata hostNodeMetadata = getHostNodeMetadata(); - - ExecResponse response = runScriptOnNodeFactory - .create(hostNodeMetadata, - Statements.exec(String - .format("VBoxManage dhcpserver add --ifname %s --ip %s --netmask %s --lowerip %s --upperip %s --enable", - hostOnlyIfName, dhcpIpAddress, dhcpNetmask, dhcpLowerIp, dhcpUpperIp)), runAsRoot(false).wrapInInitScript(false)).init().call(); - checkState(response.getExitStatus()==0); - } - - private String createHostOnlyIf() { - final String hostOnlyIfName; - NodeMetadata hostNodeMetadata = getHostNodeMetadata(); - ExecResponse createHostOnlyResponse = runScriptOnNodeFactory - .create(hostNodeMetadata, Statements.exec("VBoxManage hostonlyif create"), - runAsRoot(false).wrapInInitScript(false)).init().call(); - String output = createHostOnlyResponse.getOutput(); - checkState(createHostOnlyResponse.getExitStatus()==0); - checkState(output.contains("'"), "cannot create hostonlyif"); - hostOnlyIfName = output.substring(output.indexOf("'") + 1, output.lastIndexOf("'")); - return hostOnlyIfName; - } - - private NodeMetadata getHostNodeMetadata() { - NodeMetadata hostNodeMetadata = NodeMetadataBuilder - .fromNodeMetadata(host.get()) - .credentials(LoginCredentials.builder().user(username).password(password).build()) - .publicAddresses( - ImmutableList.of(providerSupplier.get().getHost())) - .build(); - return hostNodeMetadata; - } - - private IHostNetworkInterface returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(Iterable availableNetworkInterfaces) { - checkNotNull(availableNetworkInterfaces); - return Iterables.getFirst(filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(availableNetworkInterfaces), null); - } - - /** - * @param availableNetworkInterfaces - * @param hostOnlyIfIpAddress - * @return - */ - private Iterable filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(Iterable availableNetworkInterfaces) { - Iterable filteredNetworkInterfaces = Iterables.filter(availableNetworkInterfaces, new Predicate() { - @Override - public boolean apply(IHostNetworkInterface iHostNetworkInterface) { - // this is an horrible workaround cause iHostNetworkInterface.getDhcpEnabled is working only for windows host - boolean match = false; - List availableDHCPservers = manager.get().getVBox().getDHCPServers(); - for (IDHCPServer idhcpServer : availableDHCPservers) { - if(idhcpServer.getEnabled() && idhcpServer.getNetworkName().equals(iHostNetworkInterface.getNetworkName())) - match = true; - } - return iHostNetworkInterface.getInterfaceType().equals(HostNetworkInterfaceType.HostOnly) && - match; - } - }); - return filteredNetworkInterfaces; - } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/SendScancodes.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/SendScancodes.java index fa6a71e0d7..824a795c38 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/SendScancodes.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/SendScancodes.java @@ -19,13 +19,13 @@ package org.jclouds.virtualbox.functions; import static com.google.common.base.Predicates.in; -import static com.google.common.base.Throwables.propagate; import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Lists.partition; import static org.jclouds.compute.reference.ComputeServiceConstants.COMPUTE_LOGGER; import static org.jclouds.virtualbox.settings.KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP_LIST; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Named; @@ -34,6 +34,7 @@ import org.jclouds.logging.Logger; import org.virtualbox_4_1.ISession; import com.google.common.base.Function; +import com.google.common.util.concurrent.Uninterruptibles; class SendScancodes implements Function { @@ -56,9 +57,9 @@ class SendScancodes implements Function { logger.debug("List of scancodes sent: ", maxOrLess); assert (codesSent == maxOrLess.size()); if (any(maxOrLess, in(SPECIAL_KEYBOARD_BUTTON_MAP_LIST.values()))) { - sleepOrPropagateInterrupt(300); + Uninterruptibles.sleepUninterruptibly(300, TimeUnit.MILLISECONDS); } else { - sleepOrPropagateInterrupt(50); + Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS); } } return null; @@ -68,12 +69,4 @@ class SendScancodes implements Function { public String toString() { return "sendScancodes(" + scancodes + ")"; } - - public void sleepOrPropagateInterrupt(long ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - throw propagate(e); - } - } } \ No newline at end of file diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java index 218bd6664d..15480cdfa6 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/TakeSnapshotIfNotAlreadyAttached.java @@ -19,6 +19,8 @@ package org.jclouds.virtualbox.functions; +import java.util.concurrent.TimeUnit; + import org.jclouds.logging.Logger; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.IProgress; @@ -30,6 +32,7 @@ import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Function; import com.google.common.base.Supplier; import com.google.common.base.Throwables; +import com.google.common.util.concurrent.Uninterruptibles; /** * @author Andrea Turli @@ -93,7 +96,7 @@ public class TakeSnapshotIfNotAlreadyAttached implements Function public synchronized void start() { URI provider = providerSupplier.get(); NodeMetadata hostNodeMetadata = hardcodedHostToHostNodeMetadata.apply(host.get()); - // kill previously started vboxwebsrv (possibly dirty session) - List statements = Lists.newArrayList(); - statements.add(Statements.findPid("vboxwebsrv")); - statements.add(Statements.kill()); - StatementList statementList = new StatementList(statements); - - if (socketTester.apply(HostAndPort.fromParts(provider.getHost(), - provider.getPort()))) { - logger.debug(String.format("shutting down previously started vboxwewbsrv at %s", provider)); - ExecResponse execResponse = runScriptOnNodeFactory - .create(hostNodeMetadata, statementList, runAsRoot(false)) - .init().call(); - if(execResponse.getExitStatus()!=0) - throw new RuntimeException("Cannot execute jclouds"); - } - + cleanUpHost(provider, hostNodeMetadata); + logger.debug("disabling password access"); runScriptOnNodeFactory .create( @@ -131,6 +117,24 @@ public class StartVBoxIfNotAlreadyRunning implements Supplier logger.warn("manager is not in unlocked state " + manager.getSessionObject().getState()); } + + private void cleanUpHost(URI provider, NodeMetadata hostNodeMetadata) { + // kill previously started vboxwebsrv (possibly dirty session) + List statements = Lists.newArrayList(); + statements.add(Statements.findPid("vboxwebsrv")); + statements.add(Statements.kill()); + StatementList statementList = new StatementList(statements); + + if (socketTester.apply(HostAndPort.fromParts(provider.getHost(), + provider.getPort()))) { + logger.debug(String.format("shutting down previously started vboxwewbsrv at %s", provider)); + ExecResponse execResponse = runScriptOnNodeFactory + .create(hostNodeMetadata, statementList, runAsRoot(false)) + .init().call(); + if(execResponse.getExitStatus()!=0) + throw new RuntimeException("Cannot execute jclouds"); + } + } @Override public VirtualBoxManager get() { diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndForceDeleteItsMedia.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndForceDeleteItsMedia.java index db2761c3c4..265b18e529 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndForceDeleteItsMedia.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/functions/admin/UnregisterMachineIfExistsAndForceDeleteItsMedia.java @@ -89,7 +89,7 @@ public class UnregisterMachineIfExistsAndForceDeleteItsMedia implements Function if (!filteredMediaToBeDeleted.isEmpty()) { try { IProgress deletion = machine.delete(filteredMediaToBeDeleted); - deletion.waitForCompletion(-1); + deletion.waitForCompletion(100); } catch (Exception e) { logger.error(e, "Problem in deleting the media attached to %s", machine.getName()); Throwables.propagate(e); 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 16861ebbf3..2f8651eb24 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 @@ -87,12 +87,7 @@ public class InstallGuestAdditions implements Statement { 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("service vboxadd setup")); // - statements.add(exec("VBoxService")); // - statements.add(exec(String.format("echo VBoxService > /etc/rc.local"))); // - statements.add(exec(String.format("echo exit 0 >> /etc/rc.local"))); // - statements.add(exec(String.format("umount %s", mountPoint))); + statements.add(exec(String.format("%s%s", mountPoint, "/VBoxLinuxAdditions.run --nox11"))); // return statements; } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java index 3a59f68f18..5983a9e0dc 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineController.java @@ -20,6 +20,9 @@ package org.jclouds.virtualbox.util; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.List; +import java.util.concurrent.TimeUnit; + import javax.annotation.Resource; import javax.inject.Named; import javax.inject.Singleton; @@ -28,15 +31,25 @@ import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.virtualbox.domain.ExecutionType; import org.jclouds.virtualbox.functions.LaunchMachineIfNotAlreadyRunning; +import org.virtualbox_4_1.AdditionsFacilityStatus; +import org.virtualbox_4_1.AdditionsFacilityType; +import org.virtualbox_4_1.AdditionsRunLevelType; +import org.virtualbox_4_1.IAdditionsFacility; +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.MachineState; +import org.virtualbox_4_1.SessionState; import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.base.Strings; import com.google.common.base.Supplier; -import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Inject; /** @@ -65,27 +78,57 @@ public class MachineController { public ISession ensureMachineIsLaunched(String vmName) { ISession session = null; - while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.Running)) { + IMachine machine = manager.get().getVBox().findMachine(vmName); + while (!machine.getState().equals(MachineState.Running)) { try { - session = machineUtils.applyForMachine(vmName, new LaunchMachineIfNotAlreadyRunning(manager.get(), - executionType, "")); + session = machineUtils.applyForMachine(vmName, + new LaunchMachineIfNotAlreadyRunning(manager.get(), + executionType, "")); } catch (RuntimeException e) { - if (e.getMessage().contains( - "org.virtualbox_4_1.VBoxException: VirtualBox error: The given session is busy (0x80BB0007)")) { + if (e.getMessage() + .contains( + "org.virtualbox_4_1.VBoxException: VirtualBox error: The given session is busy (0x80BB0007)")) { throw e; - } else if (e.getMessage().contains("VirtualBox error: The object is not ready")) { + } else if (e.getMessage().contains( + "VirtualBox error: The object is not ready")) { continue; } else { throw e; } } } + // for scancode + Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS); + + String guestAdditionsInstalled = machineUtils.sharedLockMachineAndApplyToSession(vmName, + new Function() { + + @Override + public String apply(ISession session) { + int attempts = 0; + String guestAdditionsInstalled = null; + while (!!session.getConsole().getGuest() + .getAdditionsVersion().isEmpty() && attempts < 3) { + Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); + guestAdditionsInstalled = session.getConsole().getGuest() + .getAdditionsVersion(); + attempts++; + } + return guestAdditionsInstalled; + } + + }); + if(!Strings.nullToEmpty(guestAdditionsInstalled).isEmpty()) { + waitVBoxServiceIsActive(vmName); + } + return checkNotNull(session, "session"); } public ISession ensureMachineHasPowerDown(String vmName) { ISession session = manager.get().getSessionObject(); - while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.PoweredOff)) { + IMachine machine = manager.get().getVBox().findMachine(vmName); + while (!machine.getState().equals(MachineState.PoweredOff)) { try { session = machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { @@ -97,9 +140,6 @@ public class MachineController { } }); } catch (RuntimeException e) { - // sometimes the machine might be powered of between the while - // test and the call to - // lockSessionOnMachineAndApply if (e.getMessage().contains("Invalid machine state: PoweredOff")) { throw e; } else if (e.getMessage().contains("VirtualBox error: The object is not ready")) { @@ -109,6 +149,7 @@ public class MachineController { } } } + safeCheckMachineIsUnlocked(machine); return checkNotNull(session, "session"); } @@ -117,6 +158,7 @@ public class MachineController { * http://askubuntu.com/questions/82015/shutting-down-ubuntu-server-running-in-headless-virtualbox */ public ISession ensureMachineIsShutdown(String vmName) { + IMachine machine = manager.get().getVBox().findMachine(vmName); ISession session = machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { @Override @@ -125,19 +167,12 @@ public class MachineController { return session; } }); - int count = 0; - while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.PoweredOff) && count < 10) { - try { - Thread.sleep(500l * count); - } catch (InterruptedException e) { - Throwables.propagate(e); - } - count++; - } + safeCheckMachineIsUnlocked(machine); return checkNotNull(session, "session"); } public void ensureMachineIsPaused(String vmName) { + IMachine machine = manager.get().getVBox().findMachine(vmName); while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.Paused)) { try { machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { @@ -160,9 +195,11 @@ public class MachineController { } } } + safeCheckMachineIsUnlocked(machine); } public void ensureMachineIsResumed(String vmName) { + IMachine machine = manager.get().getVBox().findMachine(vmName); while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.Running)) { try { machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function() { @@ -185,6 +222,58 @@ public class MachineController { } } } + safeCheckMachineIsUnlocked(machine); + } + + private void safeCheckMachineIsUnlocked(IMachine machine) { + int guard = 0; + while (!machine.getSessionState().equals(SessionState.Unlocked)) { + if(guard >= 5) { + logger.warn("Machine session (%s) possibly still unlocked!!!", machine.getName()); + break; + } + logger.debug("Machine session (%s) not unlocked - wait ...", machine.getName()); + Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS); + guard++; + } + logger.debug("Machine session (%s) is %s", machine.getName(), machine.getSessionState()); + } + + private void waitVBoxServiceIsActive(String vmName) { + machineUtils.sharedLockMachineAndApplyToSession(vmName, new Function() { + + @Override + public Void apply(ISession session) { + session.getConsole().getGuest().setStatisticsUpdateInterval(1l); + while (!session.getConsole().getGuest().getAdditionsStatus(AdditionsRunLevelType.Userland)) { + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + } + + List facilities = session.getConsole().getGuest().getFacilities(); + while (facilities.size() != 4) { + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); + facilities = session.getConsole().getGuest().getFacilities(); + } + facilities = session.getConsole().getGuest().getFacilities(); + + Optional vboxServiceFacility = Optional.absent(); + while (!vboxServiceFacility.isPresent()) { + vboxServiceFacility = Iterables.tryFind(session.getConsole().getGuest().getFacilities(), + new Predicate() { + @Override + public boolean apply(IAdditionsFacility additionsFacility) { + return additionsFacility.getType().equals(AdditionsFacilityType.VBoxService); + }; + }); + } + + while(!vboxServiceFacility.get().getStatus().equals(AdditionsFacilityStatus.Active)) { + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + } + Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS); + return null; + } + }); } } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineNameOrIdAndNicSlot.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineNameOrIdAndNicSlot.java new file mode 100644 index 0000000000..360f08350b --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineNameOrIdAndNicSlot.java @@ -0,0 +1,132 @@ +package org.jclouds.virtualbox.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.io.Serializable; + +import com.google.common.base.Objects; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; + + +/** + * An immutable representation of a MachineNameOrId and NIC port. + * + *

Example usage: + *

+ * MachineNameOrIdAndNicSlot mp = MachineNameOrIdAndNicSlot.fromString("myMachine:1");
+ * hp.getMachineNameOrId();  // returns "myMachine"
+ * hp.getSlot();      // returns 1
+ * hp.toString();     // returns "myMachine:1"
+ * 
+ * + * @author Andrea Turli + */ +public final class MachineNameOrIdAndNicSlot implements Serializable { + + private static final String SEPARATOR = ":"; + +/** IMachine name or id*/ + private final String machineNameOrId; + + /** Validated NIC slot number in the range [0..3] */ + private final long slot; + + private MachineNameOrIdAndNicSlot(String machineNameOrId, long slot) { + this.machineNameOrId = machineNameOrId; + this.slot = slot; + } + + public String getMachineNameOrId() { + return machineNameOrId; + } + + public boolean hasSlot() { + return slot >= 0; + } + + public long getSlot() { + checkState(hasSlot()); + return slot; + } + + public String getSlotText() { + checkState(hasSlot()); + return String.valueOf(slot); + } + + public static MachineNameOrIdAndNicSlot fromParts(String machineNameOrId, long slot) { + checkArgument(isValidSlot(slot)); + return new MachineNameOrIdAndNicSlot(checkNotNull(machineNameOrId, "machineNameOrId"), slot); + } + + public static MachineNameOrIdAndNicSlot fromString( + String machineNameOrIdAndNicSlotString) { + checkNotNull(machineNameOrIdAndNicSlotString); + String machineNameOrId = null; + String nicSlotString = null; + + Iterable splittedString = Splitter.on(SEPARATOR).split( + machineNameOrIdAndNicSlotString); + checkState(Iterables.size(splittedString) == 2); + machineNameOrId = Iterables.get(splittedString, 0); + nicSlotString = Iterables.get(splittedString, 1); + + long slot = -1; + if (nicSlotString != null) { + checkArgument(!nicSlotString.startsWith("+"), + "Unparseable slot number: %s", nicSlotString); + try { + slot = Long.parseLong(nicSlotString); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Unparseable slot number: " + + nicSlotString); + } + checkArgument(isValidSlot(slot), "Slot number out of range: %s", + nicSlotString); + } + return new MachineNameOrIdAndNicSlot(machineNameOrId, slot); + } + + public MachineNameOrIdAndNicSlot withDefaultSlot(int defaultSlot) { + checkArgument(isValidSlot(defaultSlot)); + if (hasSlot() || slot == defaultSlot) { + return this; + } + return new MachineNameOrIdAndNicSlot(machineNameOrId, defaultSlot); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof MachineNameOrIdAndNicSlot) { + MachineNameOrIdAndNicSlot that = (MachineNameOrIdAndNicSlot) other; + return Objects.equal(this.machineNameOrId, that.machineNameOrId) + && this.slot == that.slot; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(machineNameOrId, slot); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("machineNameOrId", machineNameOrId) + .add("nicSlot", slot) + .toString(); + } + + private static boolean isValidSlot(long slot) { + return slot >= 0l && slot <= 3l; + } + + private static final long serialVersionUID = 0; +} diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java index c0d23708c7..768ff7e501 100644 --- a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/MachineUtils.java @@ -21,9 +21,7 @@ package org.jclouds.virtualbox.util; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; -import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Named; @@ -48,8 +46,8 @@ import org.virtualbox_4_1.VirtualBoxManager; import com.google.common.base.Function; import com.google.common.base.Supplier; -import com.google.common.base.Throwables; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Inject; /** @@ -61,6 +59,8 @@ import com.google.inject.Inject; @Singleton public class MachineUtils { + + public final String IP_V4_ADDRESS_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; @@ -71,14 +71,13 @@ public class MachineUtils { private final Supplier manager; private final Factory scriptRunner; - private final IpAddressesLoadingCache ipAddressesLoadingCache; + @Inject public MachineUtils(Supplier manager, RunScriptOnNode.Factory scriptRunner, IpAddressesLoadingCache ipAddressesLoadingCache) { this.manager = manager; this.scriptRunner = scriptRunner; - this.ipAddressesLoadingCache = ipAddressesLoadingCache; } public ListenableFuture runScriptOnNode(NodeMetadata metadata, Statement statement, @@ -183,7 +182,7 @@ public class MachineUtils { *

* Unlocks the machine before returning. * - * Tries to obtain a lock 5 times before giving up waiting 1 sec between tries. When no machine + * Tries to obtain a lock 15 times before giving up waiting 1 sec between tries. When no machine * is found null is returned. * * @param type @@ -196,30 +195,27 @@ public class MachineUtils { */ protected T lockSessionOnMachineAndApply(String machineId, LockType type, Function function) { int retries = 15; - ISession session = lockSession(machineId, type, retries); + ISession session = checkNotNull(lockSession(machineId, type, retries), "session"); try { return function.apply(session); } catch (VBoxException e) { throw new RuntimeException(String.format("error applying %s to %s with %s lock: %s", function, machineId, type, e.getMessage()), e); } finally { - if (session != null && session.getState().equals(SessionState.Locked)) - session.unlockMachine(); + if (session.getState().equals(SessionState.Locked)) { + session.unlockMachine(); + while (!session.getState().equals(SessionState.Unlocked)) { + logger.debug("Session not unlocked - wait ..."); + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); + } + } } } private ISession lockSession(String machineId, LockType type, int retries) { int count = 0; - ISession session; IMachine immutableMachine = manager.get().getVBox().findMachine(machineId); - - try { - session = manager.get().openMachineSession(immutableMachine); - if (session.getState().equals(SessionState.Locked)) - return checkNotNull(session, "session"); - } catch (Exception e) { - logger.debug("machine %s is not locked). Error: %s", immutableMachine.getName(), e.getMessage()); - } + ISession session = null; while (true) { try { @@ -237,10 +233,7 @@ public class MachineUtils { throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId, type, e.getMessage()), e); } - try { - Thread.sleep(count * 1000L); - } catch (InterruptedException e1) { - } + Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); } } checkState(session.getState().equals(SessionState.Locked)); @@ -278,24 +271,4 @@ public class MachineUtils { || e.getMessage().contains("Could not find a registered machine with UUID {"); } - public String getIpAddressFromFirstNIC(String machineName) { - try { - return ipAddressesLoadingCache.get(machineName); - } catch (ExecutionException e) { - logger.error("Problem in using the ipAddressCache", e.getCause()); - throw Throwables.propagate(e); - } - } - - - public static boolean isIpv4(String s) { - String IP_V4_ADDRESS_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." - + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; - Pattern pattern = Pattern.compile(IP_V4_ADDRESS_PATTERN); - Matcher matcher = pattern.matcher(s); - return matcher.matches(); - } - - } diff --git a/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/NetworkUtils.java b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/NetworkUtils.java new file mode 100644 index 0000000000..73ddb4e247 --- /dev/null +++ b/labs/virtualbox/src/main/java/org/jclouds/virtualbox/util/NetworkUtils.java @@ -0,0 +1,339 @@ +/** + * 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.util; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; + +import java.net.URI; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.callables.RunScriptOnNode; +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.options.RunScriptOptions; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.location.Provider; +import org.jclouds.logging.Logger; +import org.jclouds.scriptbuilder.domain.Statements; +import org.jclouds.virtualbox.domain.BridgedIf; +import org.jclouds.virtualbox.domain.NetworkAdapter; +import org.jclouds.virtualbox.domain.NetworkInterfaceCard; +import org.jclouds.virtualbox.domain.NetworkSpec; +import org.jclouds.virtualbox.functions.IpAddressesLoadingCache; +import org.jclouds.virtualbox.functions.RetrieveActiveBridgedInterfaces; +import org.jclouds.virtualbox.statements.EnableNetworkInterface; +import org.jclouds.virtualbox.statements.GetIPAddressFromMAC; +import org.jclouds.virtualbox.statements.ScanNetworkWithPing; +import org.virtualbox_4_1.HostNetworkInterfaceType; +import org.virtualbox_4_1.IDHCPServer; +import org.virtualbox_4_1.IHostNetworkInterface; +import org.virtualbox_4_1.IMachine; +import org.virtualbox_4_1.INetworkAdapter; +import org.virtualbox_4_1.ISession; +import org.virtualbox_4_1.LockType; +import org.virtualbox_4_1.NetworkAttachmentType; +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.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Uninterruptibles; +import com.google.inject.Inject; + +/** + * Utilities to manage VirtualBox networks on guests + * + * @author Andrea Turli + */ + +@Singleton +public class NetworkUtils { + + // TODO parameterize + public static final int MASTER_PORT = 2222; + private static final String VIRTUALBOX_HOST_GATEWAY = "10.0.2.15"; + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final Supplier manager; + private final MachineUtils machineUtils; + private final Supplier host; + private final Supplier providerSupplier; + private final IpAddressesLoadingCache ipAddressesLoadingCache; + private final RunScriptOnNode.Factory scriptRunnerFactory; + private final Supplier hostSupplier; + + @Inject + public NetworkUtils(Supplier manager, MachineUtils machineUtils, + MachineController machineController, + Supplier host, + @Provider Supplier providerSupplier, + IpAddressesLoadingCache ipAddressesLoadingCache, + Supplier hostSupplier, + RunScriptOnNode.Factory scriptRunnerFactory) { + this.manager = manager; + this.machineUtils = machineUtils; + this.host = checkNotNull(host, "host"); + this.providerSupplier = checkNotNull(providerSupplier, + "endpoint to virtualbox websrvd is needed"); + this.ipAddressesLoadingCache = ipAddressesLoadingCache; + this.scriptRunnerFactory = scriptRunnerFactory; + this.hostSupplier = hostSupplier; + } + + public NetworkSpec createNetworkSpecWhenVboxIsLocalhost() { + NetworkAdapter natAdapter = NetworkAdapter.builder() + .networkAttachmentType(NetworkAttachmentType.NAT) + .build(); + + NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder() + .addNetworkAdapter(natAdapter) + .slot(1L) + .build(); + NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder() + .networkAttachmentType(NetworkAttachmentType.HostOnly) + .build(); + // create new hostOnly interface if needed, otherwise use the one already there with dhcp enabled ... + String hostOnlyIfName = getHostOnlyIfOrCreate(); + NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter) + .addHostInterfaceName(hostOnlyIfName).slot(0L).build(); + return createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard); + } + + public boolean enableNetworkInterface(NodeMetadata nodeMetadata, NetworkInterfaceCard networkInterfaceCard) { + ExecResponse execResponse = null; + try { + execResponse = machineUtils.runScriptOnNode(nodeMetadata, + new EnableNetworkInterface(networkInterfaceCard), RunScriptOptions.NONE).get(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } catch (ExecutionException e) { + logger.error(e.getMessage()); + } + if(execResponse == null) + return false; + return execResponse.getExitStatus() == 0; + } + + private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard, + NetworkInterfaceCard hostOnlyIfaceCard) { + return NetworkSpec.builder() + .addNIC(hostOnlyIfaceCard) + .addNIC(natIfaceCard) + .build(); + } + + public String getHostOnlyIfOrCreate() { + IHostNetworkInterface availableHostInterfaceIf = returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(manager + .get().getVBox().getHost().getNetworkInterfaces()); + if (availableHostInterfaceIf==null) { + final String hostOnlyIfName = createHostOnlyIf(); + assignDHCPtoHostOnlyInterface(hostOnlyIfName); + return hostOnlyIfName; + } else { + return availableHostInterfaceIf.getName(); + } + } + + private void assignDHCPtoHostOnlyInterface(final String hostOnlyIfName) { + List availableNetworkInterfaces = manager.get().getVBox().getHost() + .getNetworkInterfaces(); + + IHostNetworkInterface iHostNetworkInterfaceWithHostOnlyIfName = Iterables.getOnlyElement(Iterables.filter(availableNetworkInterfaces, new Predicate() { + + @Override + public boolean apply(IHostNetworkInterface iHostNetworkInterface) { + return iHostNetworkInterface.getName().equals(hostOnlyIfName); + } + })); + + String hostOnlyIfIpAddress = iHostNetworkInterfaceWithHostOnlyIfName.getIPAddress(); + String dhcpIpAddress = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".254"; + String dhcpNetmask = "255.255.255.0"; + String dhcpLowerIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".2"; + String dhcpUpperIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".253"; + NodeMetadata hostNodeMetadata = getHostNodeMetadata(); + + ExecResponse response = scriptRunnerFactory + .create(hostNodeMetadata, + Statements.exec(String + .format("VBoxManage dhcpserver add --ifname %s --ip %s --netmask %s --lowerip %s --upperip %s --enable", + hostOnlyIfName, dhcpIpAddress, dhcpNetmask, dhcpLowerIp, dhcpUpperIp)), runAsRoot(false).wrapInInitScript(false)).init().call(); + checkState(response.getExitStatus()==0); + } + + private String createHostOnlyIf() { + final String hostOnlyIfName; + NodeMetadata hostNodeMetadata = getHostNodeMetadata(); + ExecResponse createHostOnlyResponse = scriptRunnerFactory + .create(hostNodeMetadata, Statements.exec("VBoxManage hostonlyif create"), + runAsRoot(false).wrapInInitScript(false)).init().call(); + String output = createHostOnlyResponse.getOutput(); + checkState(createHostOnlyResponse.getExitStatus()==0); + checkState(output.contains("'"), "cannot create hostonlyif"); + hostOnlyIfName = output.substring(output.indexOf("'") + 1, output.lastIndexOf("'")); + return hostOnlyIfName; + } + + private NodeMetadata getHostNodeMetadata() { + NodeMetadata hostNodeMetadata = NodeMetadataBuilder + .fromNodeMetadata(host.get()) + .publicAddresses( + ImmutableList.of(providerSupplier.get().getHost())) + .build(); + return hostNodeMetadata; + } + + private IHostNetworkInterface returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(Iterable availableNetworkInterfaces) { + checkNotNull(availableNetworkInterfaces); + return Iterables.getFirst(filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(availableNetworkInterfaces), null); + } + + /** + * @param availableNetworkInterfaces + * @param hostOnlyIfIpAddress + * @return + */ + private Iterable filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(Iterable availableNetworkInterfaces) { + Iterable filteredNetworkInterfaces = Iterables.filter(availableNetworkInterfaces, new Predicate() { + @Override + public boolean apply(IHostNetworkInterface iHostNetworkInterface) { + // this is an horrible workaround cause iHostNetworkInterface.getDhcpEnabled is working only for windows host + boolean match = false; + List availableDHCPservers = manager.get().getVBox().getDHCPServers(); + for (IDHCPServer idhcpServer : availableDHCPservers) { + if(idhcpServer.getEnabled() && idhcpServer.getNetworkName().equals(iHostNetworkInterface.getNetworkName())) + match = true; + } + return iHostNetworkInterface.getInterfaceType().equals(HostNetworkInterfaceType.HostOnly) && + match; + } + }); + return filteredNetworkInterfaces; + } + + + public String getIpAddressFromNicSlot(String machineNameOrId, long nicSlot) { + MachineNameOrIdAndNicSlot machineNameOrIdAndNicSlot = + MachineNameOrIdAndNicSlot.fromParts(machineNameOrId, nicSlot); + logger.debug("Looking for an available IP address for %s at slot %s ...", + machineNameOrIdAndNicSlot.getMachineNameOrId(), + machineNameOrIdAndNicSlot.getSlotText()); + try { + String ipAddress = ipAddressesLoadingCache.get(machineNameOrIdAndNicSlot); + while(!isValidIpForHostOnly(machineNameOrIdAndNicSlot, ipAddress)) { + ipAddressesLoadingCache.invalidate(machineNameOrIdAndNicSlot); + ipAddress = ipAddressesLoadingCache.get(machineNameOrIdAndNicSlot); + } + logger.debug("Found an available IP address %s for guest: %s at slot: %s", + ipAddress, + machineNameOrIdAndNicSlot.getMachineNameOrId(), + machineNameOrIdAndNicSlot.getSlotText()); + return ipAddress; + } catch (ExecutionException e) { + logger.error("Problem in using the ipAddressCache", e.getCause()); + throw Throwables.propagate(e); + } + } + + public static boolean isIpv4(String s) { + String IP_V4_ADDRESS_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; + Pattern pattern = Pattern.compile(IP_V4_ADDRESS_PATTERN); + Matcher matcher = pattern.matcher(s); + return matcher.matches(); + } + + public boolean isValidIpForHostOnly(MachineNameOrIdAndNicSlot machineNameOrIdAndNicSlot, String ip) { + final String vmNameOrId = machineNameOrIdAndNicSlot.getMachineNameOrId(); + IMachine machine = manager.get().getVBox().findMachine(vmNameOrId); + long slot = machineNameOrIdAndNicSlot.getSlot(); + + if(ip.equals(VIRTUALBOX_HOST_GATEWAY) || !isValidHostOnlyIpAddress(ip, slot, machine)) { + // restart vm + logger.debug("reset node (%s) to refresh guest properties.", vmNameOrId); + machineUtils.lockSessionOnMachineAndApply(vmNameOrId, LockType.Shared, + new Function() { + @Override + public Void apply(ISession session) { + session.getConsole().reset(); + long time = 15; + logger.debug("Waiting %s secs for the reset of (%s) ...", time, vmNameOrId); + Uninterruptibles.sleepUninterruptibly(time, TimeUnit.SECONDS); + return null; + } + }); + return false; + } + return true; + } + + public static boolean isValidHostOnlyIpAddress(String ip, long slot, + IMachine machine) { + boolean result = isIpv4(ip) && machine.getNetworkAdapter(slot).getAttachmentType().equals(NetworkAttachmentType.HostOnly) + && !ipBelongsToNatRange(ip); + return result; + } + + private static boolean ipBelongsToNatRange(String ip) { + return ip.startsWith("10.0.3"); + } + + protected String getIpAddressFromBridgedNIC(INetworkAdapter networkAdapter, + String network) { + // RetrieveActiveBridgedInterfaces + List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory).apply(hostSupplier.get()); + BridgedIf activeBridgedIf = checkNotNull(Iterables.get(activeBridgedInterfaces, 0), "activeBridgedInterfaces"); + network = activeBridgedIf.getIpAddress(); + + // scan ip + RunScriptOnNode ipScanRunScript = scriptRunnerFactory.create( + hostSupplier.get(), new ScanNetworkWithPing(network), + RunScriptOptions.NONE); + ExecResponse execResponse = ipScanRunScript.init().call(); + checkState(execResponse.getExitStatus() == 0); + + // retrieve ip from mac + RunScriptOnNode getIpFromMACAddressRunScript = scriptRunnerFactory + .create(hostSupplier.get(), new GetIPAddressFromMAC( + networkAdapter.getMACAddress()), + RunScriptOptions.NONE); + ExecResponse ipExecResponse = getIpFromMACAddressRunScript.init() + .call(); + checkState(ipExecResponse.getExitStatus() == 0); + return checkNotNull(ipExecResponse.getOutput(), "ipAddress"); + } +} diff --git a/labs/virtualbox/src/main/resources/default-images.yaml b/labs/virtualbox/src/main/resources/default-images.yaml index c9ed61fe71..fe4c0c82d3 100644 --- a/labs/virtualbox/src/main/resources/default-images.yaml +++ b/labs/virtualbox/src/main/resources/default-images.yaml @@ -1,4 +1,139 @@ images: + - id: ubuntu-10.04.4-server-i386 + name: ubuntu-10.04-server-i386 + description: ubuntu 10.04.4 server (i386) + os_arch: x86 + os_family: ubuntu + os_description: ubuntu + os_version: 10.04.4 + iso: http://releases.ubuntu.com/10.04.4/ubuntu-10.04.4-server-i386.iso + iso_md5: fc08a01e78348e3918180ea91a6883bb + username: toor + credential: password + keystroke_sequence: | + + /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg + debian-installer=en_US auto locale=en_US kbd-chooser/method=us + hostname=vmName + fb=false debconf/frontend=noninteractive + console-setup/ask_detect=false console-setup/modelcode=pc105 console-setup/layoutcode=us + initrd=/install/initrd.gz -- + preseed_cfg: | + ## Options to set on the command line + d-i debian-installer/locale string en_US + d-i console-setup/ask_detect boolean false + d-i console-setup/layoutcode string us + d-i netcfg/get_hostname string unassigned-hostname + d-i netcfg/get_domain string unassigned-domain + d-i time/zone string UTC + d-i clock-setup/utc-auto boolean true + d-i clock-setup/utc boolean true + d-i kbd-chooser/method select American English + d-i netcfg/wireless_wep string + d-i base-installer/kernel/override-image string linux-server + d-i debconf debconf/frontend select Noninteractive + d-i pkgsel/install-language-support boolean false + tasksel tasksel/first multiselect standard, ubuntu-server + d-i partman-auto/method string lvm + #d-i partman-auto/purge_lvm_from_device boolean true + d-i partman-lvm/confirm boolean true + d-i partman-lvm/device_remove_lvm boolean true + d-i partman-auto/choose_recipe select atomic + d-i partman/confirm_write_new_label boolean true + d-i partman/confirm_nooverwrite boolean true + d-i partman/choose_partition select finish + d-i partman/confirm boolean true + # Write the changes to disks and configure LVM? + d-i partman-lvm/confirm boolean true + d-i partman-lvm/confirm_nooverwrite boolean true + d-i partman-auto-lvm/guided_size string max + ## Default user, we can get away with a recipe to change this + d-i passwd/user-fullname string toor + d-i passwd/username string toor + d-i passwd/user-password password password + d-i passwd/user-password-again password password + d-i user-setup/encrypt-home boolean false + d-i user-setup/allow-password-weak boolean true + d-i pkgsel/include string openssh-server ntp + # Whether to upgrade packages after debootstrap. + # Allowed values: none, safe-upgrade, full-upgrade + d-i pkgsel/upgrade select full-upgrade + d-i grub-installer/only_debian boolean true + d-i grub-installer/with_other_os boolean true + d-i finish-install/reboot_in_progress note + #For the update + d-i pkgsel/update-policy select none + # debconf-get-selections --install + #Use mirror + choose-mirror-bin mirror/http/proxy string + - id: ubuntu-10.04.4-server-amd64 + name: ubuntu-10.04-server-amd64 + description: ubuntu 10.04.4 server (amd64) + os_arch: amd64 + os_family: ubuntu + os_description: ubuntu + os_version: 10.04.4 + os_64bit: true + iso: http://releases.ubuntu.com/10.04.4/ubuntu-10.04.4-server-amd64.iso + iso_md5: 9b218654cdcdf9722171648c52f8a088 + username: toor + credential: password + keystroke_sequence: | + + /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg + debian-installer=en_US auto locale=en_US kbd-chooser/method=us + hostname=vmName + fb=false debconf/frontend=noninteractive + console-setup/ask_detect=false console-setup/modelcode=pc105 console-setup/layoutcode=us + initrd=/install/initrd.gz -- + preseed_cfg: | + ## Options to set on the command line + d-i debian-installer/locale string en_US + d-i console-setup/ask_detect boolean false + d-i console-setup/layoutcode string us + d-i netcfg/get_hostname string unassigned-hostname + d-i netcfg/get_domain string unassigned-domain + d-i time/zone string UTC + d-i clock-setup/utc-auto boolean true + d-i clock-setup/utc boolean true + d-i kbd-chooser/method select American English + d-i netcfg/wireless_wep string + d-i base-installer/kernel/override-image string linux-server + d-i debconf debconf/frontend select Noninteractive + d-i pkgsel/install-language-support boolean false + tasksel tasksel/first multiselect standard, ubuntu-server + d-i partman-auto/method string lvm + #d-i partman-auto/purge_lvm_from_device boolean true + d-i partman-lvm/confirm boolean true + d-i partman-lvm/device_remove_lvm boolean true + d-i partman-auto/choose_recipe select atomic + d-i partman/confirm_write_new_label boolean true + d-i partman/confirm_nooverwrite boolean true + d-i partman/choose_partition select finish + d-i partman/confirm boolean true + # Write the changes to disks and configure LVM? + d-i partman-lvm/confirm boolean true + d-i partman-lvm/confirm_nooverwrite boolean true + d-i partman-auto-lvm/guided_size string max + ## Default user, we can get away with a recipe to change this + d-i passwd/user-fullname string toor + d-i passwd/username string toor + d-i passwd/user-password password password + d-i passwd/user-password-again password password + d-i user-setup/encrypt-home boolean false + d-i user-setup/allow-password-weak boolean true + d-i pkgsel/include string openssh-server ntp + # Whether to upgrade packages after debootstrap. + # Allowed values: none, safe-upgrade, full-upgrade + d-i pkgsel/upgrade select full-upgrade + d-i grub-installer/only_debian boolean true + d-i grub-installer/with_other_os boolean true + d-i finish-install/reboot_in_progress note + #For the update + d-i pkgsel/update-policy select none + # debconf-get-selections --install + #Use mirror + choose-mirror-bin mirror/http/proxy string - id: ubuntu-11.04-i386 name: ubuntu-11.04-server-i386 description: ubuntu 11.04 server (i386) @@ -27,7 +162,7 @@ images: d-i time/zone string UTC d-i clock-setup/utc-auto boolean true d-i clock-setup/utc boolean true - d-i kbd-chooser/method select American English + d-i kbd-chooser/method select American English d-i netcfg/wireless_wep string d-i base-installer/kernel/override-image string linux-server # Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive @@ -142,8 +277,9 @@ images: os_version: 12.04.1 os_64bit: true iso: http://releases.ubuntu.com/12.04/ubuntu-12.04.1-server-amd64.iso + iso_md5: a8c667e871f48f3a662f3fbf1c3ddb17 username: toor - credential: $user + credential: password keystroke_sequence: | /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java index 4dbe57877d..9ab564dfc2 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/BaseVirtualBoxClientLiveTest.java @@ -25,6 +25,7 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTA import java.io.File; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.inject.Named; @@ -53,6 +54,7 @@ import org.jclouds.virtualbox.functions.IMachineToVmSpec; import org.jclouds.virtualbox.functions.admin.UnregisterMachineIfExistsAndDeleteItsMedia; import org.jclouds.virtualbox.util.MachineController; import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeClass; @@ -71,6 +73,7 @@ import com.google.common.base.Supplier; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; @@ -103,6 +106,9 @@ public class BaseVirtualBoxClientLiveTest extends BaseComputeServiceContextLiveT @Inject protected MachineUtils machineUtils; + + @Inject + protected NetworkUtils networkUtils; protected String hostVersion; protected String operatingSystemIso; @@ -155,10 +161,7 @@ public class BaseVirtualBoxClientLiveTest extends BaseComputeServiceContextLiveT int attempts = 0; while (attempts < 10 && !vm.getSessionState().equals(SessionState.Unlocked)) { attempts++; - try { - Thread.sleep(200l); - } catch (InterruptedException e) { - } + Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); } machineUtils.applyForMachine(vmNameOrId, new UnregisterMachineIfExistsAndDeleteItsMedia(vmSpec)); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java index fa5e67a83f..8a06eee51c 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/compute/VirtualBoxExperimentLiveTest.java @@ -36,6 +36,7 @@ import org.jclouds.compute.options.TemplateOptions; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.logging.Logger; import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.scriptbuilder.statements.login.AdminAccess; import org.jclouds.ssh.SshClient; import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; @@ -50,7 +51,7 @@ import com.google.inject.Module; * * @author Adrian Cole */ -@Test(groups = "live", singleThreaded = true, testName = "VirtualBoxExperimentLiveTest") +@Test(groups = "live", testName = "VirtualBoxExperimentLiveTest") public class VirtualBoxExperimentLiveTest extends BaseVirtualBoxClientLiveTest { @Resource @@ -71,7 +72,7 @@ public class VirtualBoxExperimentLiveTest extends BaseVirtualBoxClientLiveTest { int numNodes = 3; final String clusterName = "test-launch-cluster"; Set nodes = context.getComputeService().createNodesInGroup(clusterName, numNodes, - TemplateOptions.Builder.overrideLoginUser("toor")); //TODO runScript(AdminAccess.standard())); + TemplateOptions.Builder.overrideLoginUser("toor").runScript(AdminAccess.standard())); assertEquals(numNodes, nodes.size(), "wrong number of nodes"); for (NodeMetadata node : nodes) { assertTrue(node.getGroup().equals("test-launch-cluster")); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java index 5a0167a14a..274c28c45c 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/IMachineToNodeMetadataTest.java @@ -30,7 +30,7 @@ import static org.testng.Assert.assertEquals; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule; -import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.testng.annotations.Test; import org.virtualbox_4_1.IMachine; import org.virtualbox_4_1.INATEngine; @@ -64,18 +64,18 @@ public class IMachineToNodeMetadataTest { expect(natEng.getRedirects()).andReturn(ImmutableList.of("0,1,127.0.0.1,2222,,22")); INetworkAdapter hostOnly = createNiceMock(INetworkAdapter.class); - MachineUtils machineUtils = createNiceMock(MachineUtils.class); + NetworkUtils networkUtils = createNiceMock(NetworkUtils.class); - replay(vm, nat, natEng, hostOnly, machineUtils); + replay(vm, nat, natEng, hostOnly, networkUtils); NodeMetadata node = new IMachineToNodeMetadata(VirtualBoxComputeServiceContextModule.toPortableNodeStatus, - machineUtils).apply(vm); + networkUtils).apply(vm); assertEquals(MASTER_NAME, node.getName()); assertEquals(1, 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(NetworkUtils.MASTER_PORT, node.getLoginPort()); assertEquals("", node.getGroup()); } @@ -103,12 +103,12 @@ public class IMachineToNodeMetadataTest { 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")); - MachineUtils machineUtils = createNiceMock(MachineUtils.class); + NetworkUtils networkUtils = createNiceMock(NetworkUtils.class); - replay(vm, nat, natEng, hostOnly, machineUtils); + replay(vm, nat, natEng, hostOnly, networkUtils); NodeMetadata node = new IMachineToNodeMetadata(VirtualBoxComputeServiceContextModule.toPortableNodeStatus, - machineUtils).apply(vm); + networkUtils).apply(vm); assertEquals(name, node.getName()); assertEquals(group, node.getGroup()); diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java index 843071ad80..0e4b3ef2f8 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/functions/admin/ImageFromYamlStringTest.java @@ -41,11 +41,11 @@ import com.google.common.collect.Iterables; public class ImageFromYamlStringTest { public static final Image TEST1 = new ImageBuilder() - .id("ubuntu-11.04-i386") - .name("ubuntu-11.04-server-i386") - .description("ubuntu 11.04 server (i386)") + .id("ubuntu-10.04.4-server-i386") + .name("ubuntu-10.04-server-i386") + .description("ubuntu") .operatingSystem( - OperatingSystem.builder().description("ubuntu").family(OsFamily.UBUNTU).version("11.04") + OperatingSystem.builder().description("ubuntu").family(OsFamily.UBUNTU).version("10.04.4") .arch("x86").build()) .status(Image.Status.AVAILABLE).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 index b21ce39586..13514c8af3 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/predicates/GuestAdditionsInstallerLiveTest.java @@ -39,8 +39,7 @@ import org.jclouds.virtualbox.domain.VmSpec; import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists; import org.jclouds.virtualbox.functions.CreateAndInstallVm; import org.jclouds.virtualbox.functions.IMachineToSshClient; -import org.jclouds.virtualbox.functions.IpAddressesLoadingCache; -import org.jclouds.virtualbox.util.MachineUtils; +import org.jclouds.virtualbox.util.NetworkUtils; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.virtualbox_4_1.CleanupMode; @@ -57,7 +56,7 @@ import com.google.inject.Injector; /** * @author Andrea Turli */ -@Test(groups = "live", singleThreaded = true, testName = "GuestAdditionsInstallerLiveTest") +@Test(groups = "live", singleThreaded = true, testName = "GuestAdditionsInstallerLiveTest", enabled=false) public class GuestAdditionsInstallerLiveTest extends BaseVirtualBoxClientLiveTest { private Injector injector; @@ -65,7 +64,6 @@ public class GuestAdditionsInstallerLiveTest extends BaseVirtualBoxClientLiveTes private Predicate sshResponds; private MasterSpec machineSpec; - private IpAddressesLoadingCache ipAddressesLoadingCache; @Override @BeforeClass(groups = "live") @@ -117,9 +115,8 @@ public class GuestAdditionsInstallerLiveTest extends BaseVirtualBoxClientLiveTes sshResponds = injector.getInstance(SshResponds.class); checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", machine.getName()); - ipAddressesLoadingCache = injector.getInstance(IpAddressesLoadingCache.class); - assertTrue(MachineUtils.isIpv4(ipAddressesLoadingCache.apply(machine.getName()))); + assertTrue(NetworkUtils.isIpv4(networkUtils.getIpAddressFromNicSlot(machine.getName(), 0l))); } finally { for (String vmNameOrId : ImmutableSet.of(machine.getName())) { diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsLiveTest.java index dff4c41464..e9993057a0 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/statements/InstallGuestAdditionsLiveTest.java @@ -45,13 +45,7 @@ public class InstallGuestAdditionsLiveTest extends BaseVirtualBoxClientLiveTest 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" - + "service vboxadd setup\n" - + "VBoxService\n" - + "echo VBoxService > /etc/rc.local\n" - + "echo exit 0 >> /etc/rc.local\n" - + "umount /mnt\n" - , scripts); + + "/mnt/VBoxLinuxAdditions.run --nox11\n", scripts); } public void testIsoNotPresent() { @@ -67,12 +61,7 @@ public class InstallGuestAdditionsLiveTest extends BaseVirtualBoxClientLiveTest + "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" - + "service vboxadd setup\n" - + "VBoxService\n" - + "echo VBoxService > /etc/rc.local\n" - + "echo exit 0 >> /etc/rc.local\n" - + "umount /mnt\n", scripts); + + "/mnt/VBoxLinuxAdditions.run --nox11\n", scripts); } } \ No newline at end of file diff --git a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java index 39bac6d52f..af69bc2732 100644 --- a/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java +++ b/labs/virtualbox/src/test/java/org/jclouds/virtualbox/util/MachineUtilsLiveTest.java @@ -18,9 +18,11 @@ */ package org.jclouds.virtualbox.util; + import static com.google.common.base.Preconditions.checkState; 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.AssertJUnit.assertTrue; import org.jclouds.config.ValueOfConfigurationKeyOrNull; import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; @@ -35,20 +37,17 @@ import org.jclouds.virtualbox.domain.StorageController; import org.jclouds.virtualbox.domain.VmSpec; import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists; import org.jclouds.virtualbox.functions.CreateAndInstallVm; -import org.testng.annotations.AfterClass; 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.ISession; -import org.virtualbox_4_1.LockType; import org.virtualbox_4_1.NetworkAttachmentType; import org.virtualbox_4_1.SessionState; 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; @Test(groups = "live", testName = "MachineControllerLiveTest") @@ -62,62 +61,134 @@ public class MachineUtilsLiveTest extends BaseVirtualBoxClientLiveTest { public void setupContext() { super.setupContext(); instanceName = VIRTUALBOX_IMAGE_PREFIX - + CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, getClass().getSimpleName()); + + 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(instanceName)).controllerPort(0).deviceSlot(1) - .autoDelete(true).build()).attachISO(1, 1, guestAdditionsIso).build(); + .builder() + .name("IDE Controller") + .bus(StorageBus.IDE) + .attachISO(0, 0, operatingSystemIso) + .attachHardDisk( + HardDisk.builder().diskpath(adminDisk(instanceName)).controllerPort(0).deviceSlot(1).autoDelete(true) + .build()).attachISO(1, 1, guestAdditionsIso).build(); VmSpec instanceVmSpec = VmSpec.builder().id(instanceName).name(instanceName).osTypeId("").memoryMB(512) - .cleanUpMode(CleanupMode.Full).controller(ideController).forceOverwrite(true).build(); + .cleanUpMode(CleanupMode.Full).controller(ideController).forceOverwrite(true).build(); Injector injector = view.utils().injector(); Function configProperties = injector.getInstance(ValueOfConfigurationKeyOrNull.class); IsoSpec isoSpec = IsoSpec - .builder() - .sourcePath(operatingSystemIso) - .installationScript( - configProperties.apply(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE).replace("HOSTNAME", - instanceVmSpec.getVmName())).build(); + .builder() + .sourcePath(operatingSystemIso) + .installationScript( + configProperties.apply(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE).replace("HOSTNAME", + instanceVmSpec.getVmName())).build(); NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT) - .tcpRedirectRule("127.0.0.1", 2222, "", 22).build(); + .tcpRedirectRule("127.0.0.1", 2222, "", 22).build(); NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter) - .build(); + .build(); NetworkSpec networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build(); machineSpec = MasterSpec.builder().iso(isoSpec).vm(instanceVmSpec).network(networkSpec).build(); } - @Test - public void lockSessionOnMachine() { - IMachine machine = cloneFromMaster(); - ISession session = machineUtils.lockSessionOnMachineAndApply(instanceName, LockType.Shared, - new Function() { + @Test(description = "write lock is acquired and released correctly") + public void writeLockSessionOnMachine() { + final IMachine clone = cloneFromMaster(); + ISession session = machineUtils.writeLockMachineAndApplyToSession(clone.getName(), + new Function() { + @Override + public ISession apply(ISession session) { + assertTrue(session.getMachine().getName().equals(clone.getName())); + return session; + } + }); + checkState(session.getState().equals(SessionState.Unlocked)); + undoVm(clone.getName()); + } + @Test(dependsOnMethods="writeLockSessionOnMachine", description = "shared lock is acquired and released correctly") + public void sharedLockSessionOnMachine() { + final IMachine clone = cloneFromMaster(); + ISession session = machineUtils.sharedLockMachineAndApplyToSession(clone.getName(), + new Function() { + @Override + public ISession apply(ISession session) { + assertTrue(session.getMachine().getName().equals(clone.getName())); + return session; + } + }); + checkState(session.getState().equals(SessionState.Unlocked)); + undoVm(clone.getName()); + } + + @Test(dependsOnMethods="sharedLockSessionOnMachine", description = "shared lock can be acquired after a write lock") + public void sharedLockCanBeAcquiredAfterWriteLockSessionOnMachine() { + final IMachine clone = cloneFromMaster(); + try { + ISession writeSession = machineUtils.writeLockMachineAndApplyToSession(clone.getName(), + new Function() { @Override - public ISession apply(ISession session) { - return session; + public ISession apply(ISession writeSession) { + checkState(writeSession.getState().equals(SessionState.Locked)); + //ISession sharedSession = sharedSession(clone); + return writeSession; } }); - checkState(session.getState().equals(SessionState.Unlocked)); - machine = manager.get().getVBox().findMachine(instanceName); - undoVm(instanceName); - + checkState(writeSession.getState().equals(SessionState.Unlocked)); + } finally { + undoVm(clone.getName()); + } + } + + private ISession sharedSession(final IMachine clone) { + ISession sharedSession = machineUtils.sharedLockMachineAndApplyToSession(clone.getName(), + new Function() { + @Override + public ISession apply(ISession sharedSession) { + checkState(sharedSession.getState().equals(SessionState.Locked)); + assertTrue(sharedSession.getMachine().getName().equals(clone.getName())); + return sharedSession; + } + }); + return sharedSession; + } + + @Test(dependsOnMethods="sharedLockCanBeAcquiredAfterWriteLockSessionOnMachine", description = "write lock cannot be acquired after a shared lock") + public void writeLockCannotBeAcquiredAfterSharedLockSessionOnMachine() { + final IMachine clone = cloneFromMaster(); + try { + ISession sharedSession = machineUtils.sharedLockMachineAndApplyToSession(clone.getName(), + new Function() { + @Override + public ISession apply(ISession sharedSession) { + checkState(sharedSession.getState().equals(SessionState.Locked)); + return sharedSession; + } + }); + checkState(sharedSession.getState().equals(SessionState.Unlocked)); + ISession writeSession = machineUtils.writeLockMachineAndApplyToSession(clone.getName(), + new Function() { + @Override + public ISession apply(ISession writeSession) { + checkState(writeSession.getState().equals(SessionState.Locked)); + assertTrue(writeSession.getMachine().getName().equals(clone.getName())); + return writeSession; + } + }); + checkState(writeSession.getState().equals(SessionState.Unlocked)); + } finally { + undoVm(clone.getName()); + } } - private IMachine cloneFromMaster() { IMachine source = getVmWithGuestAdditionsInstalled(); CloneSpec cloneSpec = CloneSpec.builder().vm(machineSpec.getVmSpec()).network(machineSpec.getNetworkSpec()) - .master(source).linked(true).build(); + .master(source).linked(true).build(); return new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(manager, workingDir, machineUtils) - .apply(cloneSpec); + .apply(cloneSpec); } private IMachine getVmWithGuestAdditionsInstalled() { @@ -130,13 +201,4 @@ public class MachineUtilsLiveTest extends BaseVirtualBoxClientLiveTest { return manager.get().getVBox().findMachine(masterSpecForTest.getVmSpec().getVmId()); } } - - @Override - @AfterClass(groups = "live") - protected void tearDown() throws Exception { - for (String vmName : ImmutableSet.of(instanceName)) { - undoVm(vmName); - } - super.tearDown(); - } } diff --git a/labs/virtualbox/src/test/resources/default-images.yaml b/labs/virtualbox/src/test/resources/default-images.yaml index fddcff3c4a..fe4c0c82d3 100644 --- a/labs/virtualbox/src/test/resources/default-images.yaml +++ b/labs/virtualbox/src/test/resources/default-images.yaml @@ -1,4 +1,139 @@ images: + - id: ubuntu-10.04.4-server-i386 + name: ubuntu-10.04-server-i386 + description: ubuntu 10.04.4 server (i386) + os_arch: x86 + os_family: ubuntu + os_description: ubuntu + os_version: 10.04.4 + iso: http://releases.ubuntu.com/10.04.4/ubuntu-10.04.4-server-i386.iso + iso_md5: fc08a01e78348e3918180ea91a6883bb + username: toor + credential: password + keystroke_sequence: | + + /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg + debian-installer=en_US auto locale=en_US kbd-chooser/method=us + hostname=vmName + fb=false debconf/frontend=noninteractive + console-setup/ask_detect=false console-setup/modelcode=pc105 console-setup/layoutcode=us + initrd=/install/initrd.gz -- + preseed_cfg: | + ## Options to set on the command line + d-i debian-installer/locale string en_US + d-i console-setup/ask_detect boolean false + d-i console-setup/layoutcode string us + d-i netcfg/get_hostname string unassigned-hostname + d-i netcfg/get_domain string unassigned-domain + d-i time/zone string UTC + d-i clock-setup/utc-auto boolean true + d-i clock-setup/utc boolean true + d-i kbd-chooser/method select American English + d-i netcfg/wireless_wep string + d-i base-installer/kernel/override-image string linux-server + d-i debconf debconf/frontend select Noninteractive + d-i pkgsel/install-language-support boolean false + tasksel tasksel/first multiselect standard, ubuntu-server + d-i partman-auto/method string lvm + #d-i partman-auto/purge_lvm_from_device boolean true + d-i partman-lvm/confirm boolean true + d-i partman-lvm/device_remove_lvm boolean true + d-i partman-auto/choose_recipe select atomic + d-i partman/confirm_write_new_label boolean true + d-i partman/confirm_nooverwrite boolean true + d-i partman/choose_partition select finish + d-i partman/confirm boolean true + # Write the changes to disks and configure LVM? + d-i partman-lvm/confirm boolean true + d-i partman-lvm/confirm_nooverwrite boolean true + d-i partman-auto-lvm/guided_size string max + ## Default user, we can get away with a recipe to change this + d-i passwd/user-fullname string toor + d-i passwd/username string toor + d-i passwd/user-password password password + d-i passwd/user-password-again password password + d-i user-setup/encrypt-home boolean false + d-i user-setup/allow-password-weak boolean true + d-i pkgsel/include string openssh-server ntp + # Whether to upgrade packages after debootstrap. + # Allowed values: none, safe-upgrade, full-upgrade + d-i pkgsel/upgrade select full-upgrade + d-i grub-installer/only_debian boolean true + d-i grub-installer/with_other_os boolean true + d-i finish-install/reboot_in_progress note + #For the update + d-i pkgsel/update-policy select none + # debconf-get-selections --install + #Use mirror + choose-mirror-bin mirror/http/proxy string + - id: ubuntu-10.04.4-server-amd64 + name: ubuntu-10.04-server-amd64 + description: ubuntu 10.04.4 server (amd64) + os_arch: amd64 + os_family: ubuntu + os_description: ubuntu + os_version: 10.04.4 + os_64bit: true + iso: http://releases.ubuntu.com/10.04.4/ubuntu-10.04.4-server-amd64.iso + iso_md5: 9b218654cdcdf9722171648c52f8a088 + username: toor + credential: password + keystroke_sequence: | + + /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg + debian-installer=en_US auto locale=en_US kbd-chooser/method=us + hostname=vmName + fb=false debconf/frontend=noninteractive + console-setup/ask_detect=false console-setup/modelcode=pc105 console-setup/layoutcode=us + initrd=/install/initrd.gz -- + preseed_cfg: | + ## Options to set on the command line + d-i debian-installer/locale string en_US + d-i console-setup/ask_detect boolean false + d-i console-setup/layoutcode string us + d-i netcfg/get_hostname string unassigned-hostname + d-i netcfg/get_domain string unassigned-domain + d-i time/zone string UTC + d-i clock-setup/utc-auto boolean true + d-i clock-setup/utc boolean true + d-i kbd-chooser/method select American English + d-i netcfg/wireless_wep string + d-i base-installer/kernel/override-image string linux-server + d-i debconf debconf/frontend select Noninteractive + d-i pkgsel/install-language-support boolean false + tasksel tasksel/first multiselect standard, ubuntu-server + d-i partman-auto/method string lvm + #d-i partman-auto/purge_lvm_from_device boolean true + d-i partman-lvm/confirm boolean true + d-i partman-lvm/device_remove_lvm boolean true + d-i partman-auto/choose_recipe select atomic + d-i partman/confirm_write_new_label boolean true + d-i partman/confirm_nooverwrite boolean true + d-i partman/choose_partition select finish + d-i partman/confirm boolean true + # Write the changes to disks and configure LVM? + d-i partman-lvm/confirm boolean true + d-i partman-lvm/confirm_nooverwrite boolean true + d-i partman-auto-lvm/guided_size string max + ## Default user, we can get away with a recipe to change this + d-i passwd/user-fullname string toor + d-i passwd/username string toor + d-i passwd/user-password password password + d-i passwd/user-password-again password password + d-i user-setup/encrypt-home boolean false + d-i user-setup/allow-password-weak boolean true + d-i pkgsel/include string openssh-server ntp + # Whether to upgrade packages after debootstrap. + # Allowed values: none, safe-upgrade, full-upgrade + d-i pkgsel/upgrade select full-upgrade + d-i grub-installer/only_debian boolean true + d-i grub-installer/with_other_os boolean true + d-i finish-install/reboot_in_progress note + #For the update + d-i pkgsel/update-policy select none + # debconf-get-selections --install + #Use mirror + choose-mirror-bin mirror/http/proxy string - id: ubuntu-11.04-i386 name: ubuntu-11.04-server-i386 description: ubuntu 11.04 server (i386) @@ -27,7 +162,7 @@ images: d-i time/zone string UTC d-i clock-setup/utc-auto boolean true d-i clock-setup/utc boolean true - d-i kbd-chooser/method select American English + d-i kbd-chooser/method select American English d-i netcfg/wireless_wep string d-i base-installer/kernel/override-image string linux-server # Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive @@ -143,6 +278,8 @@ images: os_64bit: true iso: http://releases.ubuntu.com/12.04/ubuntu-12.04.1-server-amd64.iso iso_md5: a8c667e871f48f3a662f3fbf1c3ddb17 + username: toor + credential: password keystroke_sequence: | /install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg diff --git a/labs/virtualbox/src/test/resources/logback.xml b/labs/virtualbox/src/test/resources/logback.xml index 3a5a73c80d..26f517de96 100644 --- a/labs/virtualbox/src/test/resources/logback.xml +++ b/labs/virtualbox/src/test/resources/logback.xml @@ -42,6 +42,11 @@ + + + + +