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:
1 parent
8a7f974c11
commit
3561fc6729
12 files changed
+24
-705
No files matched your search
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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,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,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,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
|
||||
|
||||
Reference in new issue
Block a user