154 lines
5.2 KiB
Bash
Executable File
154 lines
5.2 KiB
Bash
Executable File
#!/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
|