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,205 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
# Handler for the `pvectl sendkey vm` command.
|
|
6
|
+
#
|
|
7
|
+
# Sends a single QEMU monitor key event to a running VM. The key string
|
|
8
|
+
# uses QEMU's qcode format (e.g., +ctrl-alt-delete+, +ret+, +f1+) and is
|
|
9
|
+
# forwarded verbatim to the Proxmox API.
|
|
10
|
+
#
|
|
11
|
+
# @example Send Ctrl+Alt+Delete to VM 100
|
|
12
|
+
# pvectl sendkey vm 100 ctrl-alt-delete
|
|
13
|
+
#
|
|
14
|
+
# @example Send Enter to VM 100
|
|
15
|
+
# pvectl sendkey vm 100 ret
|
|
16
|
+
#
|
|
17
|
+
class SendkeyVm
|
|
18
|
+
# Registers the sendkey command with the CLI.
|
|
19
|
+
#
|
|
20
|
+
# @param cli [GLI::App] the CLI application object
|
|
21
|
+
# @return [void]
|
|
22
|
+
def self.register(cli)
|
|
23
|
+
cli.desc "Send a QEMU monitor key event to a VM"
|
|
24
|
+
cli.long_desc <<~HELP
|
|
25
|
+
Send a single QEMU monitor key event to a running virtual machine.
|
|
26
|
+
The key string is forwarded verbatim to the Proxmox API and is
|
|
27
|
+
interpreted by QEMU using its qcode key format.
|
|
28
|
+
|
|
29
|
+
Common key codes:
|
|
30
|
+
- ctrl-alt-delete Reboot signal (Linux/Windows)
|
|
31
|
+
- ctrl-alt-f1..f6 Switch Linux virtual terminals
|
|
32
|
+
- ret Enter / Return
|
|
33
|
+
- esc Escape
|
|
34
|
+
- tab, spc, backspace
|
|
35
|
+
|
|
36
|
+
Single character keys (letters, digits) are also accepted as-is.
|
|
37
|
+
|
|
38
|
+
EXAMPLES
|
|
39
|
+
Trigger Ctrl+Alt+Del on a running VM:
|
|
40
|
+
$ pvectl sendkey vm 100 ctrl-alt-delete
|
|
41
|
+
|
|
42
|
+
Send Enter (e.g., dismiss a bootloader prompt):
|
|
43
|
+
$ pvectl sendkey vm 100 ret
|
|
44
|
+
|
|
45
|
+
Send Escape:
|
|
46
|
+
$ pvectl sendkey vm 100 esc
|
|
47
|
+
|
|
48
|
+
Switch to TTY 1 on a Linux guest:
|
|
49
|
+
$ pvectl sendkey vm 100 ctrl-alt-f1
|
|
50
|
+
|
|
51
|
+
Resolve VM on a specific node:
|
|
52
|
+
$ pvectl sendkey vm 100 ret --node pve1
|
|
53
|
+
|
|
54
|
+
NOTES
|
|
55
|
+
Only VMs are supported — LXC containers do not have a QEMU monitor.
|
|
56
|
+
|
|
57
|
+
The VM must be running. If it is not, the command exits with an
|
|
58
|
+
error before issuing the API call.
|
|
59
|
+
|
|
60
|
+
Composite keys are dash-separated qcodes (e.g., "ctrl-alt-f1"),
|
|
61
|
+
not the literal "+" notation. Refer to QEMU's qcode reference
|
|
62
|
+
for the full list.
|
|
63
|
+
|
|
64
|
+
SEE ALSO
|
|
65
|
+
pvectl help console vm Interactive console (recommended for
|
|
66
|
+
sustained typing)
|
|
67
|
+
pvectl help start Start a stopped VM
|
|
68
|
+
HELP
|
|
69
|
+
cli.arg_name "RESOURCE_TYPE ID KEY"
|
|
70
|
+
cli.command :sendkey do |c|
|
|
71
|
+
c.desc "Node hosting the VM (used to disambiguate lookup)"
|
|
72
|
+
c.flag [:node, :n], arg_name: "NODE"
|
|
73
|
+
|
|
74
|
+
c.action do |global_options, options, args|
|
|
75
|
+
resource_type = args.shift
|
|
76
|
+
|
|
77
|
+
exit_code = case resource_type
|
|
78
|
+
when "vm"
|
|
79
|
+
SendkeyVm.execute(args, options, global_options)
|
|
80
|
+
else
|
|
81
|
+
$stderr.puts "Error: Unknown resource type: #{resource_type}"
|
|
82
|
+
$stderr.puts "Valid types: vm"
|
|
83
|
+
ExitCodes::USAGE_ERROR
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
exit exit_code if exit_code != 0
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Executes the sendkey VM command.
|
|
92
|
+
#
|
|
93
|
+
# @param args [Array<String>] command arguments (VMID KEY)
|
|
94
|
+
# @param options [Hash] command options
|
|
95
|
+
# @param global_options [Hash] global CLI options
|
|
96
|
+
# @return [Integer] exit code
|
|
97
|
+
def self.execute(args, options, global_options)
|
|
98
|
+
new(args, options, global_options).execute
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Initializes a sendkey VM command.
|
|
102
|
+
#
|
|
103
|
+
# @param args [Array<String>] command arguments
|
|
104
|
+
# @param options [Hash] command options
|
|
105
|
+
# @param global_options [Hash] global CLI options
|
|
106
|
+
def initialize(args, options, global_options)
|
|
107
|
+
@args = Array(args)
|
|
108
|
+
@options = options || {}
|
|
109
|
+
@global_options = global_options || {}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Executes the sendkey VM command.
|
|
113
|
+
#
|
|
114
|
+
# @return [Integer] exit code
|
|
115
|
+
def execute
|
|
116
|
+
vmid_arg = @args[0]
|
|
117
|
+
key = @args[1]
|
|
118
|
+
|
|
119
|
+
return usage_error("VMID is required") if vmid_arg.nil? || vmid_arg.to_s.strip.empty?
|
|
120
|
+
return usage_error("VMID must be numeric: #{vmid_arg}") unless vmid_arg.to_s.match?(/\A\d+\z/)
|
|
121
|
+
return usage_error("key argument is required") if key.nil? || key.to_s.strip.empty?
|
|
122
|
+
|
|
123
|
+
perform_sendkey(vmid_arg.to_i, key)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# Loads the config, builds the service, and dispatches the call.
|
|
129
|
+
#
|
|
130
|
+
# @param vmid [Integer] VM identifier
|
|
131
|
+
# @param key [String] key sequence
|
|
132
|
+
# @return [Integer] exit code
|
|
133
|
+
def perform_sendkey(vmid, key)
|
|
134
|
+
load_config
|
|
135
|
+
connection = Pvectl::Connection.new(@config)
|
|
136
|
+
vm_repo = Pvectl::Repositories::Vm.new(connection)
|
|
137
|
+
|
|
138
|
+
service = Pvectl::Services::Sendkey.new(vm_repository: vm_repo)
|
|
139
|
+
result = service.execute(vmid: vmid, key: key, node: @options[:node])
|
|
140
|
+
|
|
141
|
+
output_result(result)
|
|
142
|
+
|
|
143
|
+
return ExitCodes::NOT_FOUND if vm_not_found?(result)
|
|
144
|
+
|
|
145
|
+
result.failed? ? ExitCodes::GENERAL_ERROR : ExitCodes::SUCCESS
|
|
146
|
+
rescue Pvectl::Config::ConfigNotFoundError,
|
|
147
|
+
Pvectl::Config::InvalidConfigError,
|
|
148
|
+
Pvectl::Config::ContextNotFoundError,
|
|
149
|
+
Pvectl::Config::ClusterNotFoundError,
|
|
150
|
+
Pvectl::Config::UserNotFoundError
|
|
151
|
+
raise
|
|
152
|
+
rescue StandardError => e
|
|
153
|
+
$stderr.puts "Error: #{e.message}"
|
|
154
|
+
ExitCodes::GENERAL_ERROR
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Detects the "VM not found" failure case.
|
|
158
|
+
#
|
|
159
|
+
# @param result [Models::VmOperationResult]
|
|
160
|
+
# @return [Boolean]
|
|
161
|
+
def vm_not_found?(result)
|
|
162
|
+
result.failed? && result.vm.nil? && result.error.to_s.include?("not found")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Loads configuration from file or environment.
|
|
166
|
+
#
|
|
167
|
+
# @return [void]
|
|
168
|
+
def load_config
|
|
169
|
+
service = Pvectl::Config::Service.new
|
|
170
|
+
service.load(config: @global_options[:config])
|
|
171
|
+
@config = service.current_config
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Outputs the operation result using the configured formatter.
|
|
175
|
+
#
|
|
176
|
+
# @param result [Models::VmOperationResult]
|
|
177
|
+
# @return [void]
|
|
178
|
+
def output_result(result)
|
|
179
|
+
# When the VM could not be resolved we have no VM info — write a
|
|
180
|
+
# plain stderr error instead of a presenter row referencing nil.
|
|
181
|
+
if result.failed? && result.vm.nil?
|
|
182
|
+
$stderr.puts "Error: #{result.error}"
|
|
183
|
+
return
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
presenter = Pvectl::Presenters::VmOperationResult.new
|
|
187
|
+
format = @global_options[:output] || "table"
|
|
188
|
+
color_flag = @global_options[:color]
|
|
189
|
+
|
|
190
|
+
formatter = Pvectl::Formatters::Registry.for(format)
|
|
191
|
+
output = formatter.format([result], presenter, color: color_flag)
|
|
192
|
+
puts output
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Writes a usage error and returns the exit code.
|
|
196
|
+
#
|
|
197
|
+
# @param message [String]
|
|
198
|
+
# @return [Integer]
|
|
199
|
+
def usage_error(message)
|
|
200
|
+
$stderr.puts "Error: #{message}"
|
|
201
|
+
ExitCodes::USAGE_ERROR
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
# Top-level `pvectl service` command for managing systemd services on
|
|
6
|
+
# Proxmox nodes.
|
|
7
|
+
#
|
|
8
|
+
# Exposes four lifecycle sub-commands:
|
|
9
|
+
#
|
|
10
|
+
# * `service start <name>` start a service
|
|
11
|
+
# * `service stop <name>` stop a service (irreversible — requires --yes or prompt)
|
|
12
|
+
# * `service restart <name>` hard restart a service (irreversible — requires --yes or prompt)
|
|
13
|
+
# * `service reload <name>` reload (graceful where supported)
|
|
14
|
+
#
|
|
15
|
+
# Each sub-command requires `--node NODE` (falls back to default-node from
|
|
16
|
+
# the active context configuration when not provided).
|
|
17
|
+
#
|
|
18
|
+
# @example Register with the CLI
|
|
19
|
+
# Commands::Service.register(cli)
|
|
20
|
+
#
|
|
21
|
+
class Service
|
|
22
|
+
# All confirmable operations (stop and restart can disrupt running workloads).
|
|
23
|
+
CONFIRMABLE_OPERATIONS = %i[stop restart].freeze
|
|
24
|
+
|
|
25
|
+
# All supported operations.
|
|
26
|
+
OPERATIONS = %i[start stop restart reload].freeze
|
|
27
|
+
|
|
28
|
+
# Registers the service command and all sub-commands with the CLI.
|
|
29
|
+
#
|
|
30
|
+
# @param cli [GLI::App] the CLI application object
|
|
31
|
+
# @return [void]
|
|
32
|
+
def self.register(cli)
|
|
33
|
+
cli.desc "Manage systemd services on Proxmox nodes"
|
|
34
|
+
cli.long_desc <<~HELP
|
|
35
|
+
Manage systemd services running on Proxmox VE nodes. Wraps the Proxmox
|
|
36
|
+
/nodes/{node}/services API to start, stop, restart, or reload daemons
|
|
37
|
+
such as pveproxy, pvedaemon, corosync, and others.
|
|
38
|
+
|
|
39
|
+
SUB-COMMANDS
|
|
40
|
+
service start NAME Start a stopped service
|
|
41
|
+
service stop NAME Stop a running service (irreversible)
|
|
42
|
+
service restart NAME Hard restart a service (irreversible)
|
|
43
|
+
service reload NAME Reload a service (graceful where supported)
|
|
44
|
+
|
|
45
|
+
EXAMPLES
|
|
46
|
+
Restart the API proxy on a single node (with confirmation skipped):
|
|
47
|
+
$ pvectl service restart pveproxy --node pve1 --yes
|
|
48
|
+
|
|
49
|
+
Start a stopped service on the default node:
|
|
50
|
+
$ pvectl service start cron
|
|
51
|
+
|
|
52
|
+
Stop a service after interactive confirmation:
|
|
53
|
+
$ pvectl service stop pve-firewall --node pve1
|
|
54
|
+
|
|
55
|
+
Reload the syslog daemon (no interruption to running workloads):
|
|
56
|
+
$ pvectl service reload syslog --node pve1
|
|
57
|
+
|
|
58
|
+
NOTES
|
|
59
|
+
--node defaults to the context's default-node if configured.
|
|
60
|
+
|
|
61
|
+
stop and restart are irreversible and require either an interactive
|
|
62
|
+
"yes" confirmation or the --yes flag to skip the prompt.
|
|
63
|
+
|
|
64
|
+
Restarting pveproxy or corosync can momentarily disconnect the
|
|
65
|
+
current API session and break cluster membership respectively. Use
|
|
66
|
+
--yes only when you understand the impact.
|
|
67
|
+
|
|
68
|
+
Operations are asynchronous — the result includes the Proxmox task
|
|
69
|
+
UPID which can be inspected with `pvectl get tasks` and
|
|
70
|
+
`pvectl logs task UPID`.
|
|
71
|
+
|
|
72
|
+
SEE ALSO
|
|
73
|
+
pvectl help get List resources (try `get services`)
|
|
74
|
+
pvectl help logs Inspect task output
|
|
75
|
+
HELP
|
|
76
|
+
cli.command :service do |c|
|
|
77
|
+
# Shared flags declared on the parent so all subcommands inherit them.
|
|
78
|
+
# Avoids GLI flag-redefinition errors when the same flag is needed by
|
|
79
|
+
# multiple sibling subcommands (e.g. start/stop/restart/reload all
|
|
80
|
+
# need --node and --yes).
|
|
81
|
+
c.desc "Node name (defaults to context default-node)"
|
|
82
|
+
c.flag [:node], arg_name: "NODE"
|
|
83
|
+
|
|
84
|
+
c.desc "Skip interactive confirmation prompt"
|
|
85
|
+
c.switch [:yes, :y], negatable: false
|
|
86
|
+
|
|
87
|
+
OPERATIONS.each do |op|
|
|
88
|
+
register_subcommand(c, op)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Registers a single lifecycle sub-command.
|
|
94
|
+
#
|
|
95
|
+
# @param parent [GLI::Command] parent service command
|
|
96
|
+
# @param operation [Symbol] one of :start, :stop, :restart, :reload
|
|
97
|
+
# @return [void]
|
|
98
|
+
def self.register_subcommand(parent, operation)
|
|
99
|
+
parent.desc "#{operation.capitalize} a systemd service on a Proxmox node"
|
|
100
|
+
parent.long_desc subcommand_long_desc(operation)
|
|
101
|
+
parent.arg_name "SERVICE_NAME"
|
|
102
|
+
parent.command operation do |sub|
|
|
103
|
+
sub.action do |global_options, options, args|
|
|
104
|
+
exit_code = execute(operation, args, options, global_options)
|
|
105
|
+
exit exit_code if exit_code != 0
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Builds the man-page-style long_desc for a sub-command.
|
|
111
|
+
#
|
|
112
|
+
# @param operation [Symbol] operation
|
|
113
|
+
# @return [String] long help text
|
|
114
|
+
def self.subcommand_long_desc(operation)
|
|
115
|
+
action = operation.to_s
|
|
116
|
+
confirm_note =
|
|
117
|
+
if CONFIRMABLE_OPERATIONS.include?(operation)
|
|
118
|
+
"This operation is irreversible. Without --yes, pvectl will\n prompt for interactive confirmation before contacting the API."
|
|
119
|
+
else
|
|
120
|
+
"No confirmation is required for this operation."
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
<<~HELP
|
|
124
|
+
#{action.capitalize} a systemd service on a Proxmox node.
|
|
125
|
+
|
|
126
|
+
EXAMPLES
|
|
127
|
+
$ pvectl service #{action} pveproxy --node pve1
|
|
128
|
+
$ pvectl service #{action} pveproxy --node pve1 --yes
|
|
129
|
+
|
|
130
|
+
NOTES
|
|
131
|
+
#{confirm_note}
|
|
132
|
+
|
|
133
|
+
Restarting pveproxy or corosync can momentarily disconnect the
|
|
134
|
+
current API session and break cluster membership respectively.
|
|
135
|
+
|
|
136
|
+
--node defaults to the context's default-node.
|
|
137
|
+
|
|
138
|
+
SEE ALSO
|
|
139
|
+
pvectl help service Parent command
|
|
140
|
+
pvectl help get List resources (try `get services`)
|
|
141
|
+
HELP
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Executes the command.
|
|
145
|
+
#
|
|
146
|
+
# @param operation [Symbol] operation (:start, :stop, :restart, :reload)
|
|
147
|
+
# @param args [Array<String>] positional CLI arguments
|
|
148
|
+
# @param options [Hash] command options
|
|
149
|
+
# @param global_options [Hash] global CLI options
|
|
150
|
+
# @return [Integer] exit code
|
|
151
|
+
def self.execute(operation, args, options, global_options)
|
|
152
|
+
new(operation, args, options, global_options).execute
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Initializes a service lifecycle command.
|
|
156
|
+
#
|
|
157
|
+
# @param operation [Symbol] operation
|
|
158
|
+
# @param args [Array<String>] positional CLI arguments
|
|
159
|
+
# @param options [Hash] command options
|
|
160
|
+
# @param global_options [Hash] global CLI options
|
|
161
|
+
# @param prompt [IO] IO for confirmation prompts (default: $stdin)
|
|
162
|
+
# @param output [IO] IO for output (default: $stdout)
|
|
163
|
+
def initialize(operation, args, options, global_options, prompt: $stdin, output: $stdout)
|
|
164
|
+
@operation = operation
|
|
165
|
+
@args = Array(args).compact
|
|
166
|
+
@options = options
|
|
167
|
+
@global_options = global_options
|
|
168
|
+
@prompt = prompt
|
|
169
|
+
@output = output
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Executes the command.
|
|
173
|
+
#
|
|
174
|
+
# @return [Integer] exit code
|
|
175
|
+
def execute
|
|
176
|
+
return usage_error("service name is required") if @args.empty?
|
|
177
|
+
|
|
178
|
+
service_name = @args.first
|
|
179
|
+
load_config
|
|
180
|
+
node = resolve_node
|
|
181
|
+
return config_error("node is required (provide --node or configure default-node)") unless node
|
|
182
|
+
|
|
183
|
+
return ExitCodes::SUCCESS unless confirm!(service_name, node)
|
|
184
|
+
|
|
185
|
+
result = perform(service_name, node)
|
|
186
|
+
output_result(result)
|
|
187
|
+
result.failed? ? ExitCodes::GENERAL_ERROR : ExitCodes::SUCCESS
|
|
188
|
+
rescue Pvectl::Config::ConfigNotFoundError,
|
|
189
|
+
Pvectl::Config::InvalidConfigError,
|
|
190
|
+
Pvectl::Config::ContextNotFoundError,
|
|
191
|
+
Pvectl::Config::ClusterNotFoundError,
|
|
192
|
+
Pvectl::Config::UserNotFoundError => e
|
|
193
|
+
$stderr.puts "Error: #{e.message}"
|
|
194
|
+
ExitCodes::CONFIG_ERROR
|
|
195
|
+
rescue StandardError => e
|
|
196
|
+
$stderr.puts "Error: #{e.message}"
|
|
197
|
+
ExitCodes::GENERAL_ERROR
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
private
|
|
201
|
+
|
|
202
|
+
# Loads configuration.
|
|
203
|
+
#
|
|
204
|
+
# @return [void]
|
|
205
|
+
def load_config
|
|
206
|
+
service = Pvectl::Config::Service.new
|
|
207
|
+
service.load(config: @global_options[:config])
|
|
208
|
+
@config = service.current_config
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Resolves the node from --node option or default-node config.
|
|
212
|
+
#
|
|
213
|
+
# @return [String, nil] node name or nil if unresolvable
|
|
214
|
+
def resolve_node
|
|
215
|
+
@options[:node] || @config&.default_node
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Confirms the operation when required.
|
|
219
|
+
#
|
|
220
|
+
# @param service_name [String] service identifier
|
|
221
|
+
# @param node [String] node name
|
|
222
|
+
# @return [Boolean] true if operation should proceed
|
|
223
|
+
def confirm!(service_name, node)
|
|
224
|
+
return true unless CONFIRMABLE_OPERATIONS.include?(@operation)
|
|
225
|
+
return true if @options[:yes]
|
|
226
|
+
|
|
227
|
+
warning = service_warning(service_name)
|
|
228
|
+
@output.puts "About to #{@operation} service '#{service_name}' on node '#{node}'."
|
|
229
|
+
@output.puts warning if warning
|
|
230
|
+
@output.print "Continue? [y/N] "
|
|
231
|
+
answer = @prompt.gets&.strip&.downcase
|
|
232
|
+
confirmed = %w[y yes].include?(answer)
|
|
233
|
+
@output.puts "Aborted." unless confirmed
|
|
234
|
+
confirmed
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Returns a warning string for sensitive services, or nil.
|
|
238
|
+
#
|
|
239
|
+
# @param service_name [String] service identifier
|
|
240
|
+
# @return [String, nil] warning
|
|
241
|
+
def service_warning(service_name)
|
|
242
|
+
case service_name
|
|
243
|
+
when "pveproxy", "pvedaemon"
|
|
244
|
+
"Warning: this may disconnect the current API session."
|
|
245
|
+
when "corosync", "pve-cluster"
|
|
246
|
+
"Warning: this can disrupt cluster membership and quorum."
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Performs the API call via ServiceLifecycle.
|
|
251
|
+
#
|
|
252
|
+
# @param service_name [String] service identifier
|
|
253
|
+
# @param node [String] node name
|
|
254
|
+
# @return [Models::NodeOperationResult]
|
|
255
|
+
def perform(service_name, node)
|
|
256
|
+
connection = Pvectl::Connection.new(@config)
|
|
257
|
+
repository = Pvectl::Repositories::Service.new(connection)
|
|
258
|
+
lifecycle = Pvectl::Services::ServiceLifecycle.new(service_repository: repository)
|
|
259
|
+
lifecycle.execute(operation: @operation, node: node, service: service_name)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Outputs the operation result using the configured formatter.
|
|
263
|
+
#
|
|
264
|
+
# @param result [Models::NodeOperationResult]
|
|
265
|
+
# @return [void]
|
|
266
|
+
def output_result(result)
|
|
267
|
+
format = @global_options[:output] || "table"
|
|
268
|
+
color = @global_options[:color]
|
|
269
|
+
formatter = Pvectl::Formatters::Registry.for(format)
|
|
270
|
+
presenter = Pvectl::Presenters::NodeOperationResult.new
|
|
271
|
+
@output.puts formatter.format([result], presenter, color: color)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Outputs a usage error.
|
|
275
|
+
#
|
|
276
|
+
# @param message [String] error message
|
|
277
|
+
# @return [Integer] USAGE_ERROR exit code
|
|
278
|
+
def usage_error(message)
|
|
279
|
+
$stderr.puts "Error: #{message}"
|
|
280
|
+
ExitCodes::USAGE_ERROR
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Outputs a config error.
|
|
284
|
+
#
|
|
285
|
+
# @param message [String] error message
|
|
286
|
+
# @return [Integer] CONFIG_ERROR exit code
|
|
287
|
+
def config_error(message)
|
|
288
|
+
$stderr.puts "Error: #{message}"
|
|
289
|
+
ExitCodes::CONFIG_ERROR
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
# Handler for the `pvectl set container` command.
|
|
6
|
+
#
|
|
7
|
+
# Includes SetResourceCommand for shared workflow and overrides
|
|
8
|
+
# template methods with container-specific behavior.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic usage
|
|
11
|
+
# pvectl set container 200 memory=2048 hostname=web01
|
|
12
|
+
#
|
|
13
|
+
class SetContainer
|
|
14
|
+
include SetResourceCommand
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# @return [String] human label for container resources
|
|
19
|
+
def resource_label
|
|
20
|
+
"container"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] human label for container IDs
|
|
24
|
+
def resource_id_label
|
|
25
|
+
"CTID"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Builds execution parameters from a container ID.
|
|
29
|
+
#
|
|
30
|
+
# @param resource_id [String] CTID
|
|
31
|
+
# @param key_values [Hash] parsed key-value pairs
|
|
32
|
+
# @return [Hash] parameters for the set service
|
|
33
|
+
def execute_params(resource_id, key_values)
|
|
34
|
+
{ ctid: resource_id.to_i, params: key_values }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Builds the container set service.
|
|
38
|
+
#
|
|
39
|
+
# @param connection [Connection] API connection
|
|
40
|
+
# @return [Services::SetContainer] container set service
|
|
41
|
+
def build_set_service(connection)
|
|
42
|
+
ct_repo = Pvectl::Repositories::Container.new(connection)
|
|
43
|
+
Pvectl::Services::SetContainer.new(
|
|
44
|
+
container_repository: ct_repo,
|
|
45
|
+
options: service_options
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Commands
|
|
5
|
+
# Handler for the `pvectl set node` command.
|
|
6
|
+
#
|
|
7
|
+
# Includes SetResourceCommand for shared workflow and overrides
|
|
8
|
+
# template methods with node-specific behavior.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic usage
|
|
11
|
+
# pvectl set node pve1 description="Production node"
|
|
12
|
+
#
|
|
13
|
+
class SetNode
|
|
14
|
+
include SetResourceCommand
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
# @return [String] human label for node resources
|
|
19
|
+
def resource_label
|
|
20
|
+
"node"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] human label for node IDs
|
|
24
|
+
def resource_id_label
|
|
25
|
+
"NODE"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Builds execution parameters from a node name.
|
|
29
|
+
#
|
|
30
|
+
# @param resource_id [String] node name
|
|
31
|
+
# @param key_values [Hash] parsed key-value pairs
|
|
32
|
+
# @return [Hash] parameters for the set service
|
|
33
|
+
def execute_params(resource_id, key_values)
|
|
34
|
+
{ node_name: resource_id, params: key_values }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Builds the node set service.
|
|
38
|
+
#
|
|
39
|
+
# @param connection [Connection] API connection
|
|
40
|
+
# @return [Services::SetNode] node set service
|
|
41
|
+
def build_set_service(connection)
|
|
42
|
+
node_repo = Pvectl::Repositories::Node.new(connection)
|
|
43
|
+
time_repo = Pvectl::Repositories::TimeConfig.new(connection)
|
|
44
|
+
Pvectl::Services::SetNode.new(
|
|
45
|
+
node_repository: node_repo,
|
|
46
|
+
time_repository: time_repo,
|
|
47
|
+
options: service_options
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|