# VM State Persistence # # File-based VM state tracking. Stores metadata in JSON files. # Rule 1: Single purpose, Rule 4: Pure functions, Rule 10: Stream large data export def "record-vm-creation" [ vm_config: record # VM configuration ]: record { """Record a new VM in state tracking""" let state_dir = (get-vm-state-dir) bash -c $"mkdir -p ($state_dir)" let state_file = $"($state_dir)/($vm_config.name).json" let state = { name: $vm_config.name state: "stopped" permanent: ($vm_config.permanent // false) temporary: ($vm_config.temporary // false) auto_cleanup: ($vm_config.auto_cleanup // false) auto_cleanup_hours: ($vm_config.auto_cleanup_hours // 24) cpu: $vm_config.cpu memory_mb: $vm_config.memory_mb disk_gb: $vm_config.disk_gb base_image: $vm_config.base_image backend: ($vm_config.backend // "libvirt") taskservs: ($vm_config.taskservs // []) created_at: (now | format date "%Y-%m-%dT%H:%M:%SZ" | debug) ip_address: "" mac_address: "" } # Save state (no try-catch) let save_result = (do { bash -c $"cat > ($state_file) << 'EOF'\n($state | to json)\nEOF" } | complete) if $save_result.exit_code != 0 { return {success: false, error: $"Failed to record VM creation: ($save_result.stderr)"} } {success: true} } export def "get-vm-state" [ vm_name: string # VM name ]: record { """Get VM state from file""" let state_dir = (get-vm-state-dir) let state_file = $"($state_dir)/($vm_name).json" # Guard: Check if state file can be opened and parsed as JSON (no try-catch) let json_result = (do { open $state_file | from json } | complete) if $json_result.exit_code == 0 { $json_result.stdout } else { {} } } export def "update-vm-state" [ vm_name: string # VM name new_state: string # New state (running, stopped, etc.) ]: record { """Update VM state""" let current = (get-vm-state $vm_name) if ($current | is-empty) { return {success: false, error: "VM not found"} } let updated = ( $current | upsert state $new_state | upsert last_action (now | format date "%Y-%m-%dT%H:%M:%SZ" | debug) ) let state_dir = (get-vm-state-dir) let state_file = $"($state_dir)/($vm_name).json" # Update state (no try-catch) let update_result = (do { bash -c $"cat > ($state_file) << 'EOF'\n($updated | to json)\nEOF" } | complete) if $update_result.exit_code != 0 { return {success: false, error: $"Failed to update VM state: ($update_result.stderr)"} } {success: true} } export def "remove-vm-state" [ vm_name: string # VM name ]: record { """Remove VM from state tracking""" let state_dir = (get-vm-state-dir) let state_file = $"($state_dir)/($vm_name).json" # Remove state file (no try-catch) let rm_result = (do { bash -c $"rm -f ($state_file)" } | complete) if $rm_result.exit_code != 0 { return {success: false, error: $"Failed to remove VM state: ($rm_result.stderr)"} } {success: true} } export def "list-all-vms" []: table { """List all VMs from state tracking""" let state_dir = (get-vm-state-dir) if not ($state_dir | path exists) { return [] } # List state files (no try-catch) let ls_result = (do { bash -c $"ls -1 ($state_dir)/*.json 2>/dev/null" } | complete) if $ls_result.exit_code != 0 { return [] } $ls_result.stdout | lines | where {|f| ($f | length) > 0} | each {|f| # Guard: Check if file can be opened and parsed as JSON (no try-catch) let json_result = (do { open $f | from json } | complete) if $json_result.exit_code == 0 { $json_result.stdout } else { null } } | compact | where {|v| ($v | length) > 0} } def get-vm-state-dir []: string { """Get VM state directory""" $"{{paths.workspace}}/vms/state" } export def "get-vm-stats" []: record { """Get VM statistics""" let all_vms = (list-all-vms) let permanent = ($all_vms | where permanent == true | length) let temporary = ($all_vms | where temporary == true | length) let total = ($all_vms | length) { total_vms: $total permanent_vms: $permanent temporary_vms: $temporary running_vms: ($all_vms | where state == "running" | length) stopped_vms: ($all_vms | where state == "stopped" | length) } } export def "cleanup-temporary-vms" [ --older-than: int = 24 # Hours ]: record { """Cleanup temporary VMs older than specified hours""" let all_vms = (list-all-vms) let now = (now | format date "%Y-%m-%dT%H:%M:%SZ" | debug) let to_cleanup = ( $all_vms | filter {|vm| ($vm.temporary // false) and \ ($vm.auto_cleanup // false) } ) let results = ( $to_cleanup | map {|vm| { name: $vm.name result: "skipped" # Placeholder for Phase 2 } } ) { total_checked: ($to_cleanup | length) cleaned_up: 0 skipped: ($to_cleanup | length) results: $results } }