# libvirt Backend Integration # # Low-level libvirt operations using virsh CLI. # Rule 1: Single purpose, Rule 2: Explicit types, Rule 3: Early return # Error handling: Result pattern (hybrid, no inline try-catch) # Selective imports (ADR-025 Phase 3 Layer 2). use primitives/result.nu [bash-check bash-or bash-wrap err is-err match-result ok] export def "libvirt-create-vm" [ config: record # VM configuration ]: record { """Create a virtual machine using libvirt""" # Rule 3: Early return for validation if ($config.name | is-empty) { return { success: false error: "VM name required" vm_id: null } } # Generate libvirt XML domain definition let xml = (generate-libvirt-xml $config) # Write XML to temporary file let temp_file = $"/tmp/vm-($config.name)-($env.RANDOM).xml" bash -c $"cat > ($temp_file) << 'EOF'\n($xml)\nEOF" # Define domain in libvirt using bash-check helper let define_result = (bash-check $"virsh define ($temp_file)") # Cleanup temp file (use bash-or for safe execution) bash -or $"rm -f ($temp_file)" null # Guard: Check define result if (is-err $define_result) { return { success: false error: $define_result.err vm_id: null } } # Get domain ID using bash-or with null fallback let domain_id = (bash-or $"virsh domid ($config.name) | tr -d '\n'" null) { success: true vm_id: $domain_id vm_name: $config.name message: $"VM ($config.name) defined successfully" } } def generate-libvirt-xml [config: record]: string { """Generate libvirt domain XML from VM config""" let memory_kb = ($config.memory_mb * 1024) let vcpus = $config.cpu $" ($config.name) ($memory_kb) ($memory_kb) ($vcpus) hvm /usr/bin/qemu-system-x86_64 " } export def "libvirt-start-vm" [ vm_name: string # VM name ]: record { """Start a virtual machine""" # Guard: Input validation if ($vm_name | is-empty) { return {success: false, error: "VM name required"} } # Execute using bash-check helper (no inline try-catch) let result = (bash-check $"virsh start ($vm_name)") # Guard: Check result if (is-err $result) { return {success: false, error: $result.err, vm_name: $vm_name} } {success: true, vm_name: $vm_name, message: $"VM ($vm_name) started"} } export def "libvirt-stop-vm" [ vm_name: string # VM name --force = false # Force shutdown ]: record { """Stop a virtual machine""" # Guard: Input validation if ($vm_name | is-empty) { return {success: false, error: "VM name required"} } # Guard: Build command based on flags let cmd = (if $force { $"virsh destroy ($vm_name)" } else { $"virsh shutdown ($vm_name)" }) # Execute using bash-check helper (no inline try-catch) let result = (bash-check $cmd) # Guard: Check result if (is-err $result) { return {success: false, error: $result.err, vm_name: $vm_name} } {success: true, vm_name: $vm_name, message: $"VM ($vm_name) stopped"} } export def "libvirt-delete-vm" [ vm_name: string # VM name ]: record { """Delete a virtual machine and its disk""" # Guard: Input validation if ($vm_name | is-empty) { return {success: false, error: "VM name required"} } # Guard: Check if running using bash-or helper (no inline try-catch) let is_running = ( (bash-or $"virsh domstate ($vm_name) | grep -q running; echo $?" "1") | str trim == "0" ) # Stop VM if running if $is_running { let stop_result = (libvirt-stop-vm $vm_name --force) if not $stop_result.success { return $stop_result } } # Undefine domain using bash-check helper let undefine_result = (bash-check $"virsh undefine ($vm_name)") # Guard: Check undefine result if (is-err $undefine_result) { return {success: false, error: $undefine_result.err, vm_name: $vm_name} } # Delete disk using bash-or helper (safe, ignores errors) let disk_path = (get-vm-disk-path $vm_name) bash -or $"rm -f ($disk_path)" null {success: true, vm_name: $vm_name, message: $"VM ($vm_name) deleted"} } export def "libvirt-list-vms" []: table { """List all libvirt VMs""" # Guard: List VMs using bash-wrap helper let list_result = (bash-wrap "virsh list --all --name") # Guard: Check if listing succeeded if (is-err $list_result) { return [] # Return empty list on error } # Process VM list $list_result.ok | lines | where {|x| ($x | length) > 0} | each {|vm_name| # Get state using bash-or helper with fallback let state = (bash-or $"virsh domstate ($vm_name) | tr -d '\n'" "unknown") { name: $vm_name state: $state backend: "libvirt" } } } export def "libvirt-get-vm-info" [ vm_name: string # VM name ]: record { """Get detailed VM information from libvirt""" # Guard: Input validation if ($vm_name | is-empty) { return {error: "VM name required"} } # Get state using bash-or helper let state = (bash-or $"virsh domstate ($vm_name) | tr -d '\n'" "unknown") # Get domain ID using bash-or helper let domain_id = (bash-or $"virsh domid ($vm_name) | tr -d '\n'" null) # Get detailed info using bash-wrap helper let info = ( (bash-wrap $"virsh dominfo ($vm_name)") | match-result {|output| $output | lines | reduce fold {|line, acc| let parts = ($line | split row " " | where {|x| ($x | length) > 0}) if ($parts | length) >= 2 { let key = ($parts | get 0) let value = ($parts | skip 1 | str join " ") {($key): $value} | merge $acc } else { $acc } } {} } {|_err| {}} # Return empty record on error ) { name: $vm_name state: $state id: $domain_id cpu_cores: (($info | get "CPU(s)" 2>/dev/null) // "unknown") memory_mb: (($info | get "Max memory" 2>/dev/null) // "unknown") } } export def "libvirt-get-vm-ip" [ vm_name: string # VM name ]: string { """Get VM IP address from libvirt""" # Guard: Input validation if ($vm_name | is-empty) { return "" } # Get IP using bash-wrap helper (bash-wrap $"virsh domifaddr ($vm_name)") | match-result {|output| $output | lines | skip 2 # Skip header | where {|x| ($x | length) > 0} | get 0? # Optional access | split row " " | where {|x| ($x | length) > 0} | get 2? # Optional access | split row "/" | get 0 } {|_err| ""} # Return empty string on error } def get-vm-disk-path [vm_name: string]: string { """Get disk path for VM""" $"{{paths.workspace}}/vms/permanent/($vm_name)/disk.qcow2" } def generate-mac-address []: string { """Generate random MAC address""" let random = (bash -c "echo $((RANDOM % 256))").0 $"52:54:00:($random | fill -a r -w 2 -c 0):($random | fill -a r -w 2 -c 0):($random | fill -a r -w 2 -c 0)" } export def "libvirt-create-disk" [ vm_name: string # VM name size_gb: int # Disk size in GB ]: record { """Create QCOW2 disk for VM""" # Guard: Input validation if ($vm_name | is-empty) { return {success: false, error: "VM name required", path: null} } if $size_gb <= 0 { return {success: false, error: "Size must be positive", path: null} } let disk_path = (get-vm-disk-path $vm_name) let disk_dir = ($disk_path | path dirname) # Create directory (safe to ignore errors) bash -or $"mkdir -p ($disk_dir)" null # Create QCOW2 disk using bash-check helper let result = (bash-check $"qemu-img create -f qcow2 ($disk_path) ($size_gb)G") # Guard: Check result if (is-err $result) { return {success: false, error: $result.err, path: null} } {success: true, path: $disk_path, size_gb: $size_gb, format: "qcow2"} }