started refactoring IsoToIMachine to re-use error handling

This commit is contained in:
Adrian Cole 2011-10-22 17:54:35 +02:00
parent aab1290b85
commit 926989c5f1
3 changed files with 399 additions and 207 deletions

View File

@ -0,0 +1,57 @@
/**
* 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 org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.StorageBus;
import org.virtualbox_4_1.VBoxException;
import com.google.common.base.Function;
/**
*
* @author Adrian Cole
*
*/
public class AddIDEControllerIfNotExists implements Function<IMachine, Void> {
private final String controllerName;
public AddIDEControllerIfNotExists(String controllerName) {
this.controllerName = checkNotNull(controllerName, "controllerName");
}
@Override
public Void apply(IMachine arg0) {
try {
arg0.addStorageController(controllerName, StorageBus.IDE);
arg0.saveSettings();
} catch (VBoxException e) {
if (e.getMessage().indexOf("already exists") == -1)
throw e;
}
return null;
}
@Override
public String toString() {
return String.format("addStorageController(%s, IDE)", controllerName);
}
}

View File

@ -21,6 +21,7 @@
package org.jclouds.virtualbox.functions; package org.jclouds.virtualbox.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate; import static com.google.common.base.Throwables.propagate;
import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot; import static org.jclouds.compute.options.RunScriptOptions.Builder.runAsRoot;
import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript; import static org.jclouds.compute.options.RunScriptOptions.Builder.wrapInInitScript;
@ -28,6 +29,7 @@ import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTA
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR; import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
import static org.virtualbox_4_1.AccessMode.ReadOnly; import static org.virtualbox_4_1.AccessMode.ReadOnly;
import static org.virtualbox_4_1.DeviceType.DVD; import static org.virtualbox_4_1.DeviceType.DVD;
import static org.virtualbox_4_1.LockType.Shared;
import static org.virtualbox_4_1.LockType.Write; import static org.virtualbox_4_1.LockType.Write;
import static org.virtualbox_4_1.NATProtocol.TCP; import static org.virtualbox_4_1.NATProtocol.TCP;
import static org.virtualbox_4_1.NetworkAttachmentType.NAT; import static org.virtualbox_4_1.NetworkAttachmentType.NAT;
@ -37,11 +39,7 @@ import java.io.File;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.inject.Named; import javax.inject.Named;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.options.RunScriptOptions; import org.jclouds.compute.options.RunScriptOptions;
@ -60,14 +58,13 @@ import org.virtualbox_4_1.IMedium;
import org.virtualbox_4_1.IProgress; import org.virtualbox_4_1.IProgress;
import org.virtualbox_4_1.ISession; import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.LockType; import org.virtualbox_4_1.LockType;
import org.virtualbox_4_1.StorageBus; import org.virtualbox_4_1.VBoxException;
import org.virtualbox_4_1.VirtualBoxManager; import org.virtualbox_4_1.VirtualBoxManager;
import org.virtualbox_4_1.jaxws.MediumVariant; import org.virtualbox_4_1.jaxws.MediumVariant;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.inject.Inject; import com.google.inject.Inject;
public class IsoToIMachine implements Function<String, IMachine> { public class IsoToIMachine implements Function<String, IMachine> {
@Resource @Resource
@ -89,19 +86,9 @@ public class IsoToIMachine implements Function<String, IMachine> {
private Credentials credentials; private Credentials credentials;
@Inject @Inject
public IsoToIMachine(VirtualBoxManager manager, public IsoToIMachine(VirtualBoxManager manager, String adminDisk, String diskFormat, String settingsFile,
String adminDisk, String vmName, String osTypeId, String vmId, boolean forceOverwrite, String controllerIDE,
String diskFormat, ComputeServiceContext context, String hostId, String guestId, Credentials credentials) {
String settingsFile,
String vmName,
String osTypeId,
String vmId,
boolean forceOverwrite,
String controllerIDE,
ComputeServiceContext context,
String hostId,
String guestId,
Credentials credentials) {
super(); super();
this.manager = manager; this.manager = manager;
this.adminDisk = adminDisk; this.adminDisk = adminDisk;
@ -130,70 +117,92 @@ public class IsoToIMachine implements Function<String, IMachine> {
String defaultWorkingDir = System.getProperty("user.home") + "/jclouds-virtualbox-test"; String defaultWorkingDir = System.getProperty("user.home") + "/jclouds-virtualbox-test";
String workingDir = System.getProperty(VIRTUALBOX_WORKINGDIR, defaultWorkingDir); String workingDir = System.getProperty(VIRTUALBOX_WORKINGDIR, defaultWorkingDir);
IMedium distroMedium = manager.getVBox().openMedium(workingDir + "/" + isoName, DVD, ReadOnly, forceOverwrite);
// Change RAM // Change RAM
Long memorySize = new Long(1024); lockMachineAndApply(manager, Write, vmName, new Function<IMachine, Void>() {
ISession session = manager.getSessionObject();
IMachine machine = manager.getVBox().findMachine(vmName); @Override
machine.lockMachine(session, Write); public Void apply(IMachine arg0) {
IMachine mutable = session.getMachine(); arg0.setMemorySize(1024l);
mutable.setMemorySize(memorySize); arg0.saveSettings();
mutable.saveSettings(); return null;
session.unlockMachine(); }
});
// IDE Controller // IDE Controller
machine.lockMachine(session, Write); ensureMachineHasIDEControllerNamed(vmName, controllerIDE);
mutable = session.getMachine();
mutable.addStorageController(controllerIDE, StorageBus.IDE);
mutable.saveSettings();
session.unlockMachine();
// DISK // DISK
String adminDiskPath = workingDir + "/" + adminDisk; String adminDiskPath = workingDir + "/" + adminDisk;
if (new File(adminDiskPath).exists()) { if (new File(adminDiskPath).exists()) {
new File(adminDiskPath).delete(); new File(adminDiskPath).delete();
} }
IMedium hd = manager.getVBox().createHardDisk(diskFormat, adminDiskPath);
final IMedium distroMedium = manager.getVBox().openMedium(workingDir + "/" + isoName, DVD, ReadOnly,
forceOverwrite);
lockMachineAndApply(manager, Write, vmName, new Function<IMachine, Void>() {
@Override
public Void apply(IMachine arg0) {
try {
arg0.attachDevice(controllerIDE, 0, 0, DeviceType.DVD, distroMedium);
arg0.saveSettings();
} catch (VBoxException e) {
if (e.getMessage().indexOf("is already attached to port") == -1)
throw e;
}
return null;
}
});
// Create and attach hard disk
final IMedium hd = manager.getVBox().createHardDisk(diskFormat, adminDiskPath);
long size = 4L * 1024L * 1024L * 1024L - 4L; long size = 4L * 1024L * 1024L * 1024L - 4L;
IProgress storageCreation = hd.createBaseStorage(size, (long) MediumVariant.STANDARD.ordinal()); IProgress storageCreation = hd.createBaseStorage(size, (long) MediumVariant.STANDARD.ordinal());
storageCreation.waitForCompletion(-1); storageCreation.waitForCompletion(-1);
machine.lockMachine(session, Write); lockMachineAndApply(manager, Write, vmName, new Function<IMachine, Void>() {
mutable = session.getMachine();
mutable.attachDevice(controllerIDE, 0, 0, DeviceType.DVD, distroMedium);
mutable.saveSettings();
session.unlockMachine();
// Create and attach hard disk @Override
machine.lockMachine(session, Write); public Void apply(IMachine arg0) {
mutable = session.getMachine(); arg0.attachDevice(controllerIDE, 0, 1, DeviceType.HardDisk, hd);
mutable.attachDevice(controllerIDE, 0, 1, DeviceType.HardDisk, hd); arg0.saveSettings();
mutable.saveSettings(); return null;
session.unlockMachine(); }
// NIC });
machine.lockMachine(session, Write);
mutable = session.getMachine();
// NAT // NAT
mutable.getNetworkAdapter(0l).setAttachmentType(NAT); lockMachineAndApply(manager, Write, vmName, new Function<IMachine, Void>() {
machine.getNetworkAdapter(0l)
.getNatDriver() @Override
.addRedirect("guestssh", TCP, "127.0.0.1", 2222, "", 22); public Void apply(IMachine arg0) {
mutable.getNetworkAdapter(0l).setEnabled(true); arg0.getNetworkAdapter(0l).setAttachmentType(NAT);
mutable.saveSettings(); arg0.getNetworkAdapter(0l).getNatDriver().addRedirect("guestssh", TCP, "127.0.0.1", 2222, "", 22);
session.unlockMachine(); arg0.getNetworkAdapter(0l).setEnabled(true);
return null;
}
});
String guestAdditionsDvd = workingDir + "/VBoxGuestAdditions_4.1.2.iso"; String guestAdditionsDvd = workingDir + "/VBoxGuestAdditions_4.1.2.iso";
IMedium guestAdditionsDvdMedium = manager.getVBox().openMedium(guestAdditionsDvd, DeviceType.DVD, AccessMode.ReadOnly, forceOverwrite); final IMedium guestAdditionsDvdMedium = manager.getVBox().openMedium(guestAdditionsDvd, DeviceType.DVD,
machine.lockMachine(session, Write); AccessMode.ReadOnly, forceOverwrite);
mutable = session.getMachine();
mutable.attachDevice(controllerIDE, 1, 1, DeviceType.DVD, guestAdditionsDvdMedium);
mutable.saveSettings();
session.unlockMachine();
IProgress prog = machine.launchVMProcess(session, "gui", ""); lockMachineAndApply(manager, Write, vmName, new Function<IMachine, Void>() {
@Override
public Void apply(IMachine arg0) {
arg0.attachDevice(controllerIDE, 1, 1, DeviceType.DVD, guestAdditionsDvdMedium);
return null;
}
});
IProgress prog = vm.launchVMProcess(manager.getSessionObject(), "gui", "");
prog.waitForCompletion(-1); prog.waitForCompletion(-1);
try { try {
Thread.sleep(5000); Thread.sleep(5000);
@ -203,7 +212,6 @@ public class IsoToIMachine implements Function<String, IMachine> {
String installKeySequence = System.getProperty(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, defaultInstallSequence()); String installKeySequence = System.getProperty(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE, defaultInstallSequence());
sendKeyboardSequence(installKeySequence); sendKeyboardSequence(installKeySequence);
session.unlockMachine();
boolean sshDeamonIsRunning = false; boolean sshDeamonIsRunning = false;
while (!sshDeamonIsRunning) { while (!sshDeamonIsRunning) {
@ -218,12 +226,16 @@ public class IsoToIMachine implements Function<String, IMachine> {
} }
logger.debug("Installation of image complete. Powering down..."); logger.debug("Installation of image complete. Powering down...");
lockSessionOnMachineAndApply(manager, Shared, vmName, new Function<ISession, Void>() {
machine.lockMachine(session, LockType.Shared); @Override
IProgress powerDownProgress = session.getConsole().powerDown(); public Void apply(ISession arg0) {
IProgress powerDownProgress = arg0.getConsole().powerDown();
powerDownProgress.waitForCompletion(-1); powerDownProgress.waitForCompletion(-1);
session.unlockMachine(); return null;
}
});
try { try {
logger.debug("Stopping Jetty server..."); logger.debug("Stopping Jetty server...");
server.stop(); server.stop();
@ -234,13 +246,50 @@ public class IsoToIMachine implements Function<String, IMachine> {
return vm; return vm;
} }
public void ensureMachineHasIDEControllerNamed(String vmName, String controllerIDE) {
lockMachineAndApply(manager, Write, checkNotNull(vmName, "vmName"),
new AddIDEControllerIfNotExists(checkNotNull(controllerIDE, "controllerIDE")));
}
public static <T> T lockMachineAndApply(VirtualBoxManager manager, final LockType type, final String machineId,
final Function<IMachine, T> function) {
return lockSessionOnMachineAndApply(manager, type, machineId, new Function<ISession, T>() {
@Override
public T apply(ISession arg0) {
return function.apply(arg0.getMachine());
}
@Override
public String toString() {
return function.toString();
}
});
}
public static <T> T lockSessionOnMachineAndApply(VirtualBoxManager manager, LockType type, String machineId,
Function<ISession, T> function) {
try {
ISession session = manager.getSessionObject();
IMachine immutableMachine = manager.getVBox().findMachine(machineId);
immutableMachine.lockMachine(session, type);
try {
return function.apply(session);
} finally {
session.unlockMachine();
}
} catch (VBoxException e) {
throw new RuntimeException(String.format("error applying %s to %s with %s lock: %s", function, machineId,
type, e.getMessage()), e);
}
}
private String defaultInstallSequence() { private String defaultInstallSequence() {
return "<Esc><Esc><Enter> " return "<Esc><Esc><Enter> "
+ "/install/vmlinuz noapic preseed/url=http://10.0.2.2:8080/src/test/resources/preseed.cfg " + "/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 " + "debian-installer=en_US auto locale=en_US kbd-chooser/method=us " + "hostname=" + vmName + " "
+ "hostname="
+ vmName
+ " "
+ "fb=false debconf/frontend=noninteractive " + "fb=false debconf/frontend=noninteractive "
+ "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false " + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
+ "initrd=/install/initrd.gz -- <Enter>"; + "initrd=/install/initrd.gz -- <Enter>";
@ -252,8 +301,7 @@ public class IsoToIMachine implements Function<String, IMachine> {
for (String line : sequenceSplited) { for (String line : sequenceSplited) {
String converted = stringToKeycode(line); String converted = stringToKeycode(line);
for (String word : converted.split(" ")) { for (String word : converted.split(" ")) {
sb.append("vboxmanage controlvm " + vmName sb.append("vboxmanage controlvm " + vmName + " keyboardputscancode " + word + "; ");
+ " keyboardputscancode " + word + "; ");
if (word.endsWith(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Enter>"))) { if (word.endsWith(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Enter>"))) {
runScriptOnNode(hostId, sb.toString(), runAsRoot(false).wrapInInitScript(false)); runScriptOnNode(hostId, sb.toString(), runAsRoot(false).wrapInInitScript(false));
sb.delete(0, sb.length() - 1); sb.delete(0, sb.length() - 1);
@ -272,8 +320,7 @@ public class IsoToIMachine implements Function<String, IMachine> {
if (s.startsWith("<")) { if (s.startsWith("<")) {
String[] specials = s.split("<"); String[] specials = s.split("<");
for (int i = 1; i < specials.length; i++) { for (int i = 1; i < specials.length; i++) {
keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<" + specials[i]) + " ");
.get("<" + specials[i]) + " ");
} }
return keycodes.toString(); return keycodes.toString();
} }
@ -281,15 +328,13 @@ public class IsoToIMachine implements Function<String, IMachine> {
int i = 0; int i = 0;
while (i < s.length()) { while (i < s.length()) {
String digit = s.substring(i, i + 1); String digit = s.substring(i, i + 1);
String hex = KeyboardScancodes.NORMAL_KEYBOARD_BUTTON_MAP String hex = KeyboardScancodes.NORMAL_KEYBOARD_BUTTON_MAP.get(digit);
.get(digit);
keycodes.append(hex + " "); keycodes.append(hex + " ");
if (i != 0 && i % 14 == 0) if (i != 0 && i % 14 == 0)
keycodes.append(" "); keycodes.append(" ");
i++; i++;
} }
keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Spacebar>") + " ");
.get("<Spacebar>") + " ");
return keycodes.toString(); return keycodes.toString();
} }

View File

@ -0,0 +1,90 @@
/**
* 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.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.createNiceMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import org.testng.annotations.Test;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.IStorageController;
import org.virtualbox_4_1.StorageBus;
import org.virtualbox_4_1.VBoxException;
/**
*
* @author Adrian Cole
*
*/
@Test(groups = "unit", testName = "AddIDEControllerIfNotExistsTest")
public class AddIDEControllerIfNotExistsTest {
@Test
public void testFine() throws Exception {
IMachine vm = createMock(IMachine.class);
String controllerName = "IDE Controller";
expect(vm.addStorageController(controllerName, StorageBus.IDE)).andReturn(
createNiceMock(IStorageController.class));
vm.saveSettings();
replay(vm);
new AddIDEControllerIfNotExists(controllerName).apply(vm);
verify(vm);
}
@Test
public void testAcceptableException() throws Exception {
IMachine vm = createMock(IMachine.class);
String controllerName = "IDE Controller";
expect(vm.addStorageController(controllerName, StorageBus.IDE)).andThrow(
new VBoxException(createNiceMock(Throwable.class),
"VirtualBox error: Storage controller named 'IDE Controller' already exists (0x80BB000C)"));
replay(vm);
new AddIDEControllerIfNotExists(controllerName).apply(vm);
verify(vm);
}
@Test(expectedExceptions = VBoxException.class)
public void testUnacceptableException() throws Exception {
IMachine vm = createMock(IMachine.class);
String controllerName = "IDE Controller";
expect(vm.addStorageController(controllerName, StorageBus.IDE)).andThrow(
new VBoxException(createNiceMock(Throwable.class), "VirtualBox error: General Error"));
replay(vm);
new AddIDEControllerIfNotExists(controllerName).apply(vm);
verify(vm);
}
}