Package com.alexecollins.vbox.core.task

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

package com.alexecollins.vbox.core.task;

import au.com.bytecode.opencsv.CSVReader;
import com.alexecollins.util.DurationUtils;
import com.alexecollins.util.ExecUtils;
import com.alexecollins.vbox.core.ScanCodes;
import com.alexecollins.vbox.core.Snapshot;
import com.alexecollins.vbox.core.VBox;
import com.alexecollins.vbox.core.Work;
import com.alexecollins.vbox.provisioning.Provisioning;
import org.apache.commons.lang.ArrayUtils;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.StringReader;
import java.net.*;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

public class Provision extends AbstractTask {
  private static final Logger LOGGER = LoggerFactory.getLogger(Provision.class);
  private final Server server;
  private final Set<String> targets;

  public Provision(Work work, VBox box, Set<String> targets) throws IOException {
    super(work, box);
    this.targets = targets;

    server = new Server(Provision.findFreePort());
  }

  public Void call() throws Exception {

    if (box.exists()) {
      new Stop(box).call();
    }

    verifySignature();

    final Snapshot snapshot = Snapshot.POST_PROVISIONING;
    if (box.exists()) {
      box.powerOff();
      if (box.getSnapshots().contains(snapshot)) {
        LOGGER.info("restoring '" + box.getName() + "' from snapshot " + snapshot);
        box.restoreSnapshot(Snapshot.POST_PROVISIONING);
        return null;
      }
    }

    // if the box doesn't exist, create it
    if (!box.exists()) {
      new Create(work, box).call();
    }

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

    // TODO startServer(box);
    try {
      final List<Provisioning.Target> targets = box.getProvisioning().getTarget();
      for (Provisioning.Target target : targets) {
        if (this.targets.contains(target.getName()) || this.targets.equals(Collections.<String>singleton("*"))) {
          if (target.equals(targets.get(0)) && !box.getProperties().getProperty("VMState").equals("running")) {
            LOGGER.info("starting box");
            box.start();
          }
          executeTarget(box, target);
          if (target.equals(targets.get(targets.size() - 1))) {
            if (box.getProperties().getProperty("VMState").equals("running")) {
              LOGGER.info("stopping box");
              box.pressPowerButton();
              box.awaitState((long) 10000, "poweroff");
            }
            box.takeSnapshot(snapshot);
          }
        } else {
          LOGGER.info("skipping target " + target.getName());
        }
      }
    } finally {
      // TODO uncomment - stopServer();
    }
    return null;
  }

  private void executeTarget(final VBox box, final Provisioning.Target target) throws IOException, InterruptedException, TimeoutException, ExecutionException {
    LOGGER.info("executing target " + target.getName());
    for (Object o : target.getPortForwardOrAwaitPortOrAwaitState()) {
      LOGGER.debug("executing " + o);
      if (o instanceof Provisioning.Target.PortForward)
        portForward(box.getName(), (Provisioning.Target.PortForward) o);
      else if (o instanceof Provisioning.Target.KeyboardPutScanCodes)
        keyboardPutScanCodes(box.getName(), ((Provisioning.Target.KeyboardPutScanCodes) o));
      else if (o instanceof Provisioning.Target.Sleep) {
        final Provisioning.Target.Sleep s = (Provisioning.Target.Sleep) o;
        final long seconds = s.getMinutes() * 60 + s.getSeconds();

        LOGGER.info("sleeping for " + seconds + " second(s)");
        Thread.sleep(seconds * 1000);
      } else if (o instanceof Provisioning.Target.Exec) {
        try {
          ExecUtils.exec(new CSVReader(new StringReader(subst(((Provisioning.Target.Exec) o).getValue())), ' ').readNext());
        } catch (ExecutionException e) {
          if (((Provisioning.Target.Exec) o).isFailonerror())
            throw e;
          else
            LOGGER.info("ignoring error " + e.getMessage());
        }
      } else if (o instanceof Provisioning.Target.AwaitPort) {
        awaitPort((Provisioning.Target.AwaitPort) o);
      } else if (o instanceof Provisioning.Target.AwaitState) {
        box.awaitState( DurationUtils.millisForString(((Provisioning.Target.AwaitState) o).getTimeout()), ((Provisioning.Target.AwaitState) o).getState());
      } else
        throw new AssertionError("unexpected provision");
    }

    // snapshots are expensive in terms of disk space
    // box.takeSnapshot(Snapshot.valueOf("post-provision-" + target.getName()));
  }

  private void awaitPort(final Provisioning.Target.AwaitPort ap) throws IOException, TimeoutException, InterruptedException {
    awaitPort(ap.getHost(), ap.getPort(), ap.getTimeout());
  }

  public static void awaitPort(String host, int port, String timeout) throws IOException, TimeoutException, InterruptedException {
    final long start = System.currentTimeMillis();
    final String desc = host + ":" + port;
    while (true) {
      final long remaining = start + DurationUtils.millisForString(timeout) - System.currentTimeMillis();
      LOGGER.info("awaiting " + desc + " for " + DurationUtils.prettyPrint(remaining));
      try {
        new Socket(host, port).close();
        LOGGER.info("port available");
        return;
      } catch (ConnectException e) {
        // nop
      }

      if (remaining < 0) {
        throw new TimeoutException("timed out waiting for " + desc);
      }

      Thread.sleep(Math.min(10000l, remaining));
    }
  }

  void stopServer() throws Exception {
    if (server.isRunning()) {
      LOGGER.info("stopping local web server");
      server.stop();
    }
  }

  void startServer(final VBox box) throws Exception {
    LOGGER.info("starting local web server on port " + getServerPort());

    final ResourceHandler rh = new ResourceHandler();
    rh.setBaseResource(Resource.newResource(box.getSrc().toURL()));
    LOGGER.info("serving " + rh.getResourceBase());
    server.setHandler(rh);
    server.start();

    final URL u = new URL("http://" + InetAddress.getLocalHost().getHostAddress() + ":" + getServerPort() + "/VirtualBox.xml");
    LOGGER.info("testing server by getting " + u);
    final HttpURLConnection c = (HttpURLConnection) u.openConnection();
    c.connect();
    if (200 != c.getResponseCode()) throw new IllegalStateException(c.getResponseCode() + " " +c.getResponseMessage());
    c.disconnect();

  }

  private static int findFreePort() throws IOException {
    final ServerSocket server = new ServerSocket(0);
    final int port = server.getLocalPort();
    server.close();
    return port;
  }

  public void keyboardPutScanCodes(String name, Provisioning.Target.KeyboardPutScanCodes ksc) throws IOException, InterruptedException, ExecutionException {

    {
      final String keys = ksc.getKeys();
      if (keys != null) {
        LOGGER.info("typing keys " + keys);
        final List<Integer> sc = new ArrayList<Integer>();
        for (String key : keys.split(",")) {
          for (int c : ScanCodes.forKey(key)) {
            sc.add(c);
          }
        }
        keyboardPutScanCodes(name, ArrayUtils.toPrimitive(sc.toArray(new Integer[sc.size()])));
      }
    }
    {
      String line;
      line = ksc.getLine();
      if (line != null) {
        line = subst(line);

        LOGGER.info("typing line '" + line + "'");

        keyboardPutScanCodes(name, ArrayUtils.addAll(ScanCodes.forString(line), ScanCodes.forKey("Enter")));
      }
    }

    {
      String text = ksc.getValue();
      if (text != null && text.length() > 0) {
        text = subst(text);

        LOGGER.info("typing text '" + text + "'");

        keyboardPutScanCodes(name, ArrayUtils.addAll(ScanCodes.forString(text), ScanCodes.forKey("Enter")));
      }
    }
  }

  @Override
  public String subst(String line) throws IOException, InterruptedException, ExecutionException {
    line = line.replaceAll("\\$\\{server\\.ip\\}", InetAddress.getLocalHost().getHostAddress());
    line = line.replaceAll("\\$\\{server\\.port\\}", String.valueOf(getServerPort()));
    return super.subst(line);
  }

  private void keyboardPutScanCodes(String name, int[] scancodes) throws IOException, InterruptedException, ExecutionException {
    LOGGER.debug("typing " + Arrays.toString(scancodes));

    while (scancodes.length > 0) {
      final List<String> command = new ArrayList<String>();
      command.addAll(Arrays.asList("vboxmanage", "controlvm", name, "keyboardputscancode"));

      int i = 0;
      for (int scancode : scancodes) {
        command.add((scancode > 0xf ? "" : "0") + Integer.toHexString(scancode));
        i++;
        // split on enter
        if (i >= 16 || scancode == 156) {
          break;
        }
      }
      ExecUtils.exec(command.toArray(new String[command.size()]));
      Thread.sleep(scancodes[i - 1] == 156 ? 2000 : 100); //  a short sleep to let the OS digest
      scancodes = ArrayUtils.subarray(scancodes, i, scancodes.length);
    }
  }

  private void portForward(String name, Provisioning.Target.PortForward pf) throws IOException, InterruptedException, ExecutionException {
    final int hostPort = pf.getHostport();
    final int guestPort = pf.getGuestport();
    LOGGER.info("adding port forward hostport=" + hostPort + " guestport=" + guestPort);
    ExecUtils.exec("vboxmanage", "setextradata", name, "VBoxInternal/Devices/e1000/0/LUN#0/Config/" + guestPort + "/HostPort", String.valueOf(hostPort));
    ExecUtils.exec("vboxmanage", "setextradata", name, "VBoxInternal/Devices/e1000/0/LUN#0/Config/" + guestPort + "/GuestPort", String.valueOf(guestPort));
    ExecUtils.exec("vboxmanage", "setextradata", name, "VBoxInternal/Devices/e1000/0/LUN#0/Config/" + guestPort + "/Protocol", "TCP");
  }

  public int getServerPort() {
    return server.getConnectors()[0].getPort();
  }
}
TOP

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

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.