diff --git a/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java new file mode 100644 index 0000000000..e83665dd09 --- /dev/null +++ b/sandbox-apis/virtualbox/src/main/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIMachineIfNotAlreadyExists.java @@ -0,0 +1,186 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jclouds.virtualbox.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.virtualbox.MachineUtils.lockMachineAndApply; +import static org.virtualbox_4_1.LockType.Write; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; +import javax.annotation.Resource; +import javax.inject.Named; + +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.logging.Logger; +import org.virtualbox_4_1.CloneMode; +import org.virtualbox_4_1.CloneOptions; +import org.virtualbox_4_1.IMachine; +import org.virtualbox_4_1.IProgress; +import org.virtualbox_4_1.ISnapshot; +import org.virtualbox_4_1.IVirtualBox; +import org.virtualbox_4_1.VBoxException; +import org.virtualbox_4_1.VirtualBoxManager; + +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.inject.Inject; + +/** + * + * CloneAndRegisterMachineFromIMachineIfNotAlreadyExists will take care of the + * followings: - cloning the master - register the clone machine - + * ensureBridgedNetworkingIsAppliedToMachine(cloneName, macAddress, + * hostInterface) + * + * @author Andrea Turli + */ +public class CloneAndRegisterMachineFromIMachineIfNotAlreadyExists implements +Function { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private VirtualBoxManager manager; + private ComputeServiceContext context; + private String settingsFile; + private String osTypeId; + private String vmId; + private boolean forceOverwrite; + private String cloneName; + private String hostId; + private String snapshotName; + private String snapshotDesc; + private String controllerIDE; + + @Inject + public CloneAndRegisterMachineFromIMachineIfNotAlreadyExists( + VirtualBoxManager manager, ComputeServiceContext context, + String settingsFile, String osTypeId, String vmId, + boolean forceOverwrite, String cloneName, String hostId, + String snashotName, String snapshotDesc, String controllerIDE) { + super(); + this.manager = manager; + this.context = context; + this.settingsFile = settingsFile; + this.osTypeId = osTypeId; + this.vmId = vmId; + this.forceOverwrite = forceOverwrite; + this.cloneName = cloneName; + this.hostId = hostId; + this.snapshotName = snashotName; + this.snapshotDesc = snapshotDesc; + this.controllerIDE = controllerIDE; + } + + @Override + public IMachine apply(@Nullable IMachine master) { + + final IVirtualBox vBox = manager.getVBox(); + try { + vBox.findMachine(cloneName); + throw new IllegalStateException("Machine " + cloneName + + " is already registered."); + } catch (VBoxException e) { + if (machineNotFoundException(e)) + return cloneMachine(vBox, cloneName, master); + else + throw e; + } + } + + private boolean machineNotFoundException(VBoxException e) { + return e.getMessage().indexOf( + "VirtualBox error: Could not find a registered machine named ") != -1; + } + + private IMachine cloneMachine(IVirtualBox vBox, String cloneName, + IMachine master) { + IMachine clonedMachine = manager.getVBox().createMachine(settingsFile, + cloneName, osTypeId, vmId, forceOverwrite); + List options = new ArrayList(); + options.add(CloneOptions.Link); + + // takeSnapshotIfNotAlreadyExists + ISnapshot currentSnapshot = new TakeSnapshotIfNotAlreadyAttached(manager, + snapshotName, snapshotDesc).apply(master); + + // clone + IProgress progress = currentSnapshot.getMachine().cloneTo(clonedMachine, + CloneMode.MachineState, options); + + if (progress.getCompleted()) + logger.debug("clone done"); + + // registering + manager.getVBox().registerMachine(clonedMachine); + + // Bridged Network + List activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces( + context).apply(hostId); + checkNotNull(activeBridgedInterfaces); + String macAddress = manager.getVBox().getHost().generateMACAddress(); + + // TODO this behavior can be improved + String bridgedInterface = activeBridgedInterfaces.get(0); + ensureBridgedNetworkingIsAppliedToMachine(cloneName, macAddress, + bridgedInterface); + + // detach iso + ensureMachineHasDistroMediumDetached(cloneName, controllerIDE); + + return clonedMachine; + } + + private void ensureBridgedNetworkingIsAppliedToMachine(String vmName, + String macAddress, String hostInterface) { + lockMachineAndApply(manager, Write, vmName, + new AttachBridgedAdapterToMachine(0l, macAddress, hostInterface)); + } + + private void ensureMachineHasDistroMediumDetached(String vmName, + String controllerIDE) { + lockMachineAndApply( + manager, + Write, + vmName, + new DetachDistroMediumToMachine(checkNotNull(controllerIDE, + "controllerIDE"))); + } + + protected ExecResponse runScriptOnNode(String nodeId, String command, + RunScriptOptions options) { + return context.getComputeService().runScriptOnNode(nodeId, command, + options); + } + + protected T propagate(Exception e) { + Throwables.propagate(e); + assert false; + return null; + } + +} diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java new file mode 100644 index 0000000000..536f2fe6df --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/functions/CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest.java @@ -0,0 +1,98 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jclouds.virtualbox.functions; + +import static org.jclouds.virtualbox.experiment.TestUtils.computeServiceForLocalhostAndGuest; +import static org.testng.Assert.assertEquals; +import static org.virtualbox_4_1.NetworkAttachmentType.Bridged; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.domain.Credentials; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.InetSocketAddressConnect; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.virtualbox.BaseVirtualBoxClientLiveTest; +import org.testng.annotations.Test; +import org.virtualbox_4_1.IMachine; +import org.virtualbox_4_1.ISession; +import org.virtualbox_4_1.VirtualBoxManager; + +import com.google.common.base.Predicate; + +/** + * @author Andrea Turli + */ +@Test(groups = "live", singleThreaded = true, testName = "CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest") +public class CloneAndRegisterMachineFromIsoIfNotAlreadyExistsLiveTest extends + BaseVirtualBoxClientLiveTest { + + private String settingsFile = null; + private boolean forceOverwrite = true; + private String vmId = "jclouds-image-iso-1"; + private String osTypeId = ""; + private String controllerIDE = "IDE Controller"; + private String diskFormat = ""; + private String adminDisk = "testadmin.vdi"; + private String guestId = "guest"; + private String hostId = "host"; + private String snapshotName = "snap"; + private String snapshotDesc = "snapDesc"; + + private String vmName = "jclouds-image-virtualbox-iso-to-machine-test"; + private String cloneName = vmName + "_clone"; + private String isoName = "ubuntu-11.04-server-i386.iso"; + + @Test + public void testCloneMachineFromAnotherMachine() throws Exception { + VirtualBoxManager manager = (VirtualBoxManager) context + .getProviderSpecificContext().getApi(); + ComputeServiceContext localHostContext = computeServiceForLocalhostAndGuest( + hostId, "localhost", guestId, "localhost", new Credentials("toor", + "password")); + + IMachine master = null; + try { + Predicate socketTester = new RetryablePredicate( + new InetSocketAddressConnect(), 10, 1, TimeUnit.SECONDS); + master = new IsoToIMachine(manager, adminDisk, diskFormat, + settingsFile, vmName, osTypeId, vmId, forceOverwrite, + controllerIDE, localHostContext, hostId, guestId, socketTester, + "127.0.0.1", 8080).apply(isoName); + } catch (IllegalStateException e) { + // already created + master = manager.getVBox().findMachine(vmName); + } + + if (master.getCurrentSnapshot() != null) { + ISession session = manager.openMachineSession(master); + session.getConsole().deleteSnapshot( + master.getCurrentSnapshot().getId()); + session.unlockMachine(); + } + + IMachine clone = new CloneAndRegisterMachineFromIMachineIfNotAlreadyExists( + manager, localHostContext, settingsFile, osTypeId, vmId, + forceOverwrite, cloneName, hostId, snapshotName, snapshotDesc, + controllerIDE).apply(master); + assertEquals(clone.getNetworkAdapter(0L).getAttachmentType(), Bridged); + } +} \ No newline at end of file