diff --git a/sandbox-apis/virtualbox/README.md b/sandbox-apis/virtualbox/README.md new file mode 100644 index 0000000000..712880057f --- /dev/null +++ b/sandbox-apis/virtualbox/README.md @@ -0,0 +1,42 @@ +# Administation viewpoint + +These steps are usually in charge of cloud provider, in this case it's up to you + +The experiment assumes almost nothing: only a running Ubuntu 11.04 where java6 and maven3 are installed. + +## Prepare your Master VM +By launching "VirtualboxAdministrationTest" an helper class to execute a number of preliminary steps needed to have a properly configured ubuntu box +and to create a golden template for your experiment. + +The "VirtualboxAdministrationTest" helper will run these preliminary steps: + +1. Create a working dir on 'user.home' called by default "jclouds-virtualbox-test" +(this value can be overwritten directly on the commandline using -Dtest.virtualbox.workingDir) +2. Install VBOX-OSE (available through ubuntu repo) +3. Download a VDI from [here](http://downloads.sourceforge.net/virtualboximage/centos-5.2-x86.7z) into your working dir +3. Install p7zip on your ubuntu machine, if missing +3. Extract the 7z archive into your working dir +4. Download VirtualBox Guest Additions ISO (tested with VBoxGuestAdditions_4.0.2-update-69551.iso) into "jclouds-virtualbox-test" +5. Disable login credential: $ VBoxManage setproperty websrvauthlibrary null +6. Start webservice with increasead timeout: $ /usr/bin/vboxwebsrv --timeout 10000 and then will: +- Clone originalDisk to clonedDisk in workingDir +- Set NAT on network interface eth0 +- Set port forwarding localhost:2222->guest:22 +- Create a VM in the the vbox default machine folder ("/user.home/VirtualBox VMs") +- Mount guest additions ISO +- Install guest additions +- Shutdown VM +- Remove port forwarding rule +- Remove NAT network interface +- Detach vdisk from VM template + +At this stage, you can use your template as a master for your "private" cloud through VirtualBoxLiveTest + +## Bootstrap your private cloud through VirtualBoxLiveTest helper +To use this helper, you have to specify the name of the VM (-Dtest.virtualbox.vmname=) +and choose the numberOfVirtualMachine you need using -Dtest.virtualbox.numberOfVirtualMachine=<#ofVMs>, +These VMs will be cloned starting from the master VM created before + +It will create a "numberOfVirtualMachine" in a vbox default machine folder with 'VMName_i' name + +NB: for perfomance reason, these VM will share the same disk (the template created at step 1) attached in "multiattach mode" (http://www.virtualbox.org/manual/ch05.html#hdimagewrites) \ No newline at end of file diff --git a/sandbox-apis/virtualbox/pom.xml b/sandbox-apis/virtualbox/pom.xml new file mode 100644 index 0000000000..c0d451d4c7 --- /dev/null +++ b/sandbox-apis/virtualbox/pom.xml @@ -0,0 +1,128 @@ + + + + 4.0.0 + + org.jclouds + jclouds-project + 1.1.0-SNAPSHOT + ../../project/pom.xml + + org.jclouds.api + virtualbox + jclouds components for a virtualbox provider + + test:///default + 1.0 + FIXME + FIXME + + + + + + clojars.org + http://clojars.org/repo + + + + + org.clojars.tbatchelli + vboxjws + 4.0.2 + + + org.jclouds + jclouds-core + ${project.version} + test-jar + test + + + org.jclouds + jclouds-compute + ${project.version} + + + org.jclouds + jclouds-compute + ${project.version} + test-jar + test + + + org.jclouds.driver + jclouds-log4j + ${project.version} + test + + + org.jclouds.driver + jclouds-jsch + ${project.version} + test + + + com.github.jponge + lzma-java + 1.2 + + + org.eclipse.jetty + jetty-security + test + + + + + live + + + + org.apache.maven.plugins + maven-surefire-plugin + + + integration + integration-test + + test + + + + ${test.virtualbox.endpoint} + ${test.virtualbox.apiversion} + ${test.virtualbox.identity} + ${test.virtualbox.credential} + + + + + + + + + + diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/FileServer.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/FileServer.java new file mode 100644 index 0000000000..54237cf433 --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/FileServer.java @@ -0,0 +1,29 @@ +package org.jclouds.virtualbox.experiment; + +import org.eclipse.jetty.server.Handler; +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.eclipse.jetty.util.log.Log; + +public class FileServer { + public static void main(String[] args) throws Exception { + Server server = new Server(args.length == 0?80:Integer.parseInt(args[0])); + + ResourceHandler resource_handler = new ResourceHandler(); + resource_handler.setDirectoriesListed(true); + resource_handler.setWelcomeFiles(new String[]{ "index.html" }); + + resource_handler.setResourceBase(args.length == 2?args[1]:"."); + Log.info("serving " + resource_handler.getBaseResource()); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() }); + server.setHandler(handlers); + + server.start(); + server.join(); + } + +} \ No newline at end of file diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationKickstartTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationKickstartTest.java new file mode 100644 index 0000000000..5801fb2d62 --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationKickstartTest.java @@ -0,0 +1,726 @@ +package org.jclouds.virtualbox.experiment; + +import static com.google.common.base.Throwables.propagate; +import static com.google.common.io.ByteStreams.copy; +import static com.google.common.io.Closeables.closeQuietly; +import static org.testng.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URL; +import java.rmi.RemoteException; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.crypto.CryptoStreams; +import org.jclouds.domain.Credentials; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.InetSocketAddressConnect; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.jclouds.util.Strings2; +import org.jclouds.virtualbox.experiment.settings.KeyboardScancodes; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Optional; +import org.testng.annotations.Test; +import org.virtualbox_4_0.AccessMode; +import org.virtualbox_4_0.DeviceType; +import org.virtualbox_4_0.IMachine; +import org.virtualbox_4_0.IMedium; +import org.virtualbox_4_0.IProgress; +import org.virtualbox_4_0.ISession; +import org.virtualbox_4_0.IStorageController; +import org.virtualbox_4_0.LockType; +import org.virtualbox_4_0.MachineState; +import org.virtualbox_4_0.NATProtocol; +import org.virtualbox_4_0.NetworkAdapterType; +import org.virtualbox_4_0.SessionState; +import org.virtualbox_4_0.StorageBus; +import org.virtualbox_4_0.VirtualBoxManager; +import org.virtualbox_4_0.jaxws.MediumVariant; + +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; +import com.google.inject.Guice; +import com.google.inject.Injector; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.log.Log; + + +@Test(groups = "live", testName = "virtualbox.VirtualboxAdministrationKickstartTest") +public class VirtualboxAdministrationKickstartTest { + + protected String provider = "virtualbox"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + protected String vmName; + + VirtualBoxManager manager = VirtualBoxManager.createInstance(""); + + protected Injector injector; + protected Predicate socketTester; + protected SshClient.Factory sshFactory; + + protected String settingsFile; // Fully qualified path where the settings + protected String osTypeId; // Guest OS Type ID. + protected String vmId; // Machine UUID (optional). + protected boolean forceOverwrite; + protected String osUsername; + protected String osPassword; + protected String diskFormat; + + protected String workingDir; + protected String originalDisk; + protected String clonedDisk; + + protected String guestAdditionsDvd; + private String gaIsoUrl; + private String vboxwebsrvStartCommand; + + private String gaIsoName; + private String hostUsername; + private String hostPassword; + private String installVboxOse; + private String distroIsoUrl; + private String distroIsoName; + private String distroDvd; + private String controllerIDE; + private String controllerSATA; + private String keyboardSequence; + private String admin_pwd; + private String vdiName; + private String preseedUrl; + + protected Server server = null; + private InetSocketAddress testPort; + private String vboxManageCommand; + + + protected void setupCredentials() { + identity = System.getProperty("test." + provider + ".identity", + "administrator"); + credential = System.getProperty("test." + provider + ".credential", + "12345"); + endpoint = System.getProperty("test." + provider + ".endpoint", + "http://localhost:18083/"); + apiversion = System.getProperty("test." + provider + ".apiversion"); + } + + protected void setupConfigurationProperties() { + + admin_pwd = System.getProperty("test." + provider + ".admin_pwd", + "password"); + // OS + osUsername = System.getProperty("test." + provider + ".osusername", + "toor"); + osPassword = System.getProperty("test." + provider + ".ospassword", + "password"); + controllerIDE = System.getProperty("test." + provider + + ".controllerIde", "IDE Controller"); + controllerSATA = System.getProperty("test." + provider + + ".controllerSata", "SATA Controller"); + // Create disk If the @a format attribute is empty or null then the + // default storage format specified by + // ISystemProperties#defaultHardDiskFormat + diskFormat = System.getProperty("test." + provider + ".diskformat", ""); + + // VBOX + settingsFile = null; // Fully qualified path where the settings file + osTypeId = System.getProperty("test." + provider + ".osTypeId", ""); // Guest + // OS + // Type + // ID. + vmId = System.getProperty("test." + provider + ".vmId", null); // Machine + // UUID + // (optional). + forceOverwrite = true; // If true, an existing machine settings file + // will be overwritten. + vmName = System.getProperty("test." + provider + ".vmname", + "jclouds-virtualbox-kickstart-admin"); + + workingDir = System.getProperty("user.home") + + File.separator + + System.getProperty("test." + provider + ".workingDir", + "jclouds-virtualbox-test"); + if (new File(workingDir).mkdir()); + vdiName = System.getProperty("test." + provider + ".vdiName", + "centos-5.2-x86.7z"); + gaIsoName = System.getProperty("test." + provider + ".gaIsoName", + "VBoxGuestAdditions_4.0.2-update-69551.iso"); + gaIsoUrl = System.getProperty("test." + provider + ".gaIsoUrl", + "http://download.virtualbox.org/virtualbox/4.0.2/VBoxGuestAdditions_4.0.2-update-69551.iso"); + + distroIsoName = System.getProperty("test." + provider + + ".distroIsoName", "ubuntu-11.04-server-i386.iso"); + distroIsoUrl = System + .getProperty("test." + provider + ".distroIsoUrl", + "http://releases.ubuntu.com/11.04/ubuntu-11.04-server-i386.iso"); + + originalDisk = workingDir + + File.separator + + "VDI" + + File.separator + + System.getProperty("test." + provider + ".originalDisk", + "centos-5.2-x86.vdi"); + clonedDisk = workingDir + + File.separator + + System.getProperty("test." + provider + ".clonedDisk", + "template.vdi"); + guestAdditionsDvd = workingDir + + File.separator + + System.getProperty("test." + provider + ".guestAdditionsDvd", + "VBoxGuestAdditions_4.0.2-update-69551.iso"); + + distroDvd = workingDir + + File.separator + + System.getProperty("test." + provider + ".distroDvd", + distroIsoName); + + preseedUrl = System + .getProperty( + "test." + provider + ".preseedurl", "http://dl.dropbox.com/u/693111/preseed.cfg"); + keyboardSequence = System + .getProperty( + "test." + provider + ".keyboardSequence", + " " + //+ "/install/vmlinuz noapic preseed/url=http://192.168.1.12/src/test/resources/preseed.cfg " + + "/install/vmlinuz noapic preseed/url=" + preseedUrl + " " + + "debian-installer=en_US auto locale=en_US kbd-chooser/method=us " + + "hostname=" + vmName + " " + + "fb=false debconf/frontend=noninteractive " + + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false " + + "initrd=/install/initrd.gz -- "); + + vboxwebsrvStartCommand = System.getProperty("test." + provider + + ".vboxwebsrvStartCommand", "/usr/bin/vboxwebsrv.exe"); + vboxManageCommand = System + .getProperty( + "test." + provider + ".vboxmanage", "VBoxManage"); + if (!new File(distroDvd).exists()) { + try { + downloadFile(distroIsoUrl, workingDir, distroIsoName, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (!new File(guestAdditionsDvd).exists()) { + try { + downloadFile(gaIsoUrl, workingDir, gaIsoName, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @BeforeGroups(groups = "live") + protected void setupClient() throws IOException, InterruptedException { + hostUsername = System.getProperty("test." + provider + ".hostusername", "toor"); + hostPassword = System.getProperty("test." + provider + ".hostpassword", + "password"); + + injector = Guice.createInjector(new JschSshClientModule(), + new Log4JLoggingModule()); + sshFactory = injector.getInstance(SshClient.Factory.class); + socketTester = new RetryablePredicate( + new InetSocketAddressConnect(), 3600, 1, TimeUnit.SECONDS); + injector.injectMembers(socketTester); + + setupCredentials(); + setupConfigurationProperties(); + + installVbox(); + // startup vbox web server + startupVboxWebServer(vboxwebsrvStartCommand); + + // configure jetty HTTP server + try { + configureJettyServer(); + } catch (Exception e) { + propagate(e); + } + } + + private void configureJettyServer() throws Exception { + Server server = new Server(8080); + + ResourceHandler resource_handler = new ResourceHandler(); + resource_handler.setDirectoriesListed(true); + resource_handler.setWelcomeFiles(new String[]{ "index.html" }); + + resource_handler.setResourceBase("."); + Log.info("serving " + resource_handler.getBaseResource()); + + HandlerList handlers = new HandlerList(); + handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() }); + server.setHandler(handlers); + } + + private void installVbox() throws IOException, InterruptedException { + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials( + hostUsername, hostPassword)); + try { + client.connect(); + client.exec("echo " + hostPassword + " | " + installVboxOse); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + + } + + /** + * + * @param command + * absolute path to command. For ubuntu 10.04: + * /usr/bin/vboxwebsrv + * @throws IOException + * @throws InterruptedException + */ + private void startupVboxWebServer(String command) throws IOException, + InterruptedException { + // Disable login credential: $ + // rt.exec("VBoxManage setproperty websrvauthlibrary null"); + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + client.connect(); + ExecResponse response = client.exec("start " + command); + System.out.println(response.getOutput()); + } catch (Exception e) { + propagate(e); + } finally { + if (client != null) + client.disconnect(); + } + } + + @BeforeMethod + protected void setupManager() { + manager.connect(endpoint, identity, credential); + } + + @AfterMethod + protected void disconnectAndClenaupManager() throws RemoteException, + MalformedURLException { + manager.disconnect(); + manager.cleanup(); + } + + public void testCreateVirtualMachine() { + IMachine newVM = manager.getVBox().createMachine(settingsFile, vmName, + osTypeId, vmId, forceOverwrite); + manager.getVBox().registerMachine(newVM); + assertEquals(newVM.getName(), vmName); + } + + @Test(dependsOnMethods = "testCreateVirtualMachine") + public void testChangeRAM() { + Long memorySize = new Long(1024); + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.setMemorySize(memorySize); + mutable.saveSettings(); + session.unlockMachine(); + assertEquals(manager.getVBox().findMachine(vmName).getMemorySize(), + memorySize); + } + + @Test(dependsOnMethods = "testChangeRAM") + public void testCreateIDEController() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.addStorageController(controllerIDE, StorageBus.IDE); + mutable.saveSettings(); + session.unlockMachine(); + assertEquals(manager.getVBox().findMachine(vmName) + .getStorageControllers().size(), 1); + } + + @Test(dependsOnMethods = "testCreateIDEController") + public void testAttachIsoDvd() { + IMedium distroHD = manager.getVBox().openMedium(distroDvd, + DeviceType.DVD, AccessMode.ReadOnly); + + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.attachDevice(controllerIDE, 0, 0, DeviceType.DVD, distroHD); + mutable.saveSettings(); // write settings to xml + session.unlockMachine(); + assertEquals(distroHD.getId().equals(""), false); + } + + @Test(dependsOnMethods = "testAttachIsoDvd") + public void testCreateScsiController() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.addStorageController(controllerSATA, StorageBus.SATA); + mutable.saveSettings(); + session.unlockMachine(); + assertEquals(manager.getVBox().findMachine(vmName) + .getStorageControllers().size(), 2); + } + + @Test(dependsOnMethods = "testCreateScsiController") + public void testCreateAndAttachHardDisk() { + /* + * IMedium IVirtualBox::createHardDisk(String format, String location) + * If the format attribute is empty or null then the default + * storage format speci�ed by ISystemProperties::defaultHardDiskFormat will be used for creating + * a storage unit of the medium. + */ + /* + IMedium hd = manager.getVBox().createHardDisk(null, workingDir + File.separator + "disk.vdi"); + IProgress progress = hd.createBaseStorage(new Long(8*1024), new Long(MediumVariant.STANDARD.ordinal())); + */ + IMedium hd = null; + if(!new File(clonedDisk).exists()) { + hd = manager.getVBox().createHardDisk(diskFormat, clonedDisk); + long size = 120*1024*1024; + hd.createBaseStorage(new Long(size), new Long(MediumVariant.FIXED.ordinal())); + } else + hd = manager.getVBox().openMedium(clonedDisk, DeviceType.HardDisk, AccessMode.ReadWrite); + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.attachDevice(controllerSATA, 0, 0, DeviceType.HardDisk, hd); + mutable.saveSettings(); // write settings to xml + session.unlockMachine(); + assertEquals(hd.getId().equals(""), false); + } + + + @Test(dependsOnMethods = "testCreateAndAttachHardDisk") + public void testConfigureNIC() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + + + // network BRIDGED to access HTTP server + String hostInterface = null; + String command = vboxManageCommand + " list bridgedifs"; + try { + Process child = Runtime.getRuntime().exec(command); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(child.getInputStream())); + String line = ""; + boolean found = false; + + while ( (line = bufferedReader.readLine()) != null && !found){ + + if(line.split(":")[0].contains("Name") ){ + hostInterface = line.split(":")[1]; + } + if( line.split(":")[0].contains("Status") && line.split(":")[1].contains("Up") ){ + System.out.println("bridge: " + hostInterface.trim()); + found = true; + } + } + + // Bridged + /* + mutable.getNetworkAdapter(new Long(0)).attachToBridgedInterface(); + mutable.getNetworkAdapter(new Long(0)).setHostInterface(hostInterface.trim()); + mutable.getNetworkAdapter(new Long(0)).setEnabled(true); + */ + + // NAT + mutable.getNetworkAdapter(new Long(0)).attachToNAT(); + mutable.getNetworkAdapter(new Long(0)).setNATNetwork(""); + machine.getNetworkAdapter(new Long(0)).getNatDriver().addRedirect("guestssh", NATProtocol.TCP, "127.0.0.1", 2222, "", 22); + mutable.getNetworkAdapter(new Long(0)).setEnabled(true); + + mutable.saveSettings(); + session.unlockMachine(); + + + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + @Test(dependsOnMethods = "testConfigureNIC") + public void testStartVirtualMachine() { + IMachine machine = manager.getVBox().findMachine(vmName); + ISession session = manager.getSessionObject(); + launchVMProcess(machine, session); + assertEquals(machine.getState(), MachineState.Running); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + sendKeyboardSequence(keyboardSequence); + } + + @Test(dependsOnMethods = "testStartVirtualMachine") + public void testConfigureGuestAdditions() { + + // configure GA + IPSocket socket = new IPSocket("127.0.0.1", 2222); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(osUsername, osPassword)); + try { + client.connect(); + //Configure your system for building kernel modules by running + ExecResponse exec = client.exec("echo " + osPassword + " | " + "sudo -S m-a prepare -i"); + System.out.println(exec); + } finally { + if (client != null) { + client.disconnect(); + } + } + + + socketTester.apply(socket); + client = sshFactory.create(socket, new Credentials(osUsername, + osPassword)); + try { + client.connect(); + ExecResponse exec = client + .exec("echo " + osPassword + " | " + "sudo -S mount -o loop /usr/share/virtualbox/VBoxGuestAdditions.iso /mnt"); + System.out.println(exec); + exec = client.exec("echo " + osPassword + " | " + "sudo -S sh /mnt/VBoxLinuxAdditions.run"); + System.out.println(exec); + } finally { + if (client != null) + client.disconnect(); + } + } + + @Test(dependsOnMethods = "testConfigureGuestAdditions") + public void testStopVirtualMachine() { + IMachine machine = manager.getVBox().findMachine(vmName); + powerDownMachine(machine); + assertEquals(machine.getState(), MachineState.PoweredOff); + } + + /** + * @param machine + */ + private void powerDownMachine(IMachine machine) { + try { + ISession machineSession = manager.openMachineSession(machine); + IProgress progress = machineSession.getConsole().powerDown(); + progress.waitForCompletion(-1); + machineSession.unlockMachine(); + + while (!machine.getSessionState().equals(SessionState.Unlocked)) { + try { + System.out + .println("waiting for unlocking session - session state: " + + machine.getSessionState()); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test(dependsOnMethods = "testStopVirtualMachine") + public void cleanUp() throws IOException { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.getNetworkAdapter(new Long(0)).getNatDriver() + .removeRedirect("guestssh"); + // detach disk from controller + mutable.detachDevice(controllerIDE, 0, 0); + mutable.saveSettings(); + session.unlockMachine(); + + for (IStorageController storageController : machine + .getStorageControllers()) { + if (storageController.getName().equals(controllerSATA)) { + session = manager.getSessionObject(); + machine.lockMachine(session, LockType.Write); + + mutable = session.getMachine(); + mutable.detachDevice(storageController.getName(), 1, 1); + mutable.saveSettings(); + session.unlockMachine(); + } + } + } + + @AfterClass + void stopVboxWebServer() throws IOException { + // stop vbox web server + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials( + hostUsername, hostPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("pidof vboxwebsrv | xargs kill"); + System.out.println(exec.getOutput()); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + } + + /** + * + * @param workingDir + * @param vdiUrl + * @param proxy + * Proxy proxy , new Proxy(Proxy.Type.HTTP, new + * InetSocketAddress("localhost", 5865)); + * @return + * @throws Exception + */ + private File downloadFile(String sourceURL, String destinationDir, + String vboxGuestAdditionsName, Proxy proxy) throws Exception { + + String absolutePathName = destinationDir + File.separator + + vboxGuestAdditionsName; + File iso = new File(absolutePathName); + + final URL isoURL = new URL(sourceURL); + final HttpURLConnection uc = (HttpURLConnection) isoURL + .openConnection(); // isoURL.openConnection(proxy); + uc.connect(); + if (!iso.exists()) { + System.out.println("Start download " + sourceURL + " to " + + absolutePathName); + Files.copy(new InputSupplier() { + + @Override + public InputStream getInput() throws IOException { + return uc.getInputStream(); + } + + }, iso); + } + return iso; + } + + private void sendKeyboardSequence(String keyboardSequence) { + String[] sequenceSplited = keyboardSequence.split(" "); + /* + for (String word : sequenceSplited) { + + String converted = stringToKeycode(word); + for (String string : converted.split(" ")) { + try { + Thread.sleep(100); + Runtime.getRuntime().exec(vboxManageCommand + " controlvm " + vmName + " keyboardputscancode " + string); + Thread.sleep(50); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + */ + + for (String word : sequenceSplited) { + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + client.connect(); + String converted = stringToKeycode(word); + for (String string : converted.split(" ")) { + ExecResponse response = client.exec(vboxManageCommand + " controlvm " + vmName + " keyboardputscancode " + string); + System.out.println(response.getOutput()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + } + + } + + private String stringToKeycode(String s) { + + StringBuilder keycodes = new StringBuilder(); + for (String specialButton : KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.keySet()) { + if(s.startsWith(specialButton)) { + keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get(specialButton)); + return keycodes.toString(); + } + } + + int i = 0, j = 0; + while(i < s.length()) { + String digit = s.substring(i, i+1); + String hex = KeyboardScancodes.NORMAL_KEYBOARD_BUTTON_MAP.get(digit); + keycodes.append(hex + " "); + i++; + } + keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("") + " "); + + return keycodes.toString(); + } + + + /** + * + * @param machine + * @param session + */ + private void launchVMProcess(IMachine machine, ISession session) { + IProgress prog = machine.launchVMProcess(session, "gui", ""); + prog.waitForCompletion(-1); + session.unlockMachine(); + } +} diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationTest.java new file mode 100644 index 0000000000..961cf0806f --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxAdministrationTest.java @@ -0,0 +1,507 @@ +package org.jclouds.virtualbox.experiment; + +import static org.testng.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.URL; +import java.rmi.RemoteException; +import java.util.concurrent.TimeUnit; + +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.domain.Credentials; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.InetSocketAddressConnect; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.virtualbox_4_0.AccessMode; +import org.virtualbox_4_0.DeviceType; +import org.virtualbox_4_0.IMachine; +import org.virtualbox_4_0.IMedium; +import org.virtualbox_4_0.IProgress; +import org.virtualbox_4_0.ISession; +import org.virtualbox_4_0.IStorageController; +import org.virtualbox_4_0.LockType; +import org.virtualbox_4_0.MachineState; +import org.virtualbox_4_0.NATProtocol; +import org.virtualbox_4_0.SessionState; +import org.virtualbox_4_0.StorageBus; +import org.virtualbox_4_0.VirtualBoxManager; +import org.virtualbox_4_0.jaxws.MediumVariant; + +import com.google.common.base.Predicate; +import com.google.common.io.Files; +import com.google.common.io.InputSupplier; +import com.google.inject.Guice; +import com.google.inject.Injector; + +@Test(groups = "live", testName = "virtualbox.VirtualboxAdministrationTest") +public class VirtualboxAdministrationTest { + + protected String provider = "virtualbox"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + protected String vmName; + + VirtualBoxManager manager = VirtualBoxManager.createInstance(""); + + protected Injector injector; + protected Predicate socketTester; + protected SshClient.Factory sshFactory; + + protected String settingsFile; // Fully qualified path where the settings file should be created, or NULL for a default + // folder and file based on the name argument (see composeMachineFilename()). + + protected String osTypeId; // Guest OS Type ID. + protected String vmId; // Machine UUID (optional). + protected boolean forceOverwrite; // If true, an existing machine settings file will be overwritten. + + protected String osUsername; + protected String osPassword; + protected String controller; + protected String diskFormat; + + protected String workingDir; + protected String originalDisk; + protected String clonedDisk; + + protected String guestAdditionsDvdName; + private String vdiUrl; + private String gaIsoUrl; + private String vboxwebsrvStartCommand; +// private Process pr; + private String vdiName; + private String gaIsoName; + private String admin_pwd; + private String hostUsername; + private String hostPassword; + private String install7zip; + private String run7zip; + private String installVboxOse; + + /** + * + * + * + * @param workingDir + * @param vdiUrl + * @param proxy Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 5865)); + * @return + * @throws Exception + */ + private File downloadFile(String sourceURL, String destinationDir, String vboxGuestAdditionsName, Proxy proxy) throws Exception { + + String absolutePathName = destinationDir + File.separator + vboxGuestAdditionsName; + File iso = new File(absolutePathName); + + final URL isoURL = new URL(sourceURL); + final HttpURLConnection uc = (HttpURLConnection) isoURL.openConnection(); // isoURL.openConnection(proxy); + uc.connect(); + if(!iso.exists()) { + System.out.println("Start download " + sourceURL + " to " + absolutePathName); + Files.copy(new InputSupplier() { + + @Override + public InputStream getInput() throws IOException { + return uc.getInputStream(); + } + + }, iso); + } + return iso; + } + + protected void setupCredentials() { + identity = System.getProperty("test." + provider + ".identity", "administrator"); + credential = System.getProperty("test." + provider + ".credential", "12345"); + endpoint = System.getProperty("test." + provider + ".endpoint", "http://localhost:18083/"); + apiversion = System.getProperty("test." + provider + ".apiversion"); + } + + protected void setupConfigurationProperties() { + + admin_pwd = System.getProperty("test." + provider + ".admin_pwd", "password"); + // OS + osUsername = System.getProperty("test." + provider + ".osusername", "root"); + osPassword = System.getProperty("test." + provider + ".ospassword", "toortoor"); + controller = System.getProperty("test." + provider + ".controller", "IDE Controller"); + // Create disk If the @a format attribute is empty or null then the default storage format specified by ISystemProperties#defaultHardDiskFormat + diskFormat = System.getProperty("test." + provider + ".diskformat", ""); + + //VBOX + settingsFile = null; // Fully qualified path where the settings file should be created, or NULL for a default + // folder and file based on the name argument (see composeMachineFilename()). + + osTypeId = System.getProperty("test." + provider + ".osTypeId", ""); // Guest OS Type ID. + vmId = System.getProperty("test." + provider + ".vmId", null); // Machine UUID (optional). + forceOverwrite = true; // If true, an existing machine settings file will be overwritten. + vmName = System.getProperty("test." + provider + ".vmname", "jclouds-virtualbox-admin"); + + workingDir = System.getProperty("user.home") + File.separator + System.getProperty("test." + provider + ".workingDir", "jclouds-virtualbox-test") ; + if(new File(workingDir).mkdir()); + vdiName = System.getProperty("test." + provider + ".vdiName", "centos-5.2-x86.7z"); + vdiUrl = System.getProperty("test." + provider + ".vdiUrl", "http://leaseweb.dl.sourceforge.net/project/virtualboximage/CentOS/5.2/centos-5.2-x86.7z"); + gaIsoName = System.getProperty("test." + provider + ".gaIsoName", "VBoxGuestAdditions_4.0.2-update-69551.iso"); + gaIsoUrl = System.getProperty("test." + provider + ".gaIsoUrl", "http://download.virtualbox.org/virtualbox/4.0.2/VBoxGuestAdditions_4.0.2-update-69551.iso"); + vboxwebsrvStartCommand = System.getProperty("test." + provider + ".vboxwebsrvStartCommand","/usr/bin/vboxwebsrv"); + originalDisk = workingDir + File.separator + "VDI" +File.separator + System.getProperty("test." + provider + ".originalDisk", "centos-5.2-x86.vdi"); + clonedDisk = workingDir + File.separator + System.getProperty("test." + provider + ".clonedDisk", "template.vdi"); + guestAdditionsDvdName = workingDir + File.separator + System.getProperty("test." + provider + ".guestAdditionsDvdName", "VBoxGuestAdditions_4.0.2-update-69551.iso"); + + install7zip = System.getProperty("test." + provider + ".install7zip", "sudo -S apt-get --yes install p7zip"); + run7zip = System.getProperty("test." + provider + ".run7zip", "p7zip -d "); + installVboxOse = System.getProperty("test." + provider + ".installvboxose", "sudo -S apt-get --yes install virtualbox-ose"); + if(!new File(originalDisk).exists()) { + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + File vdi7z = downloadFile(vdiUrl, workingDir, vdiName, null); + client.connect(); + ExecResponse exec = client.exec("echo " + admin_pwd + " | " + install7zip + "; cd " + workingDir + "; " + run7zip + vdi7z.getName()); + System.out.println(exec); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + } + + if(!new File(guestAdditionsDvdName).exists()) { + try { + File gaIso = downloadFile(gaIsoUrl, workingDir, gaIsoName, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @BeforeGroups(groups = "live") + protected void setupClient() throws IOException, InterruptedException { + + hostUsername = System.getProperty("test." + provider + ".hostusername", "toor"); + hostPassword = System.getProperty("test." + provider + ".hostpassword", "password"); + + injector = Guice.createInjector(new JschSshClientModule(), + new Log4JLoggingModule()); + sshFactory = injector.getInstance(SshClient.Factory.class); + socketTester = new RetryablePredicate( + new InetSocketAddressConnect(), 3600, 1, TimeUnit.SECONDS); + injector.injectMembers(socketTester); + + setupCredentials(); + setupConfigurationProperties(); + + installVbox(); + // startup vbox web server + startupVboxWebServer(vboxwebsrvStartCommand); + } + + private void installVbox() throws IOException, InterruptedException { + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("echo " + hostPassword + " | " + installVboxOse); + System.out.println(exec); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + + } + + /** + * + * @param command absolute path to command. For ubuntu 10.04: /usr/bin/vboxwebsrv + * @throws IOException + * @throws InterruptedException + */ + private void startupVboxWebServer(String command) throws IOException, InterruptedException { + // Disable login credential: $ + //rt.exec("VBoxManage setproperty websrvauthlibrary null"); + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + client.connect(); + ExecResponse exec = client.exec(command + " --timeout 50000 -b"); + System.out.println(exec.getOutput()); + System.out.println(exec); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } + + } + + + @BeforeMethod + protected void setupManager() { + manager.connect(endpoint, identity, credential); + } + + @AfterMethod + protected void disconnectAndClenaupManager() throws RemoteException, + MalformedURLException { + manager.disconnect(); + manager.cleanup(); + } + + public void testCreateVirtualMachine() { + IMachine newVM = manager.getVBox().createMachine(settingsFile, vmName, + osTypeId, vmId, forceOverwrite); + manager.getVBox().registerMachine(newVM); + assertEquals(newVM.getName(), vmName); + } + + @Test(dependsOnMethods = "testCreateVirtualMachine") + public void testChangeRAM() { + Long memorySize = new Long(2048); + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.setMemorySize(memorySize); + mutable.saveSettings(); + session.unlockMachine(); + assertEquals(manager.getVBox().findMachine(vmName) + .getMemorySize(), memorySize); + } + + @Test(dependsOnMethods = "testChangeRAM") + public void testCreateDiskController() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.addStorageController(controller, StorageBus.IDE); + mutable.saveSettings(); + session.unlockMachine(); + assertEquals(manager.getVBox().findMachine(vmName) + .getStorageControllers().size(), 1); + } + + @Test(dependsOnMethods = "testCreateDiskController") + public void testCloneAndAttachHardDisk() { + IMedium hd = manager.getVBox().openMedium(originalDisk, + DeviceType.HardDisk, AccessMode.ReadOnly); + IMedium clonedHd = null; + if(!new File(clonedDisk).exists()) { + clonedHd = manager.getVBox().createHardDisk(diskFormat, clonedDisk); + IProgress cloning = hd.cloneTo(clonedHd, new Long(MediumVariant.VMDK_SPLIT_2_G.ordinal()), null); + cloning.waitForCompletion(-1); + } else + clonedHd = manager.getVBox().openMedium(clonedDisk, + DeviceType.HardDisk, AccessMode.ReadOnly); + + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.attachDevice(controller, 0, 0, DeviceType.HardDisk, clonedHd); + mutable.saveSettings(); // write settings to xml + session.unlockMachine(); + assertEquals(hd.getId().equals(""), false); + } + + @Test(dependsOnMethods = "testCloneAndAttachHardDisk") + public void testConfigureNIC() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + + /* + * NAT + */ + mutable.getNetworkAdapter(new Long(0)).attachToNAT(); + mutable.getNetworkAdapter(new Long(0)).setNATNetwork(""); + mutable.getNetworkAdapter(new Long(0)).setEnabled(true); + mutable.saveSettings(); + session.unlockMachine(); + + machine.lockMachine(session, LockType.Write); + mutable = session.getMachine(); + machine.getNetworkAdapter(new Long(0)) + .getNatDriver() + .addRedirect("guestssh", NATProtocol.TCP, "127.0.0.1", 2222, "", 22); + mutable.saveSettings(); + session.unlockMachine(); + } + + @Test(dependsOnMethods = "testConfigureNIC") + public void testAttachGuestAdditions() { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + + IMedium guestAdditionsDVD = manager.getVBox().openMedium(guestAdditionsDvdName, + DeviceType.DVD, AccessMode.ReadOnly); + for (IStorageController storageController : machine + .getStorageControllers()) { + // for DVD we choose IDE + if (storageController.getName().equals(controller)) { + + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + + // IDE secondary slave [1:1] + mutable.attachDevice(storageController.getName(), + new Integer(1), new Integer(1), DeviceType.DVD, + guestAdditionsDVD); + mutable.saveSettings(); + session.unlockMachine(); + } + } + } + + @Test(dependsOnMethods = "testAttachGuestAdditions") + public void testStartVirtualMachine() { + IMachine machine = manager.getVBox().findMachine(vmName); + ISession session = manager.getSessionObject(); + launchVMProcess(machine, session); + assertEquals(machine.getState(), MachineState.Running); + } + + /** + * + * @param machine + * @param session + */ + private void launchVMProcess(IMachine machine, ISession session) { + IProgress prog = machine.launchVMProcess(session, "gui", ""); + prog.waitForCompletion(-1); + session.unlockMachine(); + } + + @Test(dependsOnMethods = "testStartVirtualMachine") + public void testInstallGuestAdditionsThroughNATPortForwarding() { + + IPSocket socket = new IPSocket("127.0.0.1", 2222); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(osUsername, osPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("yum install gcc kernel kernel-devel -y"); + System.out.println(exec); + } finally { + if (client != null) + client.disconnect(); + } + + //manually restart + IMachine machine = manager.getVBox().findMachine(vmName); + powerDownMachine(machine); + launchVMProcess(machine, manager.getSessionObject()); + + socketTester.apply(socket); + client = sshFactory.create(socket, new Credentials(osUsername, osPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("mkdir -p /media/cdrom; mount /dev/cdrom /media/cdrom; sh /media/cdrom/VBoxLinuxAdditions.run --nox11 force"); + System.out.println(exec); + exec = client.exec("echo '/usr/sbin/VBoxService' >> /etc/rc.d/rc.local"); + System.out.println(exec); + } finally { + if (client != null) + client.disconnect(); + } + } + + @Test(dependsOnMethods = "testInstallGuestAdditionsThroughNATPortForwarding") + public void testStopVirtualMachine() { + IMachine machine = manager.getVBox().findMachine(vmName); + powerDownMachine(machine); + assertEquals(machine.getState(), MachineState.PoweredOff); + } + + /** + * @param machine + */ + private void powerDownMachine(IMachine machine) { + try { + ISession machineSession = manager.openMachineSession(machine); + IProgress progress = machineSession.getConsole().powerDown(); + progress.waitForCompletion(-1); + machineSession.unlockMachine(); + + while(!machine.getSessionState().equals(SessionState.Unlocked)){ + try { + System.out.println("waiting for unlocking session - session state: " + machine.getSessionState()); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test(dependsOnMethods = "testStopVirtualMachine") + public void cleanUp() throws IOException { + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(vmName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + mutable.getNetworkAdapter(new Long(0)).getNatDriver().removeRedirect("guestssh"); + // detach disk from controller + mutable.detachDevice(controller, 0, 0); + mutable.saveSettings(); + session.unlockMachine(); + + for (IStorageController storageController : machine + .getStorageControllers()) { + if (storageController.getName().equals(controller)) { + session = manager.getSessionObject(); + machine.lockMachine(session, LockType.Write); + + mutable = session.getMachine(); + mutable.detachDevice(storageController.getName(), 1, 1); + mutable.saveSettings(); + session.unlockMachine(); + } + } + } + + @AfterClass + void stopVboxWebServer() throws IOException{ + // stop vbox web server + IPSocket socket = new IPSocket("127.0.0.1", 22); + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials(hostUsername, hostPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("pidof vboxwebsrv | xargs kill"); + System.out.println(exec.getOutput()); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (client != null) + client.disconnect(); + } } +} \ No newline at end of file diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java new file mode 100644 index 0000000000..8773f5c97a --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/VirtualboxLiveTest.java @@ -0,0 +1,305 @@ +package org.jclouds.virtualbox.experiment; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.rmi.RemoteException; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +import org.jclouds.compute.domain.ExecResponse; +import org.jclouds.domain.Credentials; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.net.IPSocket; +import org.jclouds.predicates.InetSocketAddressConnect; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.ssh.SshClient; +import org.jclouds.ssh.jsch.config.JschSshClientModule; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.virtualbox_4_0.AccessMode; +import org.virtualbox_4_0.DeviceType; +import org.virtualbox_4_0.IMachine; +import org.virtualbox_4_0.IMedium; +import org.virtualbox_4_0.IProgress; +import org.virtualbox_4_0.ISession; +import org.virtualbox_4_0.LockType; +import org.virtualbox_4_0.MachineState; +import org.virtualbox_4_0.MediumType; +import org.virtualbox_4_0.SessionState; +import org.virtualbox_4_0.StorageBus; +import org.virtualbox_4_0.VirtualBoxManager; +import org.virtualbox_4_0.jaxws.MediumState; +import org.virtualbox_4_0.jaxws.MediumVariant; + +import com.google.common.base.Predicate; +import com.google.inject.Guice; +import com.google.inject.Injector; + +@Test(groups = "live", testName = "virtualbox.VirtualboxLiveTest") +public class VirtualboxLiveTest { + + protected String provider = "virtualbox"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + protected String vmName; + + VirtualBoxManager manager = VirtualBoxManager.createInstance(null); + + protected Injector injector; + protected Predicate socketTester; + protected SshClient.Factory sshFactory; + + protected String osUsername; + protected String osPassword; + protected String controller; + protected String diskFormat; + + protected String settingsFile; // Fully qualified path where the settings file should be created, or NULL for a default + // folder and file based on the name argument (see composeMachineFilename()). + + protected String osTypeId; // Guest OS Type ID. + protected String vmId; // Machine UUID (optional). + protected boolean forceOverwrite; // If true, an existing machine settings file will be overwritten. + + protected String workingDir; + protected String originalDiskPath; + protected String clonedDiskPath; + + // Create disk If the @a format attribute is empty or null then the default + // storage format specified by ISystemProperties#defaultHardDiskFormat + String format = "vdi"; + + protected int numberOfVirtualMachine ; + + @BeforeClass + protected void setupConfigurationProperties() { + //VBOX + settingsFile = null; // Fully qualified path where the settings file should be created, or NULL for a default + // folder and file based on the name argument (see composeMachineFilename()). + osTypeId = System.getProperty("test." + provider + ".osTypeId", ""); // Guest OS Type ID. + vmId = System.getProperty("test." + provider + ".vmId", null); // Machine UUID (optional). + forceOverwrite = true; // If true, an existing machine settings file will be overwritten. + + // OS specific information + vmName = checkNotNull(System.getProperty("test." + provider + ".vmname")); + osUsername = System.getProperty("test." + provider + ".osusername", "root"); + osPassword = System.getProperty("test." + provider + ".ospassword", "toortoor"); + controller = System.getProperty("test." + provider + ".controller", "IDE Controller"); + diskFormat = System.getProperty("test." + provider + ".diskformat", ""); + + workingDir = checkNotNull( + System.getProperty("test." + provider + ".workingDir")); + + originalDiskPath = workingDir + + File.separator + + checkNotNull(System.getProperty("test." + provider + + ".originalDisk")); + + numberOfVirtualMachine = Integer.parseInt(checkNotNull(System.getProperty("test." + provider + ".numberOfVirtualMachine"))); + } + + + @BeforeClass + protected void setupCredentials() throws RemoteException, + MalformedURLException { + identity = System.getProperty("test." + provider + ".identity", "administrator"); + credential = System.getProperty("test." + provider + ".credential", "12345"); + endpoint = System.getProperty("test." + provider + ".endpoint", "http://localhost:18083/"); + apiversion = System.getProperty("test." + provider + ".apiversion"); + + injector = Guice.createInjector(new JschSshClientModule(), + new Log4JLoggingModule()); + sshFactory = injector.getInstance(SshClient.Factory.class); + socketTester = new RetryablePredicate( + new InetSocketAddressConnect(), 180, 1, TimeUnit.SECONDS); + injector.injectMembers(socketTester); + } + + @BeforeMethod + protected void setupManager() throws RemoteException, MalformedURLException { + manager.connect(endpoint, identity, credential); + } + + @AfterMethod + protected void disconnectAndClenaupManager() throws RemoteException, + MalformedURLException { + manager.disconnect(); + manager.cleanup(); + } + + @Test + public void testStartVirtualMachines() { + IMedium clonedHd = cloneDisk(MediumType.MultiAttach); + for (int i = 1; i < numberOfVirtualMachine + 1; i++) { + createVirtualMachine(i, clonedHd); + } + } + + private void createVirtualMachine(int i, IMedium clonedHd) { + + String instanceName = vmName + "_" + i; + + IMachine newVM = manager.getVBox().createMachine(settingsFile, instanceName, osTypeId, vmId, forceOverwrite); + manager.getVBox().registerMachine(newVM); + + ISession session = manager.getSessionObject(); + IMachine machine = manager.getVBox().findMachine(instanceName); + machine.lockMachine(session, LockType.Write); + IMachine mutable = session.getMachine(); + + // disk + mutable.addStorageController(controller, StorageBus.IDE); + mutable.attachDevice(controller, 0, 0, DeviceType.HardDisk, clonedHd); + + // network + String hostInterface = null; + String command = "vboxmanage list bridgedifs"; + try { + Process child = Runtime.getRuntime().exec(command); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(child.getInputStream())); + String line = ""; + boolean found = false; + + while ( (line = bufferedReader.readLine()) != null && !found){ + + if(line.split(":")[0].contains("Name") ){ + hostInterface = line.split(":")[1]; + } + if( line.split(":")[0].contains("Status") && line.split(":")[1].contains("Up") ){ + System.out.println("bridge: " + hostInterface.trim()); + found = true; + } + + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + mutable.getNetworkAdapter(new Long(0)).attachToBridgedInterface(); + mutable.getNetworkAdapter(new Long(0)).setHostInterface(hostInterface.trim()); + mutable.getNetworkAdapter(new Long(0)).setEnabled(true); + + mutable.saveSettings(); + session.unlockMachine(); + } + + + /** + * @param instanceName + * @return + */ + private IMedium cloneDisk(MediumType mediumType) { + + String clonedDisk = System.getProperty("test." + provider + ".clonedDisk"); + String instanceClonedDisk = clonedDisk.split("\\.")[0] + "." +clonedDisk.split("\\.")[1]; + clonedDiskPath = workingDir + File.separator + instanceClonedDisk; + + // use template disk in multiattach mode + IMedium clonedHd = manager.getVBox().openMedium(originalDiskPath, DeviceType.HardDisk, AccessMode.ReadOnly); + + System.out.println("cloned HD state: " + clonedHd.getState()); + /* + An image in multiattach mode can be attached to more than one virtual machine at the same time, + even if these machines are running simultaneously. For each virtual machine to which such an image is attached, a differencing image + is created. As a result, data that is written to such a virtual disk by one machine is not + seen by the other machines to which the image is attached; each machine creates its own write history of the multiattach image. + */ + while(clonedHd.getState().equals(MediumState.NOT_CREATED)) { + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + clonedHd.setType(mediumType); + return clonedHd; + } + + private void launchVMProcess(IMachine machine, ISession session) { + IProgress prog = machine.launchVMProcess(session, "gui", ""); + prog.waitForCompletion(-1); + session.unlockMachine(); + } + + protected void checkSSH(IPSocket socket) { + socketTester.apply(socket); + SshClient client = sshFactory.create(socket, new Credentials( + osUsername, osPassword)); + try { + client.connect(); + ExecResponse exec = client.exec("touch /tmp/hello_" + System.currentTimeMillis()); + exec = client.exec("echo hello"); + System.out.println(exec); + assertEquals(exec.getOutput().trim(), "hello"); + } finally { + if (client != null) + client.disconnect(); + } + } + + @Test(dependsOnMethods = "testStartVirtualMachines") + public void testSshLogin() { + String ipAddress = null; + for (int i = 1; i < numberOfVirtualMachine +1; i++) { + String instanceName = vmName + "_" + i; + IMachine machine = manager.getVBox().findMachine(instanceName); + + System.out.println("\nLaunch VM named " + machine.getName() + " ..."); + launchVMProcess(machine, manager.getSessionObject()); + + while(ipAddress==null || ipAddress.equals("")){ + try { + ipAddress = machine.getGuestPropertyValue("/VirtualBox/GuestInfo/Net/0/V4/IP"); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("VM " + instanceName + " started with IP " + ipAddress); + IPSocket socket = new IPSocket(ipAddress, 22); + + System.out.println("Check SSH for " + instanceName + " ..."); + checkSSH(socket); + } + } + + @Test(dependsOnMethods = "testSshLogin") + public void testStopVirtualMachine() { + for (int i = 1; i < numberOfVirtualMachine + 1; i++) { + String instanceName = vmName + "_" + i; + IMachine machine = manager.getVBox().findMachine(instanceName); + + try { + ISession machineSession = manager.openMachineSession(machine); + IProgress progress = machineSession.getConsole().powerDown(); + progress.waitForCompletion(-1); + machineSession.unlockMachine(); + + + while(!machine.getSessionState().equals(SessionState.Unlocked)){ + try { + System.out.println("waiting for unlocking session - session state: " + machine.getSessionState()); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + assertEquals(machine.getState(), MachineState.PoweredOff); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/settings/KeyboardScancodes.java b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/settings/KeyboardScancodes.java new file mode 100644 index 0000000000..d369d03efd --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/java/org/jclouds/virtualbox/experiment/settings/KeyboardScancodes.java @@ -0,0 +1,143 @@ +package org.jclouds.virtualbox.experiment.settings; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class KeyboardScancodes { + + // http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html + + public static final Map NORMAL_KEYBOARD_BUTTON_MAP = createMap(); + public static final Map SPECIAL_KEYBOARD_BUTTON_MAP = createSpecialMap(); + + private static Map createMap() { + Map alphaToHex = new HashMap(); + alphaToHex.put("1", "02 82"); + alphaToHex.put("2", "03 83"); + alphaToHex.put("3", "04 84"); + alphaToHex.put("4", "05 85"); + alphaToHex.put("5", "06 86"); + alphaToHex.put("6", "07 87"); + alphaToHex.put("7", "08 88"); + alphaToHex.put("8", "09 89"); + alphaToHex.put("9", "0a 8a"); + alphaToHex.put("0", "0b 8b"); + alphaToHex.put("-", "0c 8c"); + alphaToHex.put("=", "0d 8d"); + alphaToHex.put("Tab", "0f 8f"); + alphaToHex.put("q", "10 90"); + alphaToHex.put("w", "11 91"); + alphaToHex.put("e", "12 92"); + alphaToHex.put("r", "13 93"); + alphaToHex.put("t", "14 94"); + alphaToHex.put("y", "15 95"); + alphaToHex.put("u", "16 96"); + alphaToHex.put("i", "17 97"); + alphaToHex.put("o", "18 98"); + alphaToHex.put("p", "19 99"); + + alphaToHex.put("Q", "2a 10 aa"); + alphaToHex.put("W", "2a 11 aa"); + alphaToHex.put("E", "2a 12 aa"); + alphaToHex.put("R", "2a 13 aa"); + alphaToHex.put("T", "2a 14 aa"); + alphaToHex.put("Y", "2a 15 aa"); + alphaToHex.put("U", "2a 16 aa"); + alphaToHex.put("I", "2a 17 aa"); + alphaToHex.put("O", "2a 18 aa"); + alphaToHex.put("P", "2a 19 aa"); + + alphaToHex.put("a", "1e 9e"); + alphaToHex.put("s", "1f 9f"); + alphaToHex.put("d", "20 a0"); + alphaToHex.put("f", "21 a1"); + alphaToHex.put("g", "22 a2"); + alphaToHex.put("h", "23 a3"); + alphaToHex.put("j", "24 a4"); + alphaToHex.put("k", "25 a5"); + alphaToHex.put("l", "26 a6"); + + alphaToHex.put("A", "2a 1e aa 9e"); + alphaToHex.put("S", "2a 1f aa 9f"); + alphaToHex.put("D", "2a 20 aa a0"); + alphaToHex.put("F", "2a 21 aa a1"); + alphaToHex.put("G", "2a 22 aa a2"); + alphaToHex.put("H", "2a 23 aa a3"); + alphaToHex.put("J", "2a 24 aa a4"); + alphaToHex.put("K", "2a 25 aa a5"); + alphaToHex.put("L", "2a 26 aa a6"); + + alphaToHex.put(");", "27 a7"); + alphaToHex.put("\"", "2a 28 aa a8"); + alphaToHex.put("\"", "28 a8"); + alphaToHex.put("\\", "2b ab"); + alphaToHex.put("|", "2a 2b aa 8b"); + alphaToHex.put("[", "1a 9a"); + alphaToHex.put("", "1b 9b"); + alphaToHex.put("<", "2a 33 aa b3"); + alphaToHex.put(">", "2a 34 aa b4"); + alphaToHex.put("$", "2a 05 aa 85"); + alphaToHex.put("+", "2a 0d aa 8d"); + + alphaToHex.put("z", "2c ac"); + alphaToHex.put("x", "2d ad"); + alphaToHex.put("c", "2e ae"); + alphaToHex.put("v", "2f af"); + alphaToHex.put("b", "30 b0"); + alphaToHex.put("n", "31 b1"); + alphaToHex.put("m", "32 b2"); + alphaToHex.put("Z", "2a 2c aa ac"); + alphaToHex.put("X", "2a 2d aa ad"); + alphaToHex.put("C", "2a 2e aa ae"); + alphaToHex.put("V", "2a 2f aa af"); + alphaToHex.put("B", "2a 30 aa b0"); + alphaToHex.put("N", "2a 31 aa b1"); + alphaToHex.put("M", "2a 32 aa b2"); + + alphaToHex.put(",", "33 b3"); + alphaToHex.put(".", "34 b4"); + alphaToHex.put("/", "35 b5"); + alphaToHex.put(":", "2a 27 aa a7"); + alphaToHex.put("%", "2a 06 aa 86"); + alphaToHex.put("_", "2a 0c aa 8c"); + alphaToHex.put("&", "2a 08 aa 88"); + alphaToHex.put("(", "2a 0a aa 8a"); + alphaToHex.put(")", "2a 0b aa 8b"); + return Collections.unmodifiableMap(alphaToHex); + } + private static Map createSpecialMap() { + Map special = new HashMap(); + special.put("", "1c 9c"); + special.put("", "0e 8e"); + special.put("", "39 b9"); + special.put("", "1c 9c"); + special.put("", "01 81"); + special.put("", "0f 8f"); + special.put("", "1d 38 0e"); + special.put("", "wait"); + + special.put("", "48 c8"); + special.put("", "50 d0"); + special.put("", "49 c9"); + special.put("", "51 d1"); + special.put("", "4f cf"); + special.put("", "52 d2"); + special.put("", "53 d3"); + special.put("", "4b cb"); + special.put("", "4d cd"); + special.put("", "47 c7"); + + special.put("", "3b"); + special.put("", "3c"); + special.put("", "3d"); + special.put("", "3e"); + special.put("", "3f"); + special.put("", "40"); + special.put("", "41"); + special.put("", "42"); + special.put("", "43"); + special.put("", "44"); + return Collections.unmodifiableMap(special); + } +} diff --git a/sandbox-apis/virtualbox/src/test/resources/preseed.cfg b/sandbox-apis/virtualbox/src/test/resources/preseed.cfg new file mode 100644 index 0000000000..d0ad769579 --- /dev/null +++ b/sandbox-apis/virtualbox/src/test/resources/preseed.cfg @@ -0,0 +1,87 @@ +## Options to set on the command line +d-i debian-installer/locale string en_US.utf8 +d-i console-setup/ask_detect boolean false +d-i console-setup/layout string USA + +#d-i netcfg/get_hostname string dummy +d-i netcfg/get_hostname string unassigned-hostname +d-i netcfg/get_domain string unassigned-domain + +# Continue without a default route +# Not working , specify a dummy in the DHCP +#d-i netcfg/no_default_route boolean + +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 base-installer/kernel/override-image string linux-image-2.6.32-21-generic + +# Choices: Dialog, Readline, Gnome, Kde, Editor, Noninteractive +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 regular +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 + +#http://ubuntu-virginia.ubuntuforums.org/showthread.php?p=9626883 +#Message: "write the changes to disk and configure lvm preseed" +#http://serverfault.com/questions/189328/ubuntu-kickstart-installation-using-lvm-waits-for-input +#preseed partman-lvm/confirm_nooverwrite 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 + +## minimum is puppet and ssh and ntp +# Individual additional packages to install +d-i pkgsel/include string openssh-server ntp dkms virtualbox-guest-additions build-essential module-assistant + +# 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 +#d-i apt-setup/use_mirror boolean true +#d-i mirror/country string manual +#choose-mirror-bin mirror/protocol string http +#choose-mirror-bin mirror/http/hostname string 192.168.4.150 +#choose-mirror-bin mirror/http/directory string /ubuntu +#choose-mirror-bin mirror/suite select maverick +#d-i debian-installer/allow_unauthenticated string true + +choose-mirror-bin mirror/http/proxy string \ No newline at end of file