The library does not depend on stdenv, that `stdenv` exposes `lib` is an artifact of the ancient origins of nixpkgs.
		
			
				
	
	
		
			538 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			538 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ system ? builtins.currentSystem,
 | 
						|
  config ? {},
 | 
						|
  pkgs ? import ../.. { inherit system config; },
 | 
						|
  debug ? false,
 | 
						|
  enableUnfree ? false,
 | 
						|
  # Nested KVM virtualization (https://www.linux-kvm.org/page/Nested_Guests)
 | 
						|
  # requires a modprobe flag on the build machine: (kvm-amd for AMD CPUs)
 | 
						|
  #   boot.extraModprobeConfig = "options kvm-intel nested=Y";
 | 
						|
  # Without this VirtualBox will use SW virtualization and will only be able
 | 
						|
  # to run 32-bit guests.
 | 
						|
  useKvmNestedVirt ? false,
 | 
						|
  # Whether to run 64-bit guests instead of 32-bit. Requires nested KVM.
 | 
						|
  use64bitGuest ? false
 | 
						|
}:
 | 
						|
 | 
						|
assert use64bitGuest -> useKvmNestedVirt;
 | 
						|
 | 
						|
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
						|
with pkgs.lib;
 | 
						|
 | 
						|
let
 | 
						|
  testVMConfig = vmName: attrs: { config, pkgs, lib, ... }: let
 | 
						|
    guestAdditions = pkgs.linuxPackages.virtualboxGuestAdditions;
 | 
						|
 | 
						|
    miniInit = ''
 | 
						|
      #!${pkgs.runtimeShell} -xe
 | 
						|
      export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.util-linux ]}"
 | 
						|
 | 
						|
      mkdir -p /run/dbus
 | 
						|
      cat > /etc/passwd <<EOF
 | 
						|
      root:x:0:0::/root:/bin/false
 | 
						|
      messagebus:x:1:1::/run/dbus:/bin/false
 | 
						|
      EOF
 | 
						|
      cat > /etc/group <<EOF
 | 
						|
      root:x:0:
 | 
						|
      messagebus:x:1:
 | 
						|
      EOF
 | 
						|
 | 
						|
      "${pkgs.dbus.daemon}/bin/dbus-daemon" --fork \
 | 
						|
        --config-file="${pkgs.dbus.daemon}/share/dbus-1/system.conf"
 | 
						|
 | 
						|
      ${guestAdditions}/bin/VBoxService
 | 
						|
      ${(attrs.vmScript or (const "")) pkgs}
 | 
						|
 | 
						|
      i=0
 | 
						|
      while [ ! -e /mnt-root/shutdown ]; do
 | 
						|
        sleep 10
 | 
						|
        i=$(($i + 10))
 | 
						|
        [ $i -le 120 ] || fail
 | 
						|
      done
 | 
						|
 | 
						|
      rm -f /mnt-root/boot-done /mnt-root/shutdown
 | 
						|
    '';
 | 
						|
  in {
 | 
						|
    boot.kernelParams = [
 | 
						|
      "console=tty0" "console=ttyS0" "ignore_loglevel"
 | 
						|
      "boot.trace" "panic=1" "boot.panic_on_fail"
 | 
						|
      "init=${pkgs.writeScript "mini-init.sh" miniInit}"
 | 
						|
    ];
 | 
						|
 | 
						|
    fileSystems."/" = {
 | 
						|
      device = "vboxshare";
 | 
						|
      fsType = "vboxsf";
 | 
						|
    };
 | 
						|
 | 
						|
    virtualisation.virtualbox.guest.enable = true;
 | 
						|
 | 
						|
    boot.initrd.kernelModules = [
 | 
						|
      "af_packet" "vboxsf"
 | 
						|
      "virtio" "virtio_pci" "virtio_ring" "virtio_net" "vboxguest"
 | 
						|
    ];
 | 
						|
 | 
						|
    boot.initrd.extraUtilsCommands = ''
 | 
						|
      copy_bin_and_libs "${guestAdditions}/bin/mount.vboxsf"
 | 
						|
      copy_bin_and_libs "${pkgs.util-linux}/bin/unshare"
 | 
						|
      ${(attrs.extraUtilsCommands or (const "")) pkgs}
 | 
						|
    '';
 | 
						|
 | 
						|
    boot.initrd.postMountCommands = ''
 | 
						|
      touch /mnt-root/boot-done
 | 
						|
      hostname "${vmName}"
 | 
						|
      mkdir -p /nix/store
 | 
						|
      unshare -m ${escapeShellArg pkgs.runtimeShell} -c '
 | 
						|
        mount -t vboxsf nixstore /nix/store
 | 
						|
        exec "$stage2Init"
 | 
						|
      '
 | 
						|
      poweroff -f
 | 
						|
    '';
 | 
						|
 | 
						|
    system.requiredKernelConfig = with config.lib.kernelConfig; [
 | 
						|
      (isYes "SERIAL_8250_CONSOLE")
 | 
						|
      (isYes "SERIAL_8250")
 | 
						|
    ];
 | 
						|
 | 
						|
    networking.usePredictableInterfaceNames = false;
 | 
						|
  };
 | 
						|
 | 
						|
  mkLog = logfile: tag: let
 | 
						|
    rotated = map (i: "${logfile}.${toString i}") (range 1 9);
 | 
						|
    all = concatMapStringsSep " " (f: "\"${f}\"") ([logfile] ++ rotated);
 | 
						|
    logcmd = "tail -F ${all} 2> /dev/null | logger -t \"${tag}\"";
 | 
						|
  in if debug then "machine.execute(ru('${logcmd} & disown'))" else "pass";
 | 
						|
 | 
						|
  testVM = vmName: vmScript: let
 | 
						|
    cfg = (import ../lib/eval-config.nix {
 | 
						|
      system = if use64bitGuest then "x86_64-linux" else "i686-linux";
 | 
						|
      modules = [
 | 
						|
        ../modules/profiles/minimal.nix
 | 
						|
        (testVMConfig vmName vmScript)
 | 
						|
      ];
 | 
						|
    }).config;
 | 
						|
  in pkgs.vmTools.runInLinuxVM (pkgs.runCommand "virtualbox-image" {
 | 
						|
    preVM = ''
 | 
						|
      mkdir -p "$out"
 | 
						|
      diskImage="$(pwd)/qimage"
 | 
						|
      ${pkgs.vmTools.qemu}/bin/qemu-img create -f raw "$diskImage" 100M
 | 
						|
    '';
 | 
						|
 | 
						|
    postVM = ''
 | 
						|
      echo "creating VirtualBox disk image..."
 | 
						|
      ${pkgs.vmTools.qemu}/bin/qemu-img convert -f raw -O vdi \
 | 
						|
        "$diskImage" "$out/disk.vdi"
 | 
						|
    '';
 | 
						|
 | 
						|
    buildInputs = [ pkgs.util-linux pkgs.perl ];
 | 
						|
  } ''
 | 
						|
    ${pkgs.parted}/sbin/parted --script /dev/vda mklabel msdos
 | 
						|
    ${pkgs.parted}/sbin/parted --script /dev/vda -- mkpart primary ext2 1M -1s
 | 
						|
    ${pkgs.e2fsprogs}/sbin/mkfs.ext4 /dev/vda1
 | 
						|
    ${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
 | 
						|
    mkdir /mnt
 | 
						|
    mount /dev/vda1 /mnt
 | 
						|
    cp "${cfg.system.build.kernel}/bzImage" /mnt/linux
 | 
						|
    cp "${cfg.system.build.initialRamdisk}/initrd" /mnt/initrd
 | 
						|
 | 
						|
    ${pkgs.grub2}/bin/grub-install --boot-directory=/mnt /dev/vda
 | 
						|
 | 
						|
    cat > /mnt/grub/grub.cfg <<GRUB
 | 
						|
    set root=hd0,1
 | 
						|
    linux /linux ${concatStringsSep " " cfg.boot.kernelParams}
 | 
						|
    initrd /initrd
 | 
						|
    boot
 | 
						|
    GRUB
 | 
						|
    umount /mnt
 | 
						|
  '');
 | 
						|
 | 
						|
  createVM = name: attrs: let
 | 
						|
    mkFlags = concatStringsSep " ";
 | 
						|
 | 
						|
    sharePath = "/home/alice/vboxshare-${name}";
 | 
						|
 | 
						|
    createFlags = mkFlags [
 | 
						|
      "--ostype ${if use64bitGuest then "Linux26_64" else "Linux26"}"
 | 
						|
      "--register"
 | 
						|
    ];
 | 
						|
 | 
						|
    vmFlags = mkFlags ([
 | 
						|
      "--uart1 0x3F8 4"
 | 
						|
      "--uartmode1 client /run/virtualbox-log-${name}.sock"
 | 
						|
      "--memory 768"
 | 
						|
      "--audio none"
 | 
						|
    ] ++ (attrs.vmFlags or []));
 | 
						|
 | 
						|
    controllerFlags = mkFlags [
 | 
						|
      "--name SATA"
 | 
						|
      "--add sata"
 | 
						|
      "--bootable on"
 | 
						|
      "--hostiocache on"
 | 
						|
    ];
 | 
						|
 | 
						|
    diskFlags = mkFlags [
 | 
						|
      "--storagectl SATA"
 | 
						|
      "--port 0"
 | 
						|
      "--device 0"
 | 
						|
      "--type hdd"
 | 
						|
      "--mtype immutable"
 | 
						|
      "--medium ${testVM name attrs}/disk.vdi"
 | 
						|
    ];
 | 
						|
 | 
						|
    sharedFlags = mkFlags [
 | 
						|
      "--name vboxshare"
 | 
						|
      "--hostpath ${sharePath}"
 | 
						|
    ];
 | 
						|
 | 
						|
    nixstoreFlags = mkFlags [
 | 
						|
      "--name nixstore"
 | 
						|
      "--hostpath /nix/store"
 | 
						|
      "--readonly"
 | 
						|
    ];
 | 
						|
  in {
 | 
						|
    machine = {
 | 
						|
      systemd.sockets."vboxtestlog-${name}" = {
 | 
						|
        description = "VirtualBox Test Machine Log Socket For ${name}";
 | 
						|
        wantedBy = [ "sockets.target" ];
 | 
						|
        before = [ "multi-user.target" ];
 | 
						|
        socketConfig.ListenStream = "/run/virtualbox-log-${name}.sock";
 | 
						|
        socketConfig.Accept = true;
 | 
						|
      };
 | 
						|
 | 
						|
      systemd.services."vboxtestlog-${name}@" = {
 | 
						|
        description = "VirtualBox Test Machine Log For ${name}";
 | 
						|
        serviceConfig.StandardInput = "socket";
 | 
						|
        serviceConfig.SyslogIdentifier = "GUEST-${name}";
 | 
						|
        serviceConfig.ExecStart = "${pkgs.coreutils}/bin/cat";
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
    testSubs = ''
 | 
						|
 | 
						|
 | 
						|
      ${name}_sharepath = "${sharePath}"
 | 
						|
 | 
						|
 | 
						|
      def check_running_${name}():
 | 
						|
          cmd = "VBoxManage list runningvms | grep -q '^\"${name}\"'"
 | 
						|
          (status, _) = machine.execute(ru(cmd))
 | 
						|
          return status == 0
 | 
						|
 | 
						|
 | 
						|
      def cleanup_${name}():
 | 
						|
          if check_running_${name}():
 | 
						|
              machine.execute(ru("VBoxManage controlvm ${name} poweroff"))
 | 
						|
          machine.succeed("rm -rf ${sharePath}")
 | 
						|
          machine.succeed("mkdir -p ${sharePath}")
 | 
						|
          machine.succeed("chown alice.users ${sharePath}")
 | 
						|
 | 
						|
 | 
						|
      def create_vm_${name}():
 | 
						|
          # fmt: off
 | 
						|
          vbm(f"createvm --name ${name} ${createFlags}")
 | 
						|
          vbm(f"modifyvm ${name} ${vmFlags}")
 | 
						|
          vbm(f"setextradata ${name} VBoxInternal/PDM/HaltOnReset 1")
 | 
						|
          vbm(f"storagectl ${name} ${controllerFlags}")
 | 
						|
          vbm(f"storageattach ${name} ${diskFlags}")
 | 
						|
          vbm(f"sharedfolder add ${name} ${sharedFlags}")
 | 
						|
          vbm(f"sharedfolder add ${name} ${nixstoreFlags}")
 | 
						|
          cleanup_${name}()
 | 
						|
 | 
						|
          ${mkLog "$HOME/VirtualBox VMs/${name}/Logs/VBox.log" "HOST-${name}"}
 | 
						|
          # fmt: on
 | 
						|
 | 
						|
 | 
						|
      def destroy_vm_${name}():
 | 
						|
          cleanup_${name}()
 | 
						|
          vbm("unregistervm ${name} --delete")
 | 
						|
 | 
						|
 | 
						|
      def wait_for_vm_boot_${name}():
 | 
						|
          machine.execute(
 | 
						|
              ru(
 | 
						|
                  "set -e; i=0; "
 | 
						|
                  "while ! test -e ${sharePath}/boot-done; do "
 | 
						|
                  "sleep 10; i=$(($i + 10)); [ $i -le 3600 ]; "
 | 
						|
                  "VBoxManage list runningvms | grep -q '^\"${name}\"'; "
 | 
						|
                  "done"
 | 
						|
              )
 | 
						|
          )
 | 
						|
 | 
						|
 | 
						|
      def wait_for_ip_${name}(interface):
 | 
						|
          property = f"/VirtualBox/GuestInfo/Net/{interface}/V4/IP"
 | 
						|
          # fmt: off
 | 
						|
          getip = f"VBoxManage guestproperty get ${name} {property} | sed -n -e 's/^Value: //p'"
 | 
						|
          # fmt: on
 | 
						|
 | 
						|
          ip = machine.succeed(
 | 
						|
              ru(
 | 
						|
                  "for i in $(seq 1000); do "
 | 
						|
                  f'if ipaddr="$({getip})" && [ -n "$ipaddr" ]; then '
 | 
						|
                  'echo "$ipaddr"; exit 0; '
 | 
						|
                  "fi; "
 | 
						|
                  "sleep 1; "
 | 
						|
                  "done; "
 | 
						|
                  "echo 'Could not get IPv4 address for ${name}!' >&2; "
 | 
						|
                  "exit 1"
 | 
						|
              )
 | 
						|
          ).strip()
 | 
						|
          return ip
 | 
						|
 | 
						|
 | 
						|
      def wait_for_startup_${name}(nudge=lambda: None):
 | 
						|
          for _ in range(0, 130, 10):
 | 
						|
              machine.sleep(10)
 | 
						|
              if check_running_${name}():
 | 
						|
                  return
 | 
						|
              nudge()
 | 
						|
          raise Exception("VirtualBox VM didn't start up within 2 minutes")
 | 
						|
 | 
						|
 | 
						|
      def wait_for_shutdown_${name}():
 | 
						|
          for _ in range(0, 130, 10):
 | 
						|
              machine.sleep(10)
 | 
						|
              if not check_running_${name}():
 | 
						|
                  return
 | 
						|
          raise Exception("VirtualBox VM didn't shut down within 2 minutes")
 | 
						|
 | 
						|
 | 
						|
      def shutdown_vm_${name}():
 | 
						|
          machine.succeed(ru("touch ${sharePath}/shutdown"))
 | 
						|
          machine.execute(
 | 
						|
              "set -e; i=0; "
 | 
						|
              "while test -e ${sharePath}/shutdown "
 | 
						|
              "        -o -e ${sharePath}/boot-done; do "
 | 
						|
              "sleep 1; i=$(($i + 1)); [ $i -le 3600 ]; "
 | 
						|
              "done"
 | 
						|
          )
 | 
						|
          wait_for_shutdown_${name}()
 | 
						|
    '';
 | 
						|
  };
 | 
						|
 | 
						|
  hostonlyVMFlags = [
 | 
						|
    "--nictype1 virtio"
 | 
						|
    "--nictype2 virtio"
 | 
						|
    "--nic2 hostonly"
 | 
						|
    "--hostonlyadapter2 vboxnet0"
 | 
						|
  ];
 | 
						|
 | 
						|
  # The VirtualBox Oracle Extension Pack lets you use USB 3.0 (xHCI).
 | 
						|
  enableExtensionPackVMFlags = [
 | 
						|
    "--usbxhci on"
 | 
						|
  ];
 | 
						|
 | 
						|
  dhcpScript = pkgs: ''
 | 
						|
    ${pkgs.dhcp}/bin/dhclient \
 | 
						|
      -lf /run/dhcp.leases \
 | 
						|
      -pf /run/dhclient.pid \
 | 
						|
      -v eth0 eth1
 | 
						|
 | 
						|
    otherIP="$(${pkgs.netcat}/bin/nc -l 1234 || :)"
 | 
						|
    ${pkgs.iputils}/bin/ping -I eth1 -c1 "$otherIP"
 | 
						|
    echo "$otherIP reachable" | ${pkgs.netcat}/bin/nc -l 5678 || :
 | 
						|
  '';
 | 
						|
 | 
						|
  sysdDetectVirt = pkgs: ''
 | 
						|
    ${pkgs.systemd}/bin/systemd-detect-virt > /mnt-root/result
 | 
						|
  '';
 | 
						|
 | 
						|
  vboxVMs = mapAttrs createVM {
 | 
						|
    simple = {};
 | 
						|
 | 
						|
    detectvirt.vmScript = sysdDetectVirt;
 | 
						|
 | 
						|
    test1.vmFlags = hostonlyVMFlags;
 | 
						|
    test1.vmScript = dhcpScript;
 | 
						|
 | 
						|
    test2.vmFlags = hostonlyVMFlags;
 | 
						|
    test2.vmScript = dhcpScript;
 | 
						|
 | 
						|
    headless.virtualisation.virtualbox.headless = true;
 | 
						|
    headless.services.xserver.enable = false;
 | 
						|
  };
 | 
						|
 | 
						|
  vboxVMsWithExtpack = mapAttrs createVM {
 | 
						|
    testExtensionPack.vmFlags = enableExtensionPackVMFlags;
 | 
						|
  };
 | 
						|
 | 
						|
  mkVBoxTest = useExtensionPack: vms: name: testScript: makeTest {
 | 
						|
    name = "virtualbox-${name}";
 | 
						|
 | 
						|
    machine = { lib, config, ... }: {
 | 
						|
      imports = let
 | 
						|
        mkVMConf = name: val: val.machine // { key = "${name}-config"; };
 | 
						|
        vmConfigs = mapAttrsToList mkVMConf vms;
 | 
						|
      in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs;
 | 
						|
      virtualisation.memorySize = 2048;
 | 
						|
      virtualisation.qemu.options =
 | 
						|
        if useKvmNestedVirt then ["-cpu" "kvm64,vmx=on"] else [];
 | 
						|
      virtualisation.virtualbox.host.enable = true;
 | 
						|
      test-support.displayManager.auto.user = "alice";
 | 
						|
      users.users.alice.extraGroups = let
 | 
						|
        inherit (config.virtualisation.virtualbox.host) enableHardening;
 | 
						|
      in lib.mkIf enableHardening (lib.singleton "vboxusers");
 | 
						|
      virtualisation.virtualbox.host.enableExtensionPack = useExtensionPack;
 | 
						|
      nixpkgs.config.allowUnfree = useExtensionPack;
 | 
						|
    };
 | 
						|
 | 
						|
    testScript = ''
 | 
						|
      from shlex import quote
 | 
						|
      ${concatStrings (mapAttrsToList (_: getAttr "testSubs") vms)}
 | 
						|
 | 
						|
      def ru(cmd: str) -> str:
 | 
						|
          return f"su - alice -c {quote(cmd)}"
 | 
						|
 | 
						|
 | 
						|
      def vbm(cmd: str) -> str:
 | 
						|
          return machine.succeed(ru(f"VBoxManage {cmd}"))
 | 
						|
 | 
						|
 | 
						|
      def remove_uuids(output: str) -> str:
 | 
						|
          return "\n".join(
 | 
						|
              [line for line in (output or "").splitlines() if not line.startswith("UUID:")]
 | 
						|
          )
 | 
						|
 | 
						|
 | 
						|
      machine.wait_for_x()
 | 
						|
 | 
						|
      # fmt: off
 | 
						|
      ${mkLog "$HOME/.config/VirtualBox/VBoxSVC.log" "HOST-SVC"}
 | 
						|
      # fmt: on
 | 
						|
 | 
						|
      ${testScript}
 | 
						|
      # (keep black happy)
 | 
						|
    '';
 | 
						|
 | 
						|
    meta = with pkgs.lib.maintainers; {
 | 
						|
      maintainers = [ aszlig cdepillabout ];
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  unfreeTests = mapAttrs (mkVBoxTest true vboxVMsWithExtpack) {
 | 
						|
    enable-extension-pack = ''
 | 
						|
      create_vm_testExtensionPack()
 | 
						|
      vbm("startvm testExtensionPack")
 | 
						|
      wait_for_startup_testExtensionPack()
 | 
						|
      machine.screenshot("cli_started")
 | 
						|
      wait_for_vm_boot_testExtensionPack()
 | 
						|
      machine.screenshot("cli_booted")
 | 
						|
 | 
						|
      with machine.nested("Checking for privilege escalation"):
 | 
						|
          machine.fail("test -e '/root/VirtualBox VMs'")
 | 
						|
          machine.fail("test -e '/root/.config/VirtualBox'")
 | 
						|
          machine.succeed("test -e '/home/alice/VirtualBox VMs'")
 | 
						|
 | 
						|
      shutdown_vm_testExtensionPack()
 | 
						|
      destroy_vm_testExtensionPack()
 | 
						|
    '';
 | 
						|
  };
 | 
						|
 | 
						|
in mapAttrs (mkVBoxTest false vboxVMs) {
 | 
						|
  simple-gui = ''
 | 
						|
    # Home to select Tools, down to move to the VM, enter to start it.
 | 
						|
    def send_vm_startup():
 | 
						|
        machine.send_key("home")
 | 
						|
        machine.send_key("down")
 | 
						|
        machine.send_key("ret")
 | 
						|
 | 
						|
 | 
						|
    create_vm_simple()
 | 
						|
    machine.succeed(ru("VirtualBox &"))
 | 
						|
    machine.wait_until_succeeds(ru("xprop -name 'Oracle VM VirtualBox Manager'"))
 | 
						|
    machine.sleep(5)
 | 
						|
    machine.screenshot("gui_manager_started")
 | 
						|
    send_vm_startup()
 | 
						|
    machine.screenshot("gui_manager_sent_startup")
 | 
						|
    wait_for_startup_simple(send_vm_startup)
 | 
						|
    machine.screenshot("gui_started")
 | 
						|
    wait_for_vm_boot_simple()
 | 
						|
    machine.screenshot("gui_booted")
 | 
						|
    shutdown_vm_simple()
 | 
						|
    machine.sleep(5)
 | 
						|
    machine.screenshot("gui_stopped")
 | 
						|
    machine.send_key("ctrl-q")
 | 
						|
    machine.sleep(5)
 | 
						|
    machine.screenshot("gui_manager_stopped")
 | 
						|
    destroy_vm_simple()
 | 
						|
  '';
 | 
						|
 | 
						|
  simple-cli = ''
 | 
						|
    create_vm_simple()
 | 
						|
    vbm("startvm simple")
 | 
						|
    wait_for_startup_simple()
 | 
						|
    machine.screenshot("cli_started")
 | 
						|
    wait_for_vm_boot_simple()
 | 
						|
    machine.screenshot("cli_booted")
 | 
						|
 | 
						|
    with machine.nested("Checking for privilege escalation"):
 | 
						|
        machine.fail("test -e '/root/VirtualBox VMs'")
 | 
						|
        machine.fail("test -e '/root/.config/VirtualBox'")
 | 
						|
        machine.succeed("test -e '/home/alice/VirtualBox VMs'")
 | 
						|
 | 
						|
    shutdown_vm_simple()
 | 
						|
    destroy_vm_simple()
 | 
						|
  '';
 | 
						|
 | 
						|
  headless = ''
 | 
						|
    create_vm_headless()
 | 
						|
    machine.succeed(ru("VBoxHeadless --startvm headless & disown %1"))
 | 
						|
    wait_for_startup_headless()
 | 
						|
    wait_for_vm_boot_headless()
 | 
						|
    shutdown_vm_headless()
 | 
						|
    destroy_vm_headless()
 | 
						|
  '';
 | 
						|
 | 
						|
  host-usb-permissions = ''
 | 
						|
    user_usb = remove_uuids(vbm("list usbhost"))
 | 
						|
    print(user_usb, file=sys.stderr)
 | 
						|
    root_usb = remove_uuids(machine.succeed("VBoxManage list usbhost"))
 | 
						|
    print(root_usb, file=sys.stderr)
 | 
						|
 | 
						|
    if user_usb != root_usb:
 | 
						|
        raise Exception("USB host devices differ for root and normal user")
 | 
						|
    if "<none>" in user_usb:
 | 
						|
        raise Exception("No USB host devices found")
 | 
						|
  '';
 | 
						|
 | 
						|
  systemd-detect-virt = ''
 | 
						|
    create_vm_detectvirt()
 | 
						|
    vbm("startvm detectvirt")
 | 
						|
    wait_for_startup_detectvirt()
 | 
						|
    wait_for_vm_boot_detectvirt()
 | 
						|
    shutdown_vm_detectvirt()
 | 
						|
    result = machine.succeed(f"cat '{detectvirt_sharepath}/result'").strip()
 | 
						|
    destroy_vm_detectvirt()
 | 
						|
    if result != "oracle":
 | 
						|
        raise Exception(f'systemd-detect-virt returned "{result}" instead of "oracle"')
 | 
						|
  '';
 | 
						|
 | 
						|
  net-hostonlyif = ''
 | 
						|
    create_vm_test1()
 | 
						|
    create_vm_test2()
 | 
						|
 | 
						|
    vbm("startvm test1")
 | 
						|
    wait_for_startup_test1()
 | 
						|
    wait_for_vm_boot_test1()
 | 
						|
 | 
						|
    vbm("startvm test2")
 | 
						|
    wait_for_startup_test2()
 | 
						|
    wait_for_vm_boot_test2()
 | 
						|
 | 
						|
    machine.screenshot("net_booted")
 | 
						|
 | 
						|
    test1_ip = wait_for_ip_test1(1)
 | 
						|
    test2_ip = wait_for_ip_test2(1)
 | 
						|
 | 
						|
    machine.succeed(f"echo '{test2_ip}' | nc -N '{test1_ip}' 1234")
 | 
						|
    machine.succeed(f"echo '{test1_ip}' | nc -N '{test2_ip}' 1234")
 | 
						|
 | 
						|
    machine.wait_until_succeeds(f"nc -N '{test1_ip}' 5678 < /dev/null >&2")
 | 
						|
    machine.wait_until_succeeds(f"nc -N '{test2_ip}' 5678 < /dev/null >&2")
 | 
						|
 | 
						|
    shutdown_vm_test1()
 | 
						|
    shutdown_vm_test2()
 | 
						|
 | 
						|
    destroy_vm_test1()
 | 
						|
    destroy_vm_test2()
 | 
						|
  '';
 | 
						|
} // (if enableUnfree then unfreeTests else {})
 |