summary refs log tree commit diff
path: root/pkgs/build-support
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2014-02-15 23:23:47 +0100
committeraszlig <aszlig@redmoonstudios.org>2014-02-26 04:51:56 +0100
commitc731467e2c83f3df0b59c44575616e6241a63c7d (patch)
treed86cba504d645898458142bd425980c8d7ebafb3 /pkgs/build-support
parent5105e7f0bfc3a9131df5a8f3bf4d899a01b0106b (diff)
downloadnixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar.gz
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar.bz2
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar.lz
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar.xz
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.tar.zst
nixlib-c731467e2c83f3df0b59c44575616e6241a63c7d.zip
vm/windows: Split install into several stages.
These stages are in particular:

 * Install of the bare Windows VM with Cygwin and shut down.
 * Boot up the same VM again without the installation media and dump the
   VMs memory to state.gz.
 * Resume from state.gz and build whatever we want to build.

Every single stage involves a new "controller", which is more like an
abstraction on the Nix side that constructs the madness described in
276b72fb93d60ae0e59088ea0e0029da87e6f31c.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'pkgs/build-support')
-rw-r--r--pkgs/build-support/vm/windows/controller/default.nix173
-rw-r--r--pkgs/build-support/vm/windows/default.nix175
-rw-r--r--pkgs/build-support/vm/windows/install/default.nix43
3 files changed, 238 insertions, 153 deletions
diff --git a/pkgs/build-support/vm/windows/controller/default.nix b/pkgs/build-support/vm/windows/controller/default.nix
new file mode 100644
index 000000000000..49d6815a3dce
--- /dev/null
+++ b/pkgs/build-support/vm/windows/controller/default.nix
@@ -0,0 +1,173 @@
+{ sshKey
+, qemuArgs ? []
+, command ? "sync"
+, suspendTo ? null
+, resumeFrom ? null
+, installMode ? false
+}:
+
+let
+  inherit (import <nixpkgs> {}) lib stdenv writeScript vmTools makeInitrd;
+  inherit (import <nixpkgs> {}) samba vde2 busybox openssh;
+  inherit (import <nixpkgs> {}) socat netcat coreutils gzip;
+
+  preInitScript = writeScript "preinit.sh" ''
+    #!${vmTools.initrdUtils}/bin/ash -e
+    export PATH=${vmTools.initrdUtils}/bin
+    mount -t proc none /proc
+    mount -t sysfs none /sys
+    for arg in $(cat /proc/cmdline); do
+      if [ "x''${arg#command=}" != "x$arg" ]; then
+        command="''${arg#command=}"
+      fi
+    done
+
+    for i in $(cat ${modulesClosure}/insmod-list); do
+      insmod $i
+    done
+
+    mkdir -p /tmp /dev
+    mknod /dev/null    c 1 3
+    mknod /dev/zero    c 1 5
+    mknod /dev/random  c 1 8
+    mknod /dev/urandom c 1 9
+    mknod /dev/tty     c 5 0
+
+    ifconfig lo up
+    ifconfig eth0 up 192.168.0.2
+
+    mkdir -p /nix/store /etc /var/run /var/log
+
+    cat > /etc/passwd <<PASSWD
+    root:x:0:0::/root:/bin/false
+    nobody:x:65534:65534::/var/empty:/bin/false
+    PASSWD
+
+    mount -t 9p \
+      -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \
+      store /nix/store
+
+    exec "$command"
+  '';
+
+  initrd = makeInitrd {
+    contents = lib.singleton {
+      object = preInitScript;
+      symlink = "/init";
+    };
+  };
+
+  initScript = writeScript "init.sh" (''
+    #!${stdenv.shell}
+    ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private /var/lib/samba
+    ${coreutils}/bin/cat > /etc/samba/smb.conf <<CONFIG
+    [global]
+    security = user
+    map to guest = Bad User
+    workgroup = cygwin
+    netbios name = controller
+    server string = %h
+    log level = 1
+    max log size = 1000
+    log file = /var/log/samba.log
+
+    [nixstore]
+    path = /nix/store
+    read only = no
+    guest ok = yes
+    CONFIG
+
+    ${samba}/sbin/nmbd -D
+    ${samba}/sbin/smbd -D
+    ${coreutils}/bin/cp -L "${sshKey}" /ssh.key
+    ${coreutils}/bin/chmod 600 /ssh.key
+  '' + (if installMode then ''
+    echo -n "Waiting for Windows installation to finish..."
+    while ! ${netcat}/bin/netcat -z 192.168.0.1 22; do
+      echo -n .
+      # Print a dot every 10 seconds only to shorten line length.
+      ${coreutils}/bin/sleep 10
+    done
+    echo " success."
+    # Loop forever, because this VM is going to be killed.
+    while :; do ${coreutils}/bin/sleep 1; done
+  '' else ''
+    echo -n "Waiting for Windows VM to become available..."
+    while ! ${netcat}/bin/netcat -z 192.168.0.1 22; do
+      echo -n .
+      ${coreutils}/bin/sleep 1
+    done
+    echo " success."
+
+    ${openssh}/bin/ssh \
+      -o UserKnownHostsFile=/dev/null \
+      -o StrictHostKeyChecking=no \
+      -i /ssh.key \
+      -l Administrator \
+      192.168.0.1 -- "${command}"
+
+    ${busybox}/sbin/poweroff -f
+  ''));
+
+  kernelAppend = lib.concatStringsSep " " [
+    "panic=1"
+    "loglevel=4"
+    "console=tty1"
+    "console=ttyS0"
+    "command=${initScript}"
+  ];
+
+  controllerQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [
+    "-nographic"
+    "-no-reboot"
+    "-virtfs local,path=/nix/store,security_model=none,mount_tag=store"
+    "-kernel ${modulesClosure.kernel}/bzImage"
+    "-initrd ${initrd}/initrd"
+    "-append \"${kernelAppend}\""
+    "-net nic,vlan=0,macaddr=52:54:00:12:01:02,model=virtio"
+    "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET"
+  ]);
+
+  maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64";
+
+  cygwinQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [
+    "-monitor unix:$MONITOR_SOCKET,server,nowait"
+    "-nographic"
+    "-net nic,vlan=0,macaddr=52:54:00:12:01:01"
+    "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET"
+    "-rtc base=2010-01-01,clock=vm"
+  ] ++ qemuArgs ++ lib.optionals (resumeFrom != null) [
+    "-incoming 'exec: ${gzip}/bin/gzip -c -d \"${resumeFrom}\"'"
+  ]);
+
+  modulesClosure = lib.overrideDerivation vmTools.modulesClosure (o: {
+    rootModules = o.rootModules ++ lib.singleton "virtio_net";
+  });
+
+  preVM = ''
+    QEMU_VDE_SOCKET="$(pwd)/vde.ctl"
+    MONITOR_SOCKET="$(pwd)/monitor"
+    ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" &
+  '';
+
+  vmExec = if installMode then ''
+    ${vmTools.qemuProg} ${controllerQemuArgs} &
+    ${vmTools.qemuProg} ${cygwinQemuArgs}
+  '' else ''
+    ${vmTools.qemuProg} ${cygwinQemuArgs} &
+    ${vmTools.qemuProg} ${controllerQemuArgs}
+  '' + lib.optionalString (suspendTo != null) ''
+    ${socat}/bin/socat - UNIX-CONNECT:$MONITOR_SOCKET <<CMD
+    stop
+    migrate_set_speed 4095m
+    migrate "exec:${gzip}/bin/gzip -c > '${suspendTo}'"
+    quit
+    CMD
+    wait %%
+  '';
+
+in writeScript "run-cygwin-vm.sh" ''
+  #!${stdenv.shell} -e
+  ${preVM}
+  ${vmExec}
+''
diff --git a/pkgs/build-support/vm/windows/default.nix b/pkgs/build-support/vm/windows/default.nix
index 470fac0437df..2ecadbae7cfb 100644
--- a/pkgs/build-support/vm/windows/default.nix
+++ b/pkgs/build-support/vm/windows/default.nix
@@ -1,160 +1,49 @@
-with import <nixpkgs> {};
-
-with import <nixpkgs/nixos/lib/build-vms.nix> {
-  inherit system;
-  minimal = false;
-};
-
 let
+  inherit (import <nixpkgs> {}) lib stdenv requireFile writeText qemu;
+
   winISO = /path/to/iso/XXX;
 
-  base = import ./install {
+  installedVM = import ./install {
     isoFile = winISO;
     productKey = "XXX";
   };
 
-  maybeKvm64 = lib.optional (stdenv.system == "x86_64-linux") "-cpu kvm64";
-
-  cygwinQemuArgs = lib.concatStringsSep " " (maybeKvm64 ++ [
-    "-monitor unix:$MONITOR_SOCKET,server,nowait"
-    "-nographic"
-    "-boot order=c,once=d"
-    "-drive file=${base.floppy},readonly,index=0,if=floppy"
-    "-drive file=winvm.img,index=0,media=disk"
-    "-drive file=${winISO},index=1,media=cdrom"
-    "-drive file=${base.iso}/iso/cd.iso,index=2,media=cdrom"
-    "-net nic,vlan=0,macaddr=52:54:00:12:01:01"
-    "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET"
-    "-rtc base=2010-01-01,clock=vm"
-  ]);
-
-  modulesClosure = lib.overrideDerivation vmTools.modulesClosure (o: {
-    rootModules = o.rootModules ++ lib.singleton "virtio_net";
+  runInVM = img: attrs: import ./controller (attrs // {
+    inherit (installedVM) sshKey;
+    qemuArgs = attrs.qemuArgs or [] ++ [
+      "-boot order=c"
+      "-drive file=${img},index=0,media=disk"
+    ];
   });
 
-  controllerQemuArgs = cmd: let
-    preInitScript = writeScript "preinit.sh" ''
-      #!${vmTools.initrdUtils}/bin/ash -e
-      export PATH=${vmTools.initrdUtils}/bin
-      mount -t proc none /proc
-      mount -t sysfs none /sys
-      for arg in $(cat /proc/cmdline); do
-        if [ "x''${arg#command=}" != "x$arg" ]; then
-          command="''${arg#command=}"
-        fi
-      done
-
-      for i in $(cat ${modulesClosure}/insmod-list); do
-        insmod $i
-      done
-
-      mkdir -p /tmp /dev
-      mknod /dev/null    c 1 3
-      mknod /dev/zero    c 1 5
-      mknod /dev/random  c 1 8
-      mknod /dev/urandom c 1 9
-      mknod /dev/tty     c 5 0
-
-      ifconfig lo up
-      ifconfig eth0 up 192.168.0.2
-
-      mkdir -p /nix/store /etc /var/run /var/log
-
-      cat > /etc/passwd <<PASSWD
-      root:x:0:0::/root:/bin/false
-      nobody:x:65534:65534::/var/empty:/bin/false
-      PASSWD
-
-      mount -t 9p \
-        -o trans=virtio,version=9p2000.L,msize=262144,cache=loose \
-        store /nix/store
-
-      exec "$command"
-    '';
-    initrd = makeInitrd {
-      contents = lib.singleton {
-        object = preInitScript;
-        symlink = "/init";
-      };
-    };
-    initScript = writeScript "init.sh" ''
-      #!${stdenv.shell}
-      ${coreutils}/bin/mkdir -p /etc/samba /etc/samba/private /var/lib/samba
-      ${coreutils}/bin/cat > /etc/samba/smb.conf <<CONFIG
-      [global]
-      security = user
-      map to guest = Bad User
-      workgroup = cygwin
-      netbios name = controller
-      server string = %h
-      log level = 1
-      max log size = 1000
-      log file = /var/log/samba.log
-
-      [nixstore]
-      path = /nix/store
-      read only = no
-      guest ok = yes
-      CONFIG
-
-      ${samba}/sbin/nmbd -D
-      ${samba}/sbin/smbd -D
-      ${coreutils}/bin/cp -L "${base.sshKey}" /ssh.key
-      ${coreutils}/bin/chmod 600 /ssh.key
-
-      echo -n "Waiting for Windows VM to become ready"
-      while ! ${netcat}/bin/netcat -z 192.168.0.1 22; do
-        echo -n .
-        ${coreutils}/bin/sleep 1
-      done
-      echo " ready."
-
-      ${openssh}/bin/ssh \
-        -o UserKnownHostsFile=/dev/null \
-        -o StrictHostKeyChecking=no \
-        -i /ssh.key \
-        -l Administrator \
-        192.168.0.1 -- "${cmd}"
+  runAndSuspend = runInVM "winvm.img" {
+    suspendTo = "state.gz";
+  };
 
-      ${busybox}/sbin/poweroff -f
+  suspendedVM = stdenv.mkDerivation {
+    name = "cygwin-suspended-vm";
+    buildCommand = ''
+      ${qemu}/bin/qemu-img create \
+        -b "${installedVM}/disk.img" \
+        -f qcow2 winvm.img
+      ${runAndSuspend}
+      ensureDir "$out"
+      cp winvm.img "$out/disk.img"
+      cp state.gz "$out/state.gz"
     '';
-    kernelAppend = lib.concatStringsSep " " [
-      "panic=1"
-      "loglevel=4"
-      "console=tty1"
-      "console=ttyS0"
-      "command=${initScript}"
-    ];
-  in lib.concatStringsSep " " (maybeKvm64 ++ [
-    "-nographic"
-    "-no-reboot"
-    "-virtfs local,path=/nix/store,security_model=none,mount_tag=store"
-    "-kernel ${modulesClosure.kernel}/bzImage"
-    "-initrd ${initrd}/initrd"
-    "-append \"${kernelAppend}\""
-    "-net nic,vlan=0,macaddr=52:54:00:12:01:02,model=virtio"
-    "-net vde,vlan=0,sock=$QEMU_VDE_SOCKET"
-  ]);
+  };
 
-  bootstrap = stdenv.mkDerivation {
-    name = "windown-vm";
+  resumeAndRun = command: runInVM "${suspendedVM}/disk.img" {
+    resumeFrom = "${suspendedVM}/state.gz";
+    qemuArgs = lib.singleton "-snapshot";
+    inherit command;
+  };
 
+  runFromSuspended = command: stdenv.mkDerivation {
+    name = "cygwin-vm-run";
     buildCommand = ''
-      ${qemu}/bin/qemu-img create -f qcow2 winvm.img 2G
-      QEMU_VDE_SOCKET="$(pwd)/vde.ctl"
-      MONITOR_SOCKET="$(pwd)/monitor"
-      ${vde2}/bin/vde_switch -s "$QEMU_VDE_SOCKET" &
-      ${vmTools.qemuProg} ${cygwinQemuArgs} &
-      ${vmTools.qemuProg} ${controllerQemuArgs "sync"}
-
-      ensureDir "$out"
-      ${socat}/bin/socat - UNIX-CONNECT:$MONITOR_SOCKET <<CMD
-      stop
-      migrate_set_speed 4095m
-      migrate "exec:${gzip}/bin/gzip -c > '$out/state.gz'"
-      CMD
-      cp winvm.img "$out/disk.img"
+      ${resumeAndRun command}
     '';
   };
 
-in bootstrap
+in runFromSuspended "uname -a"
diff --git a/pkgs/build-support/vm/windows/install/default.nix b/pkgs/build-support/vm/windows/install/default.nix
index 0021bae87bc8..d766cbcf8e3a 100644
--- a/pkgs/build-support/vm/windows/install/default.nix
+++ b/pkgs/build-support/vm/windows/install/default.nix
@@ -3,7 +3,7 @@
 }:
 
 let
-  inherit (import <nixpkgs> {}) lib stdenv runCommand openssh;
+  inherit (import <nixpkgs> {}) lib stdenv runCommand openssh qemu;
 
   bootstrapAfterLogin = runCommand "bootstrap.sh" {} ''
     cat > "$out" <<EOF
@@ -12,11 +12,11 @@ let
     $(cat "${cygwinSshKey}/key.pub")
     PUBKEY
     ssh-host-config -y -c 'binmode ntsec' -w dummy
-    cygrunsrv -S sshd
 
     net use S: '\\192.168.0.2\nixstore'
     mkdir -p /nix/store
-    mount -o bind /cygdrives/s /nix/store
+    echo "/cygdrives/s /nix/store none bind 0 0" >> /etc/fstab
+    shutdown -s now
     EOF
   '';
 
@@ -28,10 +28,16 @@ let
     '';
   };
 
-  packages = [ "openssh" ];
+  sshKey = "${cygwinSshKey}/key";
+
+  packages = [ "openssh" "shutdown" ];
+
+  instfloppy = import ./unattended-image.nix {
+    cygwinPackages = packages;
+    inherit productKey;
+  };
 
-in {
-  iso = import ../cygwin-iso {
+  cygiso = import ../cygwin-iso {
     inherit packages;
     extraContents = lib.singleton {
       source = bootstrapAfterLogin;
@@ -39,10 +45,27 @@ in {
     };
   };
 
-  floppy = import ./unattended-image.nix {
-    cygwinPackages = packages;
-    inherit productKey;
+  installController = import ../controller {
+    inherit sshKey;
+    installMode = true;
+    qemuArgs = [
+      "-boot order=c,once=d"
+      "-drive file=${instfloppy},readonly,index=0,if=floppy"
+      "-drive file=winvm.img,index=0,media=disk"
+      "-drive file=${isoFile},index=1,media=cdrom"
+      "-drive file=${cygiso}/iso/cd.iso,index=2,media=cdrom"
+    ];
   };
 
-  sshKey = "${cygwinSshKey}/key";
+in stdenv.mkDerivation {
+  name = "cygwin-base-vm";
+  buildCommand = ''
+    ${qemu}/bin/qemu-img create -f qcow2 winvm.img 2G
+    ${installController}
+    ensureDir "$out"
+    cp winvm.img "$out/disk.img"
+  '';
+  passthru = {
+    inherit sshKey;
+  };
 }