parsed names from vm so that nodemetadata has correct group and name (updated unit test), corrected transient bug obtaining locks for vms

This commit is contained in:
David Ribeiro Alves 2012-03-22 02:44:41 +00:00
parent aceaaf0eb0
commit 4a32b0107f
9 changed files with 122 additions and 48 deletions

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@ import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
import static org.jclouds.virtualbox.util.MachineUtils.machineNotFoundException;
@ -132,6 +133,14 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
return masters.get(key.getId());
}
// the yaml image
YamlImage yamlImage = imageMapping.get(key.getId());
checkNotNull(yamlImage, "could not find yaml image for image: " + key);
checkState(!yamlImage.id.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "master image names cannot contain \""
+ VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");
String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
String guestAdditionsIso = String.format("%s/%s", isosDir, guestAdditionsFileName);
String guestAdditionsUri = "http://download.virtualbox.org/virtualbox/" + version + "/" + guestAdditionsFileName;
@ -140,11 +149,6 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
}
checkState(new File(guestAdditionsIso).exists(), "guest additions iso does not exist at: " + guestAdditionsIso);
// the yaml image
YamlImage yamlImage = imageMapping.get(key.getId());
checkNotNull(yamlImage, "could not find yaml image for image: " + key);
// check if the iso is here, download if not
String localIsoUrl = getFilePathOrDownload(yamlImage.iso);
@ -162,7 +166,7 @@ public class MastersLoadingCache extends AbstractLoadingCache<Image, Master> {
.controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();
NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
.tcpRedirectRule("127.0.0.1", MASTER_PORT, "", 22).build();
.tcpRedirectRule("127.0.0.1", MASTER_PORT , "", 22).build();
NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
.slot(0L).build();

View File

@ -55,29 +55,39 @@ public class MutableMachine implements Function<String, ISession> {
@Override
public ISession apply(String machineId) {
return lockSessionOnMachineAndReturn(manager.get(), lockType, machineId);
return lockSessionOnMachineAndReturn(manager.get(), lockType, machineId);
}
/**
* Locks the machine and executes the given function using the current session.
* Since the machine is locked it is possible to perform some modifications to the IMachine.
* If locking failes tries to lock some until retries are exhausted.
* <p/>
* Unlocks the machine before returning.
*
*
* @param manager the VirtualBoxManager
* @param type the kind of lock to use when initially locking the machine.
* @param machineId the id of the machine
* @return the ISession bounded to the machine locked.
*/
public static ISession lockSessionOnMachineAndReturn(VirtualBoxManager manager, LockType type, String machineId) {
try {
ISession session = manager.getSessionObject();
IMachine immutableMachine = manager.getVBox().findMachine(machineId);
immutableMachine.lockMachine(session, type);
return session;
} catch (VBoxException e) {
throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId,
type, e.getMessage()), e);
public ISession lockSessionOnMachineAndReturn(VirtualBoxManager manager, LockType type, String machineId) {
int retries = 5;
int count = 0;
while (true) {
try {
ISession session = manager.getSessionObject();
IMachine immutableMachine = manager.getVBox().findMachine(machineId);
immutableMachine.lockMachine(session, type);
return session;
} catch (VBoxException e) {
count++;
logger.warn("Could not lock machine (try %i of %i). Error: %s", retries, count, e.getMessage());
if (count == retries){
throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId,
type, e.getMessage()), e);
}
}
}
}

View File

@ -21,6 +21,7 @@ package org.jclouds.virtualbox.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;
import java.util.concurrent.atomic.AtomicInteger;
@ -64,7 +65,7 @@ import com.google.common.collect.Iterables;
* Creates nodes, by cloning a master vm and based on the provided {@link NodeSpec}. Must be
* synchronized mainly because of snapshot creation (must be synchronized on a per-master-basis).
*
* @author dralves
* @author David Alves
*
*/
@Singleton
@ -90,24 +91,15 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
private final AtomicInteger nodePorts;
private final AtomicInteger nodeIps;
private MachineUtils machineUtils;
private Function<IMachine, NodeMetadata> imachineToNodeMetadata;
private final RunScriptOnNode.Factory scriptRunnerFactory;
private final Supplier<NodeMetadata> hostSupplier;
@Inject
public NodeCreator(Supplier<VirtualBoxManager> manager, Function<CloneSpec, IMachine> cloner,
MachineUtils machineUtils, Function<IMachine, NodeMetadata> imachineToNodeMetadata,
RunScriptOnNode.Factory scriptRunnerFactory, Supplier<NodeMetadata> hostSupplier) {
MachineUtils machineUtils, RunScriptOnNode.Factory scriptRunnerFactory) {
this.manager = manager;
this.cloner = cloner;
this.nodePorts = new AtomicInteger(NODE_PORT_INIT);
this.nodeIps = new AtomicInteger(2);
this.machineUtils = machineUtils;
this.imachineToNodeMetadata = imachineToNodeMetadata;
this.scriptRunnerFactory = scriptRunnerFactory;
this.hostSupplier = hostSupplier;
}
@Override
@ -131,8 +123,8 @@ public class NodeCreator implements Function<NodeSpec, NodeAndInitialCredentials
}
String masterNameWithoutPrefix = master.getSpec().getVmSpec().getVmName().replace(VIRTUALBOX_IMAGE_PREFIX, "");
String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + "-" + nodeSpec.getTag() + "-"
+ nodeSpec.getName();
String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + VIRTUALBOX_NODE_NAME_SEPARATOR
+ nodeSpec.getTag() + VIRTUALBOX_NODE_NAME_SEPARATOR + nodeSpec.getName();
VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(512).cleanUpMode(CleanupMode.Full)
.forceOverwrite(true).build();

View File

@ -68,7 +68,7 @@ public class MachineController {
public void ensureMachineHasPowerDown(String vmName) {
while (!manager.get().getVBox().findMachine(vmName).getState().equals(MachineState.POWERED_OFF)) {
try {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Shared, new Function<ISession, Void>() {
machineUtils.lockSessionOnMachineAndApply(vmName, LockType.Write, new Function<ISession, Void>() {
@Override
public Void apply(ISession session) {
IProgress powerDownProgress = session.getConsole().powerDown();

View File

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

View File

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