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,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for per-node DNS resolver settings.
|
|
6
|
+
#
|
|
7
|
+
# Uses the `/nodes/{node}/dns` endpoint to fetch and update
|
|
8
|
+
# the DNS configuration of a single node.
|
|
9
|
+
#
|
|
10
|
+
# @example Fetching DNS config
|
|
11
|
+
# repo = Dns.new(connection)
|
|
12
|
+
# dns = repo.fetch("pve1")
|
|
13
|
+
# dns.servers #=> ["8.8.8.8"]
|
|
14
|
+
#
|
|
15
|
+
# @example Updating DNS config
|
|
16
|
+
# repo.update("pve1", search: "example.com", dns1: "8.8.8.8")
|
|
17
|
+
#
|
|
18
|
+
# @see Pvectl::Models::DnsConfig DNS configuration model
|
|
19
|
+
#
|
|
20
|
+
class Dns < Base
|
|
21
|
+
# Fetches the DNS configuration for a node.
|
|
22
|
+
#
|
|
23
|
+
# @param node_name [String] node name
|
|
24
|
+
# @return [Models::DnsConfig] DNS configuration model
|
|
25
|
+
def fetch(node_name)
|
|
26
|
+
response = connection.client["nodes/#{node_name}/dns"].get
|
|
27
|
+
data = extract_data(response) || {}
|
|
28
|
+
build_model(data.merge(node: node_name))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Updates the DNS configuration for a node.
|
|
32
|
+
#
|
|
33
|
+
# The Proxmox PUT endpoint requires `search` and accepts optional
|
|
34
|
+
# `dns1`, `dns2`, `dns3`. Only provided keys are sent.
|
|
35
|
+
#
|
|
36
|
+
# @param node_name [String] node name
|
|
37
|
+
# @param params [Hash] update parameters (:search, :dns1, :dns2, :dns3)
|
|
38
|
+
# @return [void]
|
|
39
|
+
def update(node_name, params = {})
|
|
40
|
+
connection.client["nodes/#{node_name}/dns"].put(params)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
protected
|
|
44
|
+
|
|
45
|
+
# Builds DnsConfig model from API data.
|
|
46
|
+
#
|
|
47
|
+
# @param data [Hash] API response hash (must include :node key for context)
|
|
48
|
+
# @return [Models::DnsConfig] DNS configuration model
|
|
49
|
+
def build_model(data)
|
|
50
|
+
Models::DnsConfig.new(data)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for per-node /etc/hosts file contents.
|
|
6
|
+
#
|
|
7
|
+
# Uses the `/nodes/{node}/hosts` endpoint to fetch and update
|
|
8
|
+
# the raw contents of /etc/hosts on a single node.
|
|
9
|
+
#
|
|
10
|
+
# The Proxmox API returns a {data, digest} pair. The digest is used
|
|
11
|
+
# for optimistic concurrency control — the same digest must be sent
|
|
12
|
+
# back with the POST update.
|
|
13
|
+
#
|
|
14
|
+
# @example Fetching hosts file
|
|
15
|
+
# repo = Hosts.new(connection)
|
|
16
|
+
# hosts = repo.fetch("pve1")
|
|
17
|
+
# hosts.data #=> "127.0.0.1 localhost\n..."
|
|
18
|
+
# hosts.digest #=> "abc123def..."
|
|
19
|
+
#
|
|
20
|
+
# @example Updating hosts file with digest
|
|
21
|
+
# repo.update("pve1", "127.0.0.1 localhost\n", "abc123def...")
|
|
22
|
+
#
|
|
23
|
+
# @see Pvectl::Models::HostsFile HostsFile model
|
|
24
|
+
#
|
|
25
|
+
class Hosts < Base
|
|
26
|
+
# Fetches the /etc/hosts contents for a node.
|
|
27
|
+
#
|
|
28
|
+
# @param node_name [String] node name
|
|
29
|
+
# @return [Models::HostsFile] hosts file model with data and digest
|
|
30
|
+
def fetch(node_name)
|
|
31
|
+
response = connection.client["nodes/#{node_name}/hosts"].get
|
|
32
|
+
data = extract_data(response) || {}
|
|
33
|
+
build_model(data.merge(node: node_name))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Updates the /etc/hosts contents for a node.
|
|
37
|
+
#
|
|
38
|
+
# The Proxmox POST endpoint requires `data` and optionally accepts
|
|
39
|
+
# `digest` for optimistic locking. If the on-server digest no longer
|
|
40
|
+
# matches, Proxmox returns an error which is propagated verbatim.
|
|
41
|
+
#
|
|
42
|
+
# @param node_name [String] node name
|
|
43
|
+
# @param data [String] new /etc/hosts content
|
|
44
|
+
# @param digest [String, nil] digest from prior fetch (optional but recommended)
|
|
45
|
+
# @return [void]
|
|
46
|
+
def update(node_name, data, digest = nil)
|
|
47
|
+
params = { data: data }
|
|
48
|
+
params[:digest] = digest if digest && !digest.empty?
|
|
49
|
+
connection.client["nodes/#{node_name}/hosts"].post(params)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
protected
|
|
53
|
+
|
|
54
|
+
# Builds HostsFile model from API data.
|
|
55
|
+
#
|
|
56
|
+
# @param data [Hash] API response hash (must include :node key for context)
|
|
57
|
+
# @return [Models::HostsFile] hosts file model
|
|
58
|
+
def build_model(data)
|
|
59
|
+
Models::HostsFile.new(data)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Repositories
|
|
5
|
+
# Repository for reading node systemd journal.
|
|
6
|
+
# Uses GET /nodes/{node}/journal endpoint.
|
|
7
|
+
class Journal < Base
|
|
8
|
+
# @param node [String] node name (required)
|
|
9
|
+
# @param last_entries [Integer] number of recent entries (default 50)
|
|
10
|
+
# @param since [Integer, nil] start time (epoch)
|
|
11
|
+
# @param until_time [Integer, nil] end time (epoch)
|
|
12
|
+
# @return [Array<Models::JournalEntry>]
|
|
13
|
+
def list(node:, last_entries: 50, since: nil, until_time: nil)
|
|
14
|
+
params = { lastentries: last_entries }
|
|
15
|
+
params[:since] = since if since
|
|
16
|
+
params[:until] = until_time if until_time
|
|
17
|
+
|
|
18
|
+
response = connection.client["nodes/#{node}/journal"].get(params: params)
|
|
19
|
+
models_from(response, Models::JournalEntry)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "task_list"
|
|
4
|
+
|
|
5
|
+
module Pvectl
|
|
6
|
+
module Repositories
|
|
7
|
+
# Repository for Proxmox cluster nodes.
|
|
8
|
+
#
|
|
9
|
+
# Uses the `/nodes` API endpoint to list nodes.
|
|
10
|
+
# Optionally fetches additional details from per-node endpoints.
|
|
11
|
+
#
|
|
12
|
+
# @example Listing all nodes
|
|
13
|
+
# repo = Node.new(connection)
|
|
14
|
+
# nodes = repo.list
|
|
15
|
+
# nodes.each { |n| puts "#{n.name}: #{n.status}" }
|
|
16
|
+
#
|
|
17
|
+
# @example Getting node with extended details
|
|
18
|
+
# node = repo.get("pve-node1", include_details: true)
|
|
19
|
+
#
|
|
20
|
+
# @see Pvectl::Models::Node Node model
|
|
21
|
+
# @see Pvectl::Connection API connection
|
|
22
|
+
#
|
|
23
|
+
class Node < Base
|
|
24
|
+
# Creates a new Node repository.
|
|
25
|
+
#
|
|
26
|
+
# @param connection [Connection] API connection
|
|
27
|
+
# @param storage_repository [Repositories::Storage, nil] optional storage repository for DI
|
|
28
|
+
def initialize(connection, storage_repository: nil)
|
|
29
|
+
super(connection)
|
|
30
|
+
@storage_repository = storage_repository
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Lists all nodes in the cluster.
|
|
34
|
+
#
|
|
35
|
+
# @param include_details [Boolean] fetch version/status details (extra API calls)
|
|
36
|
+
# @return [Array<Models::Node>] collection of Node models
|
|
37
|
+
def list(include_details: false)
|
|
38
|
+
response = connection.client["nodes"].get
|
|
39
|
+
nodes_data = unwrap(response)
|
|
40
|
+
|
|
41
|
+
# Get guest counts from cluster/resources
|
|
42
|
+
guest_counts = guest_counts_for_cluster
|
|
43
|
+
|
|
44
|
+
nodes_data.map do |data|
|
|
45
|
+
node_name = data[:node] || data[:name]
|
|
46
|
+
|
|
47
|
+
# Merge guest counts
|
|
48
|
+
data = data.merge(
|
|
49
|
+
guests_vms: guest_counts.dig(node_name, :vms) || 0,
|
|
50
|
+
guests_cts: guest_counts.dig(node_name, :cts) || 0
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Fetch extended details if requested
|
|
54
|
+
if include_details && (data[:status] == "online")
|
|
55
|
+
data = data.merge(details_for(node_name))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
build_model(data)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Gets a single node by name.
|
|
63
|
+
#
|
|
64
|
+
# @param name [String] node name
|
|
65
|
+
# @param include_details [Boolean] fetch version/status details
|
|
66
|
+
# @return [Models::Node, nil] Node model or nil if not found
|
|
67
|
+
def get(name, include_details: false)
|
|
68
|
+
list(include_details: include_details).find { |n| n.name == name }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Describes a node with comprehensive details from multiple API endpoints.
|
|
72
|
+
#
|
|
73
|
+
# @param name [String] node name
|
|
74
|
+
# @return [Models::Node, nil] Node model with full details, or nil if not found
|
|
75
|
+
def describe(name)
|
|
76
|
+
# First check if node exists in cluster
|
|
77
|
+
nodes_data = unwrap(connection.client["nodes"].get)
|
|
78
|
+
basic_data = nodes_data.find { |n| (n[:node] || n[:name]) == name }
|
|
79
|
+
return nil if basic_data.nil?
|
|
80
|
+
|
|
81
|
+
# Merge guest counts
|
|
82
|
+
guest_counts = guest_counts_for_cluster
|
|
83
|
+
data = basic_data.merge(
|
|
84
|
+
guests_vms: guest_counts.dig(name, :vms) || 0,
|
|
85
|
+
guests_cts: guest_counts.dig(name, :cts) || 0
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# For offline nodes, return basic data with offline note
|
|
89
|
+
unless basic_data[:status] == "online"
|
|
90
|
+
data[:offline_note] = "Node offline - detailed metrics unavailable"
|
|
91
|
+
return build_describe_model(data)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Fetch comprehensive details
|
|
95
|
+
data = data.merge(describe_details_for(name))
|
|
96
|
+
|
|
97
|
+
build_describe_model(data)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Fetches configuration for a node.
|
|
102
|
+
#
|
|
103
|
+
# @param node_name [String] node name
|
|
104
|
+
# @return [Hash] node configuration
|
|
105
|
+
def fetch_config(node_name)
|
|
106
|
+
resp = connection.client["nodes/#{node_name}/config"].get
|
|
107
|
+
extract_data(resp)
|
|
108
|
+
rescue StandardError
|
|
109
|
+
{}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Updates node configuration.
|
|
113
|
+
#
|
|
114
|
+
# @param node_name [String] node name
|
|
115
|
+
# @param params [Hash] configuration parameters to update
|
|
116
|
+
# @return [void]
|
|
117
|
+
def update(node_name, params = {})
|
|
118
|
+
connection.client["nodes/#{node_name}/config"].put(params)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Sends a Wake-on-LAN packet to a node.
|
|
122
|
+
#
|
|
123
|
+
# Calls `POST /nodes/{node}/wakeonlan`. The target node must have its
|
|
124
|
+
# MAC address registered in the cluster configuration beforehand
|
|
125
|
+
# (via `pvecm` or the web UI). The API returns the MAC address used
|
|
126
|
+
# for the magic packet.
|
|
127
|
+
#
|
|
128
|
+
# @param node_name [String] cluster node name
|
|
129
|
+
# @return [String, nil] MAC address used to assemble the WoL packet
|
|
130
|
+
# @raise [StandardError] propagates any API error (e.g., missing MAC)
|
|
131
|
+
def wakeonlan(node_name)
|
|
132
|
+
response = connection.client["nodes/#{node_name}/wakeonlan"].post
|
|
133
|
+
data = extract_data(response)
|
|
134
|
+
data.is_a?(String) ? data : nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
protected
|
|
138
|
+
|
|
139
|
+
# Builds Node model from API response data.
|
|
140
|
+
#
|
|
141
|
+
# @param data [Hash] API response hash
|
|
142
|
+
# @return [Models::Node] Node model instance
|
|
143
|
+
def build_model(data)
|
|
144
|
+
Models::Node.new(
|
|
145
|
+
name: data[:node] || data[:name],
|
|
146
|
+
status: data[:status],
|
|
147
|
+
cpu: data[:cpu],
|
|
148
|
+
maxcpu: data[:maxcpu],
|
|
149
|
+
mem: data[:mem],
|
|
150
|
+
maxmem: data[:maxmem],
|
|
151
|
+
disk: data[:disk],
|
|
152
|
+
maxdisk: data[:maxdisk],
|
|
153
|
+
uptime: data[:uptime],
|
|
154
|
+
level: data[:level],
|
|
155
|
+
version: data[:version],
|
|
156
|
+
kernel: data[:kernel],
|
|
157
|
+
loadavg: data[:loadavg],
|
|
158
|
+
swap_used: data[:swap_used],
|
|
159
|
+
swap_total: data[:swap_total],
|
|
160
|
+
guests_vms: data[:guests_vms],
|
|
161
|
+
guests_cts: data[:guests_cts],
|
|
162
|
+
ip: data[:ip]
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
# Fetches guest counts per node from cluster/resources.
|
|
169
|
+
#
|
|
170
|
+
# @return [Hash] { "node_name" => { vms: N, cts: M } }
|
|
171
|
+
def guest_counts_for_cluster
|
|
172
|
+
response = connection.client["cluster/resources"].get(params: { type: "vm" })
|
|
173
|
+
resources = unwrap(response)
|
|
174
|
+
|
|
175
|
+
counts = Hash.new { |h, k| h[k] = { vms: 0, cts: 0 } }
|
|
176
|
+
resources.each do |r|
|
|
177
|
+
node = r[:node]
|
|
178
|
+
next if node.nil?
|
|
179
|
+
|
|
180
|
+
if r[:type] == "qemu"
|
|
181
|
+
counts[node][:vms] += 1
|
|
182
|
+
elsif r[:type] == "lxc"
|
|
183
|
+
counts[node][:cts] += 1
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
counts
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Fetches extended details for a node (version, status).
|
|
190
|
+
#
|
|
191
|
+
# @param node_name [String] node name
|
|
192
|
+
# @return [Hash] merged version and status data
|
|
193
|
+
def details_for(node_name)
|
|
194
|
+
result = {}
|
|
195
|
+
|
|
196
|
+
# Fetch version
|
|
197
|
+
begin
|
|
198
|
+
version_resp = connection.client["nodes/#{node_name}/version"].get
|
|
199
|
+
version_data = extract_data(version_resp)
|
|
200
|
+
result[:version] = version_data[:version]
|
|
201
|
+
result[:kernel] = version_data[:kernel]
|
|
202
|
+
rescue StandardError
|
|
203
|
+
# Ignore errors fetching version
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Fetch status (for load, swap)
|
|
207
|
+
begin
|
|
208
|
+
status_resp = connection.client["nodes/#{node_name}/status"].get
|
|
209
|
+
status_data = extract_data(status_resp)
|
|
210
|
+
result[:loadavg] = status_data[:loadavg]&.map(&:to_f)
|
|
211
|
+
result[:kernel] ||= extract_kernel_version(status_data[:kversion])
|
|
212
|
+
if status_data[:swap]
|
|
213
|
+
result[:swap_used] = status_data[:swap][:used]
|
|
214
|
+
result[:swap_total] = status_data[:swap][:total]
|
|
215
|
+
end
|
|
216
|
+
rescue StandardError
|
|
217
|
+
# Ignore errors fetching status
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Fetch network (for IP)
|
|
221
|
+
result[:ip] = ip_for(node_name)
|
|
222
|
+
|
|
223
|
+
result
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Fetches IP address from node network configuration.
|
|
227
|
+
#
|
|
228
|
+
# Finds the first interface with a gateway configured (default route)
|
|
229
|
+
# and extracts its IP address.
|
|
230
|
+
#
|
|
231
|
+
# @param node_name [String] node name
|
|
232
|
+
# @return [String, nil] IP address or nil if unavailable
|
|
233
|
+
def ip_for(node_name)
|
|
234
|
+
network_resp = connection.client["nodes/#{node_name}/network"].get
|
|
235
|
+
interfaces = unwrap(network_resp)
|
|
236
|
+
extract_ip_from_network(interfaces)
|
|
237
|
+
rescue StandardError
|
|
238
|
+
nil
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Extracts IP from network interfaces.
|
|
242
|
+
#
|
|
243
|
+
# Algorithm (KISS):
|
|
244
|
+
# 1. Find first interface with non-empty `gateway` field
|
|
245
|
+
# 2. Extract IP from `address` field
|
|
246
|
+
# 3. Remove CIDR suffix if present (e.g., "192.168.1.10/24" -> "192.168.1.10")
|
|
247
|
+
#
|
|
248
|
+
# @param interfaces [Array<Hash>] network interfaces from API
|
|
249
|
+
# @return [String, nil] IP address or nil
|
|
250
|
+
def extract_ip_from_network(interfaces)
|
|
251
|
+
return nil if interfaces.nil? || interfaces.empty?
|
|
252
|
+
|
|
253
|
+
# Find first interface with gateway (default route interface)
|
|
254
|
+
iface = interfaces.find { |i| i[:gateway] && !i[:gateway].to_s.empty? }
|
|
255
|
+
return nil unless iface
|
|
256
|
+
|
|
257
|
+
# Extract IP, removing CIDR suffix if present
|
|
258
|
+
address = iface[:address] || iface[:cidr]
|
|
259
|
+
return nil if address.nil? || address.to_s.empty?
|
|
260
|
+
|
|
261
|
+
address.to_s.split("/").first
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Extracts kernel version from kversion string.
|
|
265
|
+
#
|
|
266
|
+
# The kversion string from Proxmox API looks like:
|
|
267
|
+
# "Linux 6.8.12-1-pve #1 SMP PREEMPT_DYNAMIC..."
|
|
268
|
+
# This method extracts just the version: "6.8.12-1-pve"
|
|
269
|
+
#
|
|
270
|
+
# @param kversion [String, nil] full kernel version string
|
|
271
|
+
# @return [String, nil] extracted kernel version or nil
|
|
272
|
+
def extract_kernel_version(kversion)
|
|
273
|
+
return nil if kversion.nil? || kversion.empty?
|
|
274
|
+
|
|
275
|
+
# Match pattern: "Linux X.Y.Z-something ..."
|
|
276
|
+
match = kversion.match(/Linux\s+([\d.]+-[\w.-]+)/)
|
|
277
|
+
match ? match[1] : kversion
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Fetches comprehensive details for describe command.
|
|
281
|
+
# Reuses existing helper methods where possible.
|
|
282
|
+
#
|
|
283
|
+
# @param node_name [String] node name
|
|
284
|
+
# @return [Hash] aggregated data from multiple endpoints
|
|
285
|
+
def describe_details_for(node_name)
|
|
286
|
+
result = {}
|
|
287
|
+
|
|
288
|
+
# Reuse existing details_for for version/status/network
|
|
289
|
+
result.merge!(details_for(node_name))
|
|
290
|
+
|
|
291
|
+
# Additional describe-specific endpoints
|
|
292
|
+
result.merge!(subscription_for(node_name))
|
|
293
|
+
result.merge!(dns_for(node_name))
|
|
294
|
+
result.merge!(time_for(node_name))
|
|
295
|
+
result.merge!(services_for(node_name))
|
|
296
|
+
result.merge!(storage_pools_for(node_name))
|
|
297
|
+
result.merge!(disks_for(node_name))
|
|
298
|
+
result.merge!(qemu_cpu_for(node_name))
|
|
299
|
+
result.merge!(qemu_machines_for(node_name))
|
|
300
|
+
result.merge!(updates_for(node_name))
|
|
301
|
+
result.merge!(extended_status_for(node_name))
|
|
302
|
+
result.merge!(firewall_for(node_name))
|
|
303
|
+
result.merge!(tasks_for(node_name))
|
|
304
|
+
|
|
305
|
+
result
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Fetches subscription info for a node.
|
|
309
|
+
#
|
|
310
|
+
# Filters sensitive fields (license key) to prevent accidental exposure.
|
|
311
|
+
# Only safe display data is returned.
|
|
312
|
+
#
|
|
313
|
+
# @param node_name [String] node name
|
|
314
|
+
# @return [Hash] subscription data (filtered for safety)
|
|
315
|
+
def subscription_for(node_name)
|
|
316
|
+
resp = connection.client["nodes/#{node_name}/subscription"].get
|
|
317
|
+
data = extract_data(resp)
|
|
318
|
+
# Filter sensitive fields - keep only safe display data
|
|
319
|
+
safe_data = {
|
|
320
|
+
status: data[:status],
|
|
321
|
+
level: data[:level],
|
|
322
|
+
productname: data[:productname],
|
|
323
|
+
regdate: data[:regdate],
|
|
324
|
+
checktime: data[:checktime]
|
|
325
|
+
}
|
|
326
|
+
{ subscription: safe_data }
|
|
327
|
+
rescue StandardError
|
|
328
|
+
{ subscription: nil }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Fetches DNS configuration for a node.
|
|
332
|
+
#
|
|
333
|
+
# @param node_name [String] node name
|
|
334
|
+
# @return [Hash] DNS data
|
|
335
|
+
def dns_for(node_name)
|
|
336
|
+
resp = connection.client["nodes/#{node_name}/dns"].get
|
|
337
|
+
data = extract_data(resp)
|
|
338
|
+
{ dns: data }
|
|
339
|
+
rescue StandardError
|
|
340
|
+
{ dns: nil }
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Fetches time configuration for a node.
|
|
344
|
+
#
|
|
345
|
+
# @param node_name [String] node name
|
|
346
|
+
# @return [Hash] time data
|
|
347
|
+
def time_for(node_name)
|
|
348
|
+
resp = connection.client["nodes/#{node_name}/time"].get
|
|
349
|
+
data = extract_data(resp)
|
|
350
|
+
{ time_info: data }
|
|
351
|
+
rescue StandardError
|
|
352
|
+
{ time_info: nil }
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Fetches services for a node.
|
|
356
|
+
#
|
|
357
|
+
# @param node_name [String] node name
|
|
358
|
+
# @return [Hash] services data (raw hashes, not models - used in describe)
|
|
359
|
+
def services_for(node_name)
|
|
360
|
+
resp = connection.client["nodes/#{node_name}/services"].get
|
|
361
|
+
{ services: unwrap(resp) }
|
|
362
|
+
rescue StandardError
|
|
363
|
+
{ services: [] }
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Fetches storage pools for a node.
|
|
367
|
+
#
|
|
368
|
+
# Delegates to Repositories::Storage#list_for_node for consistent
|
|
369
|
+
# model creation and DRY compliance.
|
|
370
|
+
#
|
|
371
|
+
# @param node_name [String] node name
|
|
372
|
+
# @return [Hash] storage pools data with Array<Models::Storage>
|
|
373
|
+
def storage_pools_for(node_name)
|
|
374
|
+
{ storage_pools: storage_repository.list_for_node(node_name) }
|
|
375
|
+
rescue StandardError
|
|
376
|
+
{ storage_pools: [] }
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Returns storage repository instance.
|
|
380
|
+
# Uses injected repository if provided, otherwise creates new one.
|
|
381
|
+
#
|
|
382
|
+
# @return [Repositories::Storage] storage repository
|
|
383
|
+
def storage_repository
|
|
384
|
+
@storage_repository ||= Repositories::Storage.new(connection)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Fetches physical disks for a node.
|
|
388
|
+
#
|
|
389
|
+
# @param node_name [String] node name
|
|
390
|
+
# @return [Hash] disks data (raw hashes, not models - used in describe)
|
|
391
|
+
def disks_for(node_name)
|
|
392
|
+
resp = connection.client["nodes/#{node_name}/disks/list"].get
|
|
393
|
+
{ physical_disks: unwrap(resp) }
|
|
394
|
+
rescue StandardError
|
|
395
|
+
{ physical_disks: [] }
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Fetches QEMU CPU models for a node.
|
|
399
|
+
#
|
|
400
|
+
# @param node_name [String] node name
|
|
401
|
+
# @return [Hash] QEMU CPU models
|
|
402
|
+
def qemu_cpu_for(node_name)
|
|
403
|
+
resp = connection.client["nodes/#{node_name}/capabilities/qemu/cpu"].get
|
|
404
|
+
{ qemu_cpu_models: unwrap(resp) }
|
|
405
|
+
rescue StandardError
|
|
406
|
+
{ qemu_cpu_models: [] }
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Fetches QEMU machine types for a node.
|
|
410
|
+
#
|
|
411
|
+
# @param node_name [String] node name
|
|
412
|
+
# @return [Hash] QEMU machine types
|
|
413
|
+
def qemu_machines_for(node_name)
|
|
414
|
+
resp = connection.client["nodes/#{node_name}/capabilities/qemu/machines"].get
|
|
415
|
+
{ qemu_machines: unwrap(resp) }
|
|
416
|
+
rescue StandardError
|
|
417
|
+
{ qemu_machines: [] }
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# Fetches available updates for a node.
|
|
421
|
+
#
|
|
422
|
+
# @param node_name [String] node name
|
|
423
|
+
# @return [Hash] updates data
|
|
424
|
+
def updates_for(node_name)
|
|
425
|
+
resp = connection.client["nodes/#{node_name}/apt/versions"].get
|
|
426
|
+
packages = unwrap(resp)
|
|
427
|
+
upgradable = packages.select do |p|
|
|
428
|
+
p[:AvailableVersion] && p[:AvailableVersion] != p[:CurrentVersion]
|
|
429
|
+
end
|
|
430
|
+
{ updates_available: upgradable.size, updates: upgradable }
|
|
431
|
+
rescue StandardError
|
|
432
|
+
{ updates_available: 0, updates: [] }
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Fetches extended status (cpuinfo, boot_info, rootfs, network_interfaces).
|
|
436
|
+
#
|
|
437
|
+
# @param node_name [String] node name
|
|
438
|
+
# @return [Hash] extended status data
|
|
439
|
+
def extended_status_for(node_name)
|
|
440
|
+
resp = connection.client["nodes/#{node_name}/status"].get
|
|
441
|
+
data = extract_data(resp)
|
|
442
|
+
{
|
|
443
|
+
cpuinfo: data[:cpuinfo],
|
|
444
|
+
boot_info: data[:"boot-info"],
|
|
445
|
+
rootfs: data[:rootfs],
|
|
446
|
+
network_interfaces: network_interfaces_for(node_name)
|
|
447
|
+
}
|
|
448
|
+
rescue StandardError
|
|
449
|
+
{}
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Fetches network interfaces for a node.
|
|
453
|
+
#
|
|
454
|
+
# @param node_name [String] node name
|
|
455
|
+
# @return [Array<Hash>] network interfaces (raw hashes for describe output)
|
|
456
|
+
def network_interfaces_for(node_name)
|
|
457
|
+
resp = connection.client["nodes/#{node_name}/network"].get
|
|
458
|
+
unwrap(resp)
|
|
459
|
+
rescue StandardError
|
|
460
|
+
[]
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Fetches firewall configuration (options, rules, aliases, IP sets).
|
|
464
|
+
#
|
|
465
|
+
# @param node_name [String] node name
|
|
466
|
+
# @return [Hash] firewall data under :firewall key
|
|
467
|
+
def firewall_for(node_name)
|
|
468
|
+
base = "nodes/#{node_name}/firewall"
|
|
469
|
+
options = (extract_data(connection.client["#{base}/options"].get) rescue {})
|
|
470
|
+
rules = (unwrap(connection.client["#{base}/rules"].get) rescue [])
|
|
471
|
+
aliases_data = (unwrap(connection.client["#{base}/aliases"].get) rescue [])
|
|
472
|
+
ipset = (unwrap(connection.client["#{base}/ipset"].get) rescue [])
|
|
473
|
+
{ firewall: { options: options, rules: rules, aliases: aliases_data, ipset: ipset } }
|
|
474
|
+
rescue StandardError
|
|
475
|
+
{}
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Fetches recent task history for the node.
|
|
479
|
+
#
|
|
480
|
+
# @param node_name [String] node name
|
|
481
|
+
# @param limit [Integer] max entries (default 10)
|
|
482
|
+
# @return [Hash] tasks data under :tasks key
|
|
483
|
+
def tasks_for(node_name, limit: 10)
|
|
484
|
+
task_list_repo = TaskList.new(connection)
|
|
485
|
+
{ tasks: task_list_repo.list(node: node_name, limit: limit) }
|
|
486
|
+
rescue StandardError
|
|
487
|
+
{ tasks: [] }
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Builds Node model with describe-specific attributes.
|
|
491
|
+
#
|
|
492
|
+
# @param data [Hash] aggregated data from API
|
|
493
|
+
# @return [Models::Node] Node model
|
|
494
|
+
def build_describe_model(data)
|
|
495
|
+
Models::Node.new(
|
|
496
|
+
# Basic fields (existing)
|
|
497
|
+
name: data[:node] || data[:name],
|
|
498
|
+
status: data[:status],
|
|
499
|
+
cpu: data[:cpu],
|
|
500
|
+
maxcpu: data[:maxcpu],
|
|
501
|
+
mem: data[:mem],
|
|
502
|
+
maxmem: data[:maxmem],
|
|
503
|
+
disk: data[:disk],
|
|
504
|
+
maxdisk: data[:maxdisk],
|
|
505
|
+
uptime: data[:uptime],
|
|
506
|
+
level: data[:level],
|
|
507
|
+
version: data[:version],
|
|
508
|
+
kernel: data[:kernel],
|
|
509
|
+
loadavg: data[:loadavg],
|
|
510
|
+
swap_used: data[:swap_used],
|
|
511
|
+
swap_total: data[:swap_total],
|
|
512
|
+
guests_vms: data[:guests_vms],
|
|
513
|
+
guests_cts: data[:guests_cts],
|
|
514
|
+
ip: data[:ip],
|
|
515
|
+
# Extended fields for describe
|
|
516
|
+
cpuinfo: data[:cpuinfo],
|
|
517
|
+
boot_info: data[:boot_info],
|
|
518
|
+
rootfs: data[:rootfs],
|
|
519
|
+
subscription: data[:subscription],
|
|
520
|
+
dns: data[:dns],
|
|
521
|
+
time_info: data[:time_info],
|
|
522
|
+
network_interfaces: data[:network_interfaces],
|
|
523
|
+
services: data[:services],
|
|
524
|
+
storage_pools: data[:storage_pools],
|
|
525
|
+
physical_disks: data[:physical_disks],
|
|
526
|
+
qemu_cpu_models: data[:qemu_cpu_models],
|
|
527
|
+
qemu_machines: data[:qemu_machines],
|
|
528
|
+
updates_available: data[:updates_available],
|
|
529
|
+
updates: data[:updates],
|
|
530
|
+
offline_note: data[:offline_note],
|
|
531
|
+
firewall: data[:firewall],
|
|
532
|
+
tasks: data[:tasks]
|
|
533
|
+
)
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
end
|
|
537
|
+
end
|