org.jclouds.virtualbox.experiment.KickstartTest2.java Source code

Java tutorial

Introduction

Here is the source code for org.jclouds.virtualbox.experiment.KickstartTest2.java

Source

/*
 * *
 *  * 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.experiment;

import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
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.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.logging.Logger;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.InetSocketAddressConnect;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.scriptbuilder.domain.OsFamily;
import org.jclouds.scriptbuilder.statements.login.AdminAccess;
import org.jclouds.scriptbuilder.statements.login.DefaultConfiguration;
import org.jclouds.ssh.SshException;
import org.jclouds.virtualbox.experiment.settings.KeyboardScancodes;
import org.jclouds.virtualbox.functions.IMachineToNodeMetadata;
import org.virtualbox_4_1.*;

import java.io.*;
import java.net.URI;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkNotNull;
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.wrapInInitScript;

public class KickstartTest2 {

    protected String provider = "virtualbox";
    protected String identity;
    protected String credential;
    protected URI endpoint;
    protected String apiVersion;
    protected String vmName;

    protected Predicate<IPSocket> socketTester;

    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 diskFormat;

    protected String workingDir;
    protected String adminDisk;

    protected String guestAdditionsDvd;
    private URI gaIsoUrl;

    private String gaIsoName;
    private URI distroIsoUrl;
    private String distroIsoName;
    private String controllerIDE;
    private String keyboardSequence;

    private ComputeServiceContext context;
    private String hostId = "host";
    private String guestId = "guest";
    private String majorVersion;
    private URI vboxDmg;
    private String vboxVersionName;
    private String snapshotDescription;

    protected void setupCredentials() {
        identity = System.getProperty("test." + provider + ".identity", "administrator");
        credential = System.getProperty("test." + provider + ".credential", "12345");
        endpoint = URI.create(System.getProperty("test." + provider + ".endpoint", "http://localhost:18083/"));
        apiVersion = System.getProperty("test." + provider + ".apiversion", "4.1.2r73507");
        majorVersion = Iterables.get(Splitter.on('r').split(apiVersion), 0);
    }

    protected Logger logger() {
        return context.utils().loggerFactory().getLogger("jclouds.compute");
    }

    protected void setupConfigurationProperties() {

        controllerIDE = System.getProperty("test." + provider + ".controllerIde", "IDE Controller");
        diskFormat = System.getProperty("test." + provider + ".diskformat", "");

        // VBOX
        settingsFile = null;
        osTypeId = System.getProperty("test." + provider + ".osTypeId", "");
        vmId = System.getProperty("test." + provider + ".vmId", null);
        forceOverwrite = true;
        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");

        gaIsoName = System.getProperty("test." + provider + ".gaIsoName",
                "VBoxGuestAdditions_" + majorVersion + ".iso");
        gaIsoUrl = URI.create(System.getProperty("test." + provider + ".gaIsoUrl",
                "http://download.virtualbox.org/virtualbox/" + majorVersion + "/" + gaIsoName));

        distroIsoName = System.getProperty("test." + provider + ".distroIsoName", "ubuntu-11.04-server-i386.iso");
        distroIsoUrl = URI.create(System.getProperty("test." + provider + ".distroIsoUrl",
                "http://releases.ubuntu.com/11.04/ubuntu-11.04-server-i386.iso"));
        vboxDmg = URI.create(System.getProperty("test." + provider + ".vboxDmg",
                "http://download.virtualbox.org/virtualbox/4.1.2/VirtualBox-4.1.2-73507-OSX.dmg"));
        vboxVersionName = System.getProperty("test" + provider + ".vboxVersionName",
                "VirtualBox-4.1.2-73507-OSX.dmg");

        adminDisk = workingDir + File.separator
                + System.getProperty("test." + provider + ".adminDisk", "admin.vdi");
        guestAdditionsDvd = workingDir + File.separator + System.getProperty(
                "test." + provider + ".guestAdditionsDvd", "VBoxGuestAdditions_" + majorVersion + ".iso");

        snapshotDescription = System.getProperty("test." + provider + "snapshotdescription",
                "jclouds-virtualbox-snaphot");

        keyboardSequence = System.getProperty("test." + provider + ".keyboardSequence", "<Esc><Esc><Enter> "
                + "/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 " + "hostname=" + vmName + " "
                + "fb=false debconf/frontend=noninteractive "
                + "keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
                + "initrd=/install/initrd.gz -- <Enter>");

    }

    public static void main(String[] args) {

        KickstartTest2 test = new KickstartTest2();

        try {
            test.runAll();
            System.out.println("over");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private void runAll() throws Exception {
        context = TestUtils.computeServiceForLocalhostAndGuest();
        socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 130, 10, TimeUnit.SECONDS);

        setupCredentials();
        setupConfigurationProperties();

        downloadFileUnlessPresent(distroIsoUrl, workingDir, distroIsoName);
        downloadFileUnlessPresent(gaIsoUrl, workingDir, gaIsoName);

        installVbox();
        configureJettyServer();

        VirtualBoxManager manager = VirtualBoxManager.createInstance("mach");
        manager.connect(endpoint.toASCIIString(), identity, credential);

        // Create machine
        IMachine newVM = manager.getVBox().createMachine(settingsFile, vmName, osTypeId, "host", forceOverwrite);
        manager.getVBox().registerMachine(newVM);

        // Change RAM
        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();

        logMachineStatus(machine);

        // Create IDE Controller
        machine.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        mutable.addStorageController(controllerIDE, StorageBus.IDE);
        mutable.saveSettings();
        session.unlockMachine();

        IMedium hd = null;
        if (!new File(adminDisk).exists()) {
            hd = manager.getVBox().createHardDisk(diskFormat, adminDisk);
            long size = 4L * 1024L * 1024L * 1024L - 4L;
            IProgress progress = hd.createBaseStorage(new Long(size),
                    new Long(org.virtualbox_4_1.jaxws.MediumVariant.STANDARD.ordinal()));
            System.out.println(progress);
        } else {
            // TODO disk already exist: open it
        }

        logMachineStatus(machine);

        // Attach ISO DVD
        IMedium distroMedium = manager.getVBox().openMedium(workingDir + "/" + distroIsoName, DeviceType.DVD,
                AccessMode.ReadOnly, forceOverwrite);

        machine.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        mutable.attachDevice(controllerIDE, 0, 0, DeviceType.DVD, distroMedium);
        mutable.saveSettings();
        session.unlockMachine();

        logger().debug("Setting up hard drive...");
        // Create and attach hard disk
        machine.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        mutable.attachDevice(controllerIDE, 0, 1, DeviceType.HardDisk, hd);
        mutable.saveSettings();
        session.unlockMachine();

        logMachineStatus(machine);

        // Configure NIC
        // NAT
        logger().debug("Configuring NIC...");
        machine.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        mutable.getNetworkAdapter(new Long(0)).setAttachmentType(NetworkAttachmentType.NAT);
        mutable.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();

        // Attach guest additions
        logger().debug("Attaching guest additions medium...");
        distroMedium = manager.getVBox().openMedium(guestAdditionsDvd, DeviceType.DVD, AccessMode.ReadOnly,
                forceOverwrite);
        machine.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        mutable.attachDevice(controllerIDE, 1, 1, DeviceType.DVD, distroMedium);
        mutable.saveSettings();
        session.unlockMachine();

        // Start virtual machine
        logMachineStatus(machine);
        logger().debug("Starting virtual machine...");
        IProgress prog = machine.launchVMProcess(session, "gui", "");
        prog.waitForCompletion(-1);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            propagate(e);
        }

        logMachineStatus(machine);
        sendKeyboardSequence(keyboardSequence);

        // test if the sshd on the guest is ready and meanwhile apply AdminAccess
        boolean sshDeamonIsRunning = false;
        while (!sshDeamonIsRunning) {
            try {
                AdminAccess.standard().init(new DefaultConfiguration()).render(OsFamily.UNIX);
                if (runScriptOnNode(guestId, "id").getExitCode() == 0)
                    sshDeamonIsRunning = true;
            } catch (SshException e) {
                System.err.println("connection reset");
            }
        }

        logMachineStatus(machine);
        logger().debug("Configuring guest additions...");

        // Configure guest additions
        // TODO generalize
        if (isUbuntu(guestId)) {
            runScriptOnNode(guestId, "rm /etc/udev/rules.d/70-persistent-net.rules");
            runScriptOnNode(guestId, "mkdir /etc/udev/rules.d/70-persistent-net.rules");
            runScriptOnNode(guestId, "rm -rf /dev/.udev/");
            runScriptOnNode(guestId, "rm /lib/udev/rules.d/75-persistent-net-generator.rules");
            runScriptOnNode(guestId, "echo 0 | tee /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts");
        }

        logMachineStatus(machine);
        logger().debug("Powering down...");

        machine = manager.getVBox().findMachine(vmName);
        try {
            session = manager.getSessionObject();
            IProgress progress = session.getConsole().powerDown();
            progress.waitForCompletion(-1);
            session.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) {
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        logMachineStatus(machine);
        logger().debug("Changing to bridged networking...");

        session = manager.getSessionObject();
        IMachine adminNode = manager.getVBox().findMachine(vmName);
        adminNode.lockMachine(session, LockType.Write);
        mutable = session.getMachine();
        // 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)).setAttachmentType(NetworkAttachmentType.Bridged);
        mutable.getNetworkAdapter(new Long(0)).setAdapterType(NetworkAdapterType.Am79C973);
        mutable.getNetworkAdapter(new Long(0)).setMACAddress(manager.getVBox().getHost().generateMACAddress());
        mutable.getNetworkAdapter(new Long(0)).setBridgedInterface(hostInterface.trim());
        mutable.getNetworkAdapter(new Long(0)).setEnabled(true);
        mutable.saveSettings();
        session.unlockMachine();

        logMachineStatus(machine);
        logger().debug("Taking snapshot of machine...");
        session = manager.getSessionObject();
        machine.lockMachine(session, LockType.Write);
        if (machine.getCurrentSnapshot() == null
                || !machine.getCurrentSnapshot().getDescription().equals(snapshotDescription)) {
            manager.getSessionObject().getConsole().takeSnapshot(machine.getId(), snapshotDescription);
        }
        session.unlockMachine();

        logMachineStatus(machine);
        manager.disconnect();
        manager.cleanup();

    }

    private void logMachineStatus(IMachine machine) {
        NodeMetadata node = new IMachineToNodeMetadata().apply(machine);
        logger().debug("Machine status: " + node.toString());
    }

    void installVbox() throws Exception {
        if (runScriptOnNode(hostId, "VBoxManage --version", runAsRoot(false).wrapInInitScript(false))
                .getExitCode() != 0) {
            logger().debug("installing virtualbox");
            if (isOSX(hostId)) {
                downloadFileUnlessPresent(vboxDmg, workingDir, vboxVersionName);
                runScriptOnNode(hostId, "hdiutil attach " + workingDir + "/" + vboxVersionName);
                runScriptOnNode(hostId,
                        "installer -pkg /Volumes/VirtualBox/VirtualBox.mpkg -target /Volumes/Macintosh\\ HD");
            } else {
                // TODO other platforms
                runScriptOnNode(hostId, "cat > /etc/apt/sources.list.d/TODO");
                runScriptOnNode(hostId,
                        "wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc -O- | apt-key add -");
                runScriptOnNode(hostId, "apt-get update");
                runScriptOnNode(hostId, "apt-get --yes install virtualbox-4.1");
            }
        }
    }

    private Server 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(".");
        logger().info("serving " + resource_handler.getBaseResource());

        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
        server.setHandler(handlers);

        server.start();
        return server;
    }

    protected boolean isOSX(String id) {
        return context.getComputeService().getNodeMetadata(hostId).getOperatingSystem().getDescription()
                .equals("Mac OS X");
    }

    protected boolean isUbuntu(String id) {
        return context.getComputeService().getNodeMetadata(id).getOperatingSystem().getDescription()
                .contains("ubuntu");
    }

    protected ExecResponse runScriptOnNode(String nodeId, String command, RunScriptOptions options) {
        ExecResponse toReturn = context.getComputeService().runScriptOnNode(nodeId, command, options);
        assert toReturn.getExitCode() == 0 : toReturn;
        return toReturn;
    }

    protected ExecResponse runScriptOnNode(String nodeId, String command) {
        return runScriptOnNode(nodeId, command, wrapInInitScript(false));
    }

    private File downloadFileUnlessPresent(URI sourceURL, String destinationDir, String filename) throws Exception {

        File iso = new File(destinationDir, filename);

        if (!iso.exists()) {
            InputStream is = context.utils().http().get(sourceURL);
            checkNotNull(is, "%s not found", sourceURL);
            try {
                ByteStreams.copy(is, new FileOutputStream(iso));
            } finally {
                Closeables.closeQuietly(is);
            }
        }
        return iso;
    }

    private void sendKeyboardSequence(String keyboardSequence) throws InterruptedException {
        String[] sequenceSplited = keyboardSequence.split(" ");
        StringBuilder sb = new StringBuilder();
        for (String line : sequenceSplited) {
            String converted = stringToKeycode(line);
            for (String word : converted.split("  ")) {
                sb.append("vboxmanage controlvm " + vmName + " keyboardputscancode " + word + "; ");

                if (word.endsWith(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Enter>"))) {
                    runScriptOnNode(hostId, sb.toString(), runAsRoot(false).wrapInInitScript(false));
                    sb.delete(0, sb.length() - 1);
                }

                if (word.endsWith(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Return>"))) {
                    runScriptOnNode(hostId, sb.toString(), runAsRoot(false).wrapInInitScript(false));
                    sb.delete(0, sb.length() - 1);
                }

            }
        }
    }

    private String stringToKeycode(String s) {
        StringBuilder keycodes = new StringBuilder();
        if (s.startsWith("<")) {
            String[] specials = s.split("<");
            for (int i = 1; i < specials.length; i++) {
                keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<" + specials[i]) + "  ");
            }
            return keycodes.toString();
        }

        int i = 0;
        while (i < s.length()) {
            String digit = s.substring(i, i + 1);
            String hex = KeyboardScancodes.NORMAL_KEYBOARD_BUTTON_MAP.get(digit);
            keycodes.append(hex + " ");
            if (i != 0 && i % 14 == 0)
                keycodes.append(" ");
            i++;
        }
        keycodes.append(KeyboardScancodes.SPECIAL_KEYBOARD_BUTTON_MAP.get("<Spacebar>") + " ");

        return keycodes.toString();
    }

}