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,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
module Get
|
|
6
|
+
# Handles continuous watch mode for the get command.
|
|
7
|
+
#
|
|
8
|
+
# Repeatedly executes a block at specified intervals, clearing the
|
|
9
|
+
# screen between iterations when running in a TTY. Handles SIGINT
|
|
10
|
+
# for graceful termination.
|
|
11
|
+
#
|
|
12
|
+
# @example Basic usage
|
|
13
|
+
# loop = WatchLoop.new(interval: 5)
|
|
14
|
+
# loop.run { puts Time.now }
|
|
15
|
+
#
|
|
16
|
+
# @example With custom interval (clamped to minimum)
|
|
17
|
+
# loop = WatchLoop.new(interval: 0.5) # Will be clamped to 1 second
|
|
18
|
+
#
|
|
19
|
+
class WatchLoop
|
|
20
|
+
# Default refresh interval in seconds.
|
|
21
|
+
DEFAULT_INTERVAL = 2
|
|
22
|
+
|
|
23
|
+
# Minimum allowed refresh interval in seconds.
|
|
24
|
+
MIN_INTERVAL = 1
|
|
25
|
+
|
|
26
|
+
# @return [Integer] the effective refresh interval
|
|
27
|
+
attr_reader :interval
|
|
28
|
+
|
|
29
|
+
# Creates a new WatchLoop.
|
|
30
|
+
#
|
|
31
|
+
# @param interval [Integer, Float] refresh interval in seconds
|
|
32
|
+
# Values below MIN_INTERVAL are automatically clamped.
|
|
33
|
+
# @param output [IO] output stream for TTY detection (default: $stdout)
|
|
34
|
+
def initialize(interval: DEFAULT_INTERVAL, output: $stdout)
|
|
35
|
+
@interval = [interval.to_i, MIN_INTERVAL].max
|
|
36
|
+
@output = output
|
|
37
|
+
@running = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Runs the watch loop, executing the block repeatedly.
|
|
41
|
+
#
|
|
42
|
+
# @yield Block to execute on each iteration
|
|
43
|
+
# @return [void]
|
|
44
|
+
#
|
|
45
|
+
# @example
|
|
46
|
+
# loop.run do
|
|
47
|
+
# models = handler.list
|
|
48
|
+
# puts format(models)
|
|
49
|
+
# end
|
|
50
|
+
def run
|
|
51
|
+
@running = true
|
|
52
|
+
setup_signal_handler
|
|
53
|
+
|
|
54
|
+
while @running
|
|
55
|
+
clear_screen if tty?
|
|
56
|
+
print_header if tty?
|
|
57
|
+
yield
|
|
58
|
+
sleep_interruptible(@interval)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Stops the watch loop.
|
|
63
|
+
#
|
|
64
|
+
# @return [void]
|
|
65
|
+
def stop
|
|
66
|
+
@running = false
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Checks if the loop is currently running.
|
|
70
|
+
#
|
|
71
|
+
# @return [Boolean] true if running
|
|
72
|
+
def running?
|
|
73
|
+
@running
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# Clears the terminal screen using ANSI escape codes.
|
|
79
|
+
#
|
|
80
|
+
# @return [void]
|
|
81
|
+
def clear_screen
|
|
82
|
+
@output.print "\e[H\e[2J"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Prints the watch mode header with timestamp.
|
|
86
|
+
#
|
|
87
|
+
# @return [void]
|
|
88
|
+
def print_header
|
|
89
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
|
90
|
+
@output.puts "Every #{@interval}s: #{timestamp}"
|
|
91
|
+
@output.puts
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Checks if output is a TTY.
|
|
95
|
+
#
|
|
96
|
+
# @return [Boolean] true if output is a TTY
|
|
97
|
+
def tty?
|
|
98
|
+
@output.tty?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Sets up SIGINT handler for graceful termination.
|
|
102
|
+
#
|
|
103
|
+
# @return [void]
|
|
104
|
+
def setup_signal_handler
|
|
105
|
+
@previous_handler = trap("INT") do
|
|
106
|
+
stop
|
|
107
|
+
# Don't print newline in non-TTY mode to avoid extra output
|
|
108
|
+
@output.puts if tty?
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Sleeps for the specified duration, allowing interruption.
|
|
113
|
+
#
|
|
114
|
+
# Uses small sleep intervals to allow quick response to stop signal.
|
|
115
|
+
#
|
|
116
|
+
# @param duration [Integer] total sleep duration in seconds
|
|
117
|
+
# @return [void]
|
|
118
|
+
def sleep_interruptible(duration)
|
|
119
|
+
remaining = duration
|
|
120
|
+
while remaining > 0 && @running
|
|
121
|
+
chunk = [remaining, 0.1].min
|
|
122
|
+
sleep(chunk)
|
|
123
|
+
remaining -= chunk
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
# Shared functionality for irreversible batch commands (delete, template).
|
|
6
|
+
#
|
|
7
|
+
# Provides the generic flow: resolve resources → confirm → execute → format results.
|
|
8
|
+
# Submodules override hooks to customize behavior per operation.
|
|
9
|
+
#
|
|
10
|
+
# @example Including in an operation-specific module
|
|
11
|
+
# module TemplateCommand
|
|
12
|
+
# include IrreversibleCommand
|
|
13
|
+
#
|
|
14
|
+
# def self.included(base)
|
|
15
|
+
# base.extend(IrreversibleCommand::ClassMethods)
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
module IrreversibleCommand
|
|
20
|
+
# Class methods added when the module is included.
|
|
21
|
+
module ClassMethods
|
|
22
|
+
# Executes the command.
|
|
23
|
+
#
|
|
24
|
+
# @param resource_type [String, nil] resource type (vm, container)
|
|
25
|
+
# @param resource_ids [Array<String>, String, nil] resource identifiers
|
|
26
|
+
# @param options [Hash] command options
|
|
27
|
+
# @param global_options [Hash] global CLI options
|
|
28
|
+
# @return [Integer] exit code
|
|
29
|
+
def execute(resource_type, resource_ids, options, global_options)
|
|
30
|
+
new(resource_type, resource_ids, options, global_options).execute
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Hook called when module is included.
|
|
35
|
+
#
|
|
36
|
+
# @param base [Module] the module or class including this module
|
|
37
|
+
def self.included(base)
|
|
38
|
+
base.extend(ClassMethods)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Initializes an irreversible command.
|
|
42
|
+
#
|
|
43
|
+
# @param resource_type [String, nil] resource type (vm, container)
|
|
44
|
+
# @param resource_ids [Array<String>, String, nil] resource identifiers
|
|
45
|
+
# @param options [Hash] command options
|
|
46
|
+
# @param global_options [Hash] global CLI options
|
|
47
|
+
def initialize(resource_type, resource_ids, options, global_options)
|
|
48
|
+
@resource_type = resource_type
|
|
49
|
+
@resource_ids = Array(resource_ids).compact
|
|
50
|
+
@options = options
|
|
51
|
+
@global_options = global_options
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Executes the command.
|
|
55
|
+
#
|
|
56
|
+
# @return [Integer] exit code
|
|
57
|
+
def execute
|
|
58
|
+
return usage_error("Resource type required (#{supported_types.join(', ')})") unless @resource_type
|
|
59
|
+
return usage_error("Unsupported resource: #{@resource_type}") unless supported_resource?
|
|
60
|
+
|
|
61
|
+
if @resource_ids.empty? && !@options[:all] && selector_strings.empty?
|
|
62
|
+
return usage_error("VMID, --all, or -l selector required")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
perform_operation
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Returns the resource type symbol (:vm or :container).
|
|
71
|
+
#
|
|
72
|
+
# @return [Symbol] resource type
|
|
73
|
+
def resource_type_symbol
|
|
74
|
+
self.class::RESOURCE_TYPE
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns supported resource type strings.
|
|
78
|
+
#
|
|
79
|
+
# @return [Array<String>] supported types
|
|
80
|
+
def supported_types
|
|
81
|
+
self.class::SUPPORTED_RESOURCES
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Checks if resource type is supported.
|
|
85
|
+
#
|
|
86
|
+
# @return [Boolean] true if supported
|
|
87
|
+
def supported_resource?
|
|
88
|
+
supported_types.include?(@resource_type)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Performs the operation. Override in submodule for custom flow.
|
|
92
|
+
#
|
|
93
|
+
# @return [Integer] exit code
|
|
94
|
+
def perform_operation
|
|
95
|
+
load_config
|
|
96
|
+
connection = Pvectl::Connection.new(@config)
|
|
97
|
+
|
|
98
|
+
resources = resolve_resources(connection)
|
|
99
|
+
return no_resources_found if resources.empty?
|
|
100
|
+
return ExitCodes::SUCCESS unless confirm_operation(resources)
|
|
101
|
+
|
|
102
|
+
results = perform_service_call(resources, connection)
|
|
103
|
+
output_results(results)
|
|
104
|
+
determine_exit_code(results)
|
|
105
|
+
rescue Pvectl::Config::ConfigNotFoundError,
|
|
106
|
+
Pvectl::Config::InvalidConfigError,
|
|
107
|
+
Pvectl::Config::ContextNotFoundError,
|
|
108
|
+
Pvectl::Config::ClusterNotFoundError,
|
|
109
|
+
Pvectl::Config::UserNotFoundError
|
|
110
|
+
raise
|
|
111
|
+
rescue StandardError => e
|
|
112
|
+
$stderr.puts "Error: #{e.message}"
|
|
113
|
+
ExitCodes::GENERAL_ERROR
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Resolves resources based on IDs, --all flag, or selectors.
|
|
117
|
+
#
|
|
118
|
+
# @param connection [Connection] API connection
|
|
119
|
+
# @return [Array<Models::Vm, Models::Container>] resolved resources
|
|
120
|
+
def resolve_resources(connection)
|
|
121
|
+
repo = build_repository(connection)
|
|
122
|
+
|
|
123
|
+
resources = if @options[:all]
|
|
124
|
+
repo.list(node: @options[:node])
|
|
125
|
+
elsif @resource_ids.any?
|
|
126
|
+
resolved = @resource_ids.map { |id| repo.get(id.to_i) }.compact
|
|
127
|
+
resolved = resolved.select { |r| r.node == @options[:node] } if @options[:node]
|
|
128
|
+
resolved
|
|
129
|
+
else
|
|
130
|
+
return [] if selector_strings.empty?
|
|
131
|
+
repo.list(node: @options[:node])
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
apply_selectors(resources)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Builds the appropriate repository for the resource type.
|
|
138
|
+
#
|
|
139
|
+
# @param connection [Connection] API connection
|
|
140
|
+
# @return [Repositories::Base] repository
|
|
141
|
+
def build_repository(connection)
|
|
142
|
+
if resource_type_symbol == :vm
|
|
143
|
+
Pvectl::Repositories::Vm.new(connection)
|
|
144
|
+
else
|
|
145
|
+
Pvectl::Repositories::Container.new(connection)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Returns selector strings from options.
|
|
150
|
+
#
|
|
151
|
+
# @return [Array<String>] selector strings
|
|
152
|
+
def selector_strings
|
|
153
|
+
Array(@options[:selector] || @options[:l])
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Applies selectors to resource collection.
|
|
157
|
+
#
|
|
158
|
+
# @param resources [Array] Resources to filter
|
|
159
|
+
# @return [Array] Filtered resources
|
|
160
|
+
def apply_selectors(resources)
|
|
161
|
+
return resources if selector_strings.empty?
|
|
162
|
+
|
|
163
|
+
selector_class = resource_type_symbol == :vm ? Selectors::Vm : Selectors::Container
|
|
164
|
+
selector = selector_class.parse_all(selector_strings)
|
|
165
|
+
selector.apply(resources)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Confirms the operation with the user.
|
|
169
|
+
# Override in submodule for custom confirmation logic.
|
|
170
|
+
#
|
|
171
|
+
# @param resources [Array] Resources to operate on
|
|
172
|
+
# @return [Boolean] true if operation should proceed
|
|
173
|
+
def confirm_operation(resources)
|
|
174
|
+
raise NotImplementedError, "#{self.class} must implement #confirm_operation"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Performs the actual service call for the operation.
|
|
178
|
+
# Must be implemented by the including module.
|
|
179
|
+
#
|
|
180
|
+
# @param resources [Array] resources to operate on
|
|
181
|
+
# @param connection [Connection] API connection
|
|
182
|
+
# @return [Array<Models::OperationResult>] results
|
|
183
|
+
def perform_service_call(resources, connection)
|
|
184
|
+
raise NotImplementedError, "#{self.class} must implement #perform_service_call"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Builds the presenter for results output.
|
|
188
|
+
# Must be implemented by the including module.
|
|
189
|
+
#
|
|
190
|
+
# @return [Presenters::Base] presenter
|
|
191
|
+
def build_presenter
|
|
192
|
+
raise NotImplementedError, "#{self.class} must implement #build_presenter"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Loads configuration from file or environment.
|
|
196
|
+
#
|
|
197
|
+
# @return [void]
|
|
198
|
+
def load_config
|
|
199
|
+
service = Pvectl::Config::Service.new
|
|
200
|
+
service.load(config: @global_options[:config])
|
|
201
|
+
@config = service.current_config
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Builds base service options from command options.
|
|
205
|
+
#
|
|
206
|
+
# @return [Hash] service options
|
|
207
|
+
def service_options
|
|
208
|
+
opts = {}
|
|
209
|
+
opts[:timeout] = @options[:timeout] if @options[:timeout]
|
|
210
|
+
opts[:async] = true if @options[:async]
|
|
211
|
+
opts[:force] = true if @options[:force]
|
|
212
|
+
opts[:fail_fast] = true if @options[:"fail-fast"]
|
|
213
|
+
opts
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Outputs operation results using the configured formatter.
|
|
217
|
+
#
|
|
218
|
+
# @param results [Array<Models::OperationResult>] operation results
|
|
219
|
+
# @return [void]
|
|
220
|
+
def output_results(results)
|
|
221
|
+
presenter = build_presenter
|
|
222
|
+
format = @global_options[:output] || "table"
|
|
223
|
+
color_flag = @global_options[:color]
|
|
224
|
+
|
|
225
|
+
formatter = Pvectl::Formatters::Registry.for(format)
|
|
226
|
+
output = formatter.format(results, presenter, color: color_flag)
|
|
227
|
+
puts output
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Determines exit code based on results.
|
|
231
|
+
#
|
|
232
|
+
# @param results [Array<Models::OperationResult>] operation results
|
|
233
|
+
# @return [Integer] exit code
|
|
234
|
+
def determine_exit_code(results)
|
|
235
|
+
return ExitCodes::SUCCESS if results.all?(&:successful?)
|
|
236
|
+
return ExitCodes::SUCCESS if results.all?(&:pending?)
|
|
237
|
+
|
|
238
|
+
ExitCodes::GENERAL_ERROR
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Outputs usage error and returns exit code.
|
|
242
|
+
#
|
|
243
|
+
# @param message [String] error message
|
|
244
|
+
# @return [Integer] exit code
|
|
245
|
+
def usage_error(message)
|
|
246
|
+
$stderr.puts "Error: #{message}"
|
|
247
|
+
ExitCodes::USAGE_ERROR
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Outputs no resources found error and returns exit code.
|
|
251
|
+
#
|
|
252
|
+
# @return [Integer] exit code
|
|
253
|
+
def no_resources_found
|
|
254
|
+
type_plural = resource_type_symbol == :vm ? "VMs" : "containers"
|
|
255
|
+
msg = if @options[:all] || selector_strings.any?
|
|
256
|
+
@options[:node] ? "No #{type_plural} found on node #{@options[:node]}" : "No #{type_plural} found matching criteria"
|
|
257
|
+
else
|
|
258
|
+
"No #{type_plural} found for given IDs"
|
|
259
|
+
end
|
|
260
|
+
$stderr.puts "Error: #{msg}"
|
|
261
|
+
ExitCodes::NOT_FOUND
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
module Logs
|
|
6
|
+
# Dispatcher for the `pvectl logs <resource_type> <id>` command.
|
|
7
|
+
#
|
|
8
|
+
# Routes requests to appropriate log handlers:
|
|
9
|
+
# - vm/ct: task history via Handlers::TaskLogs
|
|
10
|
+
# - node: syslog via Handlers::Syslog (or journal with --journal)
|
|
11
|
+
# - task: log lines via Handlers::TaskDetail
|
|
12
|
+
#
|
|
13
|
+
# @example Basic usage
|
|
14
|
+
# Commands::Logs::Command.execute("vm", "100", options, global_options)
|
|
15
|
+
#
|
|
16
|
+
class Command
|
|
17
|
+
# VM/CT resource types that use TaskLogs handler.
|
|
18
|
+
VM_CT_TYPES = %w[vm vms ct container containers cts].freeze
|
|
19
|
+
|
|
20
|
+
# Node resource types.
|
|
21
|
+
NODE_TYPES = %w[node nodes].freeze
|
|
22
|
+
|
|
23
|
+
# Registers the logs command with the CLI.
|
|
24
|
+
#
|
|
25
|
+
# @param cli [GLI::App] the CLI application object
|
|
26
|
+
# @return [void]
|
|
27
|
+
def self.register(cli)
|
|
28
|
+
cli.desc "Show logs for resources (task history, syslog, journal)"
|
|
29
|
+
cli.long_desc <<~HELP
|
|
30
|
+
Show logs and task history for cluster resources. The log source
|
|
31
|
+
depends on the resource type:
|
|
32
|
+
|
|
33
|
+
vm / container Task history (start, stop, backup, migrate, etc.)
|
|
34
|
+
node System log (syslog by default, --journal for systemd)
|
|
35
|
+
task Detailed log output for a specific task UPID
|
|
36
|
+
|
|
37
|
+
EXAMPLES
|
|
38
|
+
Node syslog (last 50 entries):
|
|
39
|
+
$ pvectl logs node pve1
|
|
40
|
+
|
|
41
|
+
Node systemd journal:
|
|
42
|
+
$ pvectl logs node pve1 --journal
|
|
43
|
+
|
|
44
|
+
Task history for a VM:
|
|
45
|
+
$ pvectl logs vm 100
|
|
46
|
+
|
|
47
|
+
Search VM tasks across all nodes:
|
|
48
|
+
$ pvectl logs vm 100 --all-nodes
|
|
49
|
+
|
|
50
|
+
Filter by task type and date:
|
|
51
|
+
$ pvectl logs vm 100 --type vzdump --since 2026-01-01
|
|
52
|
+
|
|
53
|
+
Detailed log for a specific task:
|
|
54
|
+
$ pvectl logs task UPID:pve1:000ABC:...
|
|
55
|
+
|
|
56
|
+
Filter node syslog by service:
|
|
57
|
+
$ pvectl logs node pve1 --service pvedaemon
|
|
58
|
+
|
|
59
|
+
Increase entry limit:
|
|
60
|
+
$ pvectl logs node pve1 --limit 200
|
|
61
|
+
|
|
62
|
+
NOTES
|
|
63
|
+
Default limit is 50 entries. Task detail (UPID) defaults to 512 lines.
|
|
64
|
+
|
|
65
|
+
--all-nodes searches across every cluster node for VM/CT tasks.
|
|
66
|
+
This is useful when a VM has been migrated between nodes.
|
|
67
|
+
|
|
68
|
+
Timestamps for --since and --until accept YYYY-MM-DD format
|
|
69
|
+
or Unix epoch seconds.
|
|
70
|
+
|
|
71
|
+
SEE ALSO
|
|
72
|
+
pvectl help get tasks List task history cluster-wide
|
|
73
|
+
pvectl help describe Detailed resource information
|
|
74
|
+
HELP
|
|
75
|
+
cli.arg_name "RESOURCE_TYPE ID"
|
|
76
|
+
cli.command :logs do |c|
|
|
77
|
+
c.desc "Maximum number of entries to show"
|
|
78
|
+
c.default_value 50
|
|
79
|
+
c.flag [:limit], type: Integer, arg_name: "N"
|
|
80
|
+
|
|
81
|
+
c.desc "Show entries since timestamp (YYYY-MM-DD or epoch)"
|
|
82
|
+
c.flag [:since], arg_name: "TIMESTAMP"
|
|
83
|
+
|
|
84
|
+
c.desc "Show entries until timestamp (YYYY-MM-DD or epoch)"
|
|
85
|
+
c.flag [:until], arg_name: "TIMESTAMP"
|
|
86
|
+
|
|
87
|
+
c.desc "Filter by task type (e.g., qmstart, qmstop, vzdump)"
|
|
88
|
+
c.flag [:type], arg_name: "TYPE"
|
|
89
|
+
|
|
90
|
+
c.desc "Filter by status (running, ok, error)"
|
|
91
|
+
c.flag [:status], arg_name: "STATUS"
|
|
92
|
+
|
|
93
|
+
c.desc "Filter by service name (syslog only)"
|
|
94
|
+
c.flag [:service], arg_name: "SERVICE"
|
|
95
|
+
|
|
96
|
+
c.desc "Use systemd journal instead of syslog (node only)"
|
|
97
|
+
c.switch [:journal], negatable: false
|
|
98
|
+
|
|
99
|
+
c.desc "Search across all cluster nodes (VM/CT only)"
|
|
100
|
+
c.switch [:"all-nodes"], negatable: false
|
|
101
|
+
|
|
102
|
+
c.action do |global_options, options, args|
|
|
103
|
+
resource_type = args[0]
|
|
104
|
+
resource_id = args[1]
|
|
105
|
+
exit_code = execute(resource_type, resource_id, options, global_options)
|
|
106
|
+
exit exit_code if exit_code != 0
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Executes the logs command.
|
|
112
|
+
#
|
|
113
|
+
# @param resource_type [String, nil] type (vm, ct, node, task)
|
|
114
|
+
# @param resource_id [String, nil] identifier (VMID, node name, UPID)
|
|
115
|
+
# @param options [Hash] command options
|
|
116
|
+
# @param global_options [Hash] global CLI options
|
|
117
|
+
# @return [Integer] exit code
|
|
118
|
+
def self.execute(resource_type, resource_id, options, global_options)
|
|
119
|
+
new(resource_type, resource_id, options, global_options).execute
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Creates a new Logs command instance.
|
|
123
|
+
#
|
|
124
|
+
# @param resource_type [String, nil] type of resource
|
|
125
|
+
# @param resource_id [String, nil] resource identifier
|
|
126
|
+
# @param options [Hash] command options
|
|
127
|
+
# @param global_options [Hash] global CLI options
|
|
128
|
+
# @param handler [Object, nil] override handler for testing
|
|
129
|
+
# @param journal_handler [Object, nil] override journal handler for testing
|
|
130
|
+
# @param registry [Class] registry for handler lookup
|
|
131
|
+
def initialize(resource_type, resource_id, options, global_options,
|
|
132
|
+
handler: nil, journal_handler: nil,
|
|
133
|
+
registry: Logs::ResourceRegistry)
|
|
134
|
+
@resource_type = resource_type
|
|
135
|
+
@resource_id = resource_id
|
|
136
|
+
@options = options
|
|
137
|
+
@global_options = global_options
|
|
138
|
+
@handler = handler
|
|
139
|
+
@journal_handler = journal_handler
|
|
140
|
+
@registry = registry
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Executes the logs operation.
|
|
144
|
+
#
|
|
145
|
+
# @return [Integer] exit code
|
|
146
|
+
def execute
|
|
147
|
+
return missing_resource_type_error if @resource_type.nil?
|
|
148
|
+
return missing_resource_id_error if @resource_id.nil? || @resource_id.empty?
|
|
149
|
+
|
|
150
|
+
handler = resolve_handler
|
|
151
|
+
return unknown_resource_error unless handler
|
|
152
|
+
|
|
153
|
+
models = fetch_data(handler)
|
|
154
|
+
output = format_output(models, handler.presenter)
|
|
155
|
+
puts output
|
|
156
|
+
|
|
157
|
+
ExitCodes::SUCCESS
|
|
158
|
+
rescue ResourceNotFoundError => e
|
|
159
|
+
$stderr.puts "Error: #{e.message}"
|
|
160
|
+
ExitCodes::NOT_FOUND
|
|
161
|
+
rescue Pvectl::Config::ConfigNotFoundError,
|
|
162
|
+
Pvectl::Config::InvalidConfigError,
|
|
163
|
+
Pvectl::Config::ContextNotFoundError,
|
|
164
|
+
Pvectl::Config::ClusterNotFoundError,
|
|
165
|
+
Pvectl::Config::UserNotFoundError => e
|
|
166
|
+
$stderr.puts "Error: #{e.message}"
|
|
167
|
+
ExitCodes::CONFIG_ERROR
|
|
168
|
+
rescue Timeout::Error, Errno::ECONNREFUSED, SocketError => e
|
|
169
|
+
$stderr.puts "Error: #{e.message}"
|
|
170
|
+
ExitCodes::CONNECTION_ERROR
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
|
|
175
|
+
# Resolves the handler based on resource type and flags.
|
|
176
|
+
#
|
|
177
|
+
# @return [Object, nil] handler instance
|
|
178
|
+
def resolve_handler
|
|
179
|
+
return @handler if @handler
|
|
180
|
+
|
|
181
|
+
if NODE_TYPES.include?(@resource_type) && @options[:journal]
|
|
182
|
+
@journal_handler || Handlers::Journal.new
|
|
183
|
+
else
|
|
184
|
+
@registry.for(@resource_type)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Fetches data from the handler with appropriate parameters.
|
|
189
|
+
#
|
|
190
|
+
# @param handler [Object] the resolved handler
|
|
191
|
+
# @return [Array<Object>] models
|
|
192
|
+
def fetch_data(handler)
|
|
193
|
+
if VM_CT_TYPES.include?(@resource_type)
|
|
194
|
+
handler.list(
|
|
195
|
+
vmid: @resource_id.to_i,
|
|
196
|
+
resource_type: @resource_type,
|
|
197
|
+
all_nodes: @options[:"all-nodes"] || false,
|
|
198
|
+
limit: @options[:limit] || 50,
|
|
199
|
+
since: @options[:since],
|
|
200
|
+
until_time: @options[:until],
|
|
201
|
+
type_filter: @options[:type],
|
|
202
|
+
status_filter: @options[:status]
|
|
203
|
+
)
|
|
204
|
+
elsif NODE_TYPES.include?(@resource_type)
|
|
205
|
+
handler.list(
|
|
206
|
+
node: @resource_id,
|
|
207
|
+
limit: @options[:limit] || 50,
|
|
208
|
+
since: @options[:since],
|
|
209
|
+
until_time: @options[:until],
|
|
210
|
+
service: @options[:service]
|
|
211
|
+
)
|
|
212
|
+
elsif @resource_type == "task"
|
|
213
|
+
handler.list(
|
|
214
|
+
upid: @resource_id,
|
|
215
|
+
start: 0,
|
|
216
|
+
limit: @options[:limit] || 512
|
|
217
|
+
)
|
|
218
|
+
else
|
|
219
|
+
handler.list
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Outputs error for missing resource type argument.
|
|
224
|
+
#
|
|
225
|
+
# @return [Integer] USAGE_ERROR exit code
|
|
226
|
+
def missing_resource_type_error
|
|
227
|
+
$stderr.puts "Error: resource type is required"
|
|
228
|
+
$stderr.puts "Usage: pvectl logs RESOURCE_TYPE ID [options]"
|
|
229
|
+
$stderr.puts "Available resources: vm, ct, node, task"
|
|
230
|
+
ExitCodes::USAGE_ERROR
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Outputs error for missing resource ID argument.
|
|
234
|
+
#
|
|
235
|
+
# @return [Integer] USAGE_ERROR exit code
|
|
236
|
+
def missing_resource_id_error
|
|
237
|
+
$stderr.puts "Error: resource ID is required"
|
|
238
|
+
$stderr.puts "Usage: pvectl logs #{@resource_type} ID [options]"
|
|
239
|
+
ExitCodes::USAGE_ERROR
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Outputs error for unknown resource type.
|
|
243
|
+
#
|
|
244
|
+
# @return [Integer] USAGE_ERROR exit code
|
|
245
|
+
def unknown_resource_error
|
|
246
|
+
$stderr.puts "Unknown resource type: #{@resource_type}"
|
|
247
|
+
$stderr.puts "Available resources: vm, ct, node, task"
|
|
248
|
+
ExitCodes::USAGE_ERROR
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Formats models for output using the appropriate formatter.
|
|
252
|
+
#
|
|
253
|
+
# @param models [Array<Object>] collection of models
|
|
254
|
+
# @param presenter [Presenters::Base] presenter for the resource type
|
|
255
|
+
# @return [String] formatted output
|
|
256
|
+
def format_output(models, presenter)
|
|
257
|
+
format = @global_options[:output] || "table"
|
|
258
|
+
color_enabled = determine_color_enabled
|
|
259
|
+
formatter = Formatters::Registry.for(format)
|
|
260
|
+
formatter.format(models, presenter, color_enabled: color_enabled)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Determines if color output should be enabled.
|
|
264
|
+
#
|
|
265
|
+
# @return [Boolean] true if color should be enabled
|
|
266
|
+
def determine_color_enabled
|
|
267
|
+
explicit = @global_options[:color]
|
|
268
|
+
return explicit unless explicit.nil?
|
|
269
|
+
|
|
270
|
+
$stdout.tty?
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|