#!/usr/bin/env bash # VM operations for Sysadmin Chronicles. # Source this file; requires lib/libvirt.sh and PROJECT_ROOT set. SC_VM_TOOLS="${SC_VM_TOOLS:-${PROJECT_ROOT:-}/tools/vm}" _sc_validate_snapshot_name() { [[ "$1" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*$ ]] } _sc_protected_snapshot() { [[ "$1" == baseline.* ]] || [[ "$1" == checkpoint.* ]] } vm_build() { local profile="$1" shift local dry_run=false force=false for arg in "$@"; do case "$arg" in --dry-run) dry_run=true ;; --force) force=true ;; esac done local script="$SC_VM_TOOLS/build-${profile}.sh" [ -f "$script" ] || { echo " ✗ No build script for profile: $profile"; return 1; } local args=() [ "$dry_run" = true ] && args+=(--dry-run) [ "$force" = true ] && args+=(--force) bash "$script" "${args[@]}" } vm_rebuild() { local profile="$1" shift local dry_run=false for arg in "$@"; do [ "$arg" = "--dry-run" ] && dry_run=true; done local domain="sc-${profile}" if domain_exists "$domain" && [ "$dry_run" = false ]; then virsh destroy "$domain" >/dev/null 2>&1 || true virsh undefine "$domain" --nvram --snapshots-metadata >/dev/null 2>&1 \ || virsh undefine "$domain" --snapshots-metadata >/dev/null 2>&1 \ || virsh undefine "$domain" >/dev/null 2>&1 || true fi local extra_args=() [ "$dry_run" = true ] && extra_args+=(--dry-run) vm_build "$profile" "${extra_args[@]}" } vm_revert() { snapshot_revert "$1" "$2" } vm_status() { local vm_id="$1" domain_exists "$vm_id" && domain_state "$vm_id" || printf 'missing' } vm_start() { virsh start "$1" >/dev/null 2>&1 } vm_stop() { virsh shutdown "$1" >/dev/null 2>&1 || virsh destroy "$1" >/dev/null 2>&1 || true } vm_snapshot_create() { local vm_id="$1" local name="$2" _sc_validate_snapshot_name "$name" \ || { echo " ✗ Invalid name (letters, numbers, hyphens only): $name"; return 1; } snapshot_create "$vm_id" "$name" "User snapshot — $(date '+%Y-%m-%d %H:%M')" } vm_snapshot_list() { local vm_id="$1" virsh snapshot-list "$vm_id" 2>/dev/null || true } vm_snapshot_revert() { local vm_id="$1" local name="$2" snapshot_exists "$vm_id" "$name" \ || { echo " ✗ Snapshot not found: $name"; return 1; } snapshot_revert "$vm_id" "$name" } vm_snapshot_delete() { local vm_id="$1" local name="$2" if _sc_protected_snapshot "$name"; then echo " ✗ Cannot delete protected snapshot: $name" return 1 fi snapshot_delete "$vm_id" "$name" }