Read port forwards from per-VM forwards.conf files
A QEMU config file section cannot hold more than one hostfwd entry, so the forwards move out of the ini files into qemu/<vm>/forwards.conf, one "LABEL HOST_PORT GUEST_PORT" rule per line. The new vmforwards.py parses them, skips rules whose host port is taken and builds the -netdev argument, which every launcher now passes instead of relying on a netdev section in the ini or on -nic. The ini files move to qemu/<vm>/config.ini and qemu/<vm>/<os>-<arch>.ini. The *_SSH_PORT / *_RDP_PORT variables and --rdp-port / --ssh-port still replace the host port of the SSH and RDP rules. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
07a2c6db3e
commit
78f6fd424b
29 files changed
+272
-131
No files matched your search
+23
-39
@@ -4,12 +4,13 @@
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import socket
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import vmforwards
|
||||
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
env = os.environ.get
|
||||
|
||||
@@ -34,17 +35,6 @@ def capture(command):
|
||||
return result.stdout.rstrip("\n")
|
||||
|
||||
|
||||
def port_in_use(port):
|
||||
"""True if a host TCP port cannot be bound, as QEMU does for a hostfwd rule."""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
|
||||
probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
probe.bind(("", int(port)))
|
||||
except OSError:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_executable(path):
|
||||
return path.is_file() and os.access(path, os.X_OK)
|
||||
|
||||
@@ -63,10 +53,12 @@ def find_image_downloader():
|
||||
|
||||
state_dir = Path(env("WIN11_VM_STATE_DIR") or script_dir / "vm" / "windows11")
|
||||
disk_size = env("WIN11_VM_DISK_SIZE") or "64G"
|
||||
rdp_port = env("WIN11_VM_RDP_PORT") or "3389"
|
||||
ssh_port = env("WIN11_VM_SSH_PORT") or "2222"
|
||||
rdp_port = env("WIN11_VM_RDP_PORT")
|
||||
ssh_port = env("WIN11_VM_SSH_PORT")
|
||||
forwards_file = Path(
|
||||
env("WIN11_VM_FORWARDS") or script_dir / "qemu" / "windows11" / "forwards.conf")
|
||||
qemu_config_template = Path(
|
||||
env("WIN11_VM_QEMU_CONFIG") or script_dir / "qemu" / "windows11.ini")
|
||||
env("WIN11_VM_QEMU_CONFIG") or script_dir / "qemu" / "windows11" / "config.ini")
|
||||
|
||||
|
||||
def normalize_arch(arch):
|
||||
@@ -95,8 +87,8 @@ Usage: {Path(sys.argv[0]).name} [--daemon] [--download-only] [--seed-only] [--vn
|
||||
If the system disk (DIR/disk.qcow2, see --state-dir) is missing, create it from
|
||||
the Windows 11 QCOW2 base image.
|
||||
The VM hardware (accelerator, memory, CPUs, drives, devices) is defined in
|
||||
qemu/windows11.ini, which is rendered to qemu.ini in the state directory and
|
||||
loaded with QEMU's -readconfig, followed by qemu/windows11-<os>-<arch>.ini (os is linux or darwin, arch is amd64 or arm64, chosen from the host system and
|
||||
qemu/windows11/config.ini, which is rendered to qemu.ini in the state directory and
|
||||
loaded with QEMU's -readconfig, followed by qemu/windows11/<os>-<arch>.ini (os is linux or darwin, arch is amd64 or arm64, chosen from the host system and
|
||||
architecture, --arch or VM_IMAGE_ARCH). Later QEMU arguments override them, e.g. -- -m 4G -smp 4.
|
||||
Use --install-from-iso to perform an automatic ISO installation instead, or
|
||||
--install-from-img to install from a disk image built from the ISO.
|
||||
@@ -124,10 +116,10 @@ Options:
|
||||
host runs under slow TCG emulation
|
||||
--state-dir DIR Directory for the VM disk, seed ISO and runtime files
|
||||
(default: vm/windows11; overrides WIN11_VM_STATE_DIR)
|
||||
--rdp-port PORT Host port forwarded to guest RDP (default: 3389;
|
||||
overrides WIN11_VM_RDP_PORT)
|
||||
--ssh-port PORT Host port forwarded to guest SSH (default: 2222;
|
||||
overrides WIN11_VM_SSH_PORT)
|
||||
--rdp-port PORT Host port of the RDP rule in the port forward file
|
||||
(default: 3389; overrides WIN11_VM_RDP_PORT)
|
||||
--ssh-port PORT Host port of the SSH rule in the port forward file
|
||||
(default: 2222; overrides WIN11_VM_SSH_PORT)
|
||||
-h, --help Show this help
|
||||
|
||||
Environment variables:
|
||||
@@ -135,8 +127,9 @@ Environment variables:
|
||||
WIN11_VM_INSTALL_ISO Windows 11 installation ISO used with --install-from-iso
|
||||
(--install-from-img uses the same path ending in .img)
|
||||
WIN11_VM_TOOLS_ISO VirtIO Windows tools ISO (default: resolved by shared image helper)
|
||||
WIN11_VM_RDP_PORT Host RDP port (default: 3389)
|
||||
WIN11_VM_SSH_PORT Host SSH port (default: 2222)
|
||||
WIN11_VM_RDP_PORT Host RDP port (default: from the port forward file, 3389)
|
||||
WIN11_VM_SSH_PORT Host SSH port (default: from the port forward file, 2222)
|
||||
WIN11_VM_FORWARDS Port forward file (default: qemu/windows11/forwards.conf)
|
||||
WIN11_VM_STATE_DIR Runtime directory (default: vm/windows11)
|
||||
WIN11_VM_DISK_IMAGE System disk path
|
||||
WIN11_VM_DISK_SIZE New system disk size (default: 64G)
|
||||
@@ -144,7 +137,7 @@ Environment variables:
|
||||
WIN11_VM_UEFI_CODE OVMF read-only firmware path (auto-detected if unset)
|
||||
WIN11_VM_UEFI_VARS OVMF variable-store template path (auto-detected if unset)
|
||||
WIN11_VM_QEMU_CONFIG QEMU config template passed to -readconfig
|
||||
(default: qemu/windows11.ini)
|
||||
(default: qemu/windows11/config.ini)
|
||||
WIN11_VM_DISPLAY QEMU display backend (default: cocoa on macOS,
|
||||
gtk elsewhere)
|
||||
""")
|
||||
@@ -209,7 +202,7 @@ qemu_binary = "qemu-system-aarch64" if host_arch == "arm64" else "qemu-system-x8
|
||||
host_os = platform.system().lower()
|
||||
if host_os == "darwin" and host_arch == "amd64":
|
||||
die("amd64 guests are not supported on macOS hosts; use an arm64 image")
|
||||
qemu_platform_config = script_dir / "qemu" / f"windows11-{host_os}-{host_arch}.ini"
|
||||
qemu_platform_config = script_dir / "qemu" / "windows11" / f"{host_os}-{host_arch}.ini"
|
||||
|
||||
|
||||
def pick_firmware(override, *candidates):
|
||||
@@ -430,18 +423,9 @@ except (KeyError, ValueError) as error:
|
||||
die(f"invalid placeholder in {qemu_config_template}: {error}")
|
||||
qemu_config.write_text(qemu_config_text)
|
||||
|
||||
# QEMU refuses to start when a forwarded host port is taken, so warn and leave
|
||||
# that forward out instead.
|
||||
forwards = []
|
||||
forwarded_ports = []
|
||||
for label, host_port, guest_port in (("RDP", rdp_port, 3389), ("SSH", ssh_port, 22)):
|
||||
in_use = port_in_use(host_port)
|
||||
forwarded_ports.append((label, host_port, not in_use))
|
||||
if in_use:
|
||||
print(f"Warning: host port {host_port} is already in use; "
|
||||
f"{label} will not be forwarded to the guest.", file=sys.stderr)
|
||||
else:
|
||||
forwards.append(f"hostfwd=tcp::{host_port}-:{guest_port}")
|
||||
# Each rule of the port forward file is one hostfwd entry of the network.
|
||||
forwards = vmforwards.usable_forwards(vmforwards.load_forwards(
|
||||
forwards_file, {label: port for label, port in (("RDP", rdp_port), ("SSH", ssh_port)) if port}))
|
||||
|
||||
# Neither Windows Setup nor the base image has the viogpudo driver yet, and
|
||||
# without it the virtio-gpu in the config file shows "display output is not
|
||||
@@ -456,7 +440,7 @@ qemu_args += [
|
||||
# A config file section cannot hold two hostfwd entries, so the network
|
||||
# that the NIC in the config file uses is defined here. -nic none stops
|
||||
# QEMU from adding its default NIC as well.
|
||||
"-netdev", ",".join(["user,id=net0", *forwards]),
|
||||
"-netdev", vmforwards.netdev_argument(forwards),
|
||||
"-nic", "none",
|
||||
"-display", display_backend,
|
||||
# "-display", "none", "-spice", "port=5900,addr=127.0.0.1,disable-ticketing=on,agent-mouse=off", "-device", "virtio-vga",
|
||||
@@ -499,7 +483,7 @@ elif installing:
|
||||
else:
|
||||
print("Starting Windows 11 normally.")
|
||||
print("Account: guofang / password; " + "; ".join(
|
||||
f"{label}: 0.0.0.0:{port}" for label, port, forwarded in forwarded_ports if forwarded),
|
||||
f"{forward.label}: 0.0.0.0:{forward.host_port}" for forward in forwards),
|
||||
flush=True)
|
||||
|
||||
if daemonize:
|
||||
|
||||
Reference in new issue
Block a user