clan-cli: implement waypipe backend
This commit is contained in:
parent
9fef40258a
commit
c417157941
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user