clan-cli/vms: add option --publish to forward ports from host to guest
This commit is contained in:
@@ -94,6 +94,7 @@ def qemu_command(
|
|||||||
virtiofsd_socket: Path,
|
virtiofsd_socket: Path,
|
||||||
qmp_socket_file: Path,
|
qmp_socket_file: Path,
|
||||||
qga_socket_file: Path,
|
qga_socket_file: Path,
|
||||||
|
portmap: list[tuple[int, int]] = [],
|
||||||
) -> QemuCommand:
|
) -> QemuCommand:
|
||||||
kernel_cmdline = [
|
kernel_cmdline = [
|
||||||
(Path(nixos_config["toplevel"]) / "kernel-params").read_text(),
|
(Path(nixos_config["toplevel"]) / "kernel-params").read_text(),
|
||||||
@@ -103,6 +104,7 @@ def qemu_command(
|
|||||||
]
|
]
|
||||||
if not vm.waypipe:
|
if not vm.waypipe:
|
||||||
kernel_cmdline.append("console=tty0")
|
kernel_cmdline.append("console=tty0")
|
||||||
|
hostfwd = ",".join(f"hostfwd=tcp::{h}-:{g}" for h, g in portmap)
|
||||||
# fmt: off
|
# fmt: off
|
||||||
command = [
|
command = [
|
||||||
"qemu-kvm",
|
"qemu-kvm",
|
||||||
@@ -116,7 +118,7 @@ def qemu_command(
|
|||||||
# speed-up boot by not waiting for the boot menu
|
# speed-up boot by not waiting for the boot menu
|
||||||
"-boot", "menu=off,strict=on",
|
"-boot", "menu=off,strict=on",
|
||||||
"-device", "virtio-rng-pci",
|
"-device", "virtio-rng-pci",
|
||||||
"-netdev", "user,id=user.0",
|
"-netdev", f"user,id=user.0,{hostfwd}",
|
||||||
"-device", "virtio-net-pci,netdev=user.0,romfile=",
|
"-device", "virtio-net-pci,netdev=user.0,romfile=",
|
||||||
"-chardev", f"socket,id=char1,path={virtiofsd_socket}",
|
"-chardev", f"socket,id=char1,path={virtiofsd_socket}",
|
||||||
"-device", "vhost-user-fs-pci,chardev=char1,tag=nix-store",
|
"-device", "vhost-user-fs-pci,chardev=char1,tag=nix-store",
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ def run_vm(
|
|||||||
cachedir: Path | None = None,
|
cachedir: Path | None = None,
|
||||||
socketdir: Path | None = None,
|
socketdir: Path | None = None,
|
||||||
nix_options: list[str] = [],
|
nix_options: list[str] = [],
|
||||||
|
portmap: list[tuple[int, int]] = [],
|
||||||
) -> None:
|
) -> None:
|
||||||
with ExitStack() as stack:
|
with ExitStack() as stack:
|
||||||
machine = Machine(name=vm.machine_name, flake=vm.flake_url)
|
machine = Machine(name=vm.machine_name, flake=vm.flake_url)
|
||||||
@@ -168,6 +169,7 @@ def run_vm(
|
|||||||
virtiofsd_socket=virtiofsd_socket,
|
virtiofsd_socket=virtiofsd_socket,
|
||||||
qmp_socket_file=qmp_socket_file,
|
qmp_socket_file=qmp_socket_file,
|
||||||
qga_socket_file=qga_socket_file,
|
qga_socket_file=qga_socket_file,
|
||||||
|
portmap=portmap,
|
||||||
)
|
)
|
||||||
|
|
||||||
packages = ["nixpkgs#qemu"]
|
packages = ["nixpkgs#qemu"]
|
||||||
@@ -199,7 +201,9 @@ def run_command(
|
|||||||
|
|
||||||
vm: VmConfig = inspect_vm(machine=machine_obj)
|
vm: VmConfig = inspect_vm(machine=machine_obj)
|
||||||
|
|
||||||
run_vm(vm, nix_options=args.option)
|
portmap = [(h, g) for h, g in (p.split(":") for p in args.publish)]
|
||||||
|
|
||||||
|
run_vm(vm, nix_options=args.option, portmap=portmap)
|
||||||
|
|
||||||
|
|
||||||
def register_run_parser(parser: argparse.ArgumentParser) -> None:
|
def register_run_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
@@ -207,4 +211,13 @@ def register_run_parser(parser: argparse.ArgumentParser) -> None:
|
|||||||
"machine", type=str, help="machine in the flake to run"
|
"machine", type=str, help="machine in the flake to run"
|
||||||
)
|
)
|
||||||
add_dynamic_completer(machine_action, complete_machines)
|
add_dynamic_completer(machine_action, complete_machines)
|
||||||
|
# option: --publish 2222:22
|
||||||
|
parser.add_argument(
|
||||||
|
"--publish",
|
||||||
|
"-p",
|
||||||
|
action="append",
|
||||||
|
type=str,
|
||||||
|
default=[],
|
||||||
|
help="Forward ports from host to guest",
|
||||||
|
)
|
||||||
parser.set_defaults(func=lambda args: run_command(args))
|
parser.set_defaults(func=lambda args: run_command(args))
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import contextlib
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
@@ -12,11 +14,21 @@ from clan_cli.qemu.qmp import QEMUMonitorProtocol
|
|||||||
from . import cli
|
from . import cli
|
||||||
|
|
||||||
|
|
||||||
def run_vm_in_thread(machine_name: str) -> None:
|
def find_free_port() -> int:
|
||||||
|
"""Find an unused localhost port from 1024-65535 and return it."""
|
||||||
|
with contextlib.closing(socket.socket(type=socket.SOCK_STREAM)) as sock:
|
||||||
|
sock.bind(("127.0.0.1", 0))
|
||||||
|
return sock.getsockname()[1]
|
||||||
|
|
||||||
|
|
||||||
|
def run_vm_in_thread(machine_name: str, ssh_port: int | None = None) -> int:
|
||||||
# runs machine and prints exceptions
|
# runs machine and prints exceptions
|
||||||
|
if ssh_port is None:
|
||||||
|
ssh_port = find_free_port()
|
||||||
|
|
||||||
def run() -> None:
|
def run() -> None:
|
||||||
try:
|
try:
|
||||||
cli.run(["vms", "run", machine_name])
|
cli.run(["vms", "run", machine_name, "--publish", f"{ssh_port}:22"])
|
||||||
except Exception:
|
except Exception:
|
||||||
# print exception details
|
# print exception details
|
||||||
print(traceback.format_exc(), file=sys.stderr)
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
@@ -26,7 +38,7 @@ def run_vm_in_thread(machine_name: str) -> None:
|
|||||||
t = threading.Thread(target=run, name="run")
|
t = threading.Thread(target=run, name="run")
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
return
|
return ssh_port
|
||||||
|
|
||||||
|
|
||||||
# wait for qmp socket to exist
|
# wait for qmp socket to exist
|
||||||
|
|||||||
Reference in New Issue
Block a user