wfvm/wfvm/utils.nix

132 lines
3.6 KiB
Nix

{ pkgs
, baseRtc ? "2022-10-10T10:10:10"
, cores ? "4"
, qemuMem ? "4G"
, efi ? true
, enableTpm ? false
, ...
}:
rec {
# qemu_test is a smaller closure only building for a single system arch
qemu = pkgs.qemu;
OVMF = pkgs.OVMF.override {
secureBoot = true;
};
mkQemuFlags = extraFlags: [
"-enable-kvm"
"-cpu host"
"-smp ${cores}"
"-m ${qemuMem}"
"-M q35,smm=on"
"-vga qxl"
"-rtc base=${baseRtc}"
"-device qemu-xhci"
"-device virtio-net-pci,netdev=n1"
] ++ pkgs.lib.optionals efi [
"-bios ${OVMF.fd}/FV/OVMF.fd"
] ++ pkgs.lib.optionals enableTpm [
"-chardev" "socket,id=chrtpm,path=tpm.sock"
"-tpmdev" "emulator,id=tpm0,chardev=chrtpm"
"-device" "tpm-tis,tpmdev=tpm0"
] ++ extraFlags;
tpmStartCommands = pkgs.lib.optionalString enableTpm ''
mkdir -p tpmstate
${pkgs.swtpm}/bin/swtpm socket \
--tpmstate dir=tpmstate \
--ctrl type=unixio,path=tpm.sock &
'';
# Pass empty config file to prevent ssh from failing to create ~/.ssh
sshOpts = "-F /dev/null -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=1";
win-exec = pkgs.writeShellScriptBin "win-exec" ''
set -e
${pkgs.sshpass}/bin/sshpass -p1234 -- \
${pkgs.openssh}/bin/ssh -np 2022 ${sshOpts} \
wfvm@localhost \
$1
'';
win-wait = pkgs.writeShellScriptBin "win-wait" ''
set -e
# If the machine is not up within 10 minutes it's likely never coming up
timeout=600
# Wait for VM to be accessible
sleep 20
echo "Waiting for SSH..."
while true; do
if test "$timeout" -eq 0; then
echo "SSH connection timed out"
exit 1
fi
output=$(${win-exec}/bin/win-exec 'echo|set /p="Ran command"' || echo "")
if test "$output" = "Ran command"; then
break
fi
echo "Retrying in 1 second, timing out in $timeout seconds"
((timeout=$timeout-1))
sleep 1
done
echo "SSH OK"
'';
win-put = pkgs.writeShellScriptBin "win-put" ''
set -e
echo win-put $1 -\> $2
${pkgs.sshpass}/bin/sshpass -p1234 -- \
${pkgs.openssh}/bin/sftp -r -P 2022 ${sshOpts} \
wfvm@localhost -b- << EOF
cd $2
put $1
EOF
'';
win-get = pkgs.writeShellScriptBin "win-get" ''
set -e
echo win-get $1
${pkgs.sshpass}/bin/sshpass -p1234 -- \
${pkgs.openssh}/bin/sftp -r -P 2022 ${sshOpts} \
wfvm@localhost:$1 .
'';
wfvm-run = { name, image, script, display ? false, isolateNetwork ? true, forwardedPorts ? [], fakeRtc ? true }:
let
restrict =
if isolateNetwork
then "on"
else "off";
# use socat instead of `tcp:...` to allow multiple connections
guestfwds =
builtins.concatStringsSep ""
(map ({ listenAddr, targetAddr, port }:
",guestfwd=tcp:${listenAddr}:${toString port}-cmd:${pkgs.socat}/bin/socat\\ -\\ tcp:${targetAddr}:${toString port}"
) forwardedPorts);
qemuParams = mkQemuFlags (pkgs.lib.optional (!display) "-display none" ++ pkgs.lib.optional (!fakeRtc) "-rtc base=localtime" ++ [
"-drive"
"file=${image},index=0,media=disk,cache=unsafe"
"-snapshot"
"-netdev user,id=n1,net=192.168.1.0/24,restrict=${restrict},hostfwd=tcp::2022-:22${guestfwds}"
]);
in pkgs.writeShellScriptBin "wfvm-run-${name}" ''
set -e -m
${tpmStartCommands}
${qemu}/bin/qemu-system-x86_64 ${pkgs.lib.concatStringsSep " " qemuParams} &
${win-wait}/bin/win-wait
${script}
echo "Shutting down..."
${win-exec}/bin/win-exec 'shutdown /s'
echo "Waiting for VM to terminate..."
fg
echo "Done"
'';
}