provisioning-core/nulib/platform/vm/detector.nu

208 lines
5.1 KiB
Text

# VM Hypervisor Detection System
#
# Detects available hypervisor capabilities on host system.
# Follows Rule 1 (single purpose) and Rule 2 (explicit types).
# Error handling: do/complete pattern (no try-catch)
export def "detect-hypervisors" []: table {
"""Detect all available hypervisors on the system"""
let kvm_available = (detect-kvm)
let libvirt_available = (detect-libvirt)
let qemu_available = (detect-qemu)
let docker_available = (detect-docker)
[
$kvm_available,
$libvirt_available,
$qemu_available,
$docker_available,
] | compact
}
def detect-kvm []: record {
"""Detect KVM kernel module and CPU support"""
# Check CPU support (vmx=Intel, svm=AMD)
let cpu_support = (
open /proc/cpuinfo
| grep -E "vmx|svm"
| length
) > 0
if not $cpu_support {
return null
}
# Check if module is loaded
let module_loaded = (
lsmod
| grep "^kvm "
| length
) > 0
# Check /dev/kvm device
let device_present = ("/dev/kvm" | path exists)
{
name: "kvm"
available: (if ($module_loaded and $device_present) { "ready" } else { "available" })
cpu_support: $cpu_support
module_loaded: $module_loaded
device_present: $device_present
description: "Kernel-based Virtual Machine"
}
}
def detect-libvirt []: record {
"""Detect libvirt daemon"""
# Check if package is installed (no try-catch)
let installed = (
let result = (do { virsh --version -q } | complete)
$result.exit_code == 0 and (($result.stdout | length) > 0)
)
if not $installed {
return null
}
# Check if service is running (no try-catch)
let running = (
let result = (do { systemctl is-active --quiet libvirtd } | complete)
$result.exit_code == 0
)
# Check libvirt socket
let socket_present = ("/var/run/libvirt/libvirt-sock" | path exists)
{
name: "libvirt"
available: (if $running { "ready" } else { "available" })
installed: $installed
running: $running
socket_present: $socket_present
description: "libvirt virtualization management"
}
}
def detect-qemu []: record {
"""Detect QEMU emulator"""
# Check if QEMU is installed (no try-catch)
let installed = (
let result = (do { qemu-system-x86_64 --version } | complete)
$result.exit_code == 0 and (($result.stdout | length) > 0)
)
if not $installed {
return null
}
# Get supported architectures
let archs = [
"x86_64",
"i386",
"aarch64",
"arm",
]
{
name: "qemu"
available: "available"
installed: $installed
architectures: $archs
description: "QEMU machine emulator"
}
}
def detect-docker []: record {
"""Detect Docker Desktop VM support (macOS/Windows)"""
# Check if Docker is installed (no try-catch)
let docker_installed = (
let result = (do { docker --version } | complete)
$result.exit_code == 0 and (($result.stdout | length) > 0)
)
if not $docker_installed {
return null
}
# Check Docker Desktop (via context) (no try-catch)
let is_desktop = (
let result = (do { docker context ls } | complete)
$result.exit_code == 0 and (($result.stdout | grep "desktop" | length) > 0)
)
{
name: "docker-vm"
available: (if $is_desktop { "ready" } else { "available" })
docker_installed: $docker_installed
is_desktop: $is_desktop
description: "Docker Desktop VM support"
}
}
export def "get-primary-hypervisor" []: string {
"""Get the best available hypervisor for VM creation"""
let hypervisors = (detect-hypervisors)
# Preference order: libvirt (ready) > kvm (ready) > qemu > docker
let ready_libvirt = (
$hypervisors
| where name == "libvirt" and available == "ready"
| length
) > 0
let ready_kvm = (
$hypervisors
| where name == "kvm" and available == "ready"
| length
) > 0
let available_qemu = (
$hypervisors
| where name == "qemu"
| length
) > 0
let available_docker = (
$hypervisors
| where name == "docker-vm"
| length
) > 0
if $ready_libvirt {
"libvirt"
} else if $ready_kvm {
"kvm"
} else if $available_qemu {
"qemu"
} else if $available_docker {
"docker-vm"
} else {
error make {msg: "No hypervisors detected on system"}
}
}
export def "check-vm-capability" [host: string]: record {
"""Check if host can run virtual machines"""
let hypervisors = (detect-hypervisors)
{
host: $host
can_run_vms: (($hypervisors | length) > 0)
available_hypervisors: $hypervisors
primary_backend: (
# Guard: Ensure at least one hypervisor detected before calling get-primary-hypervisor
if ($hypervisors | length) > 0 {
get-primary-hypervisor
} else {
"none"
}
)
}
}