clan-cli: implement waypipe backend

This commit is contained in:
Jörg Thalheim
2024-01-30 10:29:39 +07:00
parent 7b60678d1f
commit 33d755360d

View File

@@ -1,9 +1,15 @@
import argparse
import contextlib
import importlib
import json
import logging
import os
import random
import socket
import subprocess
import tempfile
import time
from collections.abc import Iterator
from dataclasses import dataclass, field
from pathlib import Path
from typing import IO
@@ -19,21 +25,38 @@ from .inspect import VmConfig, inspect_vm
log = logging.getLogger(__name__)
def graphics_options(vm: VmConfig) -> list[str]:
common = ["-audio", "driver=pa,model=virtio"]
@dataclass
class GraphicOptions:
args: list[str]
vsock_cid: int | None = None
def graphics_options(vm: VmConfig) -> GraphicOptions:
common = [
"-audio",
"driver=pa,model=virtio",
]
if vm.wayland:
# FIXME: check for collisions
cid = random.randint(1, 2**32)
# fmt: off
return [
return GraphicOptions([
*common,
"-nographic",
"-vga", "none",
"-device", "virtio-gpu-rutabaga,gfxstream-vulkan=on,cross-domain=on,hostmem=4G,wsi=headless",
]
"-device", f"vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid={cid}",
# TODO: vgpu
#"-display", "egl-headless,gl=core",
#"-device", "virtio-vga,blob=true",
#"-device", "virtio-serial-pci",
#"-device", "vhost-user-vga,chardev=vgpu",
#"-chardev", "socket,id=vgpu,path=/tmp/vgpu.sock",
], cid)
# fmt: on
else:
# fmt: off
return [
return GraphicOptions([
*common,
"-vga", "none",
"-display", "gtk,gl=on",
@@ -52,10 +75,16 @@ def graphics_options(vm: VmConfig) -> list[str]:
"-device", "pci-ohci,id=smartpass",
"-device", "usb-ccid",
"-chardev", "spicevmc,id=ccid,name=smartcard",
]
], None)
# fmt: on
@dataclass
class QemuCommand:
args: list[str]
vsock_cid: int | None = None
def qemu_command(
vm: VmConfig,
nixos_config: dict[str, str],
@@ -63,7 +92,7 @@ def qemu_command(
secrets_dir: Path,
state_dir: Path,
disk_img: Path,
) -> list[str]:
) -> QemuCommand:
kernel_cmdline = [
(Path(nixos_config["toplevel"]) / "kernel-params").read_text(),
f'init={nixos_config["toplevel"]}/init',
@@ -77,6 +106,8 @@ def qemu_command(
"qemu-kvm",
"-name", vm.machine_name,
"-m", f'{nixos_config["memorySize"]}M',
"-object", f"memory-backend-memfd,id=mem,size={nixos_config['memorySize']}M",
"-machine", "pc,memory-backend=mem,accel=kvm",
"-smp", str(nixos_config["cores"]),
"-cpu", "max",
"-enable-kvm",
@@ -97,11 +128,14 @@ def qemu_command(
"-append", " ".join(kernel_cmdline),
] # fmt: on
vsock_cid = None
if vm.graphics:
command.extend(graphics_options(vm))
opts = graphics_options(vm)
vsock_cid = opts.vsock_cid
command.extend(opts.args)
else:
command.append("-nographic")
return command
return QemuCommand(command, vsock_cid=vsock_cid)
# TODO move this to the Machines class
@@ -185,6 +219,45 @@ def prepare_disk(tmpdir: Path, log_fd: IO[str] | None) -> Path:
return disk_img
VMADDR_CID_HYPERVISOR = 2
def test_vsock_port(port: int) -> bool:
try:
s = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
s.connect((VMADDR_CID_HYPERVISOR, port))
s.close()
return True
except OSError:
return False
@contextlib.contextmanager
def start_waypipe(cid: int | None, title_prefix: str) -> Iterator[None]:
if cid is None:
yield
return
waypipe = nix_shell(
["git+https://git.clan.lol/clan/clan-core#waypipe"],
[
"waypipe",
"--vsock",
"--socket",
f"s{cid}:3049",
"--title-prefix",
title_prefix,
"client",
],
)
with subprocess.Popen(waypipe) as proc:
try:
while not test_vsock_port(3049):
time.sleep(0.1)
yield
finally:
proc.kill()
def run_vm(
vm: VmConfig,
nix_options: list[str] = [],
@@ -229,12 +302,13 @@ def run_vm(
"XDG_DATA_DIRS"
] = f"{remote_viewer_mimetypes}:{env.get('XDG_DATA_DIRS', '')}"
run(
nix_shell(packages, qemu_cmd),
env=env,
log=Log.BOTH,
error_msg=f"Could not start vm {machine}",
)
with start_waypipe(qemu_cmd.vsock_cid, f"[{vm.machine_name}] "):
run(
nix_shell(packages, qemu_cmd.args),
env=env,
log=Log.BOTH,
error_msg=f"Could not start vm {machine}",
)
@dataclass