Remove shell VM launchers in favor of the Python ones

Delete run-*.sh and point the documentation at run-*.py. The
seediso build-seed.sh helpers stay, as the Python launchers call them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HL6bzSwWGNwru9bDS9ZCLg
This commit is contained in:
GuofangandClaude Opus 5.5 committed 2026-09-30 09:22:40 +08:00
1 parent 8a7f974c11
commit 3561fc6729
12 files changed
+24 -705

No files matched your search

+17 -17
View File
@@ -3,10 +3,10 @@
Start the required operating system with its distribution-specific launcher:
```bash
./run-debian13.sh
./run-almalinux10.sh
./run-fedora44.sh
./run-windows11.sh
./run-debian13.py
./run-almalinux10.py
./run-fedora44.py
./run-windows11.py
```
All virtual machines use OVMF/UEFI firmware. Each launcher creates a private,
@@ -16,16 +16,16 @@ Closing the QEMU window stops a foreground VM. To keep the launcher in the
background while still showing the graphical window:
```bash
./run-debian13.sh --daemon
./run-debian13.py --daemon
```
The script uses KVM when `/dev/kvm` is accessible, forwards host port `2222`
The launcher uses KVM when `/dev/kvm` is accessible, forwards host port `2222`
to guest port `22`, and creates `.vm/debian13/disk.qcow2` as a 20G writable overlay with the
original qcow2 image as its backing file. The base image therefore stays
unchanged. Resource settings can be overridden with environment variables;
run `./run-debian13.sh --help` for the full list.
run `./run-debian13.py --help` for the full list.
The Python launchers (`run-*.py`) accept `--state-dir DIR` to keep
The launchers accept `--state-dir DIR` to keep
the VM disk, seed ISO, UEFI variables, PID file and console log in `DIR`
instead of the default `.vm/<name>/`. The option takes precedence over the
corresponding `*_STATE_DIR` environment variable:
@@ -58,7 +58,7 @@ helper with `debian13`.
Start an AlmaLinux 10 Generic Cloud VM with:
```bash
./run-almalinux10.sh
./run-almalinux10.py
```
When the shared AlmaLinux image is missing, the launcher calls the selected
@@ -73,10 +73,10 @@ ssh -p 2223 guofang@localhost
AlmaLinux settings use the `ALMA_VM_` prefix. For example:
```bash
ALMA_VM_MEMORY=4G ALMA_VM_CPUS=4 ALMA_VM_SSH_PORT=2224 ./run-almalinux10.sh
ALMA_VM_MEMORY=4G ALMA_VM_CPUS=4 ALMA_VM_SSH_PORT=2224 ./run-almalinux10.py
```
Run `./run-almalinux10.sh --help` for all supported settings. The seed ISO is
Run `./run-almalinux10.py --help` for all supported settings. The seed ISO is
stored alongside the VM disk at `.vm/almalinux10/seed.iso`. If it already
exists after editing its source files, rebuild it explicitly with
`./seediso/almalinux10/build-seed.sh .vm/almalinux10/seed.iso`.
@@ -86,7 +86,7 @@ exists after editing its source files, rebuild it explicitly with
Start a Fedora 44 Cloud Base VM with:
```bash
./run-fedora44.sh
./run-fedora44.py
```
The launcher downloads the `fedora44` image through the shared helper when it
@@ -101,7 +101,7 @@ On the first boot, cloud-init updates package metadata and installs Fedora's
GNOME package group through its native package module, then reboots the guest.
Keep the VM running until this initial installation and reboot have completed.
Fedora settings use the `FEDORA_VM_` prefix. Run `./run-fedora44.sh --help`
Fedora settings use the `FEDORA_VM_` prefix. Run `./run-fedora44.py --help`
for the complete list of overrides.
## Windows 11 23H2
@@ -110,7 +110,7 @@ On first boot, the Windows 11 launcher creates a writable system-disk overlay
from the shared Windows 11 QCOW2 base image:
```bash
./run-windows11.sh
./run-windows11.py
```
It creates a persistent 64G disk at `.vm/windows11/disk.qcow2`, automatically
@@ -126,15 +126,15 @@ exposes the vdagent channel for host/guest clipboard integration and the QEMU Gu
The generated answer-file ISO is stored beside the disk as
`.vm/windows11/seed.iso`.
To install from the Windows ISO instead, use `./run-windows11.sh
To install from the Windows ISO instead, use `./run-windows11.py
--install-from-iso`. This choice only applies while creating a missing system
disk; subsequent starts always boot the existing system disk normally. During
ISO installation, the launcher resolves its tools ISO through the shared image
helper and downloads it when missing. Override the base QCOW2, source ISO,
tools ISO, resources, RDP port, or SSH port with the `WIN11_VM_` environment
variables shown by `./run-windows11.sh --help`. For example, when the VirtIO ISO
variables shown by `./run-windows11.py --help`. For example, when the VirtIO ISO
is elsewhere:
```bash
WIN11_VM_TOOLS_ISO=/custom/path/virtio-win.iso ./run-windows11.sh --install-from-iso
WIN11_VM_TOOLS_ISO=/custom/path/virtio-win.iso ./run-windows11.py --install-from-iso
```
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Start the AlmaLinux 10 virtual machine (Python port of run-almalinux10.sh)."""
"""Start the AlmaLinux 10 virtual machine."""
import os
import platform
-169
View File
@@ -1,169 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -n "${VM_IMAGE_HELPER:-}" ]]; then
image_downloader="$VM_IMAGE_HELPER"
elif [[ -x "${script_dir}/vm-images/download-images.sh" ]]; then
image_downloader="${script_dir}/vm-images/download-images.sh"
else
image_downloader="${script_dir}/../vm-images/download-images.sh"
fi
uses_shared_base_image=false
if [[ -n "${ALMA_VM_BASE_IMAGE:-}" ]]; then
base_image="$ALMA_VM_BASE_IMAGE"
else
[[ -x "$image_downloader" ]] || { printf 'Error: shared image helper is unavailable: %s\n' "$image_downloader" >&2; exit 1; }
base_image="$("$image_downloader" --print-path almalinux10)"
uses_shared_base_image=true
fi
state_dir="${ALMA_VM_STATE_DIR:-${script_dir}/.vm/almalinux10}"
disk_image="${ALMA_VM_DISK_IMAGE:-${state_dir}/disk.qcow2}"
seed_image="${ALMA_VM_SEED_IMAGE:-$(dirname -- "$disk_image")/seed.iso}"
disk_size="${ALMA_VM_DISK_SIZE:-20G}"
memory="${ALMA_VM_MEMORY:-8G}"
cpus="${ALMA_VM_CPUS:-8}"
ssh_port="${ALMA_VM_SSH_PORT:-2223}"
uefi_code="${ALMA_VM_UEFI_CODE:-/usr/share/OVMF/OVMF_CODE_4M.fd}"
uefi_vars_template="${ALMA_VM_UEFI_VARS:-/usr/share/OVMF/OVMF_VARS_4M.fd}"
uefi_vars="${state_dir}/OVMF_VARS_4M.fd"
pid_file="${state_dir}/qemu.pid"
log_file="${state_dir}/console.log"
daemonize=false
extra_args=()
usage() {
cat <<EOF
Usage: $(basename "$0") [--daemon] [-- QEMU_ARGUMENT ...]
Start an AlmaLinux 10 Generic Cloud virtual machine. The launcher obtains a
missing base image through ../vm-images/download-images.sh and creates a
writable overlay under .vm/almalinux10/, leaving the base image unchanged.
Options:
--daemon Run in the background while keeping the graphical window
-h, --help Show this help
Environment variables:
ALMA_VM_MEMORY Guest memory (default: 8G)
ALMA_VM_CPUS Virtual CPU count (default: 8)
ALMA_VM_SSH_PORT Host port forwarded to guest SSH (default: 2223)
ALMA_VM_BASE_IMAGE Base qcow2 image path (default: resolved by shared image helper)
ALMA_VM_SEED_IMAGE cloud-init seed ISO path (default: alongside the VM disk)
ALMA_VM_STATE_DIR Runtime state directory (default: .vm/almalinux10)
ALMA_VM_DISK_IMAGE Writable overlay path
ALMA_VM_DISK_SIZE New overlay virtual capacity (default: 20G)
ALMA_VM_UEFI_CODE OVMF read-only firmware path
ALMA_VM_UEFI_VARS OVMF variable-store template path
EOF
}
while (($#)); do
case "$1" in
--daemon)
daemonize=true
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
extra_args+=("$@")
break
;;
*)
printf 'Error: unknown option: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
for command_name in qemu-system-x86_64 qemu-img; do
if ! command -v "$command_name" >/dev/null 2>&1; then
printf 'Error: %s is not installed.\n' "$command_name" >&2
exit 1
fi
done
if [[ ! -f "$base_image" ]]; then
if [[ "$uses_shared_base_image" == true ]]; then
"$image_downloader" almalinux10
else
printf 'Error: AlmaLinux base image was not found: %s\n' "$base_image" >&2
exit 1
fi
fi
[[ -f "$base_image" ]] || { printf 'Error: downloader did not create: %s\n' "$base_image" >&2; exit 1; }
for firmware_file in "$uefi_code" "$uefi_vars_template"; do
[[ -f "$firmware_file" ]] || { printf 'Error: required UEFI firmware was not found: %s\n' "$firmware_file" >&2; exit 1; }
done
if [[ ! -f "$seed_image" ]]; then
seed_builder="${script_dir}/seediso/almalinux10/build-seed.sh"
if [[ ! -x "$seed_builder" ]]; then
printf 'Error: seed image is missing and builder is unavailable: %s\n' \
"$seed_builder" >&2
exit 1
fi
printf 'Seed image not found; building %s...\n' "$seed_image"
"$seed_builder" "$seed_image"
fi
mkdir -p "$state_dir"
[[ -f "$uefi_vars" ]] || cp -- "$uefi_vars_template" "$uefi_vars"
if [[ -f "$pid_file" ]]; then
old_pid="$(<"$pid_file")"
if [[ "$old_pid" =~ ^[0-9]+$ ]] && kill -0 "$old_pid" 2>/dev/null; then
printf 'Error: the AlmaLinux virtual machine is already running (PID %s).\n' \
"$old_pid" >&2
exit 1
fi
rm -f "$pid_file"
fi
if [[ ! -f "$disk_image" ]]; then
qemu-img create -q -f qcow2 -F qcow2 -b "$base_image" "$disk_image" "$disk_size"
printf 'Created writable disk overlay: %s\n' "$disk_image"
fi
qemu_args=(
-name almalinux10
-machine q35
-display gtk
-m "$memory"
-smp "$cpus"
-drive "if=pflash,format=raw,readonly=on,file=${uefi_code}"
-drive "if=pflash,format=raw,file=${uefi_vars}"
-drive "file=${disk_image},if=virtio,format=qcow2"
-drive "file=${seed_image},media=cdrom,readonly=on"
-nic "user,model=virtio-net-pci,hostfwd=tcp::${ssh_port}-:22"
)
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
qemu_args+=(-enable-kvm -cpu host)
accelerator=KVM
else
qemu_args+=(-accel tcg -cpu max)
accelerator=TCG
fi
printf 'Starting AlmaLinux 10 with %s acceleration; SSH is forwarded to localhost:%s.\n' \
"$accelerator" "$ssh_port"
if [[ "$daemonize" == true ]]; then
: >"$log_file"
qemu_args+=(
-serial "file:${log_file}"
-monitor none
-daemonize
-pidfile "$pid_file"
)
qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
printf 'VM started (PID %s); console log: %s\n' "$(<"$pid_file")" "$log_file"
else
exec qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
fi
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Start the Debian 13 virtual machine (Python port of run-debian13.sh)."""
"""Start the Debian 13 virtual machine."""
import os
import platform
-167
View File
@@ -1,167 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -n "${VM_IMAGE_HELPER:-}" ]]; then
image_downloader="$VM_IMAGE_HELPER"
elif [[ -x "${script_dir}/vm-images/download-images.sh" ]]; then
image_downloader="${script_dir}/vm-images/download-images.sh"
else
image_downloader="${script_dir}/../vm-images/download-images.sh"
fi
uses_shared_base_image=false
if [[ -n "${VM_BASE_IMAGE:-}" ]]; then
base_image="$VM_BASE_IMAGE"
else
[[ -x "$image_downloader" ]] || { printf 'Error: shared image helper is unavailable: %s\n' "$image_downloader" >&2; exit 1; }
base_image="$("$image_downloader" --print-path debian13)"
uses_shared_base_image=true
fi
state_dir="${VM_STATE_DIR:-${script_dir}/.vm/debian13}"
disk_image="${VM_DISK_IMAGE:-${state_dir}/disk.qcow2}"
seed_image="${VM_SEED_IMAGE:-$(dirname -- "$disk_image")/seed.iso}"
disk_size="${VM_DISK_SIZE:-20G}"
memory="${VM_MEMORY:-8G}"
cpus="${VM_CPUS:-8}"
ssh_port="${VM_SSH_PORT:-2222}"
uefi_code="${VM_UEFI_CODE:-/usr/share/OVMF/OVMF_CODE_4M.fd}"
uefi_vars_template="${VM_UEFI_VARS:-/usr/share/OVMF/OVMF_VARS_4M.fd}"
uefi_vars="${state_dir}/OVMF_VARS_4M.fd"
pid_file="${state_dir}/qemu.pid"
log_file="${state_dir}/console.log"
daemonize=false
extra_args=()
usage() {
cat <<EOF
Usage: $(basename "$0") [--daemon] [-- QEMU_ARGUMENT ...]
Start the Debian 13 virtual machine. A writable overlay is created under
.vm/debian13/, so the downloaded base image is never modified.
Options:
--daemon Run in the background while keeping the graphical window
-h, --help Show this help
Environment variables:
VM_MEMORY Guest memory (default: 8G)
VM_CPUS Virtual CPU count (default: 8)
VM_SSH_PORT Host port forwarded to guest SSH (default: 2222)
VM_BASE_IMAGE Base qcow2 image path (default: resolved by shared image helper)
VM_SEED_IMAGE cloud-init seed ISO path (default: alongside the VM disk)
VM_STATE_DIR Runtime state directory (default: .vm/debian13)
VM_DISK_IMAGE Writable overlay path
VM_DISK_SIZE New overlay virtual capacity (default: 20G)
VM_UEFI_CODE OVMF read-only firmware path
VM_UEFI_VARS OVMF variable-store template path
EOF
}
while (($#)); do
case "$1" in
--daemon)
daemonize=true
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
extra_args+=("$@")
break
;;
*)
printf 'Error: unknown option: %s\n' "$1" >&2
usage >&2
exit 2
;;
esac
done
for command_name in qemu-system-x86_64 qemu-img; do
if ! command -v "$command_name" >/dev/null 2>&1; then
printf 'Error: %s is not installed.\n' "$command_name" >&2
exit 1
fi
done
if [[ ! -f "$base_image" ]]; then
if [[ "$uses_shared_base_image" == true ]]; then
"$image_downloader" debian13
else
printf 'Error: Debian base image was not found: %s\n' "$base_image" >&2
exit 1
fi
fi
[[ -f "$base_image" ]] || { printf 'Error: downloader did not create: %s\n' "$base_image" >&2; exit 1; }
for firmware_file in "$uefi_code" "$uefi_vars_template"; do
[[ -f "$firmware_file" ]] || { printf 'Error: required UEFI firmware was not found: %s\n' "$firmware_file" >&2; exit 1; }
done
if [[ ! -f "$seed_image" ]]; then
seed_builder="${script_dir}/seediso/debian13/build-seed.sh"
if [[ ! -x "$seed_builder" ]]; then
printf 'Error: seed image is missing and builder is unavailable: %s\n' \
"$seed_builder" >&2
exit 1
fi
printf 'Seed image not found; building %s...\n' "$seed_image"
"$seed_builder" "$seed_image"
fi
mkdir -p "$state_dir"
[[ -f "$uefi_vars" ]] || cp -- "$uefi_vars_template" "$uefi_vars"
if [[ -f "$pid_file" ]]; then
old_pid="$(<"$pid_file")"
if [[ "$old_pid" =~ ^[0-9]+$ ]] && kill -0 "$old_pid" 2>/dev/null; then
printf 'Error: the virtual machine is already running (PID %s).\n' "$old_pid" >&2
exit 1
fi
rm -f "$pid_file"
fi
if [[ ! -f "$disk_image" ]]; then
qemu-img create -q -f qcow2 -F qcow2 -b "$base_image" "$disk_image" "$disk_size"
printf 'Created writable disk overlay: %s\n' "$disk_image"
fi
qemu_args=(
-name debian13
-machine q35
-display gtk
-m "$memory"
-smp "$cpus"
-drive "if=pflash,format=raw,readonly=on,file=${uefi_code}"
-drive "if=pflash,format=raw,file=${uefi_vars}"
-drive "file=${disk_image},if=virtio,format=qcow2"
-drive "file=${seed_image},media=cdrom,readonly=on"
-nic "user,model=virtio-net-pci,hostfwd=tcp::${ssh_port}-:22"
)
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
qemu_args+=(-enable-kvm -cpu host)
accelerator=KVM
else
qemu_args+=(-accel tcg -cpu max)
accelerator=TCG
fi
printf 'Starting VM with %s acceleration; SSH is forwarded to localhost:%s.\n' \
"$accelerator" "$ssh_port"
if [[ "$daemonize" == true ]]; then
: >"$log_file"
qemu_args+=(
-serial "file:${log_file}"
-monitor none
-daemonize
-pidfile "$pid_file"
)
qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
printf 'VM started (PID %s); console log: %s\n' "$(<"$pid_file")" "$log_file"
else
exec qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
fi
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Start the Fedora 44 virtual machine (Python port of run-fedora44.sh)."""
"""Start the Fedora 44 virtual machine."""
import os
import platform
-153
View File
@@ -1,153 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -n "${VM_IMAGE_HELPER:-}" ]]; then
image_downloader="$VM_IMAGE_HELPER"
elif [[ -x "${script_dir}/vm-images/download-images.sh" ]]; then
image_downloader="${script_dir}/vm-images/download-images.sh"
else
image_downloader="${script_dir}/../vm-images/download-images.sh"
fi
uses_shared_base_image=false
if [[ -n "${FEDORA_VM_BASE_IMAGE:-}" ]]; then
base_image="$FEDORA_VM_BASE_IMAGE"
else
[[ -x "$image_downloader" ]] || { printf 'Error: shared image helper is unavailable: %s\n' "$image_downloader" >&2; exit 1; }
base_image="$("$image_downloader" --print-path fedora44)"
uses_shared_base_image=true
fi
state_dir="${FEDORA_VM_STATE_DIR:-${script_dir}/.vm/fedora44}"
disk_image="${FEDORA_VM_DISK_IMAGE:-${state_dir}/disk.qcow2}"
seed_image="${FEDORA_VM_SEED_IMAGE:-$(dirname -- "$disk_image")/seed.iso}"
disk_size="${FEDORA_VM_DISK_SIZE:-20G}"
memory="${FEDORA_VM_MEMORY:-8G}"
cpus="${FEDORA_VM_CPUS:-8}"
ssh_port="${FEDORA_VM_SSH_PORT:-2224}"
uefi_code="${FEDORA_VM_UEFI_CODE:-/usr/share/OVMF/OVMF_CODE_4M.fd}"
uefi_vars_template="${FEDORA_VM_UEFI_VARS:-/usr/share/OVMF/OVMF_VARS_4M.fd}"
uefi_vars="${state_dir}/OVMF_VARS_4M.fd"
pid_file="${state_dir}/qemu.pid"
log_file="${state_dir}/console.log"
daemonize=false
extra_args=()
usage() {
cat <<EOF
Usage: $(basename "$0") [--daemon] [-- QEMU_ARGUMENT ...]
Start a Fedora 44 Cloud Base virtual machine. The launcher obtains a missing
base image through the shared image helper and creates a writable overlay
under .vm/fedora44/, leaving the base image unchanged.
Options:
--daemon Run in the background while keeping the graphical window
-h, --help Show this help
Environment variables:
FEDORA_VM_MEMORY Guest memory (default: 8G)
FEDORA_VM_CPUS Virtual CPU count (default: 8)
FEDORA_VM_SSH_PORT Host port forwarded to guest SSH (default: 2224)
FEDORA_VM_BASE_IMAGE Base qcow2 image path (default: resolved by shared image helper)
FEDORA_VM_SEED_IMAGE cloud-init seed ISO path (default: alongside the VM disk)
FEDORA_VM_STATE_DIR Runtime state directory (default: .vm/fedora44)
FEDORA_VM_DISK_IMAGE Writable overlay path
FEDORA_VM_DISK_SIZE New overlay virtual capacity (default: 20G)
FEDORA_VM_UEFI_CODE OVMF read-only firmware path
FEDORA_VM_UEFI_VARS OVMF variable-store template path
EOF
}
while (($#)); do
case "$1" in
--daemon) daemonize=true; shift ;;
-h|--help) usage; exit 0 ;;
--) shift; extra_args+=("$@"); break ;;
*) printf 'Error: unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;;
esac
done
for command_name in qemu-system-x86_64 qemu-img; do
if ! command -v "$command_name" >/dev/null 2>&1; then
printf 'Error: %s is not installed.\n' "$command_name" >&2
exit 1
fi
done
if [[ ! -f "$base_image" ]]; then
if [[ "$uses_shared_base_image" == true ]]; then
"$image_downloader" fedora44
else
printf 'Error: Fedora base image was not found: %s\n' "$base_image" >&2
exit 1
fi
fi
[[ -f "$base_image" ]] || { printf 'Error: downloader did not create: %s\n' "$base_image" >&2; exit 1; }
for firmware_file in "$uefi_code" "$uefi_vars_template"; do
[[ -f "$firmware_file" ]] || { printf 'Error: required UEFI firmware was not found: %s\n' "$firmware_file" >&2; exit 1; }
done
if [[ ! -f "$seed_image" ]]; then
seed_builder="${script_dir}/seediso/fedora44/build-seed.sh"
if [[ ! -x "$seed_builder" ]]; then
printf 'Error: seed image is missing and builder is unavailable: %s\n' "$seed_builder" >&2
exit 1
fi
printf 'Seed image not found; building %s...\n' "$seed_image"
"$seed_builder" "$seed_image"
fi
mkdir -p "$state_dir"
[[ -f "$uefi_vars" ]] || cp -- "$uefi_vars_template" "$uefi_vars"
if [[ -f "$pid_file" ]]; then
old_pid="$(<"$pid_file")"
if [[ "$old_pid" =~ ^[0-9]+$ ]] && kill -0 "$old_pid" 2>/dev/null; then
printf 'Error: the Fedora virtual machine is already running (PID %s).\n' "$old_pid" >&2
exit 1
fi
rm -f "$pid_file"
fi
if [[ ! -f "$disk_image" ]]; then
qemu-img create -q -f qcow2 -F qcow2 -b "$base_image" "$disk_image" "$disk_size"
printf 'Created writable disk overlay: %s\n' "$disk_image"
fi
qemu_args=(
-name fedora44
-machine q35
-display gtk
-m "$memory"
-smp "$cpus"
-drive "if=pflash,format=raw,readonly=on,file=${uefi_code}"
-drive "if=pflash,format=raw,file=${uefi_vars}"
-drive "file=${disk_image},if=virtio,format=qcow2"
-drive "file=${seed_image},media=cdrom,readonly=on"
-nic "user,model=virtio-net-pci,hostfwd=tcp::${ssh_port}-:22"
)
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
qemu_args+=(-enable-kvm -cpu host)
accelerator=KVM
else
qemu_args+=(-accel tcg -cpu max)
accelerator=TCG
fi
printf 'Starting Fedora 44 with %s acceleration; SSH is forwarded to localhost:%s.\n' \
"$accelerator" "$ssh_port"
if [[ "$daemonize" == true ]]; then
: >"$log_file"
qemu_args+=(
-serial "file:${log_file}"
-monitor none
-daemonize
-pidfile "$pid_file"
)
qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
printf 'VM started (PID %s); console log: %s\n' "$(<"$pid_file")" "$log_file"
else
exec qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
fi
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Start the Windows 11 virtual machine (Python port of run-windows11.sh)."""
"""Start the Windows 11 virtual machine."""
import os
import platform
-192
View File
@@ -1,192 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
state_dir="${WIN11_VM_STATE_DIR:-${script_dir}/.vm/windows11}"
disk_image="${WIN11_VM_DISK_IMAGE:-${state_dir}/disk.qcow2}"
seed_iso="${WIN11_VM_SEED_ISO:-$(dirname -- "$disk_image")/seed.iso}"
disk_size="${WIN11_VM_DISK_SIZE:-64G}"
memory="${WIN11_VM_MEMORY:-8G}"
cpus="${WIN11_VM_CPUS:-8}"
rdp_port="${WIN11_VM_RDP_PORT:-3389}"
ssh_port="${WIN11_VM_SSH_PORT:-2222}"
uefi_code="${WIN11_VM_UEFI_CODE:-/usr/share/OVMF/OVMF_CODE_4M.fd}"
uefi_vars_template="${WIN11_VM_UEFI_VARS:-/usr/share/OVMF/OVMF_VARS_4M.fd}"
uefi_vars="${state_dir}/OVMF_VARS_4M.fd"
pid_file="${state_dir}/qemu.pid"
log_file="${state_dir}/console.log"
monitor_socket="${state_dir}/qemu-monitor.sock"
guest_agent_socket="${state_dir}/qemu-guest-agent.sock"
daemonize=false
install_from_iso=false
extra_args=()
usage() {
cat <<EOF
Usage: $(basename "$0") [--daemon] [--install-from-iso] [-- QEMU_ARGUMENT ...]
If ${disk_image} is missing, create it from the Windows 11 QCOW2 base image.
Use --install-from-iso to perform an automatic ISO installation instead.
If it exists, start Windows normally without attaching installation media.
Options:
--daemon Run QEMU in the background
--install-from-iso Install from ISO when the system disk is missing
-h, --help Show this help
Environment variables:
WIN11_VM_BASE_IMAGE Windows 11 base QCOW2 image (default: beside shared images)
WIN11_VM_INSTALL_ISO Windows 11 installation ISO used with --install-from-iso
WIN11_VM_TOOLS_ISO VirtIO Windows tools ISO (default: resolved by shared image helper)
WIN11_VM_MEMORY Guest memory (default: 8G)
WIN11_VM_CPUS Virtual CPU count (default: 8)
WIN11_VM_RDP_PORT Host RDP port (default: 3389)
WIN11_VM_SSH_PORT Host SSH port (default: 2222)
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)
WIN11_VM_SEED_ISO Answer-file ISO path (default: alongside the VM disk)
WIN11_VM_UEFI_CODE OVMF read-only firmware path
WIN11_VM_UEFI_VARS OVMF variable-store template path
EOF
}
while (($#)); do
case "$1" in
--daemon) daemonize=true; shift ;;
--install-from-iso) install_from_iso=true; shift ;;
-h|--help) usage; exit 0 ;;
--) shift; extra_args+=("$@"); break ;;
*) printf 'Error: unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;;
esac
done
installing=false
if [[ ! -f "$disk_image" ]]; then
installing=true
if [[ -n "${VM_IMAGE_HELPER:-}" ]]; then
image_downloader="$VM_IMAGE_HELPER"
elif [[ -x "${script_dir}/vm-images/download-images.sh" ]]; then
image_downloader="${script_dir}/vm-images/download-images.sh"
else
image_downloader="${script_dir}/../vm-images/download-images.sh"
fi
[[ -x "$image_downloader" ]] || { printf 'Error: shared image helper is unavailable: %s\n' "$image_downloader" >&2; exit 1; }
if [[ "$install_from_iso" == true ]]; then
install_iso="${WIN11_VM_INSTALL_ISO:-$("$image_downloader" --print-path windows11 --iso)}"
uses_shared_tools_iso=false
if [[ -n "${WIN11_VM_TOOLS_ISO:-}" ]]; then
tools_iso="$WIN11_VM_TOOLS_ISO"
else
tools_iso="$("$image_downloader" --print-path virtio-win)"
uses_shared_tools_iso=true
fi
else
base_image="${WIN11_VM_BASE_IMAGE:-$("$image_downloader" --print-path windows11)}"
fi
fi
for command_name in qemu-system-x86_64; do
command -v "$command_name" >/dev/null 2>&1 || { printf 'Error: %s is not installed.\n' "$command_name" >&2; exit 1; }
done
if [[ "$installing" == true ]]; then
command -v qemu-img >/dev/null 2>&1 || { printf 'Error: qemu-img is not installed.\n' >&2; exit 1; }
if [[ "$install_from_iso" == true ]]; then
[[ -f "$install_iso" ]] || { printf 'Error: Windows 11 ISO was not found: %s\n' "$install_iso" >&2; exit 1; }
else
[[ -f "$base_image" ]] || { printf 'Error: Windows 11 base QCOW2 image was not found: %s\n' "$base_image" >&2; exit 1; }
fi
fi
for firmware_file in "$uefi_code" "$uefi_vars_template"; do
[[ -f "$firmware_file" ]] || { printf 'Error: required UEFI firmware was not found: %s\n' "$firmware_file" >&2; exit 1; }
done
mkdir -p "$state_dir"
if [[ "$installing" == true && "$install_from_iso" == true ]]; then
if [[ ! -f "$tools_iso" ]]; then
if [[ "$uses_shared_tools_iso" == true ]]; then
"$image_downloader" virtio-win
else
printf 'Error: VirtIO Windows tools ISO was not found: %s\n' "$tools_iso" >&2
exit 1
fi
fi
[[ -f "$tools_iso" ]] || { printf 'Error: downloader did not create: %s\n' "$tools_iso" >&2; exit 1; }
if [[ ! -f "$seed_iso" ||
"${script_dir}/seediso/windows11/Autounattend.xml" -nt "$seed_iso" ||
"${script_dir}/seediso/windows11/Configure-Windows.ps1" -nt "$seed_iso" ]]; then
"${script_dir}/seediso/windows11/build-seed.sh" "$seed_iso"
fi
fi
if [[ -f "$pid_file" ]]; then
old_pid="$(<"$pid_file")"
if [[ "$old_pid" =~ ^[0-9]+$ ]] && kill -0 "$old_pid" 2>/dev/null; then
printf 'Error: Windows 11 VM is already running (PID %s).\n' "$old_pid" >&2
exit 1
fi
rm -f "$pid_file"
fi
rm -f "$monitor_socket" "$guest_agent_socket"
[[ -f "$uefi_vars" ]] || cp -- "$uefi_vars_template" "$uefi_vars"
if [[ "$installing" == true ]]; then
if [[ "$install_from_iso" == true ]]; then
qemu-img create -q -f qcow2 "$disk_image" "$disk_size"
else
qemu-img create -q -f qcow2 -F qcow2 -b "$base_image" "$disk_image" "$disk_size"
fi
printf 'Created Windows 11 system disk: %s\n' "$disk_image"
fi
qemu_args=(
-name windows11 -machine q35 -m "$memory" -smp "$cpus"
-display gtk
# -display none -spice port=5900,addr=127.0.0.1,disable-ticketing=on,agent-mouse=off -device virtio-vga
-drive "if=pflash,format=raw,readonly=on,file=${uefi_code}"
-drive "if=pflash,format=raw,file=${uefi_vars}"
-drive "file=${disk_image},if=none,id=system_disk,format=qcow2"
-device "ide-hd,drive=system_disk,bus=ide.0,bootindex=1"
-nic "user,model=e1000e,hostfwd=tcp::${rdp_port}-:3389,hostfwd=tcp::${ssh_port}-:22"
-device "virtio-serial-pci,id=virtio_serial"
-chardev "qemu-vdagent,id=vdagent,name=vdagent,clipboard=on,mouse=off"
-device "virtserialport,chardev=vdagent,name=com.redhat.spice.0"
# Register the USB tablet after vdagent so QEMU selects it as the active pointer.
-device "qemu-xhci,id=usb_controller" -device "usb-tablet,bus=usb_controller.0"
-chardev "socket,path=${guest_agent_socket},server=on,wait=off,id=qga"
-device "virtserialport,chardev=qga,name=org.qemu.guest_agent.0"
-monitor "unix:${monitor_socket},server=on,wait=off"
)
if [[ "$installing" == true && "$install_from_iso" == true ]]; then
qemu_args+=(
-drive "file=${install_iso},if=none,id=install_media,media=cdrom,readonly=on"
-device "ide-cd,drive=install_media,bus=ide.1,bootindex=2"
-drive "file=${seed_iso},if=none,id=answer_media,media=cdrom,readonly=on"
-device "ide-cd,drive=answer_media,bus=ide.2,bootindex=3"
-drive "file=${tools_iso},if=none,id=tools_media,media=cdrom,readonly=on"
-device "ide-cd,drive=tools_media,bus=ide.3,bootindex=4"
)
fi
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
qemu_args+=(-enable-kvm -cpu host)
accelerator=KVM
else
qemu_args+=(-accel tcg -cpu max)
accelerator=TCG
fi
if [[ "$installing" == true && "$install_from_iso" == true ]]; then
printf 'Starting Windows 11 automatic installation with %s acceleration.\n' "$accelerator"
elif [[ "$installing" == true ]]; then
printf 'Starting Windows 11 from the QCOW2 base image with %s acceleration.\n' "$accelerator"
else
printf 'Starting Windows 11 normally with %s acceleration.\n' "$accelerator"
fi
printf 'Account: guofang / password; RDP: localhost:%s; SSH: localhost:%s\n' "$rdp_port" "$ssh_port"
if [[ "$daemonize" == true ]]; then
: >"$log_file"
qemu_args+=(-serial "file:${log_file}" -daemonize -pidfile "$pid_file")
qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
printf 'VM started (PID %s); console log: %s\n' "$(<"$pid_file")" "$log_file"
else
exec qemu-system-x86_64 "${qemu_args[@]}" "${extra_args[@]}"
fi
+1 -1
View File
@@ -1,7 +1,7 @@
# AlmaLinux 10 seed.iso
This directory contains the cloud-init NoCloud configuration used by
`run-almalinux10.sh`.
`run-almalinux10.py`.
After changing `user-data`, `meta-data`, or `network-config`, remove the old
`seed.iso` and rebuild it:
+1 -1
View File
@@ -1,7 +1,7 @@
# Debian 13 seed.iso
This directory contains the cloud-init NoCloud configuration used by
`run-debian13.sh`.
`run-debian13.py`.
1. Edit `user-data`, `meta-data`, and (if needed) `network-config`.
2. Build the image:
+1 -1
View File
@@ -1,7 +1,7 @@
# Fedora 44 seed.iso
This directory contains the cloud-init NoCloud configuration used by
`run-fedora44.sh`. On first boot, cloud-init's package module installs Fedora's
`run-fedora44.py`. On first boot, cloud-init's package module installs Fedora's
GNOME package group and reboots the guest.
After changing `user-data`, `meta-data`, or `network-config`, remove the old