# Volume Management System (Phase 4) # # Manages storage volumes for VMs and containers. # Rule 1: Single purpose, Rule 5: Atomic operations export def "volume-create" [ name: string # Volume name size_gb: int # Size in GB --type: string = "local" # Volume type --mount-path: string = "" # Mount path --readonly = false # Read-only ]: record { """ Create a storage volume for VM or container. Supports local, NFS, CIFS, and cloud storage types. """ if ($name | is-empty) { return {success: false, error: "Volume name required"} } if ($size_gb < 1 or $size_gb > 1000) { return {success: false, error: "Size must be 1-1000 GB"} } let volume_dir = (get-volumes-directory) bash -c $"mkdir -p ($volume_dir)" | complete let volume_meta = { name: $name type: $type size_gb: $size_gb mount_path: $mount_path readonly: $readonly created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ") status: "created" path: $"($volume_dir)/($name).img" } # Create backing file (no try-catch) let create_result = (do { bash -c $"qemu-img create -f qcow2 ($volume_meta.path) ($size_gb)G" } | complete) if $create_result.exit_code != 0 { return {success: false, error: $"Failed to create volume: ($create_result.stderr)"} } # Save metadata (no try-catch) let mkdir_result = (do { bash -c $"mkdir -p ($volume_dir)/meta" } | complete) if $mkdir_result.exit_code != 0 { return {success: false, error: $"Failed to create metadata directory: ($mkdir_result.stderr)"} } let save_result = (do { bash -c $"cat > ($volume_dir)/meta/($name).json << 'EOF'\n($volume_meta | to json)\nEOF" } | complete) if $save_result.exit_code != 0 { return {success: false, error: $"Failed to save metadata: ($save_result.stderr)"} } { success: true volume_name: $name volume_path: $volume_meta.path size_gb: $size_gb mount_path: $mount_path } } export def "volume-list" []: table { """ List all available volumes. """ let volume_dir = (get-volumes-directory) if (not ($volume_dir | path exists)) { return [] } bash -c $"ls -1 ($volume_dir)/meta/*.json 2>/dev/null" | lines | each {|file| # Guard: Check if file can be opened and parsed as JSON (no try-catch) let json_result = (do { open $file | from json } | complete) if $json_result.exit_code == 0 { let meta = ($json_result.stdout) { name: $meta.name type: $meta.type size_gb: $meta.size_gb mount_path: $meta.mount_path status: $meta.status created: $meta.created_at } } else { null } } | compact } export def "volume-info" [ name: string # Volume name ]: record { """ Get detailed volume information. """ let volume_dir = (get-volumes-directory) let meta_file = $"($volume_dir)/meta/($name).json" if (not ($meta_file | path exists)) { return {success: false, error: "Volume not found"} } # Load metadata (no try-catch) let meta_result = (do { open $meta_file | from json } | complete) if $meta_result.exit_code != 0 { return {success: false, error: $"Failed to load volume metadata: ($meta_result.stderr)"} } let meta = ($meta_result.stdout) let usage = ( bash -c $"du -h ($meta.path) 2>/dev/null | cut -f1" | str trim ) { success: true name: $meta.name type: $meta.type size_gb: $meta.size_gb used: $usage mount_path: $meta.mount_path readonly: $meta.readonly created: $meta.created_at status: $meta.status } } export def "volume-attach" [ volume_name: string # Volume name vm_name: string # VM name --mount-path: string = "" # Mount path ]: record { """ Attach volume to a VM. """ let volume_dir = (get-volumes-directory) let meta_file = $"($volume_dir)/meta/($volume_name).json" if (not ($meta_file | path exists)) { return {success: false, error: "Volume not found"} } # Load metadata (no try-catch) let meta_result = (do { open $meta_file | from json } | complete) if $meta_result.exit_code != 0 { return {success: false, error: $"Failed to load volume metadata: ($meta_result.stderr)"} } let meta = ($meta_result.stdout) let mount = (if ($mount_path | is-empty) {$meta.mount_path} else {$mount_path}) # Record attachment (no try-catch) let attachment = { vm_name: $vm_name attached_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ") mount_path: $mount } let mkdir_result = (do { bash -c $"mkdir -p ($volume_dir)/attachments" } | complete) if $mkdir_result.exit_code != 0 { return {success: false, error: $"Failed to create attachments directory: ($mkdir_result.stderr)"} } let append_result = (do { bash -c $"cat >> ($volume_dir)/attachments/($volume_name).txt << 'EOF'\n($vm_name)|($mount)\nEOF" } | complete) if $append_result.exit_code != 0 { return {success: false, error: $"Failed to record attachment: ($append_result.stderr)"} } { success: true volume: $volume_name vm: $vm_name mount_path: $mount } } export def "volume-detach" [ volume_name: string # Volume name vm_name: string # VM name ]: record { """ Detach volume from VM. """ let volume_dir = (get-volumes-directory) let attachments_file = $"($volume_dir)/attachments/($volume_name).txt" if (not ($attachments_file | path exists)) { return {success: false, error: "No attachments found"} } # Remove attachment entry (no try-catch) let detach_result = (do { bash -c $"grep -v ($vm_name) ($attachments_file) > ($attachments_file).tmp && mv ($attachments_file).tmp ($attachments_file)" } | complete) if $detach_result.exit_code != 0 { return {success: false, error: $"Failed to detach volume: ($detach_result.stderr)"} } { success: true message: $"Volume detached from VM" } } export def "volume-snapshot" [ volume_name: string # Volume name snapshot_name: string # Snapshot name --description: string = "" # Description ]: record { """ Create snapshot of a volume. """ let volume_dir = (get-volumes-directory) let meta_file = $"($volume_dir)/meta/($volume_name).json" if (not ($meta_file | path exists)) { return {success: false, error: "Volume not found"} } # Load metadata (no try-catch) let meta_result = (do { open $meta_file | from json } | complete) if $meta_result.exit_code != 0 { return {success: false, error: $"Failed to load volume metadata: ($meta_result.stderr)"} } let meta = ($meta_result.stdout) let snapshot_path = $"($volume_dir)/snapshots/($volume_name)/($snapshot_name).qcow2" let mkdir_result = (do { bash -c $"mkdir -p $(dirname ($snapshot_path))" } | complete) if $mkdir_result.exit_code != 0 { return {success: false, error: $"Failed to create snapshot directory: ($mkdir_result.stderr)"} } # Create snapshot (no try-catch) let snapshot_result = (do { bash -c $"qemu-img snapshot -c ($snapshot_name) ($meta.path)" } | complete) if $snapshot_result.exit_code != 0 { return {success: false, error: $"Failed to create snapshot: ($snapshot_result.stderr)"} } let convert_result = (do { bash -c $"qemu-img convert -f qcow2 -O qcow2 -o backing_file=($meta.path) ($snapshot_path)" } | complete) if $convert_result.exit_code != 0 { return {success: false, error: $"Failed to convert snapshot: ($convert_result.stderr)"} } # Save snapshot metadata (no try-catch) let snapshot_meta = { name: $snapshot_name volume: $volume_name path: $snapshot_path created_at: (date now | format date "%Y-%m-%dT%H:%M:%SZ") description: $description } let meta_mkdir_result = (do { bash -c $"mkdir -p ($volume_dir)/snapshots/($volume_name)" } | complete) if $meta_mkdir_result.exit_code != 0 { return {success: false, error: $"Failed to create snapshot metadata directory: ($meta_mkdir_result.stderr)"} } let meta_save_result = (do { bash -c $"cat > ($volume_dir)/snapshots/($volume_name)/($snapshot_name).json << 'EOF'\n($snapshot_meta | to json)\nEOF" } | complete) if $meta_save_result.exit_code != 0 { return {success: false, error: $"Failed to save snapshot metadata: ($meta_save_result.stderr)"} } { success: true snapshot: $snapshot_name volume: $volume_name path: $snapshot_path } } export def "volume-restore" [ volume_name: string # Volume name snapshot_name: string # Snapshot name ]: record { """ Restore volume from snapshot. """ let volume_dir = (get-volumes-directory) let snapshot_dir = $"($volume_dir)/snapshots/($volume_name)" let snapshot_meta_file = $"($snapshot_dir)/($snapshot_name).json" if (not ($snapshot_meta_file | path exists)) { return {success: false, error: "Snapshot not found"} } # Load snapshot metadata (no try-catch) let snap_result = (do { open $snapshot_meta_file | from json } | complete) if $snap_result.exit_code != 0 { return {success: false, error: $"Failed to load snapshot metadata: ($snap_result.stderr)"} } let snapshot_meta = ($snap_result.stdout) let meta_file = $"($volume_dir)/meta/($volume_name).json" # Load volume metadata (no try-catch) let meta_result = (do { open $meta_file | from json } | complete) if $meta_result.exit_code != 0 { return {success: false, error: $"Failed to load volume metadata: ($meta_result.stderr)"} } let meta = ($meta_result.stdout) # Restore from snapshot (no try-catch) let restore_result = (do { bash -c $"qemu-img snapshot -a ($snapshot_name) ($meta.path)" } | complete) if $restore_result.exit_code != 0 { return {success: false, error: $"Failed to restore snapshot: ($restore_result.stderr)"} } { success: true message: $"Volume restored from snapshot" volume: $volume_name snapshot: $snapshot_name } } export def "volume-delete" [ name: string # Volume name --force = false # Force delete if in use ]: record { """ Delete a volume. """ let volume_dir = (get-volumes-directory) let meta_file = $"($volume_dir)/meta/($name).json" if (not ($meta_file | path exists)) { return {success: false, error: "Volume not found"} } # Load metadata (no try-catch) let meta_result = (do { open $meta_file | from json } | complete) if $meta_result.exit_code != 0 { return {success: false, error: $"Failed to load volume metadata: ($meta_result.stderr)"} } let meta = ($meta_result.stdout) # Check if in use (no try-catch) let attachments_file = $"($volume_dir)/attachments/($name).txt" if (($attachments_file | path exists) and (not $force)) { let count_result = (do { bash -c $"wc -l < ($attachments_file)" } | complete) if $count_result.exit_code == 0 { let count = ($count_result.stdout | str trim | into int) return { success: false error: $"Volume in use by ($count) VM(s)" } } } # Delete files (no try-catch) let rm_img_result = (do { bash -c $"rm -f ($meta.path)" } | complete) if $rm_img_result.exit_code != 0 { return {success: false, error: $"Failed to delete volume image: ($rm_img_result.stderr)"} } let rm_meta_result = (do { bash -c $"rm -f ($meta_file)" } | complete) if $rm_meta_result.exit_code != 0 { return {success: false, error: $"Failed to delete metadata file: ($rm_meta_result.stderr)"} } let rm_snapshots_result = (do { bash -c $"rm -rf ($volume_dir)/snapshots/($name)" } | complete) if $rm_snapshots_result.exit_code != 0 { return {success: false, error: $"Failed to delete snapshots: ($rm_snapshots_result.stderr)"} } let rm_attachments_result = (do { bash -c $"rm -f ($attachments_file)" } | complete) if $rm_attachments_result.exit_code != 0 { return {success: false, error: $"Failed to delete attachments: ($rm_attachments_result.stderr)"} } { success: true message: $"Volume deleted" } } export def "volume-stats" []: record { """ Get volume system statistics. """ let volume_dir = (get-volumes-directory) let volumes = (volume-list) let total_size = ( $volumes | each {|v| $v.size_gb} | math sum ) let total_used = ( bash -c $"du -sb ($volume_dir) 2>/dev/null | cut -f1" | into int | math round -p 2 ) / 1024 / 1024 / 1024 { total_volumes: ($volumes | length) total_size_gb: $total_size total_used_gb: $total_used utilization_percent: (($total_used / $total_size * 100) | math round -p 1) } } def get-volumes-directory []: string { """Get volumes directory path""" "{{paths.workspace}}/vms/volumes" }