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,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for systemd services on Proxmox nodes.
|
|
6
|
+
#
|
|
7
|
+
# Wraps the `/nodes/{node}/services` API endpoints. Provides listing of
|
|
8
|
+
# services on a node and lifecycle operations (start/stop/restart/reload)
|
|
9
|
+
# which return Proxmox task UPIDs.
|
|
10
|
+
#
|
|
11
|
+
# @example Listing services on a node
|
|
12
|
+
# repo = Service.new(connection)
|
|
13
|
+
# services = repo.list(node: "pve1")
|
|
14
|
+
# services.each { |s| puts "#{s.service}: #{s.active_state}" }
|
|
15
|
+
#
|
|
16
|
+
# @example Restarting a service
|
|
17
|
+
# upid = repo.restart("pve1", "pveproxy")
|
|
18
|
+
#
|
|
19
|
+
# @see Pvectl::Models::Service Service model
|
|
20
|
+
# @see Pvectl::Connection API connection
|
|
21
|
+
#
|
|
22
|
+
class Service < Base
|
|
23
|
+
# Lists systemd services on a node.
|
|
24
|
+
#
|
|
25
|
+
# When node is nil, iterates over all online nodes in the cluster.
|
|
26
|
+
# When node is specified, queries only that node.
|
|
27
|
+
#
|
|
28
|
+
# @param node [String, nil] node name (or nil for all online nodes)
|
|
29
|
+
# @return [Array<Models::Service>] collection of Service models
|
|
30
|
+
def list(node: nil)
|
|
31
|
+
if node
|
|
32
|
+
services_for_node(node)
|
|
33
|
+
else
|
|
34
|
+
online_nodes.flat_map { |node_name| services_for_node(node_name) }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Reads single service state.
|
|
39
|
+
#
|
|
40
|
+
# @param node [String] node name
|
|
41
|
+
# @param service [String] service identifier (e.g., "pveproxy")
|
|
42
|
+
# @return [Models::Service, nil] service model or nil on error
|
|
43
|
+
def state(node, service)
|
|
44
|
+
resp = connection.client["nodes/#{node}/services/#{service}/state"].get
|
|
45
|
+
data = extract_data(resp)
|
|
46
|
+
return nil if data.nil? || data.empty?
|
|
47
|
+
|
|
48
|
+
build_model(data.merge(node: node))
|
|
49
|
+
rescue StandardError
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Starts a service. Returns the task UPID.
|
|
54
|
+
#
|
|
55
|
+
# @param node [String] node name
|
|
56
|
+
# @param service [String] service identifier
|
|
57
|
+
# @return [String] task UPID
|
|
58
|
+
def start(node, service)
|
|
59
|
+
post_action(node, service, "start")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Stops a service. Returns the task UPID.
|
|
63
|
+
#
|
|
64
|
+
# @param node [String] node name
|
|
65
|
+
# @param service [String] service identifier
|
|
66
|
+
# @return [String] task UPID
|
|
67
|
+
def stop(node, service)
|
|
68
|
+
post_action(node, service, "stop")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Hard-restarts a service. Returns the task UPID.
|
|
72
|
+
#
|
|
73
|
+
# @param node [String] node name
|
|
74
|
+
# @param service [String] service identifier
|
|
75
|
+
# @return [String] task UPID
|
|
76
|
+
def restart(node, service)
|
|
77
|
+
post_action(node, service, "restart")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Reloads a service (falls back to restart if unsupported). Returns the task UPID.
|
|
81
|
+
#
|
|
82
|
+
# @param node [String] node name
|
|
83
|
+
# @param service [String] service identifier
|
|
84
|
+
# @return [String] task UPID
|
|
85
|
+
def reload(node, service)
|
|
86
|
+
post_action(node, service, "reload")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
protected
|
|
90
|
+
|
|
91
|
+
# Builds Service model from API response data.
|
|
92
|
+
#
|
|
93
|
+
# @param data [Hash] API response hash
|
|
94
|
+
# @return [Models::Service] Service model instance
|
|
95
|
+
def build_model(data)
|
|
96
|
+
Models::Service.new(data)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# POSTs a lifecycle action and extracts the UPID from the response.
|
|
102
|
+
#
|
|
103
|
+
# @param node [String] node name
|
|
104
|
+
# @param service [String] service identifier
|
|
105
|
+
# @param action [String] one of "start", "stop", "restart", "reload"
|
|
106
|
+
# @return [String] task UPID
|
|
107
|
+
def post_action(node, service, action)
|
|
108
|
+
resp = connection.client["nodes/#{node}/services/#{service}/#{action}"].post({})
|
|
109
|
+
data = extract_data(resp)
|
|
110
|
+
data.is_a?(String) ? data : data.to_s
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Fetches services for a single node.
|
|
114
|
+
#
|
|
115
|
+
# @param node_name [String] node name
|
|
116
|
+
# @return [Array<Models::Service>] services on that node
|
|
117
|
+
def services_for_node(node_name)
|
|
118
|
+
response = connection.client["nodes/#{node_name}/services"].get
|
|
119
|
+
services_data = unwrap(response)
|
|
120
|
+
services_data.map { |data| build_model(data.merge(node: node_name)) }
|
|
121
|
+
rescue StandardError
|
|
122
|
+
[]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Fetches list of online node names.
|
|
126
|
+
#
|
|
127
|
+
# @return [Array<String>] online node names
|
|
128
|
+
def online_nodes
|
|
129
|
+
response = connection.client["nodes"].get
|
|
130
|
+
nodes_data = unwrap(response)
|
|
131
|
+
nodes_data
|
|
132
|
+
.select { |n| n[:status] == "online" }
|
|
133
|
+
.map { |n| n[:node] || n[:name] }
|
|
134
|
+
rescue StandardError
|
|
135
|
+
[]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for VM/container snapshots.
|
|
6
|
+
#
|
|
7
|
+
# Handles listing snapshots for both QEMU VMs and LXC containers.
|
|
8
|
+
# Filters out the "current" snapshot which represents the live state.
|
|
9
|
+
#
|
|
10
|
+
# @example Listing snapshots for a VM
|
|
11
|
+
# repo = Snapshot.new(connection)
|
|
12
|
+
# snapshots = repo.list(100, "pve1", :qemu)
|
|
13
|
+
# snapshots.each { |s| puts "#{s.name}: #{s.description}" }
|
|
14
|
+
#
|
|
15
|
+
# @example Listing snapshots for a container
|
|
16
|
+
# snapshots = repo.list(101, "pve1", :lxc)
|
|
17
|
+
#
|
|
18
|
+
# @see Pvectl::Models::Snapshot Snapshot model
|
|
19
|
+
# @see Pvectl::Connection API connection
|
|
20
|
+
#
|
|
21
|
+
class Snapshot < Base
|
|
22
|
+
# Lists all snapshots for a VM or container.
|
|
23
|
+
#
|
|
24
|
+
# Uses `/nodes/{node}/qemu/{vmid}/snapshot` for VMs or
|
|
25
|
+
# `/nodes/{node}/lxc/{vmid}/snapshot` for containers.
|
|
26
|
+
# Filters out the "current" snapshot which represents live state.
|
|
27
|
+
#
|
|
28
|
+
# @param vmid [Integer, String] VM or container identifier
|
|
29
|
+
# @param node [String] node name where the resource resides
|
|
30
|
+
# @param resource_type [Symbol] :qemu for VMs, :lxc for containers
|
|
31
|
+
# @return [Array<Models::Snapshot>] collection of snapshot models
|
|
32
|
+
def list(vmid, node, resource_type)
|
|
33
|
+
endpoint = resource_endpoint(resource_type)
|
|
34
|
+
response = connection.client["nodes/#{node}/#{endpoint}/#{vmid}/snapshot"].get
|
|
35
|
+
|
|
36
|
+
response
|
|
37
|
+
.reject { |s| s[:name] == "current" }
|
|
38
|
+
.map { |data| build_model(data, vmid, node, resource_type) }
|
|
39
|
+
rescue StandardError
|
|
40
|
+
[]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Creates a snapshot for a VM or container.
|
|
44
|
+
#
|
|
45
|
+
# Uses `POST /nodes/{node}/qemu|lxc/{vmid}/snapshot` with parameters:
|
|
46
|
+
# - snapname: Name of the snapshot
|
|
47
|
+
# - description: Optional description
|
|
48
|
+
# - vmstate: Include VM RAM state (QEMU only, ignored for LXC)
|
|
49
|
+
#
|
|
50
|
+
# @param vmid [Integer, String] VM or container identifier
|
|
51
|
+
# @param node [String] node name where the resource resides
|
|
52
|
+
# @param resource_type [Symbol] :qemu for VMs, :lxc for containers
|
|
53
|
+
# @param name [String] name for the snapshot
|
|
54
|
+
# @param description [String, nil] optional description
|
|
55
|
+
# @param vmstate [Boolean] include RAM state (QEMU only)
|
|
56
|
+
# @return [String] UPID of the snapshot creation task
|
|
57
|
+
def create(vmid, node, resource_type, name:, description: nil, vmstate: false)
|
|
58
|
+
endpoint = resource_endpoint(resource_type)
|
|
59
|
+
params = { snapname: name }
|
|
60
|
+
params[:description] = description if description
|
|
61
|
+
params[:vmstate] = vmstate if vmstate && resource_type == :qemu
|
|
62
|
+
|
|
63
|
+
connection.client["nodes/#{node}/#{endpoint}/#{vmid}/snapshot"].post(params)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Deletes a snapshot.
|
|
67
|
+
#
|
|
68
|
+
# Uses `DELETE /nodes/{node}/qemu|lxc/{vmid}/snapshot/{snapname}`.
|
|
69
|
+
#
|
|
70
|
+
# @param vmid [Integer, String] VM or container identifier
|
|
71
|
+
# @param node [String] node name where the resource resides
|
|
72
|
+
# @param resource_type [Symbol] :qemu for VMs, :lxc for containers
|
|
73
|
+
# @param snapname [String] name of the snapshot to delete
|
|
74
|
+
# @param force [Boolean] force deletion even if snapshot is referenced
|
|
75
|
+
# @return [String] UPID of the delete task
|
|
76
|
+
def delete(vmid, node, resource_type, snapname, force: false)
|
|
77
|
+
endpoint = resource_endpoint(resource_type)
|
|
78
|
+
params = {}
|
|
79
|
+
params[:force] = true if force
|
|
80
|
+
|
|
81
|
+
connection.client["nodes/#{node}/#{endpoint}/#{vmid}/snapshot/#{snapname}"].delete(params)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Rolls back to a snapshot.
|
|
85
|
+
#
|
|
86
|
+
# Uses `POST /nodes/{node}/qemu|lxc/{vmid}/snapshot/{snapname}/rollback`.
|
|
87
|
+
#
|
|
88
|
+
# @param vmid [Integer, String] VM or container identifier
|
|
89
|
+
# @param node [String] node name where the resource resides
|
|
90
|
+
# @param resource_type [Symbol] :qemu for VMs, :lxc for containers
|
|
91
|
+
# @param snapname [String] name of the snapshot to rollback to
|
|
92
|
+
# @param start [Boolean] start VM/container after rollback
|
|
93
|
+
# @return [String] UPID of the rollback task
|
|
94
|
+
def rollback(vmid, node, resource_type, snapname, start: false)
|
|
95
|
+
endpoint = resource_endpoint(resource_type)
|
|
96
|
+
params = {}
|
|
97
|
+
params[:start] = true if start
|
|
98
|
+
|
|
99
|
+
connection.client["nodes/#{node}/#{endpoint}/#{vmid}/snapshot/#{snapname}/rollback"].post(params)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
# Returns the API endpoint prefix for the resource type.
|
|
105
|
+
#
|
|
106
|
+
# @param resource_type [Symbol] :qemu or :lxc
|
|
107
|
+
# @return [String] "qemu" or "lxc"
|
|
108
|
+
def resource_endpoint(resource_type)
|
|
109
|
+
resource_type == :lxc ? "lxc" : "qemu"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Builds Snapshot model from API response data.
|
|
113
|
+
#
|
|
114
|
+
# @param data [Hash] API response hash
|
|
115
|
+
# @param vmid [Integer, String] VM/container ID
|
|
116
|
+
# @param node [String] node name
|
|
117
|
+
# @param resource_type [Symbol] :qemu or :lxc
|
|
118
|
+
# @return [Models::Snapshot] snapshot model instance
|
|
119
|
+
def build_model(data, vmid, node, resource_type)
|
|
120
|
+
Models::Snapshot.new(
|
|
121
|
+
name: data[:name],
|
|
122
|
+
snaptime: data[:snaptime],
|
|
123
|
+
description: data[:description],
|
|
124
|
+
vmstate: data[:vmstate],
|
|
125
|
+
parent: data[:parent],
|
|
126
|
+
vmid: vmid,
|
|
127
|
+
node: node,
|
|
128
|
+
resource_type: resource_type
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for Proxmox cluster storage pools.
|
|
6
|
+
#
|
|
7
|
+
# Uses the `/cluster/resources?type=storage` API endpoint for cluster-wide view.
|
|
8
|
+
# Handles aggregation of shared storage (deduplication by name).
|
|
9
|
+
#
|
|
10
|
+
# @example Listing all storage pools
|
|
11
|
+
# repo = Storage.new(connection)
|
|
12
|
+
# storage_pools = repo.list
|
|
13
|
+
# storage_pools.each { |s| puts "#{s.name}: #{s.plugintype}" }
|
|
14
|
+
#
|
|
15
|
+
# @example Listing storage on a specific node
|
|
16
|
+
# storage_pools = repo.list(node: "pve1")
|
|
17
|
+
#
|
|
18
|
+
# @see Pvectl::Models::Storage Storage model
|
|
19
|
+
# @see Pvectl::Connection API connection
|
|
20
|
+
#
|
|
21
|
+
class Storage < Base
|
|
22
|
+
# Lists all storage pools in the cluster.
|
|
23
|
+
#
|
|
24
|
+
# Uses `/cluster/resources?type=storage` endpoint for cluster-wide view.
|
|
25
|
+
# Aggregates shared storage by keeping first entry per storage name.
|
|
26
|
+
#
|
|
27
|
+
# @param node [String, nil] filter by node name
|
|
28
|
+
# @return [Array<Models::Storage>] collection of Storage models
|
|
29
|
+
def list(node: nil)
|
|
30
|
+
response = connection.client["cluster/resources"].get(params: { type: "storage" })
|
|
31
|
+
storage_data = normalize_response(response)
|
|
32
|
+
|
|
33
|
+
# Aggregate shared storage (deduplicate by name, keep first entry)
|
|
34
|
+
aggregated = aggregate_storage(storage_data)
|
|
35
|
+
|
|
36
|
+
# Filter by node if specified
|
|
37
|
+
if node
|
|
38
|
+
aggregated = aggregated.select { |data| data[:node] == node || data[:shared] == 1 }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
aggregated.map { |data| build_model(data) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Gets a single storage pool by name.
|
|
45
|
+
#
|
|
46
|
+
# @param name [String] storage pool name
|
|
47
|
+
# @return [Models::Storage, nil] Storage model or nil if not found
|
|
48
|
+
def get(name)
|
|
49
|
+
list.find { |s| s.name == name }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Lists all instances of a storage by name.
|
|
53
|
+
#
|
|
54
|
+
# For shared storage: returns single instance.
|
|
55
|
+
# For local storage: returns all instances (one per node).
|
|
56
|
+
#
|
|
57
|
+
# @param name [String] storage name
|
|
58
|
+
# @return [Array<Models::Storage>] array of storage instances
|
|
59
|
+
def list_instances(name)
|
|
60
|
+
response = connection.client["cluster/resources"].get(params: { type: "storage" })
|
|
61
|
+
storage_data = normalize_response(response)
|
|
62
|
+
storage_data.select { |s| s[:storage] == name }.map { |data| build_model(data) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Gets storage for a specific node.
|
|
66
|
+
#
|
|
67
|
+
# @param name [String] storage name
|
|
68
|
+
# @param node [String] node name
|
|
69
|
+
# @return [Models::Storage, nil] Storage model or nil if not found
|
|
70
|
+
def get_for_node(name, node)
|
|
71
|
+
list_instances(name).find { |s| s.node == node }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Describes a storage with comprehensive details from multiple API endpoints.
|
|
75
|
+
#
|
|
76
|
+
# Fetches:
|
|
77
|
+
# - Basic storage info from cluster resources (via get or get_for_node)
|
|
78
|
+
# - Configuration from /storage/{name}
|
|
79
|
+
# - Status from /nodes/{node}/storage/{name}/status
|
|
80
|
+
# - Content (volumes) from /nodes/{node}/storage/{name}/content
|
|
81
|
+
#
|
|
82
|
+
# @param name [String] storage name
|
|
83
|
+
# @param node [String, nil] specific node for local storage
|
|
84
|
+
# @return [Models::Storage, nil] Storage model with full details, or nil if not found
|
|
85
|
+
def describe(name, node: nil)
|
|
86
|
+
storage = node ? get_for_node(name, node) : get(name)
|
|
87
|
+
return nil unless storage
|
|
88
|
+
|
|
89
|
+
# GET /storage/{name} - configuration
|
|
90
|
+
config = fetch_storage_config(name)
|
|
91
|
+
|
|
92
|
+
# Find active node for this storage
|
|
93
|
+
node = find_node_for_storage(name, storage)
|
|
94
|
+
|
|
95
|
+
# GET /nodes/{node}/storage/{name}/status (if node available)
|
|
96
|
+
status = node ? fetch_storage_status(node, name) : {}
|
|
97
|
+
|
|
98
|
+
# GET /nodes/{node}/storage/{name}/content (volumes)
|
|
99
|
+
content = node ? fetch_storage_content(node, name) : []
|
|
100
|
+
|
|
101
|
+
build_describe_model(storage, config, status, content)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Lists storage pools for a specific node.
|
|
105
|
+
#
|
|
106
|
+
# Uses `/nodes/{node}/storage` endpoint which returns detailed
|
|
107
|
+
# per-node storage information including avail, enabled, active flags.
|
|
108
|
+
#
|
|
109
|
+
# @param node_name [String] node name
|
|
110
|
+
# @return [Array<Models::Storage>] collection of Storage models
|
|
111
|
+
def list_for_node(node_name)
|
|
112
|
+
response = connection.client["nodes/#{node_name}/storage"].get
|
|
113
|
+
storage_data = normalize_response(response)
|
|
114
|
+
|
|
115
|
+
storage_data.map { |data| build_model_from_node_api(data, node_name) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
protected
|
|
119
|
+
|
|
120
|
+
# Builds Storage model from API response data.
|
|
121
|
+
#
|
|
122
|
+
# @param data [Hash] API response hash
|
|
123
|
+
# @return [Models::Storage] Storage model instance
|
|
124
|
+
def build_model(data)
|
|
125
|
+
Models::Storage.new(
|
|
126
|
+
name: data[:storage],
|
|
127
|
+
plugintype: data[:plugintype],
|
|
128
|
+
status: data[:status],
|
|
129
|
+
node: data[:node],
|
|
130
|
+
disk: data[:disk],
|
|
131
|
+
maxdisk: data[:maxdisk],
|
|
132
|
+
content: data[:content],
|
|
133
|
+
shared: data[:shared]
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Builds Storage model from /nodes/{node}/storage API response.
|
|
138
|
+
#
|
|
139
|
+
# Maps node-specific API fields to model attributes:
|
|
140
|
+
# - type -> plugintype
|
|
141
|
+
# - used -> disk
|
|
142
|
+
# - total -> maxdisk
|
|
143
|
+
# - active -> status (derived)
|
|
144
|
+
#
|
|
145
|
+
# @param data [Hash] API response hash
|
|
146
|
+
# @param node_name [String] node name (not in response, passed as param)
|
|
147
|
+
# @return [Models::Storage] Storage model instance
|
|
148
|
+
def build_model_from_node_api(data, node_name)
|
|
149
|
+
Models::Storage.new(
|
|
150
|
+
name: data[:storage],
|
|
151
|
+
plugintype: data[:type],
|
|
152
|
+
node: node_name,
|
|
153
|
+
disk: data[:used],
|
|
154
|
+
maxdisk: data[:total],
|
|
155
|
+
avail: data[:avail],
|
|
156
|
+
content: data[:content],
|
|
157
|
+
enabled: data[:enabled],
|
|
158
|
+
active: data[:active],
|
|
159
|
+
shared: 0 # /nodes/{node}/storage doesn't return shared flag
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
# Fetches storage configuration from /storage/{name}.
|
|
166
|
+
#
|
|
167
|
+
# @param name [String] storage name
|
|
168
|
+
# @return [Hash] configuration data or empty hash on error
|
|
169
|
+
def fetch_storage_config(name)
|
|
170
|
+
response = connection.client["storage/#{name}"].get
|
|
171
|
+
extract_data(response)
|
|
172
|
+
rescue StandardError
|
|
173
|
+
{}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Fetches storage status from /nodes/{node}/storage/{name}/status.
|
|
177
|
+
#
|
|
178
|
+
# @param node [String] node name
|
|
179
|
+
# @param name [String] storage name
|
|
180
|
+
# @return [Hash] status data or empty hash on error
|
|
181
|
+
def fetch_storage_status(node, name)
|
|
182
|
+
response = connection.client["nodes/#{node}/storage/#{name}/status"].get
|
|
183
|
+
extract_data(response)
|
|
184
|
+
rescue StandardError
|
|
185
|
+
{}
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Fetches storage content (volumes) from /nodes/{node}/storage/{name}/content.
|
|
189
|
+
#
|
|
190
|
+
# @param node [String] node name
|
|
191
|
+
# @param name [String] storage name
|
|
192
|
+
# @return [Array<Hash>] volumes array or empty array on error
|
|
193
|
+
def fetch_storage_content(node, name)
|
|
194
|
+
response = connection.client["nodes/#{node}/storage/#{name}/content"].get
|
|
195
|
+
unwrap(response)
|
|
196
|
+
rescue StandardError
|
|
197
|
+
[]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Finds an active node where this storage is accessible.
|
|
201
|
+
#
|
|
202
|
+
# For local storage: uses the node it belongs to.
|
|
203
|
+
# For shared storage: finds first online node in the cluster.
|
|
204
|
+
#
|
|
205
|
+
# @param name [String] storage name
|
|
206
|
+
# @param storage [Models::Storage] storage model
|
|
207
|
+
# @return [String, nil] node name or nil if unavailable
|
|
208
|
+
def find_node_for_storage(name, storage)
|
|
209
|
+
# For local storage, use the node it belongs to
|
|
210
|
+
return storage.node unless storage.shared?
|
|
211
|
+
|
|
212
|
+
# For shared storage, find first available online node
|
|
213
|
+
nodes_response = connection.client["nodes"].get
|
|
214
|
+
nodes = unwrap(nodes_response)
|
|
215
|
+
online_node = nodes.find { |n| n[:status] == "online" }
|
|
216
|
+
online_node&.dig(:node)
|
|
217
|
+
rescue StandardError
|
|
218
|
+
nil
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Builds Storage model with comprehensive describe data.
|
|
222
|
+
#
|
|
223
|
+
# Merges data from basic storage, config, status, and content endpoints.
|
|
224
|
+
#
|
|
225
|
+
# @param storage [Models::Storage] base storage model
|
|
226
|
+
# @param config [Hash] configuration from /storage/{name}
|
|
227
|
+
# @param status [Hash] status from /nodes/{node}/storage/{name}/status
|
|
228
|
+
# @param content [Array<Hash>] volumes from /content endpoint
|
|
229
|
+
# @return [Models::Storage] complete storage model
|
|
230
|
+
def build_describe_model(storage, config, status, content)
|
|
231
|
+
Models::Storage.new(
|
|
232
|
+
# Basic attributes from list
|
|
233
|
+
name: storage.name,
|
|
234
|
+
plugintype: storage.plugintype,
|
|
235
|
+
status: storage.status,
|
|
236
|
+
node: storage.node,
|
|
237
|
+
disk: storage.disk,
|
|
238
|
+
maxdisk: storage.maxdisk,
|
|
239
|
+
content: storage.content,
|
|
240
|
+
shared: storage.shared,
|
|
241
|
+
|
|
242
|
+
# Config attributes
|
|
243
|
+
path: config[:path],
|
|
244
|
+
server: config[:server],
|
|
245
|
+
export: config[:export],
|
|
246
|
+
pool: config[:pool],
|
|
247
|
+
vgname: config[:vgname],
|
|
248
|
+
thinpool: config[:thinpool],
|
|
249
|
+
nodes: config[:nodes],
|
|
250
|
+
"prune-backups": config[:"prune-backups"],
|
|
251
|
+
maxfiles: config[:maxfiles],
|
|
252
|
+
|
|
253
|
+
# Status attributes (override if available)
|
|
254
|
+
avail: status[:avail] || storage.avail,
|
|
255
|
+
enabled: status[:enabled] || storage.enabled,
|
|
256
|
+
active: status[:active] || storage.active_flag,
|
|
257
|
+
|
|
258
|
+
# Content (volumes)
|
|
259
|
+
volumes: content
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Normalizes API response to array format.
|
|
264
|
+
#
|
|
265
|
+
# @param response [Array, Hash] API response
|
|
266
|
+
# @return [Array<Hash>] array of storage data hashes
|
|
267
|
+
def normalize_response(response)
|
|
268
|
+
if response.is_a?(Array)
|
|
269
|
+
response
|
|
270
|
+
elsif response.is_a?(Hash) && response[:data]
|
|
271
|
+
response[:data]
|
|
272
|
+
else
|
|
273
|
+
response.to_a
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Aggregates storage data by name.
|
|
278
|
+
# For shared storage, keeps first entry (data is identical across nodes).
|
|
279
|
+
# For local storage, keeps all entries.
|
|
280
|
+
#
|
|
281
|
+
# @param storage_data [Array<Hash>] raw storage data from API
|
|
282
|
+
# @return [Array<Hash>] aggregated storage data
|
|
283
|
+
def aggregate_storage(storage_data)
|
|
284
|
+
seen = {}
|
|
285
|
+
storage_data.each do |data|
|
|
286
|
+
name = data[:storage]
|
|
287
|
+
next if name.nil?
|
|
288
|
+
|
|
289
|
+
# For shared storage, keep only first entry
|
|
290
|
+
if data[:shared] == 1
|
|
291
|
+
seen[name] ||= data
|
|
292
|
+
else
|
|
293
|
+
# For local storage, include all (unique by name+node)
|
|
294
|
+
key = "#{name}:#{data[:node]}"
|
|
295
|
+
seen[key] ||= data
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
seen.values
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for Proxmox subscription information per node.
|
|
6
|
+
#
|
|
7
|
+
# Wraps the `GET /nodes/{node}/subscription` endpoint and aggregates
|
|
8
|
+
# subscription records across all nodes in the cluster.
|
|
9
|
+
#
|
|
10
|
+
# @example Listing subscriptions across the cluster
|
|
11
|
+
# repo = Subscription.new(connection)
|
|
12
|
+
# repo.list.each { |s| puts "#{s.node}: #{s.status} #{s.level}" }
|
|
13
|
+
#
|
|
14
|
+
# @see Pvectl::Models::Subscription
|
|
15
|
+
#
|
|
16
|
+
class Subscription < Base
|
|
17
|
+
# Lists subscription records for cluster nodes.
|
|
18
|
+
#
|
|
19
|
+
# Returns one Subscription per online node. Offline / unreachable
|
|
20
|
+
# nodes return a Subscription with status="unreachable" so the output
|
|
21
|
+
# remains stable instead of silently dropping rows.
|
|
22
|
+
#
|
|
23
|
+
# @param node [String, nil] filter to a single node (otherwise all online nodes)
|
|
24
|
+
# @return [Array<Models::Subscription>]
|
|
25
|
+
def list(node: nil)
|
|
26
|
+
node_names = node ? [node] : online_node_names
|
|
27
|
+
node_names.map { |name| fetch_for(name) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Fetches the subscription record for a single node.
|
|
31
|
+
#
|
|
32
|
+
# @param node [String] node name
|
|
33
|
+
# @return [Models::Subscription]
|
|
34
|
+
def get(node)
|
|
35
|
+
fetch_for(node)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
# Builds a Subscription model from API data plus the node name.
|
|
41
|
+
#
|
|
42
|
+
# @param data [Hash] API response payload (already extracted from :data wrapper)
|
|
43
|
+
# @param node_name [String] node the record belongs to
|
|
44
|
+
# @return [Models::Subscription]
|
|
45
|
+
def build_model(data, node_name)
|
|
46
|
+
attrs = (data || {}).merge(node: node_name)
|
|
47
|
+
Models::Subscription.new(attrs)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# Returns names of all online nodes in the cluster.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<String>]
|
|
55
|
+
def online_node_names
|
|
56
|
+
response = connection.client["nodes"].get
|
|
57
|
+
nodes = unwrap(response)
|
|
58
|
+
nodes
|
|
59
|
+
.select { |n| n[:status].nil? || n[:status] == "online" }
|
|
60
|
+
.map { |n| n[:node] || n[:name] }
|
|
61
|
+
.compact
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Fetches one node's subscription, recovering from API errors gracefully.
|
|
65
|
+
#
|
|
66
|
+
# @param node_name [String]
|
|
67
|
+
# @return [Models::Subscription]
|
|
68
|
+
def fetch_for(node_name)
|
|
69
|
+
response = connection.client["nodes/#{node_name}/subscription"].get
|
|
70
|
+
data = extract_data(response)
|
|
71
|
+
build_model(data, node_name)
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
build_model({ status: "unreachable", message: e.message }, node_name)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for reading node syslog.
|
|
6
|
+
# Uses GET /nodes/{node}/syslog endpoint.
|
|
7
|
+
class Syslog < Base
|
|
8
|
+
# @param node [String] node name (required)
|
|
9
|
+
# @param limit [Integer] max entries (default 50)
|
|
10
|
+
# @param since [String, nil] start timestamp
|
|
11
|
+
# @param until_time [String, nil] end timestamp
|
|
12
|
+
# @param service [String, nil] filter by service name
|
|
13
|
+
# @return [Array<Models::SyslogEntry>]
|
|
14
|
+
def list(node:, limit: 50, since: nil, until_time: nil, service: nil)
|
|
15
|
+
params = { limit: limit }
|
|
16
|
+
params[:since] = since if since
|
|
17
|
+
params[:until] = until_time if until_time
|
|
18
|
+
params[:service] = service if service
|
|
19
|
+
|
|
20
|
+
response = connection.client["nodes/#{node}/syslog"].get(params: params)
|
|
21
|
+
models_from(response, Models::SyslogEntry)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|