mirror of https://github.com/apache/jclouds.git
Merge pull request #378 from andreaturli/development
issue 384: fixed GuestAdditions LiveTest
This commit is contained in:
commit
6317c880ad
|
@ -139,7 +139,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
machineSession.getConsole().resume();
|
machineSession.getConsole().resume();
|
||||||
machineSession.unlockMachine();
|
machineSession.unlockMachine();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
propogate(e);
|
throw Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,16 +152,10 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
machineSession.getConsole().pause();
|
machineSession.getConsole().pause();
|
||||||
machineSession.unlockMachine();
|
machineSession.unlockMachine();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
propogate(e);
|
throw Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T propogate(Exception e) {
|
|
||||||
Throwables.propagate(e);
|
|
||||||
assert false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void launchVMProcess(IMachine machine, ISession session) {
|
private void launchVMProcess(IMachine machine, ISession session) {
|
||||||
IProgress prog = machine.launchVMProcess(session, "gui", "");
|
IProgress prog = machine.launchVMProcess(session, "gui", "");
|
||||||
prog.waitForCompletion(-1);
|
prog.waitForCompletion(-1);
|
||||||
|
@ -184,8 +178,7 @@ public class VirtualBoxComputeServiceAdapter implements ComputeServiceAdapter<IM
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
throw Throwables.propagate(e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,20 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.virtualbox.functions;
|
package org.jclouds.virtualbox.functions;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import com.google.common.base.Predicate;
|
import static com.google.common.collect.Iterables.transform;
|
||||||
import com.google.common.base.Splitter;
|
import static org.jclouds.scriptbuilder.domain.Statements.call;
|
||||||
import com.google.common.base.Supplier;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
import java.net.URI;
|
||||||
import com.google.inject.Inject;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import org.jclouds.compute.ComputeServiceContext;
|
import org.jclouds.compute.ComputeServiceContext;
|
||||||
|
import org.jclouds.compute.domain.ExecResponse;
|
||||||
|
import org.jclouds.compute.options.RunScriptOptions;
|
||||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
import org.jclouds.ssh.SshClient;
|
import org.jclouds.ssh.SshClient;
|
||||||
|
@ -36,16 +42,20 @@ import org.jclouds.virtualbox.domain.MasterSpec;
|
||||||
import org.jclouds.virtualbox.domain.VmSpec;
|
import org.jclouds.virtualbox.domain.VmSpec;
|
||||||
import org.jclouds.virtualbox.predicates.GuestAdditionsInstaller;
|
import org.jclouds.virtualbox.predicates.GuestAdditionsInstaller;
|
||||||
import org.jclouds.virtualbox.util.MachineUtils;
|
import org.jclouds.virtualbox.util.MachineUtils;
|
||||||
import org.virtualbox_4_1.*;
|
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.VirtualBoxManager;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import com.google.common.base.Function;
|
||||||
import javax.inject.Named;
|
import com.google.common.base.Predicate;
|
||||||
import javax.inject.Singleton;
|
import com.google.common.base.Splitter;
|
||||||
import java.net.URI;
|
import com.google.common.base.Supplier;
|
||||||
import java.util.List;
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import static com.google.common.collect.Iterables.transform;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
|
public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
|
||||||
|
@ -107,6 +117,12 @@ public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
|
||||||
logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
|
logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
|
||||||
checkState(new GuestAdditionsInstaller(context).apply(vmName));
|
checkState(new GuestAdditionsInstaller(context).apply(vmName));
|
||||||
|
|
||||||
|
logger.debug(">> awaiting post-installation actions on vm: %s", vmName);
|
||||||
|
ListenableFuture<ExecResponse> execFuture = context.getComputeService().submitScriptOnNode(vmName,
|
||||||
|
call("cleanupUdevIfNeeded"), RunScriptOptions.NONE);
|
||||||
|
ExecResponse execResponse = Futures.getUnchecked(execFuture);
|
||||||
|
checkState(execResponse.getExitCode() == 0);
|
||||||
|
|
||||||
logger.debug("<< installation of image complete. Powering down node(%s)", vmName);
|
logger.debug("<< installation of image complete. Powering down node(%s)", vmName);
|
||||||
ensureMachineHasPowerDown(vmName);
|
ensureMachineHasPowerDown(vmName);
|
||||||
return vm;
|
return vm;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
function cleanupUdevIfNeeded {
|
||||||
|
unset OSNAME;
|
||||||
|
local OSNAME=`lsb_release -d -s | cut -d ' ' -f 1`; shift
|
||||||
|
if [ $OSNAME = 'Ubuntu' ]
|
||||||
|
then
|
||||||
|
echo "OS is Ubuntu"
|
||||||
|
rm /etc/udev/rules.d/70-persistent-net.rules;
|
||||||
|
mkdir /etc/udev/rules.d/70-persistent-net.rules;
|
||||||
|
rm -rf /dev/.udev/;
|
||||||
|
rm /lib/udev/rules.d/75-persistent-net-generator.rules
|
||||||
|
fi
|
||||||
|
}
|
|
@ -50,11 +50,6 @@ public class VirtualBoxComputeServiceAdapterLiveTest extends BaseVirtualBoxClien
|
||||||
adapter = context.utils().injector().getInstance(VirtualBoxComputeServiceAdapter.class);
|
adapter = context.utils().injector().getInstance(VirtualBoxComputeServiceAdapter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testListLocations() {
|
|
||||||
assertFalse(Iterables.isEmpty(adapter.listLocations()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate = new PrioritizeCredentialsFromTemplate(
|
private static final PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate = new PrioritizeCredentialsFromTemplate(
|
||||||
new DefaultCredentialsFromImageOrOverridingCredentials());
|
new DefaultCredentialsFromImageOrOverridingCredentials());
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.jclouds.virtualbox.domain.StorageController;
|
||||||
import org.jclouds.virtualbox.domain.VmSpec;
|
import org.jclouds.virtualbox.domain.VmSpec;
|
||||||
import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists;
|
import org.jclouds.virtualbox.functions.CloneAndRegisterMachineFromIMachineIfNotAlreadyExists;
|
||||||
import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
|
import org.jclouds.virtualbox.functions.CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
|
||||||
|
import org.testng.annotations.AfterClass;
|
||||||
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.BeforeClass;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
import org.virtualbox_4_1.CleanupMode;
|
import org.virtualbox_4_1.CleanupMode;
|
||||||
|
@ -138,4 +139,5 @@ public class CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest extends
|
||||||
.findMachine(sourceMachineSpec.getVmSpec().getVmId());
|
.findMachine(sourceMachineSpec.getVmSpec().getVmId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -19,10 +19,19 @@
|
||||||
|
|
||||||
package org.jclouds.virtualbox.functions;
|
package org.jclouds.virtualbox.functions;
|
||||||
|
|
||||||
import com.google.common.base.CaseFormat;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
import com.google.common.base.Function;
|
import static com.google.common.base.Predicates.equalTo;
|
||||||
import com.google.inject.Guice;
|
import static com.google.common.collect.Iterables.any;
|
||||||
import com.google.inject.Injector;
|
import static com.google.common.collect.Iterables.transform;
|
||||||
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
||||||
|
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
|
||||||
|
import static org.testng.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.jclouds.compute.config.BaseComputeServiceContextModule;
|
import org.jclouds.compute.config.BaseComputeServiceContextModule;
|
||||||
import org.jclouds.compute.domain.Image;
|
import org.jclouds.compute.domain.Image;
|
||||||
import org.jclouds.compute.domain.OsFamily;
|
import org.jclouds.compute.domain.OsFamily;
|
||||||
|
@ -30,25 +39,35 @@ import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
|
import org.jclouds.config.ValueOfConfigurationKeyOrNull;
|
||||||
import org.jclouds.json.Json;
|
import org.jclouds.json.Json;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
|
import org.jclouds.ssh.SshClient;
|
||||||
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
|
import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest;
|
||||||
import org.jclouds.virtualbox.domain.*;
|
import org.jclouds.virtualbox.domain.ExecutionType;
|
||||||
|
import org.jclouds.virtualbox.domain.HardDisk;
|
||||||
|
import org.jclouds.virtualbox.domain.IsoSpec;
|
||||||
|
import org.jclouds.virtualbox.domain.MasterSpec;
|
||||||
|
import org.jclouds.virtualbox.domain.NatAdapter;
|
||||||
|
import org.jclouds.virtualbox.domain.NetworkSpec;
|
||||||
|
import org.jclouds.virtualbox.domain.StorageController;
|
||||||
|
import org.jclouds.virtualbox.domain.VmSpec;
|
||||||
|
import org.jclouds.virtualbox.predicates.SshResponds;
|
||||||
import org.testng.annotations.AfterClass;
|
import org.testng.annotations.AfterClass;
|
||||||
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.BeforeClass;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
import org.virtualbox_4_1.CleanupMode;
|
import org.virtualbox_4_1.CleanupMode;
|
||||||
import org.virtualbox_4_1.IMachine;
|
import org.virtualbox_4_1.IMachine;
|
||||||
|
import org.virtualbox_4_1.IProgress;
|
||||||
|
import org.virtualbox_4_1.ISession;
|
||||||
|
import org.virtualbox_4_1.LockType;
|
||||||
import org.virtualbox_4_1.StorageBus;
|
import org.virtualbox_4_1.StorageBus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import com.google.common.base.CaseFormat;
|
||||||
import java.util.Map;
|
import com.google.common.base.Function;
|
||||||
import java.util.Set;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
import static com.google.common.base.Predicates.equalTo;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import static com.google.common.collect.Iterables.any;
|
import com.google.common.collect.Iterables;
|
||||||
import static com.google.common.collect.Iterables.transform;
|
import com.google.inject.Guice;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
|
import com.google.inject.Injector;
|
||||||
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
|
|
||||||
import static org.testng.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Andrea Turli, Mattias Holmqvist
|
* @author Andrea Turli, Mattias Holmqvist
|
||||||
|
@ -61,6 +80,11 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
.createInjector(new GsonModule()).getInstance(Json.class));
|
.createInjector(new GsonModule()).getInstance(Json.class));
|
||||||
|
|
||||||
private VmSpec vmSpecification;
|
private VmSpec vmSpecification;
|
||||||
|
private MasterSpec masterSpec;
|
||||||
|
private Injector injector;
|
||||||
|
private Function<IMachine, SshClient> sshClientForIMachine;
|
||||||
|
private Predicate<SshClient> sshResponds;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@BeforeClass(groups = "live")
|
@BeforeClass(groups = "live")
|
||||||
|
@ -76,18 +100,16 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
.attachISO(0, 0, operatingSystemIso)
|
.attachISO(0, 0, operatingSystemIso)
|
||||||
.attachHardDisk(hardDisk)
|
.attachHardDisk(hardDisk)
|
||||||
.attachISO(1, 1, guestAdditionsIso).build();
|
.attachISO(1, 1, guestAdditionsIso).build();
|
||||||
vmSpecification = VmSpec.builder().id("jclouds-image-create-and-install-vm-test").name(vmName).memoryMB(512).osTypeId("")
|
vmSpecification = VmSpec.builder().id(vmName).name(vmName).memoryMB(512).osTypeId("")
|
||||||
.controller(ideController)
|
.controller(ideController)
|
||||||
.forceOverwrite(true)
|
.forceOverwrite(true)
|
||||||
.cleanUpMode(CleanupMode.Full).build();
|
.cleanUpMode(CleanupMode.Full).build();
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreateImageMachineFromIso() throws Exception {
|
injector = context.utils().injector();
|
||||||
Injector injector = context.utils().injector();
|
|
||||||
Function<String, String> configProperties = injector
|
Function<String, String> configProperties = injector
|
||||||
.getInstance(ValueOfConfigurationKeyOrNull.class);
|
.getInstance(ValueOfConfigurationKeyOrNull.class);
|
||||||
|
|
||||||
MasterSpec masterSpec = MasterSpec.builder().vm(vmSpecification)
|
masterSpec = MasterSpec.builder().vm(vmSpecification)
|
||||||
.iso(IsoSpec.builder()
|
.iso(IsoSpec.builder()
|
||||||
.sourcePath(operatingSystemIso)
|
.sourcePath(operatingSystemIso)
|
||||||
.installationScript(configProperties
|
.installationScript(configProperties
|
||||||
|
@ -97,7 +119,12 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
.network(NetworkSpec.builder()
|
.network(NetworkSpec.builder()
|
||||||
.natNetworkAdapter(0, NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build())
|
.natNetworkAdapter(0, NatAdapter.builder().tcpRedirectRule("127.0.0.1", 2222, "", 22).build())
|
||||||
.build()).build();
|
.build()).build();
|
||||||
IMachine imageMachine = injector.getInstance(CreateAndInstallVm.class).apply(masterSpec);
|
|
||||||
|
undoVm(vmSpecification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreateImageMachineFromIso() throws Exception {
|
||||||
|
IMachine imageMachine = getVmWithGuestAdditionsInstalled();
|
||||||
|
|
||||||
IMachineToImage iMachineToImage = new IMachineToImage(manager, map);
|
IMachineToImage iMachineToImage = new IMachineToImage(manager, map);
|
||||||
Image newImage = iMachineToImage.apply(imageMachine);
|
Image newImage = iMachineToImage.apply(imageMachine);
|
||||||
|
@ -108,6 +135,33 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
assertTrue(any(imageIds, equalTo(newImage.getId())));
|
assertTrue(any(imageIds, equalTo(newImage.getId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGuestAdditionsAreInstalled() throws Exception {
|
||||||
|
try {
|
||||||
|
IMachine machine = getVmWithGuestAdditionsInstalled();
|
||||||
|
|
||||||
|
machineUtils.applyForMachine(machine.getName(), new LaunchMachineIfNotAlreadyRunning(manager.get(), ExecutionType.GUI, ""));
|
||||||
|
sshClientForIMachine = injector.getInstance(IMachineToSshClient.class);
|
||||||
|
SshClient client = sshClientForIMachine.apply(machine);
|
||||||
|
|
||||||
|
sshResponds = injector.getInstance(SshResponds.class);
|
||||||
|
checkState(sshResponds.apply(client), "timed out waiting for guest %s to be accessible via ssh", machine.getName());
|
||||||
|
|
||||||
|
assertTrue(machineUtils.lockSessionOnMachineAndApply(machine.getName(), LockType.Shared, new Function<ISession, Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean apply(ISession session) {
|
||||||
|
String vboxVersion = Iterables.get(Splitter.on('r').split(context.getProviderSpecificContext().getBuildVersion()), 0);
|
||||||
|
return session.getMachine().getGuestPropertyValue("/VirtualBox/GuestAdd/Version").equals(vboxVersion);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} finally {
|
||||||
|
for (VmSpec spec : ImmutableSet.of(
|
||||||
|
vmSpecification)) {
|
||||||
|
ensureMachineHasPowerDown(spec.getVmName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Function<Image, String> extractId() {
|
private Function<Image, String> extractId() {
|
||||||
return new Function<Image, String>() {
|
return new Function<Image, String>() {
|
||||||
|
|
||||||
|
@ -118,10 +172,37 @@ public class CreateAndInstallVmLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IMachine getVmWithGuestAdditionsInstalled() {
|
||||||
|
try {
|
||||||
|
Injector injector = context.utils().injector();
|
||||||
|
return injector.getInstance(
|
||||||
|
CreateAndInstallVm.class).apply(
|
||||||
|
masterSpec);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// already created
|
||||||
|
return manager.get().getVBox()
|
||||||
|
.findMachine(masterSpec.getVmSpec().getVmId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureMachineHasPowerDown(String vmName) {
|
||||||
|
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function<ISession, Void>() {
|
||||||
|
@Override
|
||||||
|
public Void apply(ISession session) {
|
||||||
|
IProgress powerDownProgress = session.getConsole().powerDown();
|
||||||
|
powerDownProgress.waitForCompletion(-1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@AfterClass(groups = "live")
|
@AfterClass(groups = "live")
|
||||||
protected void tearDown() throws Exception {
|
protected void tearDown() throws Exception {
|
||||||
undoVm(vmSpecification);
|
for (VmSpec spec : ImmutableSet.of(
|
||||||
|
vmSpecification)) {
|
||||||
|
undoVm(spec);
|
||||||
|
}
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,17 +96,6 @@ public class IMachinePredicatesLiveTest extends BaseVirtualBoxClientLiveTest {
|
||||||
assertTrue(isLinkedClone().apply(clone));
|
assertTrue(isLinkedClone().apply(clone));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* public void testFullClone() { IMachine master =
|
|
||||||
* context.utils().injector().
|
|
||||||
* getInstance(CreateAndRegisterMachineFromIsoIfNotAlreadyExists.class)
|
|
||||||
* .apply(masterSpec); IMachine clone = new
|
|
||||||
* CloneAndRegisterMachineFromIMachineIfNotAlreadyExists(manager, workingDir,
|
|
||||||
* cloneSpec, !IS_LINKED_CLONE).apply(master);
|
|
||||||
*
|
|
||||||
* assertFalse(new IsLinkedClone(manager).apply(clone)); }
|
|
||||||
*/
|
|
||||||
|
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
@AfterMethod
|
@AfterMethod
|
||||||
void cleanUpVms() {
|
void cleanUpVms() {
|
||||||
|
|
Loading…
Reference in New Issue