pvectl 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.claude/rules/branch-before-changes.md +52 -0
- data/.claude/rules/documentation-updates.md +104 -0
- data/.claude/rules/git-workflow.md +84 -0
- data/.claude/rules/proxmox-api-docs.md +58 -0
- data/.claude/rules/rbs-signatures.md +80 -0
- data/.claude/rules/refactoring-as-design-option.md +35 -0
- data/.claude/scheduled_tasks.lock +1 -0
- data/.claude/settings.json +51 -0
- data/.mcp.json +8 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +138 -0
- data/CLAUDE.md +211 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +143 -0
- data/Rakefile +8 -0
- data/docs/proxmox-api-update.sh +96 -0
- data/exe/pvectl +5 -0
- data/lib/pvectl/argv_preprocessor.rb +334 -0
- data/lib/pvectl/cli.rb +102 -0
- data/lib/pvectl/commands/apt.rb +389 -0
- data/lib/pvectl/commands/clone_container.rb +230 -0
- data/lib/pvectl/commands/clone_vm.rb +331 -0
- data/lib/pvectl/commands/cloudinit/command.rb +122 -0
- data/lib/pvectl/commands/cloudinit/dump.rb +94 -0
- data/lib/pvectl/commands/cloudinit/pending.rb +137 -0
- data/lib/pvectl/commands/cloudinit/regenerate.rb +79 -0
- data/lib/pvectl/commands/config/command.rb +65 -0
- data/lib/pvectl/commands/config/get_contexts.rb +68 -0
- data/lib/pvectl/commands/config/set_cluster.rb +103 -0
- data/lib/pvectl/commands/config/set_context.rb +136 -0
- data/lib/pvectl/commands/config/set_credentials.rb +181 -0
- data/lib/pvectl/commands/config/use_context.rb +69 -0
- data/lib/pvectl/commands/config/view.rb +67 -0
- data/lib/pvectl/commands/console.rb +93 -0
- data/lib/pvectl/commands/console_ct.rb +187 -0
- data/lib/pvectl/commands/console_vm.rb +187 -0
- data/lib/pvectl/commands/container_lifecycle_command.rb +77 -0
- data/lib/pvectl/commands/create_backup.rb +173 -0
- data/lib/pvectl/commands/create_container.rb +141 -0
- data/lib/pvectl/commands/create_resource_command.rb +244 -0
- data/lib/pvectl/commands/create_snapshot.rb +242 -0
- data/lib/pvectl/commands/create_vm.rb +267 -0
- data/lib/pvectl/commands/delete_backup.rb +139 -0
- data/lib/pvectl/commands/delete_command.rb +119 -0
- data/lib/pvectl/commands/delete_container.rb +30 -0
- data/lib/pvectl/commands/delete_snapshot.rb +248 -0
- data/lib/pvectl/commands/delete_vm.rb +127 -0
- data/lib/pvectl/commands/describe/command.rb +251 -0
- data/lib/pvectl/commands/edit_container.rb +56 -0
- data/lib/pvectl/commands/edit_dns.rb +149 -0
- data/lib/pvectl/commands/edit_hosts.rb +135 -0
- data/lib/pvectl/commands/edit_node.rb +54 -0
- data/lib/pvectl/commands/edit_resource_command.rb +180 -0
- data/lib/pvectl/commands/edit_vm.rb +154 -0
- data/lib/pvectl/commands/edit_volume.rb +189 -0
- data/lib/pvectl/commands/feature_command.rb +230 -0
- data/lib/pvectl/commands/feature_container.rb +21 -0
- data/lib/pvectl/commands/feature_vm.rb +94 -0
- data/lib/pvectl/commands/get/command.rb +360 -0
- data/lib/pvectl/commands/get/handlers/backups.rb +76 -0
- data/lib/pvectl/commands/get/handlers/capabilities.rb +107 -0
- data/lib/pvectl/commands/get/handlers/containers.rb +148 -0
- data/lib/pvectl/commands/get/handlers/disks.rb +107 -0
- data/lib/pvectl/commands/get/handlers/dns.rb +94 -0
- data/lib/pvectl/commands/get/handlers/hosts.rb +94 -0
- data/lib/pvectl/commands/get/handlers/nodes.rb +162 -0
- data/lib/pvectl/commands/get/handlers/services.rb +81 -0
- data/lib/pvectl/commands/get/handlers/snapshots.rb +97 -0
- data/lib/pvectl/commands/get/handlers/storage.rb +118 -0
- data/lib/pvectl/commands/get/handlers/subscription.rb +69 -0
- data/lib/pvectl/commands/get/handlers/tasks.rb +89 -0
- data/lib/pvectl/commands/get/handlers/templates.rb +175 -0
- data/lib/pvectl/commands/get/handlers/time.rb +118 -0
- data/lib/pvectl/commands/get/handlers/vms.rb +145 -0
- data/lib/pvectl/commands/get/handlers/volume.rb +134 -0
- data/lib/pvectl/commands/get/resource_handler.rb +63 -0
- data/lib/pvectl/commands/get/resource_registry.rb +18 -0
- data/lib/pvectl/commands/get/watch_loop.rb +129 -0
- data/lib/pvectl/commands/irreversible_command.rb +265 -0
- data/lib/pvectl/commands/logs/command.rb +275 -0
- data/lib/pvectl/commands/logs/handlers/journal.rb +46 -0
- data/lib/pvectl/commands/logs/handlers/syslog.rb +53 -0
- data/lib/pvectl/commands/logs/handlers/task_detail.rb +52 -0
- data/lib/pvectl/commands/logs/handlers/task_logs.rb +115 -0
- data/lib/pvectl/commands/logs/resource_handler.rb +46 -0
- data/lib/pvectl/commands/logs/resource_registry.rb +22 -0
- data/lib/pvectl/commands/migrate_command.rb +282 -0
- data/lib/pvectl/commands/migrate_container.rb +23 -0
- data/lib/pvectl/commands/migrate_vm.rb +122 -0
- data/lib/pvectl/commands/move_disk_command.rb +239 -0
- data/lib/pvectl/commands/move_disk_container.rb +21 -0
- data/lib/pvectl/commands/move_disk_vm.rb +127 -0
- data/lib/pvectl/commands/ping.rb +249 -0
- data/lib/pvectl/commands/pull.rb +342 -0
- data/lib/pvectl/commands/push.rb +352 -0
- data/lib/pvectl/commands/reset.rb +64 -0
- data/lib/pvectl/commands/resource_lifecycle_command.rb +277 -0
- data/lib/pvectl/commands/resource_registry.rb +73 -0
- data/lib/pvectl/commands/restart.rb +70 -0
- data/lib/pvectl/commands/restart_container.rb +18 -0
- data/lib/pvectl/commands/restore_backup.rb +236 -0
- data/lib/pvectl/commands/resume.rb +57 -0
- data/lib/pvectl/commands/rollback_snapshot.rb +228 -0
- data/lib/pvectl/commands/sendkey_vm.rb +205 -0
- data/lib/pvectl/commands/service.rb +293 -0
- data/lib/pvectl/commands/set_container.rb +50 -0
- data/lib/pvectl/commands/set_node.rb +52 -0
- data/lib/pvectl/commands/set_resource_command.rb +185 -0
- data/lib/pvectl/commands/set_vm.rb +136 -0
- data/lib/pvectl/commands/set_volume.rb +212 -0
- data/lib/pvectl/commands/shared_config_parsers.rb +126 -0
- data/lib/pvectl/commands/shared_flags.rb +155 -0
- data/lib/pvectl/commands/shutdown.rb +73 -0
- data/lib/pvectl/commands/shutdown_container.rb +18 -0
- data/lib/pvectl/commands/start.rb +79 -0
- data/lib/pvectl/commands/start_container.rb +18 -0
- data/lib/pvectl/commands/stop.rb +75 -0
- data/lib/pvectl/commands/stop_container.rb +18 -0
- data/lib/pvectl/commands/suspend.rb +64 -0
- data/lib/pvectl/commands/template_command.rb +205 -0
- data/lib/pvectl/commands/template_container.rb +27 -0
- data/lib/pvectl/commands/template_vm.rb +106 -0
- data/lib/pvectl/commands/top/command.rb +206 -0
- data/lib/pvectl/commands/top/handlers/containers.rb +61 -0
- data/lib/pvectl/commands/top/handlers/nodes.rb +61 -0
- data/lib/pvectl/commands/top/handlers/vms.rb +61 -0
- data/lib/pvectl/commands/top/resource_handler.rb +46 -0
- data/lib/pvectl/commands/top/resource_registry.rb +22 -0
- data/lib/pvectl/commands/unlink_disk_vm.rb +232 -0
- data/lib/pvectl/commands/vm_lifecycle_command.rb +77 -0
- data/lib/pvectl/commands/wakeonlan_node.rb +153 -0
- data/lib/pvectl/config/errors.rb +62 -0
- data/lib/pvectl/config/models/cluster.rb +180 -0
- data/lib/pvectl/config/models/context.rb +100 -0
- data/lib/pvectl/config/models/resolved_config.rb +171 -0
- data/lib/pvectl/config/models/user.rb +133 -0
- data/lib/pvectl/config/provider.rb +297 -0
- data/lib/pvectl/config/service.rb +300 -0
- data/lib/pvectl/config/store.rb +161 -0
- data/lib/pvectl/config/wizard.rb +309 -0
- data/lib/pvectl/config_serializer.rb +1034 -0
- data/lib/pvectl/connection/retry_handler.rb +161 -0
- data/lib/pvectl/connection.rb +157 -0
- data/lib/pvectl/console/terminal_session.rb +449 -0
- data/lib/pvectl/editor_session.rb +157 -0
- data/lib/pvectl/exit_codes.rb +43 -0
- data/lib/pvectl/formatters/base.rb +55 -0
- data/lib/pvectl/formatters/color_support.rb +90 -0
- data/lib/pvectl/formatters/json.rb +45 -0
- data/lib/pvectl/formatters/output_helper.rb +77 -0
- data/lib/pvectl/formatters/registry.rb +72 -0
- data/lib/pvectl/formatters/table.rb +235 -0
- data/lib/pvectl/formatters/wide.rb +93 -0
- data/lib/pvectl/formatters/yaml.rb +49 -0
- data/lib/pvectl/manifest_serializer.rb +142 -0
- data/lib/pvectl/models/apt_package.rb +107 -0
- data/lib/pvectl/models/backup.rb +173 -0
- data/lib/pvectl/models/base.rb +49 -0
- data/lib/pvectl/models/capability.rb +62 -0
- data/lib/pvectl/models/container.rb +205 -0
- data/lib/pvectl/models/container_operation_result.rb +27 -0
- data/lib/pvectl/models/dns_config.rb +54 -0
- data/lib/pvectl/models/hosts_file.rb +47 -0
- data/lib/pvectl/models/journal_entry.rb +16 -0
- data/lib/pvectl/models/network_interface.rb +85 -0
- data/lib/pvectl/models/node.rb +195 -0
- data/lib/pvectl/models/node_operation_result.rb +45 -0
- data/lib/pvectl/models/operation_result.rb +110 -0
- data/lib/pvectl/models/physical_disk.rb +193 -0
- data/lib/pvectl/models/service.rb +80 -0
- data/lib/pvectl/models/snapshot.rb +101 -0
- data/lib/pvectl/models/snapshot_description.rb +39 -0
- data/lib/pvectl/models/storage.rb +180 -0
- data/lib/pvectl/models/subscription.rb +87 -0
- data/lib/pvectl/models/syslog_entry.rb +17 -0
- data/lib/pvectl/models/task.rb +95 -0
- data/lib/pvectl/models/task_entry.rb +52 -0
- data/lib/pvectl/models/task_log_line.rb +17 -0
- data/lib/pvectl/models/time_config.rb +47 -0
- data/lib/pvectl/models/vm.rb +137 -0
- data/lib/pvectl/models/vm_operation_result.rb +27 -0
- data/lib/pvectl/models/volume.rb +133 -0
- data/lib/pvectl/models/volume_operation_result.rb +26 -0
- data/lib/pvectl/parsers/cloud_init_config.rb +92 -0
- data/lib/pvectl/parsers/disk_config.rb +97 -0
- data/lib/pvectl/parsers/lxc_mount_config.rb +98 -0
- data/lib/pvectl/parsers/lxc_net_config.rb +97 -0
- data/lib/pvectl/parsers/net_config.rb +95 -0
- data/lib/pvectl/parsers/smart_text.rb +42 -0
- data/lib/pvectl/plugin_loader.rb +157 -0
- data/lib/pvectl/presenters/apt_package.rb +99 -0
- data/lib/pvectl/presenters/backup.rb +128 -0
- data/lib/pvectl/presenters/base.rb +283 -0
- data/lib/pvectl/presenters/capability.rb +104 -0
- data/lib/pvectl/presenters/config/context.rb +80 -0
- data/lib/pvectl/presenters/container.rb +574 -0
- data/lib/pvectl/presenters/container_operation_result.rb +109 -0
- data/lib/pvectl/presenters/disk.rb +184 -0
- data/lib/pvectl/presenters/dns_config.rb +68 -0
- data/lib/pvectl/presenters/hosts_file.rb +61 -0
- data/lib/pvectl/presenters/journal_entry.rb +20 -0
- data/lib/pvectl/presenters/node.rb +762 -0
- data/lib/pvectl/presenters/node_operation_result.rb +50 -0
- data/lib/pvectl/presenters/operation_result.rb +61 -0
- data/lib/pvectl/presenters/service.rb +76 -0
- data/lib/pvectl/presenters/snapshot.rb +239 -0
- data/lib/pvectl/presenters/snapshot_operation_result.rb +125 -0
- data/lib/pvectl/presenters/storage.rb +329 -0
- data/lib/pvectl/presenters/subscription.rb +189 -0
- data/lib/pvectl/presenters/syslog_entry.rb +20 -0
- data/lib/pvectl/presenters/task_entry.rb +69 -0
- data/lib/pvectl/presenters/task_log_line.rb +20 -0
- data/lib/pvectl/presenters/template.rb +76 -0
- data/lib/pvectl/presenters/time_config.rb +86 -0
- data/lib/pvectl/presenters/top_container.rb +112 -0
- data/lib/pvectl/presenters/top_node.rb +115 -0
- data/lib/pvectl/presenters/top_presenter.rb +59 -0
- data/lib/pvectl/presenters/top_vm.rb +105 -0
- data/lib/pvectl/presenters/vm.rb +853 -0
- data/lib/pvectl/presenters/vm_operation_result.rb +109 -0
- data/lib/pvectl/presenters/volume.rb +136 -0
- data/lib/pvectl/presenters/volume_operation_result.rb +58 -0
- data/lib/pvectl/repositories/apt.rb +93 -0
- data/lib/pvectl/repositories/backup.rb +186 -0
- data/lib/pvectl/repositories/base.rb +110 -0
- data/lib/pvectl/repositories/capabilities.rb +96 -0
- data/lib/pvectl/repositories/container.rb +503 -0
- data/lib/pvectl/repositories/disk.rb +87 -0
- data/lib/pvectl/repositories/dns.rb +54 -0
- data/lib/pvectl/repositories/hosts.rb +63 -0
- data/lib/pvectl/repositories/journal.rb +23 -0
- data/lib/pvectl/repositories/node.rb +537 -0
- data/lib/pvectl/repositories/service.rb +139 -0
- data/lib/pvectl/repositories/snapshot.rb +133 -0
- data/lib/pvectl/repositories/storage.rb +302 -0
- data/lib/pvectl/repositories/subscription.rb +77 -0
- data/lib/pvectl/repositories/syslog.rb +25 -0
- data/lib/pvectl/repositories/task.rb +82 -0
- data/lib/pvectl/repositories/task_list.rb +30 -0
- data/lib/pvectl/repositories/task_log.rb +31 -0
- data/lib/pvectl/repositories/time_config.rb +53 -0
- data/lib/pvectl/repositories/vm.rb +616 -0
- data/lib/pvectl/repositories/volume.rb +306 -0
- data/lib/pvectl/selectors/base.rb +201 -0
- data/lib/pvectl/selectors/container.rb +116 -0
- data/lib/pvectl/selectors/disk.rb +59 -0
- data/lib/pvectl/selectors/vm.rb +116 -0
- data/lib/pvectl/selectors/volume.rb +59 -0
- data/lib/pvectl/services/backup.rb +209 -0
- data/lib/pvectl/services/clone_container.rb +260 -0
- data/lib/pvectl/services/clone_vm.rb +265 -0
- data/lib/pvectl/services/cloudinit.rb +96 -0
- data/lib/pvectl/services/console.rb +152 -0
- data/lib/pvectl/services/container_lifecycle.rb +124 -0
- data/lib/pvectl/services/create_container.rb +179 -0
- data/lib/pvectl/services/create_vm.rb +191 -0
- data/lib/pvectl/services/edit_container.rb +125 -0
- data/lib/pvectl/services/edit_dns.rb +159 -0
- data/lib/pvectl/services/edit_hosts.rb +78 -0
- data/lib/pvectl/services/edit_node.rb +147 -0
- data/lib/pvectl/services/edit_vm.rb +125 -0
- data/lib/pvectl/services/edit_volume.rb +224 -0
- data/lib/pvectl/services/get/resource_service.rb +98 -0
- data/lib/pvectl/services/move_disk.rb +132 -0
- data/lib/pvectl/services/pull_config.rb +94 -0
- data/lib/pvectl/services/push_config.rb +524 -0
- data/lib/pvectl/services/resize_volume.rb +253 -0
- data/lib/pvectl/services/resource_delete.rb +169 -0
- data/lib/pvectl/services/resource_migration.rb +170 -0
- data/lib/pvectl/services/sendkey.rb +108 -0
- data/lib/pvectl/services/service_lifecycle.rb +89 -0
- data/lib/pvectl/services/set_container.rb +128 -0
- data/lib/pvectl/services/set_node.rb +236 -0
- data/lib/pvectl/services/set_vm.rb +128 -0
- data/lib/pvectl/services/set_volume.rb +126 -0
- data/lib/pvectl/services/snapshot.rb +261 -0
- data/lib/pvectl/services/task_listing.rb +75 -0
- data/lib/pvectl/services/unlink_disk.rb +86 -0
- data/lib/pvectl/services/vm_lifecycle.rb +124 -0
- data/lib/pvectl/services/wakeonlan.rb +79 -0
- data/lib/pvectl/utils/resource_resolver.rb +80 -0
- data/lib/pvectl/version.rb +13 -0
- data/lib/pvectl/wizards/create_container.rb +105 -0
- data/lib/pvectl/wizards/create_vm.rb +98 -0
- data/lib/pvectl.rb +439 -0
- data/sig/external/gli.rbs +16 -0
- data/sig/external/proxmox_api.rbs +10 -0
- data/sig/pvectl/argv_preprocessor.rbs +53 -0
- data/sig/pvectl/cli.rbs +26 -0
- data/sig/pvectl/commands/apt.rbs +47 -0
- data/sig/pvectl/commands/clone_container.rbs +31 -0
- data/sig/pvectl/commands/clone_vm.rbs +33 -0
- data/sig/pvectl/commands/cloudinit/command.rbs +13 -0
- data/sig/pvectl/commands/cloudinit/dump.rbs +13 -0
- data/sig/pvectl/commands/cloudinit/pending.rbs +17 -0
- data/sig/pvectl/commands/cloudinit/regenerate.rbs +11 -0
- data/sig/pvectl/commands/config/command.rbs +9 -0
- data/sig/pvectl/commands/config/get_contexts.rbs +11 -0
- data/sig/pvectl/commands/config/set_cluster.rbs +11 -0
- data/sig/pvectl/commands/config/set_context.rbs +15 -0
- data/sig/pvectl/commands/config/set_credentials.rbs +15 -0
- data/sig/pvectl/commands/config/use_context.rbs +11 -0
- data/sig/pvectl/commands/config/view.rbs +11 -0
- data/sig/pvectl/commands/console.rbs +9 -0
- data/sig/pvectl/commands/console_ct.rbs +27 -0
- data/sig/pvectl/commands/console_vm.rbs +27 -0
- data/sig/pvectl/commands/container_lifecycle_command.rbs +25 -0
- data/sig/pvectl/commands/create_backup.rbs +29 -0
- data/sig/pvectl/commands/create_container.rbs +30 -0
- data/sig/pvectl/commands/create_resource_command.rbs +53 -0
- data/sig/pvectl/commands/create_snapshot.rbs +35 -0
- data/sig/pvectl/commands/create_vm.rbs +30 -0
- data/sig/pvectl/commands/delete_backup.rbs +25 -0
- data/sig/pvectl/commands/delete_command.rbs +45 -0
- data/sig/pvectl/commands/delete_container.rbs +11 -0
- data/sig/pvectl/commands/delete_snapshot.rbs +35 -0
- data/sig/pvectl/commands/delete_vm.rbs +13 -0
- data/sig/pvectl/commands/describe/command.rbs +27 -0
- data/sig/pvectl/commands/edit_container.rbs +17 -0
- data/sig/pvectl/commands/edit_dns.rbs +25 -0
- data/sig/pvectl/commands/edit_hosts.rbs +23 -0
- data/sig/pvectl/commands/edit_node.rbs +17 -0
- data/sig/pvectl/commands/edit_resource_command.rbs +35 -0
- data/sig/pvectl/commands/edit_vm.rbs +19 -0
- data/sig/pvectl/commands/edit_volume.rbs +24 -0
- data/sig/pvectl/commands/feature_command.rbs +43 -0
- data/sig/pvectl/commands/feature_container.rbs +10 -0
- data/sig/pvectl/commands/feature_vm.rbs +12 -0
- data/sig/pvectl/commands/get/command.rbs +42 -0
- data/sig/pvectl/commands/get/handlers/backups.rbs +23 -0
- data/sig/pvectl/commands/get/handlers/capabilities.rbs +29 -0
- data/sig/pvectl/commands/get/handlers/containers.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/disks.rbs +27 -0
- data/sig/pvectl/commands/get/handlers/dns.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/hosts.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/nodes.rbs +33 -0
- data/sig/pvectl/commands/get/handlers/services.rbs +23 -0
- data/sig/pvectl/commands/get/handlers/snapshots.rbs +27 -0
- data/sig/pvectl/commands/get/handlers/storage.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/subscription.rbs +25 -0
- data/sig/pvectl/commands/get/handlers/tasks.rbs +28 -0
- data/sig/pvectl/commands/get/handlers/templates.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/time.rbs +29 -0
- data/sig/pvectl/commands/get/handlers/vms.rbs +35 -0
- data/sig/pvectl/commands/get/handlers/volume.rbs +27 -0
- data/sig/pvectl/commands/get/resource_handler.rbs +13 -0
- data/sig/pvectl/commands/get/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/get/watch_loop.rbs +33 -0
- data/sig/pvectl/commands/irreversible_command.rbs +32 -0
- data/sig/pvectl/commands/logs/command.rbs +35 -0
- data/sig/pvectl/commands/logs/handlers/journal.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/syslog.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/task_detail.rbs +21 -0
- data/sig/pvectl/commands/logs/handlers/task_logs.rbs +35 -0
- data/sig/pvectl/commands/logs/resource_handler.rbs +11 -0
- data/sig/pvectl/commands/logs/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/migrate_command.rbs +45 -0
- data/sig/pvectl/commands/migrate_container.rbs +11 -0
- data/sig/pvectl/commands/migrate_vm.rbs +13 -0
- data/sig/pvectl/commands/move_disk_command.rbs +43 -0
- data/sig/pvectl/commands/move_disk_container.rbs +11 -0
- data/sig/pvectl/commands/move_disk_vm.rbs +13 -0
- data/sig/pvectl/commands/ping.rbs +39 -0
- data/sig/pvectl/commands/pull.rbs +33 -0
- data/sig/pvectl/commands/push.rbs +32 -0
- data/sig/pvectl/commands/reset.rbs +11 -0
- data/sig/pvectl/commands/resource_lifecycle_command.rbs +55 -0
- data/sig/pvectl/commands/resource_registry.rbs +19 -0
- data/sig/pvectl/commands/restart.rbs +11 -0
- data/sig/pvectl/commands/restart_container.rbs +9 -0
- data/sig/pvectl/commands/restore_backup.rbs +27 -0
- data/sig/pvectl/commands/resume.rbs +11 -0
- data/sig/pvectl/commands/rollback_snapshot.rbs +31 -0
- data/sig/pvectl/commands/sendkey_vm.rbs +25 -0
- data/sig/pvectl/commands/service.rbs +38 -0
- data/sig/pvectl/commands/set_container.rbs +13 -0
- data/sig/pvectl/commands/set_node.rbs +13 -0
- data/sig/pvectl/commands/set_resource_command.rbs +25 -0
- data/sig/pvectl/commands/set_vm.rbs +15 -0
- data/sig/pvectl/commands/set_volume.rbs +24 -0
- data/sig/pvectl/commands/shared_config_parsers.rbs +19 -0
- data/sig/pvectl/commands/shared_flags.rbs +10 -0
- data/sig/pvectl/commands/shutdown.rbs +11 -0
- data/sig/pvectl/commands/shutdown_container.rbs +9 -0
- data/sig/pvectl/commands/start.rbs +11 -0
- data/sig/pvectl/commands/start_container.rbs +9 -0
- data/sig/pvectl/commands/stop.rbs +11 -0
- data/sig/pvectl/commands/stop_container.rbs +9 -0
- data/sig/pvectl/commands/suspend.rbs +11 -0
- data/sig/pvectl/commands/template_command.rbs +21 -0
- data/sig/pvectl/commands/template_container.rbs +10 -0
- data/sig/pvectl/commands/template_vm.rbs +12 -0
- data/sig/pvectl/commands/top/command.rbs +31 -0
- data/sig/pvectl/commands/top/handlers/containers.rbs +21 -0
- data/sig/pvectl/commands/top/handlers/nodes.rbs +21 -0
- data/sig/pvectl/commands/top/handlers/vms.rbs +21 -0
- data/sig/pvectl/commands/top/resource_handler.rbs +11 -0
- data/sig/pvectl/commands/top/resource_registry.rbs +8 -0
- data/sig/pvectl/commands/unlink_disk_vm.rbs +27 -0
- data/sig/pvectl/commands/vm_lifecycle_command.rbs +25 -0
- data/sig/pvectl/commands/wakeonlan_node.rbs +21 -0
- data/sig/pvectl/config/errors.rbs +24 -0
- data/sig/pvectl/config/models/cluster.rbs +39 -0
- data/sig/pvectl/config/models/context.rbs +23 -0
- data/sig/pvectl/config/models/resolved_config.rbs +51 -0
- data/sig/pvectl/config/models/user.rbs +31 -0
- data/sig/pvectl/config/provider.rbs +40 -0
- data/sig/pvectl/config/service.rbs +65 -0
- data/sig/pvectl/config/store.rbs +14 -0
- data/sig/pvectl/config/wizard.rbs +48 -0
- data/sig/pvectl/config_serializer.rbs +121 -0
- data/sig/pvectl/connection/retry_handler.rbs +31 -0
- data/sig/pvectl/connection.rbs +35 -0
- data/sig/pvectl/console/terminal_session.rbs +63 -0
- data/sig/pvectl/editor_session.rbs +33 -0
- data/sig/pvectl/exit_codes.rbs +19 -0
- data/sig/pvectl/formatters/base.rbs +13 -0
- data/sig/pvectl/formatters/color_support.rbs +13 -0
- data/sig/pvectl/formatters/json.rbs +7 -0
- data/sig/pvectl/formatters/output_helper.rbs +9 -0
- data/sig/pvectl/formatters/registry.rbs +13 -0
- data/sig/pvectl/formatters/table.rbs +25 -0
- data/sig/pvectl/formatters/wide.rbs +15 -0
- data/sig/pvectl/formatters/yaml.rbs +7 -0
- data/sig/pvectl/manifest_serializer.rbs +18 -0
- data/sig/pvectl/models/apt_package.rbs +26 -0
- data/sig/pvectl/models/backup.rbs +31 -0
- data/sig/pvectl/models/base.rbs +11 -0
- data/sig/pvectl/models/capability.rbs +16 -0
- data/sig/pvectl/models/container.rbs +44 -0
- data/sig/pvectl/models/container_operation_result.rbs +9 -0
- data/sig/pvectl/models/dns_config.rbs +15 -0
- data/sig/pvectl/models/hosts_file.rbs +13 -0
- data/sig/pvectl/models/journal_entry.rbs +10 -0
- data/sig/pvectl/models/network_interface.rbs +20 -0
- data/sig/pvectl/models/node.rbs +47 -0
- data/sig/pvectl/models/node_operation_result.rbs +12 -0
- data/sig/pvectl/models/operation_result.rbs +21 -0
- data/sig/pvectl/models/physical_disk.rbs +35 -0
- data/sig/pvectl/models/service.rbs +18 -0
- data/sig/pvectl/models/snapshot.rbs +21 -0
- data/sig/pvectl/models/snapshot_description.rbs +18 -0
- data/sig/pvectl/models/storage.rbs +39 -0
- data/sig/pvectl/models/subscription.rbs +24 -0
- data/sig/pvectl/models/syslog_entry.rbs +10 -0
- data/sig/pvectl/models/task.rbs +22 -0
- data/sig/pvectl/models/task_entry.rbs +24 -0
- data/sig/pvectl/models/task_log_line.rbs +10 -0
- data/sig/pvectl/models/time_config.rbs +12 -0
- data/sig/pvectl/models/vm.rbs +32 -0
- data/sig/pvectl/models/vm_operation_result.rbs +9 -0
- data/sig/pvectl/models/volume.rbs +29 -0
- data/sig/pvectl/models/volume_operation_result.rbs +9 -0
- data/sig/pvectl/parsers/cloud_init_config.rbs +15 -0
- data/sig/pvectl/parsers/disk_config.rbs +19 -0
- data/sig/pvectl/parsers/lxc_mount_config.rbs +19 -0
- data/sig/pvectl/parsers/lxc_net_config.rbs +19 -0
- data/sig/pvectl/parsers/net_config.rbs +19 -0
- data/sig/pvectl/parsers/smart_text.rbs +7 -0
- data/sig/pvectl/plugin_loader.rbs +25 -0
- data/sig/pvectl/presenters/apt_package.rbs +19 -0
- data/sig/pvectl/presenters/backup.rbs +25 -0
- data/sig/pvectl/presenters/base.rbs +41 -0
- data/sig/pvectl/presenters/capability.rbs +19 -0
- data/sig/pvectl/presenters/config/context.rbs +17 -0
- data/sig/pvectl/presenters/container.rbs +78 -0
- data/sig/pvectl/presenters/container_operation_result.rbs +19 -0
- data/sig/pvectl/presenters/disk.rbs +31 -0
- data/sig/pvectl/presenters/dns_config.rbs +13 -0
- data/sig/pvectl/presenters/hosts_file.rbs +13 -0
- data/sig/pvectl/presenters/journal_entry.rbs +11 -0
- data/sig/pvectl/presenters/node.rbs +118 -0
- data/sig/pvectl/presenters/node_operation_result.rbs +11 -0
- data/sig/pvectl/presenters/operation_result.rbs +17 -0
- data/sig/pvectl/presenters/service.rbs +15 -0
- data/sig/pvectl/presenters/snapshot.rbs +35 -0
- data/sig/pvectl/presenters/snapshot_operation_result.rbs +27 -0
- data/sig/pvectl/presenters/storage.rbs +59 -0
- data/sig/pvectl/presenters/subscription.rbs +36 -0
- data/sig/pvectl/presenters/syslog_entry.rbs +11 -0
- data/sig/pvectl/presenters/task_entry.rbs +21 -0
- data/sig/pvectl/presenters/task_log_line.rbs +11 -0
- data/sig/pvectl/presenters/template.rbs +15 -0
- data/sig/pvectl/presenters/time_config.rbs +19 -0
- data/sig/pvectl/presenters/top_container.rbs +17 -0
- data/sig/pvectl/presenters/top_node.rbs +17 -0
- data/sig/pvectl/presenters/top_presenter.rbs +13 -0
- data/sig/pvectl/presenters/top_vm.rbs +17 -0
- data/sig/pvectl/presenters/vm.rbs +91 -0
- data/sig/pvectl/presenters/vm_operation_result.rbs +19 -0
- data/sig/pvectl/presenters/volume.rbs +23 -0
- data/sig/pvectl/presenters/volume_operation_result.rbs +11 -0
- data/sig/pvectl/repositories/apt.rbs +17 -0
- data/sig/pvectl/repositories/backup.rbs +27 -0
- data/sig/pvectl/repositories/base.rbs +23 -0
- data/sig/pvectl/repositories/capabilities.rbs +20 -0
- data/sig/pvectl/repositories/container.rbs +63 -0
- data/sig/pvectl/repositories/disk.rbs +17 -0
- data/sig/pvectl/repositories/dns.rbs +13 -0
- data/sig/pvectl/repositories/hosts.rbs +13 -0
- data/sig/pvectl/repositories/journal.rbs +7 -0
- data/sig/pvectl/repositories/node.rbs +68 -0
- data/sig/pvectl/repositories/service.rbs +27 -0
- data/sig/pvectl/repositories/snapshot.rbs +19 -0
- data/sig/pvectl/repositories/storage.rbs +37 -0
- data/sig/pvectl/repositories/subscription.rbs +17 -0
- data/sig/pvectl/repositories/syslog.rbs +7 -0
- data/sig/pvectl/repositories/task.rbs +19 -0
- data/sig/pvectl/repositories/task_list.rbs +7 -0
- data/sig/pvectl/repositories/task_log.rbs +11 -0
- data/sig/pvectl/repositories/time_config.rbs +13 -0
- data/sig/pvectl/repositories/vm.rbs +85 -0
- data/sig/pvectl/repositories/volume.rbs +43 -0
- data/sig/pvectl/selectors/base.rbs +37 -0
- data/sig/pvectl/selectors/container.rbs +19 -0
- data/sig/pvectl/selectors/disk.rbs +13 -0
- data/sig/pvectl/selectors/vm.rbs +19 -0
- data/sig/pvectl/selectors/volume.rbs +13 -0
- data/sig/pvectl/services/backup.rbs +27 -0
- data/sig/pvectl/services/clone_container.rbs +35 -0
- data/sig/pvectl/services/clone_vm.rbs +35 -0
- data/sig/pvectl/services/cloudinit.rbs +19 -0
- data/sig/pvectl/services/console.rbs +23 -0
- data/sig/pvectl/services/container_lifecycle.rbs +26 -0
- data/sig/pvectl/services/create_container.rbs +64 -0
- data/sig/pvectl/services/create_vm.rbs +72 -0
- data/sig/pvectl/services/edit_container.rbs +17 -0
- data/sig/pvectl/services/edit_dns.rbs +23 -0
- data/sig/pvectl/services/edit_hosts.rbs +13 -0
- data/sig/pvectl/services/edit_node.rbs +21 -0
- data/sig/pvectl/services/edit_vm.rbs +17 -0
- data/sig/pvectl/services/edit_volume.rbs +18 -0
- data/sig/pvectl/services/get/resource_service.rbs +23 -0
- data/sig/pvectl/services/move_disk.rbs +21 -0
- data/sig/pvectl/services/pull_config.rbs +18 -0
- data/sig/pvectl/services/push_config.rbs +37 -0
- data/sig/pvectl/services/resize_volume.rbs +47 -0
- data/sig/pvectl/services/resource_delete.rbs +27 -0
- data/sig/pvectl/services/resource_migration.rbs +29 -0
- data/sig/pvectl/services/sendkey.rbs +19 -0
- data/sig/pvectl/services/service_lifecycle.rbs +17 -0
- data/sig/pvectl/services/set_container.rbs +14 -0
- data/sig/pvectl/services/set_node.rbs +23 -0
- data/sig/pvectl/services/set_vm.rbs +14 -0
- data/sig/pvectl/services/set_volume.rbs +12 -0
- data/sig/pvectl/services/snapshot.rbs +35 -0
- data/sig/pvectl/services/task_listing.rbs +13 -0
- data/sig/pvectl/services/unlink_disk.rbs +17 -0
- data/sig/pvectl/services/vm_lifecycle.rbs +26 -0
- data/sig/pvectl/services/wakeonlan.rbs +17 -0
- data/sig/pvectl/utils/resource_resolver.rbs +17 -0
- data/sig/pvectl/wizards/create_container.rbs +21 -0
- data/sig/pvectl/wizards/create_vm.rbs +21 -0
- data/sig/pvectl.rbs +9 -0
- metadata +675 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
require_relative "task_list"
|
|
5
|
+
|
|
6
|
+
module Pvectl
|
|
7
|
+
module Repositories
|
|
8
|
+
# Repository for QEMU virtual machines.
|
|
9
|
+
#
|
|
10
|
+
# Uses the `/cluster/resources` API endpoint to list VMs across the cluster.
|
|
11
|
+
# Filters to only include QEMU VMs (excludes LXC containers).
|
|
12
|
+
#
|
|
13
|
+
# @example Listing all VMs
|
|
14
|
+
# repo = Vm.new(connection)
|
|
15
|
+
# vms = repo.list
|
|
16
|
+
# vms.each { |vm| puts "#{vm.vmid}: #{vm.name}" }
|
|
17
|
+
#
|
|
18
|
+
# @example Listing VMs on a specific node
|
|
19
|
+
# vms = repo.list(node: "pve-node1")
|
|
20
|
+
#
|
|
21
|
+
# @example Getting a single VM
|
|
22
|
+
# vm = repo.get(100)
|
|
23
|
+
# puts vm.name if vm
|
|
24
|
+
#
|
|
25
|
+
# @see Pvectl::Models::Vm VM model
|
|
26
|
+
# @see Pvectl::Connection API connection
|
|
27
|
+
#
|
|
28
|
+
class Vm < Base
|
|
29
|
+
# Lists all VMs in the cluster.
|
|
30
|
+
#
|
|
31
|
+
# Uses `/cluster/resources?type=vm` endpoint for efficient cluster-wide
|
|
32
|
+
# listing. Filters to only include QEMU VMs (type == "qemu").
|
|
33
|
+
#
|
|
34
|
+
# @param node [String, nil] filter by node name
|
|
35
|
+
# @return [Array<Models::Vm>] collection of VM models
|
|
36
|
+
def list(node: nil)
|
|
37
|
+
response = connection.client["cluster/resources"].get(params: { type: "vm" })
|
|
38
|
+
vms = response.select { |r| r[:type] == "qemu" }
|
|
39
|
+
vms = vms.select { |r| r[:node] == node } if node
|
|
40
|
+
vms.map { |data| build_model(data) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Gets a single VM by VMID.
|
|
44
|
+
#
|
|
45
|
+
# @param vmid [Integer, String] VM identifier
|
|
46
|
+
# @return [Models::Vm, nil] VM model or nil if not found
|
|
47
|
+
def get(vmid)
|
|
48
|
+
list.find { |vm| vm.vmid == vmid.to_i }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Describes a VM with comprehensive details from multiple API endpoints.
|
|
52
|
+
#
|
|
53
|
+
# @param vmid [Integer, String] VM identifier
|
|
54
|
+
# @return [Models::Vm, nil] VM model with full details, or nil if not found
|
|
55
|
+
def describe(vmid)
|
|
56
|
+
vmid = vmid.to_i
|
|
57
|
+
|
|
58
|
+
# 1. Find VM in cluster to get node
|
|
59
|
+
basic_data = find_vm_basic_data(vmid)
|
|
60
|
+
return nil if basic_data.nil?
|
|
61
|
+
|
|
62
|
+
node = basic_data[:node]
|
|
63
|
+
|
|
64
|
+
# 2. Fetch detailed data from node-specific endpoints
|
|
65
|
+
describe_data = {
|
|
66
|
+
config: fetch_config(node, vmid),
|
|
67
|
+
status: fetch_status(node, vmid),
|
|
68
|
+
snapshots: fetch_snapshots(node, vmid),
|
|
69
|
+
agent_ips: fetch_agent_ips(node, vmid),
|
|
70
|
+
pending: fetch_pending(node, vmid),
|
|
71
|
+
tasks: fetch_tasks(node, vmid),
|
|
72
|
+
firewall: fetch_firewall(node, vmid)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
build_describe_model(basic_data, describe_data)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# ---------------------------
|
|
79
|
+
# Lifecycle Operations
|
|
80
|
+
# ---------------------------
|
|
81
|
+
|
|
82
|
+
# Starts a VM.
|
|
83
|
+
#
|
|
84
|
+
# @param vmid [Integer, String] VM identifier
|
|
85
|
+
# @param node [String] Node name
|
|
86
|
+
# @return [String] Task UPID
|
|
87
|
+
def start(vmid, node)
|
|
88
|
+
post_status(vmid, node, "start")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Stops a VM immediately (hard stop).
|
|
92
|
+
#
|
|
93
|
+
# @param vmid [Integer, String] VM identifier
|
|
94
|
+
# @param node [String] Node name
|
|
95
|
+
# @return [String] Task UPID
|
|
96
|
+
def stop(vmid, node)
|
|
97
|
+
post_status(vmid, node, "stop")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Shuts down a VM gracefully (ACPI).
|
|
101
|
+
#
|
|
102
|
+
# @param vmid [Integer, String] VM identifier
|
|
103
|
+
# @param node [String] Node name
|
|
104
|
+
# @return [String] Task UPID
|
|
105
|
+
def shutdown(vmid, node)
|
|
106
|
+
post_status(vmid, node, "shutdown")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Restarts a VM (reboot).
|
|
110
|
+
#
|
|
111
|
+
# @param vmid [Integer, String] VM identifier
|
|
112
|
+
# @param node [String] Node name
|
|
113
|
+
# @return [String] Task UPID
|
|
114
|
+
def restart(vmid, node)
|
|
115
|
+
post_status(vmid, node, "reboot")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Resets a VM (hard reset).
|
|
119
|
+
#
|
|
120
|
+
# @param vmid [Integer, String] VM identifier
|
|
121
|
+
# @param node [String] Node name
|
|
122
|
+
# @return [String] Task UPID
|
|
123
|
+
def reset(vmid, node)
|
|
124
|
+
post_status(vmid, node, "reset")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Suspends a VM (hibernate).
|
|
128
|
+
#
|
|
129
|
+
# @param vmid [Integer, String] VM identifier
|
|
130
|
+
# @param node [String] Node name
|
|
131
|
+
# @return [String] Task UPID
|
|
132
|
+
def suspend(vmid, node)
|
|
133
|
+
post_status(vmid, node, "suspend")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Resumes a suspended VM.
|
|
137
|
+
#
|
|
138
|
+
# @param vmid [Integer, String] VM identifier
|
|
139
|
+
# @param node [String] Node name
|
|
140
|
+
# @return [String] Task UPID
|
|
141
|
+
def resume(vmid, node)
|
|
142
|
+
post_status(vmid, node, "resume")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Opens a terminal proxy session for a VM.
|
|
146
|
+
#
|
|
147
|
+
# @param vmid [Integer, String] VM identifier
|
|
148
|
+
# @param node [String] Node name
|
|
149
|
+
# @return [Hash] termproxy data with :port, :ticket, :user keys
|
|
150
|
+
def termproxy(vmid, node)
|
|
151
|
+
response = connection.client["nodes/#{node}/qemu/#{vmid}/termproxy"].post({})
|
|
152
|
+
normalize_hash_response(response)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Deletes a VM from the cluster.
|
|
156
|
+
#
|
|
157
|
+
# @param vmid [Integer, String] VM identifier
|
|
158
|
+
# @param node [String] Node name
|
|
159
|
+
# @param destroy_disks [Boolean] destroy unreferenced disks (default: true)
|
|
160
|
+
# @param purge [Boolean] remove from HA, replication, backups (default: false)
|
|
161
|
+
# @param force [Boolean] skip lock (default: false)
|
|
162
|
+
# @return [String] Task UPID
|
|
163
|
+
def delete(vmid, node, destroy_disks: true, purge: false, force: false)
|
|
164
|
+
params = {}
|
|
165
|
+
params["destroy-unreferenced-disks"] = 1 if destroy_disks
|
|
166
|
+
params[:purge] = 1 if purge
|
|
167
|
+
params[:skiplock] = 1 if force
|
|
168
|
+
|
|
169
|
+
connection.client["nodes/#{node}/qemu/#{vmid}"].delete(params)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Clones a VM to create a new VM.
|
|
173
|
+
#
|
|
174
|
+
# Posts to `/nodes/{node}/qemu/{vmid}/clone` with the specified parameters.
|
|
175
|
+
#
|
|
176
|
+
# @param vmid [Integer, String] source VM identifier
|
|
177
|
+
# @param node [String] source node name
|
|
178
|
+
# @param new_vmid [Integer] VMID for the new cloned VM
|
|
179
|
+
# @param options [Hash] optional clone parameters
|
|
180
|
+
# @option options [String] :name name for the new VM
|
|
181
|
+
# @option options [String] :target target node for the clone
|
|
182
|
+
# @option options [String] :storage target storage for the clone
|
|
183
|
+
# @option options [Boolean] :full full clone (true) or linked clone (false)
|
|
184
|
+
# @option options [String] :description description for the new VM
|
|
185
|
+
# @option options [String] :pool resource pool for the new VM
|
|
186
|
+
# @return [String] Task UPID
|
|
187
|
+
def clone(vmid, node, new_vmid, options = {})
|
|
188
|
+
params = { newid: new_vmid }
|
|
189
|
+
params[:name] = options[:name] if options[:name]
|
|
190
|
+
params[:target] = options[:target] if options[:target]
|
|
191
|
+
params[:storage] = options[:storage] if options[:storage]
|
|
192
|
+
params[:full] = options[:full] ? 1 : 0 if options.key?(:full)
|
|
193
|
+
params[:description] = options[:description] if options[:description]
|
|
194
|
+
params[:pool] = options[:pool] if options[:pool]
|
|
195
|
+
|
|
196
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/clone"].post(params)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Converts a VM to a template.
|
|
200
|
+
#
|
|
201
|
+
# This is an irreversible operation. The VM will become read-only
|
|
202
|
+
# and can only be used as a source for cloning.
|
|
203
|
+
#
|
|
204
|
+
# @param vmid [Integer, String] VM identifier
|
|
205
|
+
# @param node [String] Node name
|
|
206
|
+
# @param disk [String, nil] specific disk to convert (e.g., "scsi0")
|
|
207
|
+
# @return [void]
|
|
208
|
+
def convert_to_template(vmid, node, disk: nil)
|
|
209
|
+
params = {}
|
|
210
|
+
params[:disk] = disk if disk
|
|
211
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/template"].post(params)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Creates a new VM on the specified node.
|
|
215
|
+
#
|
|
216
|
+
# Posts to `/nodes/{node}/qemu` with the VM configuration parameters.
|
|
217
|
+
# The vmid is merged into params automatically.
|
|
218
|
+
#
|
|
219
|
+
# @param node [String] target node name
|
|
220
|
+
# @param vmid [Integer] VM identifier
|
|
221
|
+
# @param params [Hash] VM configuration parameters (name, cores, memory, etc.)
|
|
222
|
+
# @return [String] Task UPID
|
|
223
|
+
#
|
|
224
|
+
# @example Create a basic VM
|
|
225
|
+
# repo.create("pve1", 100, { name: "web-server", cores: 4, memory: 4096 })
|
|
226
|
+
# #=> "UPID:pve1:..."
|
|
227
|
+
def create(node, vmid, params = {})
|
|
228
|
+
api_params = params.merge(vmid: vmid)
|
|
229
|
+
connection.client["nodes/#{node}/qemu"].post(api_params)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Updates an existing VM configuration.
|
|
233
|
+
#
|
|
234
|
+
# PUTs to +/nodes/{node}/qemu/{vmid}/config+ with configuration parameters.
|
|
235
|
+
# This is a synchronous operation — changes are applied immediately.
|
|
236
|
+
#
|
|
237
|
+
# @param vmid [Integer, String] VM identifier
|
|
238
|
+
# @param node [String] node name
|
|
239
|
+
# @param params [Hash] VM configuration parameters to update
|
|
240
|
+
# @return [nil]
|
|
241
|
+
def update(vmid, node, params = {})
|
|
242
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/config"].put(params)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Resizes a VM disk.
|
|
246
|
+
#
|
|
247
|
+
# PUTs to +/nodes/{node}/qemu/{vmid}/resize+ with disk and size parameters.
|
|
248
|
+
# Size can be absolute (e.g., "50G") or relative (e.g., "+10G").
|
|
249
|
+
#
|
|
250
|
+
# @param vmid [Integer, String] VM identifier
|
|
251
|
+
# @param node [String] node name
|
|
252
|
+
# @param disk [String] disk name (e.g., "scsi0", "virtio0")
|
|
253
|
+
# @param size [String] new size or size increment (e.g., "50G", "+10G")
|
|
254
|
+
# @return [nil]
|
|
255
|
+
def resize(vmid, node, disk:, size:)
|
|
256
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/resize"].put({ disk: disk, size: size })
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Sends a QEMU monitor key event to a running VM.
|
|
260
|
+
#
|
|
261
|
+
# PUTs to +/nodes/{node}/qemu/{vmid}/sendkey+ with the +key+ parameter.
|
|
262
|
+
# The +key+ uses QEMU qcode format (e.g., "ctrl-alt-delete", "ret", "f1").
|
|
263
|
+
# This is a synchronous operation — Proxmox returns null on success.
|
|
264
|
+
#
|
|
265
|
+
# @param vmid [Integer, String] VM identifier
|
|
266
|
+
# @param node [String] node name
|
|
267
|
+
# @param key [String] QEMU qcode key sequence (e.g., "ctrl-alt-delete")
|
|
268
|
+
# @return [nil]
|
|
269
|
+
def sendkey(vmid, node, key)
|
|
270
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/sendkey"].put({ key: key })
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Unlinks (removes) one or more disks from a VM configuration.
|
|
274
|
+
#
|
|
275
|
+
# PUTs to +/nodes/{node}/qemu/{vmid}/unlink+ with the comma-separated
|
|
276
|
+
# list of disk IDs. By default, Proxmox keeps removed volumes as
|
|
277
|
+
# +unused[n]+ entries in the config; with +force: true+ the underlying
|
|
278
|
+
# volume is physically removed.
|
|
279
|
+
#
|
|
280
|
+
# @param node [String] node name
|
|
281
|
+
# @param vmid [Integer, String] VM identifier
|
|
282
|
+
# @param disk_ids [Array<String>, String] disk identifiers (e.g., "scsi0"
|
|
283
|
+
# or %w[scsi0 scsi1] or "scsi0,scsi1")
|
|
284
|
+
# @param force [Boolean] physically delete the underlying volume(s)
|
|
285
|
+
# (default: false — keep as unused[n])
|
|
286
|
+
# @return [nil] this is a synchronous operation and returns no UPID
|
|
287
|
+
def unlink_disks(node, vmid, disk_ids, force: false)
|
|
288
|
+
idlist = Array(disk_ids).flat_map { |id| id.to_s.split(",") }.map(&:strip).reject(&:empty?).join(",")
|
|
289
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/unlink"].put(
|
|
290
|
+
{ idlist: idlist, force: force ? 1 : 0 }
|
|
291
|
+
)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Fetches VM configuration.
|
|
295
|
+
#
|
|
296
|
+
# @param node [String] node name
|
|
297
|
+
# @param vmid [Integer] VM identifier
|
|
298
|
+
# @return [Hash] config data
|
|
299
|
+
def fetch_config(node, vmid)
|
|
300
|
+
resp = connection.client["nodes/#{node}/qemu/#{vmid}/config"].get
|
|
301
|
+
normalize_hash_response(resp)
|
|
302
|
+
rescue StandardError
|
|
303
|
+
{}
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Migrates a VM to another node.
|
|
307
|
+
#
|
|
308
|
+
# @param vmid [Integer, String] VM identifier
|
|
309
|
+
# @param node [String] current node name
|
|
310
|
+
# @param params [Hash] migration parameters (:target, :online, :"with-local-disks", :targetstorage)
|
|
311
|
+
# @return [String] Task UPID
|
|
312
|
+
# @raise [ArgumentError] if node name or vmid format is invalid
|
|
313
|
+
def migrate(vmid, node, params = {})
|
|
314
|
+
unless node.match?(/\A[a-z][a-z0-9-]*\z/)
|
|
315
|
+
raise ArgumentError, "Invalid node name: #{node}"
|
|
316
|
+
end
|
|
317
|
+
unless vmid.is_a?(Integer) && vmid.positive?
|
|
318
|
+
raise ArgumentError, "Invalid VMID: #{vmid}"
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/migrate"].post(params)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Moves a VM disk to a different storage on the same node.
|
|
325
|
+
#
|
|
326
|
+
# POSTs to +/nodes/{node}/qemu/{vmid}/move_disk+ with the disk identifier
|
|
327
|
+
# and target storage. The operation is asynchronous — the returned UPID
|
|
328
|
+
# can be polled via Repositories::Task to track completion.
|
|
329
|
+
#
|
|
330
|
+
# @param vmid [Integer, String] VM identifier
|
|
331
|
+
# @param node [String] node name where the VM currently resides
|
|
332
|
+
# @param disk [String] disk identifier (e.g., "scsi0", "virtio0")
|
|
333
|
+
# @param target_storage [String] destination storage ID
|
|
334
|
+
# @param format [String, nil] target disk format ("raw", "qcow2", "vmdk")
|
|
335
|
+
# @param delete [Boolean] delete the source disk after copy (default: false)
|
|
336
|
+
# @param bwlimit [Integer, nil] I/O bandwidth limit in KiB/s
|
|
337
|
+
# @return [String] Task UPID
|
|
338
|
+
#
|
|
339
|
+
# @example Move scsi0 to local-lvm storage
|
|
340
|
+
# repo.move_disk(100, "pve1", "scsi0", "local-lvm")
|
|
341
|
+
# #=> "UPID:pve1:..."
|
|
342
|
+
#
|
|
343
|
+
# @example Move and convert format, delete source
|
|
344
|
+
# repo.move_disk(100, "pve1", "scsi0", "local-lvm",
|
|
345
|
+
# format: "qcow2", delete: true)
|
|
346
|
+
def move_disk(vmid, node, disk, target_storage, format: nil, delete: false, bwlimit: nil)
|
|
347
|
+
params = { disk: disk, storage: target_storage }
|
|
348
|
+
params[:format] = format if format
|
|
349
|
+
params[:delete] = 1 if delete
|
|
350
|
+
params[:bwlimit] = bwlimit if bwlimit
|
|
351
|
+
|
|
352
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/move_disk"].post(params)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Regenerates the cloud-init configuration ISO for a VM.
|
|
356
|
+
#
|
|
357
|
+
# PUTs to +/nodes/{node}/qemu/{vmid}/cloudinit+. The Proxmox API
|
|
358
|
+
# endpoint returns null on success.
|
|
359
|
+
#
|
|
360
|
+
# @param node [String] node name
|
|
361
|
+
# @param vmid [Integer, String] VM identifier
|
|
362
|
+
# @return [nil]
|
|
363
|
+
def cloudinit_regenerate(node, vmid)
|
|
364
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/cloudinit"].put
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Fetches pending cloud-init configuration changes for a VM.
|
|
368
|
+
#
|
|
369
|
+
# GETs from +/nodes/{node}/qemu/{vmid}/cloudinit+. Returns an array
|
|
370
|
+
# of pending entries, each with +:key+, +:value+, +:pending+, and
|
|
371
|
+
# optional +:delete+ keys.
|
|
372
|
+
#
|
|
373
|
+
# @param node [String] node name
|
|
374
|
+
# @param vmid [Integer, String] VM identifier
|
|
375
|
+
# @return [Array<Hash{Symbol => untyped}>] pending entries
|
|
376
|
+
def cloudinit_pending(node, vmid)
|
|
377
|
+
response = connection.client["nodes/#{node}/qemu/#{vmid}/cloudinit"].get
|
|
378
|
+
normalize_response(response)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Dumps the generated cloud-init configuration for a VM.
|
|
382
|
+
#
|
|
383
|
+
# GETs from +/nodes/{node}/qemu/{vmid}/cloudinit/dump+ with the
|
|
384
|
+
# specified +type+ query parameter. Returns the raw YAML/text body.
|
|
385
|
+
#
|
|
386
|
+
# @param node [String] node name
|
|
387
|
+
# @param vmid [Integer, String] VM identifier
|
|
388
|
+
# @param type [String] config type — one of +"user"+, +"network"+, +"meta"+
|
|
389
|
+
# @return [String] cloud-init configuration as raw text
|
|
390
|
+
def cloudinit_dump(node, vmid, type)
|
|
391
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/cloudinit/dump"].get(params: { type: type })
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Checks whether a feature (clone, snapshot, copy) is available for a VM.
|
|
395
|
+
#
|
|
396
|
+
# Calls +GET /nodes/{node}/qemu/{vmid}/feature+ with the feature and
|
|
397
|
+
# optional snapshot name. The Proxmox API returns +hasFeature+ (0/1) and
|
|
398
|
+
# a +nodes+ array listing cluster nodes that satisfy the feature.
|
|
399
|
+
#
|
|
400
|
+
# @param vmid [Integer, String] VM identifier
|
|
401
|
+
# @param node [String] node currently hosting the VM
|
|
402
|
+
# @param feature [String] feature name (one of: clone, snapshot, copy)
|
|
403
|
+
# @param snapname [String, nil] snapshot name (required for some checks)
|
|
404
|
+
# @return [Hash] result with :available (Boolean) and :nodes (Array<String>)
|
|
405
|
+
#
|
|
406
|
+
# @example Check whether VM 100 can be cloned
|
|
407
|
+
# repo.feature_available?(100, "pve1", "clone")
|
|
408
|
+
# #=> { available: true, nodes: ["pve1", "pve2"] }
|
|
409
|
+
def feature_available?(vmid, node, feature, snapname: nil)
|
|
410
|
+
params = { feature: feature }
|
|
411
|
+
params[:snapname] = snapname unless snapname.nil?
|
|
412
|
+
|
|
413
|
+
response = connection.client["nodes/#{node}/qemu/#{vmid}/feature"].get(params: params)
|
|
414
|
+
data = normalize_hash_response(response)
|
|
415
|
+
|
|
416
|
+
{
|
|
417
|
+
available: data[:hasFeature].to_i == 1,
|
|
418
|
+
nodes: Array(data[:nodes])
|
|
419
|
+
}
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Returns the next available VMID from the Proxmox cluster.
|
|
423
|
+
#
|
|
424
|
+
# Uses the +/cluster/nextid+ API endpoint which performs server-side allocation.
|
|
425
|
+
# This is more reliable than client-side scanning because it detects stale
|
|
426
|
+
# config files that don't appear in +/cluster/resources+.
|
|
427
|
+
#
|
|
428
|
+
# @return [Integer] next available VMID
|
|
429
|
+
def next_available_vmid
|
|
430
|
+
connection.client["cluster/nextid"].get.to_i
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
protected
|
|
434
|
+
|
|
435
|
+
# Builds Vm model from API response data.
|
|
436
|
+
#
|
|
437
|
+
# @param data [Hash] API response hash with string keys
|
|
438
|
+
# @return [Models::Vm] VM model instance
|
|
439
|
+
def build_model(data)
|
|
440
|
+
Models::Vm.new(
|
|
441
|
+
vmid: data[:vmid],
|
|
442
|
+
name: data[:name],
|
|
443
|
+
status: data[:status],
|
|
444
|
+
node: data[:node],
|
|
445
|
+
cpu: data[:cpu],
|
|
446
|
+
maxcpu: data[:maxcpu],
|
|
447
|
+
mem: data[:mem],
|
|
448
|
+
maxmem: data[:maxmem],
|
|
449
|
+
disk: data[:disk],
|
|
450
|
+
maxdisk: data[:maxdisk],
|
|
451
|
+
uptime: data[:uptime],
|
|
452
|
+
template: data[:template],
|
|
453
|
+
tags: data[:tags],
|
|
454
|
+
hastate: data[:hastate],
|
|
455
|
+
netin: data[:netin],
|
|
456
|
+
netout: data[:netout],
|
|
457
|
+
type: data[:type]
|
|
458
|
+
)
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
private
|
|
462
|
+
|
|
463
|
+
# Posts to VM status endpoint.
|
|
464
|
+
#
|
|
465
|
+
# @param vmid [Integer, String] VM identifier
|
|
466
|
+
# @param node [String] Node name
|
|
467
|
+
# @param action [String] Action (start, stop, etc.)
|
|
468
|
+
# @return [String] Task UPID
|
|
469
|
+
def post_status(vmid, node, action)
|
|
470
|
+
connection.client["nodes/#{node}/qemu/#{vmid}/status/#{action}"].post
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# Finds VM basic data from cluster resources.
|
|
474
|
+
#
|
|
475
|
+
# @param vmid [Integer] VM identifier
|
|
476
|
+
# @return [Hash, nil] VM data or nil if not found
|
|
477
|
+
def find_vm_basic_data(vmid)
|
|
478
|
+
response = connection.client["cluster/resources"].get(params: { type: "vm" })
|
|
479
|
+
response.find { |r| r[:type] == "qemu" && r[:vmid] == vmid }
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Fetches VM runtime status.
|
|
483
|
+
#
|
|
484
|
+
# @param node [String] node name
|
|
485
|
+
# @param vmid [Integer] VM identifier
|
|
486
|
+
# @return [Hash] status data
|
|
487
|
+
def fetch_status(node, vmid)
|
|
488
|
+
resp = connection.client["nodes/#{node}/qemu/#{vmid}/status/current"].get
|
|
489
|
+
normalize_hash_response(resp)
|
|
490
|
+
rescue StandardError
|
|
491
|
+
{}
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# Fetches VM snapshots.
|
|
495
|
+
#
|
|
496
|
+
# @param node [String] node name
|
|
497
|
+
# @param vmid [Integer] VM identifier
|
|
498
|
+
# @return [Array<Hash>] snapshots list
|
|
499
|
+
def fetch_snapshots(node, vmid)
|
|
500
|
+
resp = connection.client["nodes/#{node}/qemu/#{vmid}/snapshot"].get
|
|
501
|
+
normalize_response(resp).reject { |s| s[:name] == "current" }
|
|
502
|
+
rescue StandardError
|
|
503
|
+
[]
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Fetches IP addresses from QEMU guest agent.
|
|
507
|
+
# Graceful failure - returns nil on any error.
|
|
508
|
+
#
|
|
509
|
+
# @param node [String] node name
|
|
510
|
+
# @param vmid [Integer] VM identifier
|
|
511
|
+
# @return [Array<Hash>, nil] interfaces or nil on error
|
|
512
|
+
def fetch_agent_ips(node, vmid)
|
|
513
|
+
resp = connection.client["nodes/#{node}/qemu/#{vmid}/agent/network-get-interfaces"].get
|
|
514
|
+
data = normalize_hash_response(resp)
|
|
515
|
+
data[:result]
|
|
516
|
+
rescue StandardError
|
|
517
|
+
nil
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Fetches pending configuration changes.
|
|
521
|
+
#
|
|
522
|
+
# @param node [String] node name
|
|
523
|
+
# @param vmid [Integer] VM identifier
|
|
524
|
+
# @return [Array<Hash>] pending changes
|
|
525
|
+
def fetch_pending(node, vmid)
|
|
526
|
+
resp = connection.client["nodes/#{node}/qemu/#{vmid}/pending"].get
|
|
527
|
+
normalize_response(resp)
|
|
528
|
+
rescue StandardError
|
|
529
|
+
[]
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Fetches firewall configuration (options, rules, aliases, IP sets).
|
|
533
|
+
#
|
|
534
|
+
# @param node [String] node name
|
|
535
|
+
# @param vmid [Integer] VM identifier
|
|
536
|
+
# @return [Hash] firewall data with :options, :rules, :aliases, :ipset keys
|
|
537
|
+
def fetch_firewall(node, vmid)
|
|
538
|
+
base = "nodes/#{node}/qemu/#{vmid}/firewall"
|
|
539
|
+
options = (normalize_hash_response(connection.client["#{base}/options"].get) rescue {})
|
|
540
|
+
rules = (normalize_response(connection.client["#{base}/rules"].get) rescue [])
|
|
541
|
+
aliases_data = (normalize_response(connection.client["#{base}/aliases"].get) rescue [])
|
|
542
|
+
ipset = (normalize_response(connection.client["#{base}/ipset"].get) rescue [])
|
|
543
|
+
{ options: options, rules: rules, aliases: aliases_data, ipset: ipset }
|
|
544
|
+
rescue StandardError
|
|
545
|
+
{}
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# Fetches recent task history for the VM.
|
|
549
|
+
#
|
|
550
|
+
# @param node [String] node name
|
|
551
|
+
# @param vmid [Integer] VM identifier
|
|
552
|
+
# @param limit [Integer] max entries (default 10)
|
|
553
|
+
# @return [Array<Models::TaskEntry>] recent tasks
|
|
554
|
+
def fetch_tasks(node, vmid, limit: 10)
|
|
555
|
+
task_list_repo = TaskList.new(connection)
|
|
556
|
+
task_list_repo.list(node: node, vmid: vmid, limit: limit)
|
|
557
|
+
rescue StandardError
|
|
558
|
+
[]
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
# Normalizes hash response that may be wrapped in :data key.
|
|
562
|
+
#
|
|
563
|
+
# @param response [Hash] API response
|
|
564
|
+
# @return [Hash] normalized hash
|
|
565
|
+
def normalize_hash_response(response)
|
|
566
|
+
if response.is_a?(Hash) && response[:data]
|
|
567
|
+
response[:data]
|
|
568
|
+
else
|
|
569
|
+
response || {}
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Normalizes array response.
|
|
574
|
+
#
|
|
575
|
+
# @param response [Array, Hash] API response
|
|
576
|
+
# @return [Array<Hash>] normalized array
|
|
577
|
+
def normalize_response(response)
|
|
578
|
+
if response.is_a?(Array)
|
|
579
|
+
response
|
|
580
|
+
elsif response.is_a?(Hash) && response[:data]
|
|
581
|
+
response[:data]
|
|
582
|
+
else
|
|
583
|
+
response.to_a
|
|
584
|
+
end
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# Builds VM model with describe-specific attributes.
|
|
588
|
+
#
|
|
589
|
+
# @param basic_data [Hash] basic VM data from cluster/resources
|
|
590
|
+
# @param describe_data [Hash] aggregated describe data
|
|
591
|
+
# @return [Models::Vm] VM model
|
|
592
|
+
def build_describe_model(basic_data, describe_data)
|
|
593
|
+
Models::Vm.new(
|
|
594
|
+
vmid: basic_data[:vmid],
|
|
595
|
+
name: basic_data[:name],
|
|
596
|
+
status: basic_data[:status],
|
|
597
|
+
node: basic_data[:node],
|
|
598
|
+
cpu: basic_data[:cpu],
|
|
599
|
+
maxcpu: basic_data[:maxcpu],
|
|
600
|
+
mem: basic_data[:mem],
|
|
601
|
+
maxmem: basic_data[:maxmem],
|
|
602
|
+
disk: basic_data[:disk],
|
|
603
|
+
maxdisk: basic_data[:maxdisk],
|
|
604
|
+
uptime: basic_data[:uptime],
|
|
605
|
+
template: basic_data[:template],
|
|
606
|
+
tags: basic_data[:tags],
|
|
607
|
+
hastate: basic_data[:hastate],
|
|
608
|
+
netin: basic_data[:netin],
|
|
609
|
+
netout: basic_data[:netout],
|
|
610
|
+
type: basic_data[:type],
|
|
611
|
+
describe_data: describe_data
|
|
612
|
+
)
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
end
|