diff options
author | aszlig <aszlig@redmoonstudios.org> | 2014-02-15 23:23:47 +0100 |
---|---|---|
committer | aszlig <aszlig@redmoonstudios.org> | 2014-02-26 04:51:56 +0100 |
commit | c731467e2c83f3df0b59c44575616e6241a63c7d (patch) | |
tree | d86cba504d645898458142bd425980c8d7ebafb3 /pkgs/build-support | |
parent | 5105e7f0bfc3a9131df5a8f3bf4d899a01b0106b (diff) | |
download | nixlib-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.nix | 173 | ||||
-rw-r--r-- | pkgs/build-support/vm/windows/default.nix | 175 | ||||
-rw-r--r-- | pkgs/build-support/vm/windows/install/default.nix | 43 |
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; + }; } |