Merge pull request 'clan-cli: implement waypipe backend' (#772) from Mic92-main into main
All checks were successful
checks / test (push) Successful in 29s
checks-impure / test (push) Successful in 1m21s

This commit is contained in:
clan-bot 2024-01-30 03:42:07 +00:00
commit c7b9db8b49
4 changed files with 92 additions and 25 deletions

View File

@ -43,14 +43,14 @@ def handle_output(process: subprocess.Popen, log: Log) -> tuple[str, str]:
return b""
ret = handle_fd(process.stdout)
if log in [Log.STDOUT, Log.BOTH]:
if ret and log in [Log.STDOUT, Log.BOTH]:
sys.stdout.buffer.write(ret)
sys.stdout.flush()
stdout_buf += ret
ret = handle_fd(process.stderr)
if log in [Log.STDERR, Log.BOTH]:
if ret and log in [Log.STDERR, Log.BOTH]:
sys.stderr.buffer.write(ret)
sys.stderr.flush()
stderr_buf += ret

View File

@ -86,7 +86,3 @@ def nixpkgs_flake() -> Path:
def nixpkgs_source() -> Path:
return (module_root() / "nixpkgs" / "path").resolve()
def unfree_nixpkgs() -> Path:
return module_root() / "nixpkgs" / "unfree"

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

View File

@ -31,7 +31,6 @@
, mypy
, rope
, clan-core-path
, writeShellScriptBin
}:
let
@ -40,8 +39,6 @@ let
];
pytestDependencies = runtimeDependencies ++ dependencies ++ [
#schemathesis # optional for http fuzzing
pytest
pytest-cov
pytest-subprocess