Package com.alexecollins.vbox.core.task

Source Code of com.alexecollins.vbox.core.task.Create

package com.alexecollins.vbox.core.task;

import com.alexecollins.util.ExecUtils;
import com.alexecollins.util.FileUtils2;
import com.alexecollins.util.ImageUtils;
import com.alexecollins.util.SystemUtils2;
import com.alexecollins.vbox.core.Snapshot;
import com.alexecollins.vbox.core.VBox;
import com.alexecollins.vbox.core.Work;
import com.alexecollins.vbox.mediaregistry.DVDImage;
import com.alexecollins.vbox.mediaregistry.FloppyImage;
import com.alexecollins.vbox.mediaregistry.Image;
import com.alexecollins.vbox.mediaregistry.MediaRegistry;
import com.google.common.collect.ImmutableMap;
import de.innotek.virtualbox_settings.AttachedDeviceType;
import de.innotek.virtualbox_settings.OrderDevice;
import de.innotek.virtualbox_settings.StorageControllerType;
import de.innotek.virtualbox_settings.VirtualBox;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
* Create a new virtual box.
*/
public class Create extends AbstractTask {
  private static final Logger LOGGER = LoggerFactory.getLogger(Create.class);

  public Create(final Work work, final VBox box) {
    super(work, box);
  }

  public Void call() throws Exception {

    verifySignature();

    final Snapshot snapshot = Snapshot.POST_CREATION;
    if (box.exists()) {

      box.powerOff();

      if (box.getSnapshots().contains(snapshot)) {
        LOGGER.info("restoring '" + box.getName() + "' from snapshot " + snapshot);
        box.restoreSnapshot(snapshot);
        return null;
      }
      // the box may have been created, but this was incomplete,
      // hence the lack of snapshot, delete and try again
      box.unregister();
    }

    LOGGER.info("creating '" + box.getName() + "'");

    final VirtualBox vb = box.getVirtualBox();
    final VirtualBox.Machine machine = vb.getMachine();

    assert machine != null;

    if (box.is64Bit() != SystemUtils2.is64Bit()) {
      LOGGER.warn("host is "+(SystemUtils2.is64Bit() ? "64" : "32") +"bit, guest " +(box.is64Bit() ? "64":"32")+"bit: mismatch, performance might be very slow (see http://alexec.github.com/maven-vbox-plugin/64-bit.html)");
    }

    final File t = work.targetOf(box);
    if (t.exists()) {
      LOGGER.info("deleting " + t);
      FileUtils.deleteDirectory(t);
    }
    if (!t.mkdirs()) throw new IllegalStateException("failed to create " + t);

    FileUtils.writeByteArrayToFile(getSignatureFile(), getSignature());

    final String osType = machine.getOSType();
    final Set<VBox.OSType> osTypes = VBox.getOSTypes();
    if (!osTypes.contains(new VBox.OSType(osType))) {
      throw new IllegalStateException("invalid OS " +osType +",  must be one of " + osTypes);
    }

    // set-up media before we create box, so any problems are easier to fix
    final Map<Object, File> idToFile = createMedia(box, t, box.getMediaRegistry());

        ExecUtils.exec("vboxmanage", "createvm", "--name", box.getName(), "--ostype", osType, "--register", "--basefolder", t.getParentFile().getCanonicalPath());

        configureMachine(box, machine.getHardware());

    setupStorage(box, machine.getStorageControllers(), idToFile);

    box.takeSnapshot(snapshot);
    return null;
  }

  private void setupStorage(final VBox box, final VirtualBox.Machine.StorageControllers controllers, final Map<Object, File> idToFile) throws IOException, InterruptedException, ExecutionException {
    final Map<StorageControllerType, String> x = ImmutableMap.of(
        StorageControllerType.PIIX_4, "ide",
        StorageControllerType.AHCI, "sata",
        StorageControllerType.I_82078, "floppy"
    );

    final Map<AttachedDeviceType, String> y = ImmutableMap.of(
        AttachedDeviceType.DVD, "dvddrive",
        AttachedDeviceType.HARD_DISK, "hdd",
        AttachedDeviceType.FLOPPY, "fdd"
    );

    for (VirtualBox.Machine.StorageControllers.StorageController s : controllers.getStorageController()) {
      final String n = s.getName();
      LOGGER.debug("creating controller " + n);
      ExecUtils.exec("vboxmanage", "storagectl", box.getName(), "--name", n, "--add", x.get(s.getType()),
          "--bootable", s.isBootable() ? "on" : "off",
          "--sataportcount", String.valueOf(s.getPortCount()),
          "--hostiocache", s.isUseHostIOCache() ? "on" : "off"
      );

      for (VirtualBox.Machine.StorageControllers.StorageController.AttachedDevice a : s.getAttachedDevice()) {
        final String u = a.getImage().getUuid();
        final File f = idToFile.get(u);
        LOGGER.debug("attaching " + f + " (UUID " + u + ")");
        if (!f.exists()) throw new IllegalStateException(f + " does not exist");
        ExecUtils.exec("vboxmanage", "storageattach", box.getName(), "--storagectl", n,
            "--port", String.valueOf(a.getPort()),
            "--device", String.valueOf(a.getDevice()),
            "--type", y.get(a.getType()),
            "--medium", f.getCanonicalPath());
      }
    }
  }

  private Map<Object, File> createMedia(final VBox box, final File target, final MediaRegistry mr) throws IOException, InterruptedException, URISyntaxException, ExecutionException {

    VBox.installAdditions(work);

    final Map<Object, File> idToFile = new HashMap<Object, File>();

    for (final Image hd : mr.getHardDisks().getHardDisk()) {
      LOGGER.debug("creating HD " + hd.getUuid());
      final File hdImg = new File(target, hd.getUuid() + ".vdi");
      if (hdImg.exists() && !hdImg.delete()) throw new IllegalStateException();
      ExecUtils.exec("vboxmanage", "createhd", "--filename", hdImg.getCanonicalPath(),
          "--size", String.valueOf(hd.getSize()));
      idToFile.put(hd.getUuid(), hdImg);
    }

    for (DVDImage dvd : mr.getDVDImages().getDVDImage()) {
      idToFile.put(dvd.getUuid(), acquireImage(box, dvd));
    }

    for (final Image floppy : mr.getFloppyImages().getFloppyImage()) {
      idToFile.put(floppy.getUuid(), acquireImage(box, floppy));
    }

    LOGGER.debug("images " + idToFile);

    return idToFile;
  }

  private void configureMachine(final VBox box, final VirtualBox.Machine.Hardware hardware) throws IOException, InterruptedException, ExecutionException {
    LOGGER.info("configuring machine");

    final List<String> modifyVm = new ArrayList<String>(Arrays.asList("vboxmanage", "modifyvm", box.getName()));
    modifyVm.addAll(Arrays.asList("--memory", String.valueOf(hardware.getMemory().getRAMSize())));

    final VirtualBox.Machine.Hardware.CPU c = hardware.getCPU();
    final int cpus = c != null ? c.getCount() : 1;
    modifyVm.add("--cpus");
    modifyVm.add(String.valueOf(cpus));

    if (hardware.getBoot() != null) {
      LOGGER.info("setting boot order");
      final Map<OrderDevice, String> odm = ImmutableMap.of(
          OrderDevice.NONE, "none",
          OrderDevice.FLOPPY, "floppy",
          OrderDevice.DVD, "dvd",
          OrderDevice.HARD_DISK, "disk");

      for (VirtualBox.Machine.Hardware.Boot.Order o : hardware.getBoot().getOrder()) {
        modifyVm.add("--boot" + o.getPosition());
        modifyVm.add(odm.get(o.getDevice()));
      }
    }

    // http://askubuntu.com/questions/82015/shutting-down-ubuntu-server-running-in-headless-virtualbox
    final VirtualBox.Machine.Hardware.BIOS b = hardware.getBIOS();
    boolean ioAcpi = false;
    if (b != null) {
      final VirtualBox.Machine.Hardware.BIOS.ACPI a = b.getACPI();
      if (a != null) {
        modifyVm.add("--acpi");
        modifyVm.add(a.isEnabled() ? "on" : "off");
      }
      final VirtualBox.Machine.Hardware.BIOS.IOACPI i = b.getIOACPI();
      if (i != null) {
        modifyVm.add("--ioapic");
        modifyVm.add(i.isEnabled() ? "on" : "off");
        ioAcpi = i.isEnabled();
      }
    }

    if (cpus > 1 && !ioAcpi)
      LOGGER.warn("multiple CPUs require IOACPI enabled, you probably want to enable this");



    final Set<Integer> usedSlots = new HashSet<Integer>();
    for (VirtualBox.Machine.Hardware.Network.Adapter a : hardware.getNetwork().getAdapter()) {

      if (!usedSlots.add(a.getSlot())) {
        throw new IllegalStateException("used slot "  +a.getSlot() + " twice");
      }

      final int n = a.getSlot() + 1;

      modifyVm.addAll(Arrays.asList("--cableconnected" + n, a.isCable() ? "on" : "off"));

      if (a.getNAT() != null)
        modifyVm.addAll(Arrays.asList("--nic" + n, "nat"));
      else if (a.getBridgedInterface() != null)
        modifyVm.addAll(Arrays.asList("--nic" + n, "bridged", "--bridgeadapter" + n, a.getBridgedInterface().getName()));
      else if (a.getHostOnlyInterface() != null) {
                final String adapter = a.getHostOnlyInterface().getName();
                if (!VBox.findHostOnlyInterfaces().contains(adapter)) {
                    throw new IllegalStateException("host-only interface '" + adapter + "' does not exist, you will need to create it in the VirtualBox settings");
                }
                modifyVm.addAll(Arrays.asList("--nic" + n, "hostonly", "--hostonlyadapter" + n, adapter));
            } else if (a.getInternalNetwork() != null)
        modifyVm.addAll(Arrays.asList("--nic" + n, "intnet", "--intnet" + n, a.getInternalNetwork().getName()));
      else
        throw new UnsupportedOperationException();
    }

    final VirtualBox.Machine.Hardware.IO io = hardware.getIO();
    if (io != null) {
      final VirtualBox.Machine.Hardware.IO.IoCache ic = io.getIoCache();
      if (ic != null) {
        // TODO nop
      }
    }

    ExecUtils.exec(modifyVm.toArray(new String[modifyVm.size()]));
  }

  File acquireImage(VBox box, Image image) throws IOException, URISyntaxException, InterruptedException, ExecutionException {
    String location = subst(image.getLocation());

    if (location.startsWith("http://") || location.startsWith("ftp://")) {
            // 1. make sure the file is in the download cache, this means if we have multiple machines
            // then we won't re-download the same file
            final File cache = new File(work.getCacheDir(), URLEncoder.encode(location, "UTF-8"));
            if (!cache.exists()) {
                if (!cache.getParentFile().exists() && !cache.getParentFile().mkdirs()) throw new IllegalStateException();
                LOGGER.info("downloading " + location + " to " + cache);
                FileUtils2.copyURLToFile(new URL(location), cache);
            }
            // 2. copy the cached version to the dest, if the dest might need freshening
      final File dest = new File(work.targetOf(this.box), image.getUuid() + ".iso");
            if (!dest.exists() || dest.lastModified() != cache.lastModified()) {
                LOGGER.info("copying " + cache + " to " + dest);
                FileUtils.copyFile(cache, dest);
                if (!dest.setLastModified(cache.lastModified())) {
                  throw new IllegalStateException("failed to set last modified of " + dest);
                }
            }
      location = dest.toString();
    } else if (location.startsWith("file://")) {
      location = new URI(location).getPath();
    }

    final File src = new File(box.getSrc().toURL().getFile(), location);

    if (src.isDirectory())
            if (image instanceof FloppyImage) {
              final File dest = new File(work.targetOf(this.box), image.getUuid() + ".img");

                LOGGER.info("creating floppy image for " + src + " as " + dest);

                ImageUtils.createImage(work, src, dest);

          location = dest.toString();
            } else if (image instanceof DVDImage) {
              final File dest = new File(work.targetOf(this.box), image.getUuid() + ".iso");

                LOGGER.info("creating ISO image for " + src + " as " + dest);

                ImageUtils.createImage(work, src, dest);

                location = dest.toString();

            } else {
                throw new UnsupportedOperationException("do not know how to create image for " + src);
    }

    final File file = new File(location);

    if (!file.exists()) {
      throw new IllegalStateException(file + " does not exist");
    }

    return file;
  }
}
TOP

Related Classes of com.alexecollins.vbox.core.task.Create

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.