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,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses and converts cloud-init configuration strings for Proxmox VMs.
|
|
6
|
+
#
|
|
7
|
+
# CloudInitConfig handles the conversion between user-friendly key=value
|
|
8
|
+
# cloud-init specifications and the parameter names required by the Proxmox API.
|
|
9
|
+
# Unlike DiskConfig and NetConfig, all keys are optional.
|
|
10
|
+
#
|
|
11
|
+
# @example Parsing a cloud-init config string
|
|
12
|
+
# config = CloudInitConfig.parse("user=admin,password=secret,ip=dhcp")
|
|
13
|
+
# config[:user] #=> "admin"
|
|
14
|
+
# config[:password] #=> "secret"
|
|
15
|
+
# config[:ip] #=> "dhcp"
|
|
16
|
+
#
|
|
17
|
+
# @example Converting to Proxmox API parameters
|
|
18
|
+
# config = { user: "admin", ip: "dhcp" }
|
|
19
|
+
# CloudInitConfig.to_proxmox_params(config)
|
|
20
|
+
# #=> { ciuser: "admin", ipconfig0: "ip=dhcp" }
|
|
21
|
+
#
|
|
22
|
+
class CloudInitConfig
|
|
23
|
+
# All recognized cloud-init configuration keys.
|
|
24
|
+
VALID_KEYS = %w[user password sshkeys ip gw nameserver searchdomain].freeze
|
|
25
|
+
|
|
26
|
+
# Parses a comma-separated key=value cloud-init config string into a Hash.
|
|
27
|
+
#
|
|
28
|
+
# @param string [String] cloud-init config in "key=value,key=value" format
|
|
29
|
+
# @return [Hash<Symbol, String>] parsed configuration
|
|
30
|
+
# @raise [ArgumentError] if unknown keys are present
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# CloudInitConfig.parse("user=admin,ip=dhcp,nameserver=8.8.8.8")
|
|
34
|
+
# #=> { user: "admin", ip: "dhcp", nameserver: "8.8.8.8" }
|
|
35
|
+
def self.parse(string)
|
|
36
|
+
pairs = string.split(",").map { |pair| pair.strip.split("=", 2).map(&:strip) }
|
|
37
|
+
config = pairs.to_h { |k, v| [k.to_sym, v] }
|
|
38
|
+
|
|
39
|
+
validate!(config)
|
|
40
|
+
config
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Converts a parsed cloud-init config Hash to Proxmox API parameter names.
|
|
44
|
+
#
|
|
45
|
+
# Maps user-friendly keys to their Proxmox API equivalents:
|
|
46
|
+
# - +user+ becomes +ciuser+
|
|
47
|
+
# - +password+ becomes +cipassword+
|
|
48
|
+
# - +sshkeys+ stays +sshkeys+
|
|
49
|
+
# - +ip+ becomes +ipconfig0+ with "ip=" prefix
|
|
50
|
+
# - +nameserver+ stays +nameserver+
|
|
51
|
+
# - +searchdomain+ stays +searchdomain+
|
|
52
|
+
#
|
|
53
|
+
# @param config [Hash<Symbol, String>] parsed cloud-init configuration
|
|
54
|
+
# @return [Hash<Symbol, String>] Proxmox API parameters
|
|
55
|
+
#
|
|
56
|
+
# @example Minimal config
|
|
57
|
+
# CloudInitConfig.to_proxmox_params({ user: "admin" })
|
|
58
|
+
# #=> { ciuser: "admin" }
|
|
59
|
+
#
|
|
60
|
+
# @example With IP and nameserver
|
|
61
|
+
# CloudInitConfig.to_proxmox_params({ ip: "dhcp", nameserver: "8.8.8.8" })
|
|
62
|
+
# #=> { ipconfig0: "ip=dhcp", nameserver: "8.8.8.8" }
|
|
63
|
+
def self.to_proxmox_params(config)
|
|
64
|
+
params = {}
|
|
65
|
+
params[:ciuser] = config[:user] if config[:user]
|
|
66
|
+
params[:cipassword] = config[:password] if config[:password]
|
|
67
|
+
params[:sshkeys] = config[:sshkeys] if config[:sshkeys]
|
|
68
|
+
if config[:ip]
|
|
69
|
+
ip_str = "ip=#{config[:ip]}"
|
|
70
|
+
ip_str += ",gw=#{config[:gw]}" if config[:gw]
|
|
71
|
+
params[:ipconfig0] = ip_str
|
|
72
|
+
end
|
|
73
|
+
params[:nameserver] = config[:nameserver] if config[:nameserver]
|
|
74
|
+
params[:searchdomain] = config[:searchdomain] if config[:searchdomain]
|
|
75
|
+
params
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Validates that config contains only known keys.
|
|
79
|
+
#
|
|
80
|
+
# @param config [Hash<Symbol, String>] parsed cloud-init configuration
|
|
81
|
+
# @return [void]
|
|
82
|
+
# @raise [ArgumentError] if unknown keys are present
|
|
83
|
+
def self.validate!(config)
|
|
84
|
+
unknown = config.keys.map(&:to_s) - VALID_KEYS
|
|
85
|
+
unless unknown.empty?
|
|
86
|
+
raise ArgumentError, "Unknown cloud-init config key(s): #{unknown.join(', ')}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
private_class_method :validate!
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses and formats disk configuration strings for Proxmox VMs.
|
|
6
|
+
#
|
|
7
|
+
# DiskConfig handles the conversion between user-friendly key=value
|
|
8
|
+
# disk specifications and the format required by the Proxmox API.
|
|
9
|
+
#
|
|
10
|
+
# @example Parsing a disk config string
|
|
11
|
+
# config = DiskConfig.parse("storage=local-lvm,size=32G,format=qcow2")
|
|
12
|
+
# config[:storage] #=> "local-lvm"
|
|
13
|
+
# config[:size] #=> "32G"
|
|
14
|
+
# config[:format] #=> "qcow2"
|
|
15
|
+
#
|
|
16
|
+
# @example Converting to Proxmox API format
|
|
17
|
+
# config = { storage: "local-lvm", size: "32G", format: "qcow2" }
|
|
18
|
+
# DiskConfig.to_proxmox(config) #=> "local-lvm:32,format=qcow2"
|
|
19
|
+
#
|
|
20
|
+
class DiskConfig
|
|
21
|
+
# All recognized disk configuration keys.
|
|
22
|
+
VALID_KEYS = %w[storage size format cache discard ssd iothread backup].freeze
|
|
23
|
+
|
|
24
|
+
# Keys that must be present in every disk configuration.
|
|
25
|
+
REQUIRED_KEYS = %w[storage size].freeze
|
|
26
|
+
|
|
27
|
+
# Optional flags appended to the Proxmox API string.
|
|
28
|
+
OPTIONAL_FLAGS = %w[cache discard ssd iothread backup].freeze
|
|
29
|
+
|
|
30
|
+
# Parses a comma-separated key=value disk config string into a Hash.
|
|
31
|
+
#
|
|
32
|
+
# @param string [String] disk config in "key=value,key=value" format
|
|
33
|
+
# @return [Hash<Symbol, String>] parsed configuration
|
|
34
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# DiskConfig.parse("storage=local-lvm,size=32G")
|
|
38
|
+
# #=> { storage: "local-lvm", size: "32G" }
|
|
39
|
+
def self.parse(string)
|
|
40
|
+
pairs = string.split(",").map { |pair| pair.strip.split("=", 2).map(&:strip) }
|
|
41
|
+
config = pairs.to_h { |k, v| [k.to_sym, v] }
|
|
42
|
+
|
|
43
|
+
validate!(config)
|
|
44
|
+
config
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Converts a parsed disk config Hash to a Proxmox API string.
|
|
48
|
+
#
|
|
49
|
+
# The Proxmox API expects disk specifications in the format
|
|
50
|
+
# "storage:size,format=fmt,flag=val". Size is extracted as a
|
|
51
|
+
# numeric value (without the "G" suffix). Format defaults to "raw"
|
|
52
|
+
# when not specified.
|
|
53
|
+
#
|
|
54
|
+
# @param config [Hash<Symbol, String>] parsed disk configuration
|
|
55
|
+
# @return [String] Proxmox API-compatible disk string
|
|
56
|
+
#
|
|
57
|
+
# @example Minimal config
|
|
58
|
+
# DiskConfig.to_proxmox({ storage: "local-lvm", size: "32G" })
|
|
59
|
+
# #=> "local-lvm:32,format=raw"
|
|
60
|
+
#
|
|
61
|
+
# @example With optional flags
|
|
62
|
+
# DiskConfig.to_proxmox({ storage: "local-lvm", size: "32G", cache: "writeback" })
|
|
63
|
+
# #=> "local-lvm:32,format=raw,cache=writeback"
|
|
64
|
+
def self.to_proxmox(config)
|
|
65
|
+
size_num = config[:size].to_s.gsub(/[^0-9]/, "")
|
|
66
|
+
format = config[:format] || "raw"
|
|
67
|
+
parts = ["#{config[:storage]}:#{size_num}", "format=#{format}"]
|
|
68
|
+
|
|
69
|
+
OPTIONAL_FLAGS.each do |flag|
|
|
70
|
+
parts << "#{flag}=#{config[flag.to_sym]}" if config[flag.to_sym]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
parts.join(",")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Validates that config contains only known keys and all required keys.
|
|
77
|
+
#
|
|
78
|
+
# @param config [Hash<Symbol, String>] parsed disk configuration
|
|
79
|
+
# @return [void]
|
|
80
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
81
|
+
def self.validate!(config)
|
|
82
|
+
unknown = config.keys.map(&:to_s) - VALID_KEYS
|
|
83
|
+
unless unknown.empty?
|
|
84
|
+
raise ArgumentError, "Unknown disk config key(s): #{unknown.join(', ')}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
REQUIRED_KEYS.each do |key|
|
|
88
|
+
value = config[key.to_sym]
|
|
89
|
+
if value.nil? || value.strip.empty?
|
|
90
|
+
raise ArgumentError, "Missing required disk config key: #{key}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
private_class_method :validate!
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses and formats LXC mount configurations for Proxmox containers.
|
|
6
|
+
#
|
|
7
|
+
# Handles rootfs and mountpoint (mp0, mp1, ...) configuration strings.
|
|
8
|
+
# Format: key=value pairs separated by commas.
|
|
9
|
+
#
|
|
10
|
+
# @example Parsing a rootfs config
|
|
11
|
+
# config = LxcMountConfig.parse("storage=local-lvm,size=8G")
|
|
12
|
+
# config[:storage] #=> "local-lvm"
|
|
13
|
+
# config[:size] #=> "8G"
|
|
14
|
+
#
|
|
15
|
+
# @example Converting to Proxmox API format
|
|
16
|
+
# LxcMountConfig.to_proxmox({ storage: "local-lvm", size: "8G" })
|
|
17
|
+
# #=> "local-lvm:8"
|
|
18
|
+
#
|
|
19
|
+
# LxcMountConfig.to_proxmox({ storage: "local-lvm", size: "32G", mp: "/mnt/data" })
|
|
20
|
+
# #=> "local-lvm:32,mp=/mnt/data"
|
|
21
|
+
#
|
|
22
|
+
class LxcMountConfig
|
|
23
|
+
# All recognized mount configuration keys.
|
|
24
|
+
VALID_KEYS = %w[storage size mp acl backup quota replicate ro shared].freeze
|
|
25
|
+
|
|
26
|
+
# Keys that must be present in every mount configuration.
|
|
27
|
+
REQUIRED_KEYS = %w[storage size].freeze
|
|
28
|
+
|
|
29
|
+
# Optional flags appended to the Proxmox API string.
|
|
30
|
+
OPTIONAL_FLAGS = %w[acl backup quota replicate ro shared].freeze
|
|
31
|
+
|
|
32
|
+
# Parses a comma-separated key=value mount config string into a Hash.
|
|
33
|
+
#
|
|
34
|
+
# @param string [String] mount config in "key=value,key=value" format
|
|
35
|
+
# @return [Hash<Symbol, String>] parsed configuration
|
|
36
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# LxcMountConfig.parse("storage=local-lvm,size=8G")
|
|
40
|
+
# #=> { storage: "local-lvm", size: "8G" }
|
|
41
|
+
def self.parse(string)
|
|
42
|
+
pairs = string.split(",").map { |pair| pair.strip.split("=", 2).map(&:strip) }
|
|
43
|
+
config = pairs.to_h { |k, v| [k.to_sym, v] }
|
|
44
|
+
|
|
45
|
+
validate!(config)
|
|
46
|
+
config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Converts a parsed mount config Hash to a Proxmox API string.
|
|
50
|
+
#
|
|
51
|
+
# The Proxmox API expects mount specifications in the format
|
|
52
|
+
# "storage:size[,mp=/path][,flag=val]". Size is extracted as a
|
|
53
|
+
# numeric value (without the "G" suffix).
|
|
54
|
+
#
|
|
55
|
+
# @param config [Hash<Symbol, String>] parsed mount configuration
|
|
56
|
+
# @return [String] Proxmox API-compatible mount string
|
|
57
|
+
#
|
|
58
|
+
# @example Rootfs config
|
|
59
|
+
# LxcMountConfig.to_proxmox({ storage: "local-lvm", size: "8G" })
|
|
60
|
+
# #=> "local-lvm:8"
|
|
61
|
+
#
|
|
62
|
+
# @example Mountpoint with path and flags
|
|
63
|
+
# LxcMountConfig.to_proxmox({ storage: "local-lvm", size: "32G", mp: "/mnt/data", backup: "1" })
|
|
64
|
+
# #=> "local-lvm:32,mp=/mnt/data,backup=1"
|
|
65
|
+
def self.to_proxmox(config)
|
|
66
|
+
size_num = config[:size].to_s.gsub(/[^0-9]/, "")
|
|
67
|
+
parts = ["#{config[:storage]}:#{size_num}"]
|
|
68
|
+
parts << "mp=#{config[:mp]}" if config[:mp]
|
|
69
|
+
|
|
70
|
+
OPTIONAL_FLAGS.each do |flag|
|
|
71
|
+
parts << "#{flag}=#{config[flag.to_sym]}" if config[flag.to_sym]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
parts.join(",")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Validates that config contains only known keys and all required keys.
|
|
78
|
+
#
|
|
79
|
+
# @param config [Hash<Symbol, String>] parsed mount configuration
|
|
80
|
+
# @return [void]
|
|
81
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
82
|
+
def self.validate!(config)
|
|
83
|
+
unknown = config.keys.map(&:to_s) - VALID_KEYS
|
|
84
|
+
unless unknown.empty?
|
|
85
|
+
raise ArgumentError, "Unknown mount config key(s): #{unknown.join(', ')}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
REQUIRED_KEYS.each do |key|
|
|
89
|
+
value = config[key.to_sym]
|
|
90
|
+
if value.nil? || value.strip.empty?
|
|
91
|
+
raise ArgumentError, "Missing required mount config key: #{key}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
private_class_method :validate!
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses and formats LXC network configurations for Proxmox containers.
|
|
6
|
+
#
|
|
7
|
+
# LXC network format differs from QEMU: uses name/type instead of model,
|
|
8
|
+
# and supports IP configuration directly in the network spec.
|
|
9
|
+
#
|
|
10
|
+
# @example Parsing a net config string
|
|
11
|
+
# config = LxcNetConfig.parse("bridge=vmbr0,name=eth0,ip=dhcp")
|
|
12
|
+
# config[:bridge] #=> "vmbr0"
|
|
13
|
+
# config[:name] #=> "eth0"
|
|
14
|
+
# config[:ip] #=> "dhcp"
|
|
15
|
+
#
|
|
16
|
+
# @example Converting to Proxmox API format
|
|
17
|
+
# LxcNetConfig.to_proxmox({ bridge: "vmbr0", ip: "dhcp" })
|
|
18
|
+
# #=> "name=eth0,bridge=vmbr0,ip=dhcp,type=veth"
|
|
19
|
+
#
|
|
20
|
+
class LxcNetConfig
|
|
21
|
+
# All recognized LXC network configuration keys.
|
|
22
|
+
VALID_KEYS = %w[bridge name ip gw ip6 gw6 tag firewall mtu rate type].freeze
|
|
23
|
+
|
|
24
|
+
# Keys that must be present in every network configuration.
|
|
25
|
+
REQUIRED_KEYS = %w[bridge].freeze
|
|
26
|
+
|
|
27
|
+
# Optional flags appended to the Proxmox API string.
|
|
28
|
+
OPTIONAL_FLAGS = %w[ip gw ip6 gw6 tag firewall mtu rate].freeze
|
|
29
|
+
|
|
30
|
+
# Parses a comma-separated key=value LXC net config string into a Hash.
|
|
31
|
+
#
|
|
32
|
+
# @param string [String] net config in "key=value,key=value" format
|
|
33
|
+
# @return [Hash<Symbol, String>] parsed configuration
|
|
34
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# LxcNetConfig.parse("bridge=vmbr0,name=eth0,ip=dhcp")
|
|
38
|
+
# #=> { bridge: "vmbr0", name: "eth0", ip: "dhcp" }
|
|
39
|
+
def self.parse(string)
|
|
40
|
+
pairs = string.split(",").map { |pair| pair.strip.split("=", 2).map(&:strip) }
|
|
41
|
+
config = pairs.to_h { |k, v| [k.to_sym, v] }
|
|
42
|
+
|
|
43
|
+
validate!(config)
|
|
44
|
+
config
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Converts a parsed LXC net config Hash to a Proxmox API string.
|
|
48
|
+
#
|
|
49
|
+
# The Proxmox API expects LXC network specifications in the format
|
|
50
|
+
# "name=eth0,bridge=vmbr0,[flags],type=veth". Name defaults to "eth0"
|
|
51
|
+
# and type defaults to "veth" when not specified.
|
|
52
|
+
#
|
|
53
|
+
# @param config [Hash<Symbol, String>] parsed network configuration
|
|
54
|
+
# @return [String] Proxmox API-compatible network string
|
|
55
|
+
#
|
|
56
|
+
# @example Minimal config
|
|
57
|
+
# LxcNetConfig.to_proxmox({ bridge: "vmbr0" })
|
|
58
|
+
# #=> "name=eth0,bridge=vmbr0,type=veth"
|
|
59
|
+
#
|
|
60
|
+
# @example With IP and gateway
|
|
61
|
+
# LxcNetConfig.to_proxmox({ bridge: "vmbr0", ip: "10.0.0.5/24", gw: "10.0.0.1" })
|
|
62
|
+
# #=> "name=eth0,bridge=vmbr0,ip=10.0.0.5/24,gw=10.0.0.1,type=veth"
|
|
63
|
+
def self.to_proxmox(config)
|
|
64
|
+
name = config[:name] || "eth0"
|
|
65
|
+
type = config[:type] || "veth"
|
|
66
|
+
parts = ["name=#{name}", "bridge=#{config[:bridge]}"]
|
|
67
|
+
|
|
68
|
+
OPTIONAL_FLAGS.each do |flag|
|
|
69
|
+
parts << "#{flag}=#{config[flag.to_sym]}" if config[flag.to_sym]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
parts << "type=#{type}"
|
|
73
|
+
parts.join(",")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Validates that config contains only known keys and all required keys.
|
|
77
|
+
#
|
|
78
|
+
# @param config [Hash<Symbol, String>] parsed network configuration
|
|
79
|
+
# @return [void]
|
|
80
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
81
|
+
def self.validate!(config)
|
|
82
|
+
unknown = config.keys.map(&:to_s) - VALID_KEYS
|
|
83
|
+
unless unknown.empty?
|
|
84
|
+
raise ArgumentError, "Unknown LXC net config key(s): #{unknown.join(', ')}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
REQUIRED_KEYS.each do |key|
|
|
88
|
+
value = config[key.to_sym]
|
|
89
|
+
if value.nil? || value.strip.empty?
|
|
90
|
+
raise ArgumentError, "Missing required LXC net config key: #{key}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
private_class_method :validate!
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses and formats network configuration strings for Proxmox VMs.
|
|
6
|
+
#
|
|
7
|
+
# NetConfig handles the conversion between user-friendly key=value
|
|
8
|
+
# network specifications and the format required by the Proxmox API.
|
|
9
|
+
#
|
|
10
|
+
# @example Parsing a net config string
|
|
11
|
+
# config = NetConfig.parse("bridge=vmbr0,model=virtio,tag=100")
|
|
12
|
+
# config[:bridge] #=> "vmbr0"
|
|
13
|
+
# config[:model] #=> "virtio"
|
|
14
|
+
# config[:tag] #=> "100"
|
|
15
|
+
#
|
|
16
|
+
# @example Converting to Proxmox API format
|
|
17
|
+
# config = { bridge: "vmbr0", tag: "100" }
|
|
18
|
+
# NetConfig.to_proxmox(config) #=> "virtio,bridge=vmbr0,tag=100"
|
|
19
|
+
#
|
|
20
|
+
class NetConfig
|
|
21
|
+
# All recognized network configuration keys.
|
|
22
|
+
VALID_KEYS = %w[bridge model tag firewall mtu queues].freeze
|
|
23
|
+
|
|
24
|
+
# Keys that must be present in every network configuration.
|
|
25
|
+
REQUIRED_KEYS = %w[bridge].freeze
|
|
26
|
+
|
|
27
|
+
# Optional flags appended to the Proxmox API string.
|
|
28
|
+
OPTIONAL_FLAGS = %w[tag firewall mtu queues].freeze
|
|
29
|
+
|
|
30
|
+
# Parses a comma-separated key=value net config string into a Hash.
|
|
31
|
+
#
|
|
32
|
+
# @param string [String] net config in "key=value,key=value" format
|
|
33
|
+
# @return [Hash<Symbol, String>] parsed configuration
|
|
34
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# NetConfig.parse("bridge=vmbr0,model=virtio,tag=100")
|
|
38
|
+
# #=> { bridge: "vmbr0", model: "virtio", tag: "100" }
|
|
39
|
+
def self.parse(string)
|
|
40
|
+
pairs = string.split(",").map { |pair| pair.strip.split("=", 2).map(&:strip) }
|
|
41
|
+
config = pairs.to_h { |k, v| [k.to_sym, v] }
|
|
42
|
+
|
|
43
|
+
validate!(config)
|
|
44
|
+
config
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Converts a parsed net config Hash to a Proxmox API string.
|
|
48
|
+
#
|
|
49
|
+
# The Proxmox API expects network specifications in the format
|
|
50
|
+
# "model,bridge=name,flag=val". Model defaults to "virtio"
|
|
51
|
+
# when not specified.
|
|
52
|
+
#
|
|
53
|
+
# @param config [Hash<Symbol, String>] parsed network configuration
|
|
54
|
+
# @return [String] Proxmox API-compatible network string
|
|
55
|
+
#
|
|
56
|
+
# @example Minimal config
|
|
57
|
+
# NetConfig.to_proxmox({ bridge: "vmbr0" })
|
|
58
|
+
# #=> "virtio,bridge=vmbr0"
|
|
59
|
+
#
|
|
60
|
+
# @example With optional flags
|
|
61
|
+
# NetConfig.to_proxmox({ bridge: "vmbr0", tag: "100", firewall: "1" })
|
|
62
|
+
# #=> "virtio,bridge=vmbr0,tag=100,firewall=1"
|
|
63
|
+
def self.to_proxmox(config)
|
|
64
|
+
model = config[:model] || "virtio"
|
|
65
|
+
parts = [model, "bridge=#{config[:bridge]}"]
|
|
66
|
+
|
|
67
|
+
OPTIONAL_FLAGS.each do |flag|
|
|
68
|
+
parts << "#{flag}=#{config[flag.to_sym]}" if config[flag.to_sym]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
parts.join(",")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Validates that config contains only known keys and all required keys.
|
|
75
|
+
#
|
|
76
|
+
# @param config [Hash<Symbol, String>] parsed network configuration
|
|
77
|
+
# @return [void]
|
|
78
|
+
# @raise [ArgumentError] if unknown keys are present or required keys are missing
|
|
79
|
+
def self.validate!(config)
|
|
80
|
+
unknown = config.keys.map(&:to_s) - VALID_KEYS
|
|
81
|
+
unless unknown.empty?
|
|
82
|
+
raise ArgumentError, "Unknown net config key(s): #{unknown.join(', ')}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
REQUIRED_KEYS.each do |key|
|
|
86
|
+
value = config[key.to_sym]
|
|
87
|
+
if value.nil? || value.strip.empty?
|
|
88
|
+
raise ArgumentError, "Missing required net config key: #{key}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
private_class_method :validate!
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
module Parsers
|
|
5
|
+
# Parses smartctl text output (NVMe/SAS) into structured key-value pairs.
|
|
6
|
+
#
|
|
7
|
+
# NVMe and SAS disks return SMART data as plain text from the Proxmox API
|
|
8
|
+
# (field +text+ in +GET /nodes/{node}/disks/smart+). This parser extracts
|
|
9
|
+
# +Key: Value+ lines into an Array of Hashes suitable for table display.
|
|
10
|
+
#
|
|
11
|
+
# @example Parsing NVMe SMART text
|
|
12
|
+
# text = "Critical Warning: 0x00\nTemperature: 34 Celsius\n"
|
|
13
|
+
# Pvectl::Parsers::SmartText.parse(text)
|
|
14
|
+
# # => [{ "Attribute" => "Critical Warning", "Value" => "0x00" },
|
|
15
|
+
# # { "Attribute" => "Temperature", "Value" => "34 Celsius" }]
|
|
16
|
+
#
|
|
17
|
+
class SmartText
|
|
18
|
+
# Parses smartctl text output into structured attributes.
|
|
19
|
+
#
|
|
20
|
+
# Splits each line on the first colon. Lines without a colon or
|
|
21
|
+
# without a non-empty value after the colon are skipped.
|
|
22
|
+
# Uses String#split instead of regex to avoid ReDoS risk.
|
|
23
|
+
#
|
|
24
|
+
# @param text [String, nil] raw smartctl text output
|
|
25
|
+
# @return [Array<Hash{String => String}>] parsed attributes
|
|
26
|
+
def self.parse(text)
|
|
27
|
+
return [] if text.nil? || text.empty?
|
|
28
|
+
|
|
29
|
+
text.each_line.filter_map { |line|
|
|
30
|
+
key, value = line.strip.split(":", 2)
|
|
31
|
+
next unless value
|
|
32
|
+
|
|
33
|
+
key = key.strip
|
|
34
|
+
value = value.strip
|
|
35
|
+
next if key.empty? || value.empty?
|
|
36
|
+
|
|
37
|
+
{ "Attribute" => key, "Value" => value }
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pvectl
|
|
4
|
+
# Discovers and loads commands from built-in sources and external plugins.
|
|
5
|
+
#
|
|
6
|
+
# Loading order:
|
|
7
|
+
# 1. Built-in commands (BUILTIN_COMMANDS list)
|
|
8
|
+
# 2. Gem-based plugins (pvectl-plugin-* gems)
|
|
9
|
+
# 3. Directory-based plugins (~/.pvectl/plugins/*.rb)
|
|
10
|
+
#
|
|
11
|
+
# Order matters: built-in commands register first (including their
|
|
12
|
+
# ResourceRegistries), then plugins can extend those registries.
|
|
13
|
+
#
|
|
14
|
+
# @example Plugin registration from a gem
|
|
15
|
+
# Pvectl::PluginLoader.register_plugin(MyPlugin::Command)
|
|
16
|
+
#
|
|
17
|
+
class PluginLoader
|
|
18
|
+
# Built-in commands that ship with pvectl.
|
|
19
|
+
# Each must implement .register(cli).
|
|
20
|
+
BUILTIN_COMMANDS = [
|
|
21
|
+
Commands::Ping,
|
|
22
|
+
Commands::Config::Command,
|
|
23
|
+
Commands::Cloudinit,
|
|
24
|
+
Commands::Get::Command,
|
|
25
|
+
Commands::Top::Command,
|
|
26
|
+
Commands::Logs::Command,
|
|
27
|
+
Commands::Describe::Command,
|
|
28
|
+
Commands::Start,
|
|
29
|
+
Commands::Stop,
|
|
30
|
+
Commands::Shutdown,
|
|
31
|
+
Commands::Restart,
|
|
32
|
+
Commands::Reset,
|
|
33
|
+
Commands::Suspend,
|
|
34
|
+
Commands::Resume,
|
|
35
|
+
Commands::CreateVm,
|
|
36
|
+
Commands::DeleteVm,
|
|
37
|
+
Commands::EditVm,
|
|
38
|
+
Commands::SetVm,
|
|
39
|
+
Commands::CloneVm,
|
|
40
|
+
Commands::MigrateVm,
|
|
41
|
+
Commands::MoveDiskVm,
|
|
42
|
+
Commands::FeatureVm,
|
|
43
|
+
Commands::TemplateVm,
|
|
44
|
+
Commands::RollbackSnapshot,
|
|
45
|
+
Commands::RestoreBackup,
|
|
46
|
+
Commands::Console,
|
|
47
|
+
Commands::SendkeyVm,
|
|
48
|
+
Commands::Pull,
|
|
49
|
+
Commands::Push,
|
|
50
|
+
Commands::Service,
|
|
51
|
+
Commands::Apt,
|
|
52
|
+
Commands::WakeonlanNode,
|
|
53
|
+
Commands::UnlinkDiskVm,
|
|
54
|
+
].freeze
|
|
55
|
+
|
|
56
|
+
@registered_plugins = []
|
|
57
|
+
|
|
58
|
+
class << self
|
|
59
|
+
# Registers an external plugin command for loading.
|
|
60
|
+
#
|
|
61
|
+
# @param klass [Class] plugin class that implements .register(cli)
|
|
62
|
+
# @return [void]
|
|
63
|
+
def register_plugin(klass)
|
|
64
|
+
@registered_plugins << klass
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns currently registered plugins (for testing).
|
|
68
|
+
#
|
|
69
|
+
# @return [Array<Class>] registered plugin classes
|
|
70
|
+
def registered_plugins
|
|
71
|
+
@registered_plugins.dup
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Loads all commands: built-in, gem plugins, directory plugins.
|
|
75
|
+
#
|
|
76
|
+
# @param cli [GLI::App] the CLI application object
|
|
77
|
+
# @return [void]
|
|
78
|
+
def load_all(cli)
|
|
79
|
+
load_builtins(cli)
|
|
80
|
+
load_gem_plugins(cli)
|
|
81
|
+
load_directory_plugins(cli)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Loads built-in commands.
|
|
85
|
+
#
|
|
86
|
+
# @param cli [GLI::App] the CLI application object
|
|
87
|
+
# @return [void]
|
|
88
|
+
def load_builtins(cli)
|
|
89
|
+
BUILTIN_COMMANDS.each { |cmd| cmd.register(cli) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Discovers and loads gem-based plugins.
|
|
93
|
+
#
|
|
94
|
+
# Searches for gems matching pvectl-plugin-* via
|
|
95
|
+
# Gem.find_files("pvectl_plugin/register").
|
|
96
|
+
#
|
|
97
|
+
# @param cli [GLI::App] the CLI application object
|
|
98
|
+
# @return [void]
|
|
99
|
+
def load_gem_plugins(cli)
|
|
100
|
+
Gem.find_files("pvectl_plugin/register").each do |register_file|
|
|
101
|
+
require register_file
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
warn "Warning: Failed to load plugin #{register_file}: #{e.message}"
|
|
104
|
+
warn e.backtrace.join("\n") if ENV["GLI_DEBUG"] == "true"
|
|
105
|
+
end
|
|
106
|
+
flush_registered_plugins(cli)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Discovers and loads directory-based plugins.
|
|
110
|
+
#
|
|
111
|
+
# Scans ~/.pvectl/plugins/*.rb for plugin files.
|
|
112
|
+
#
|
|
113
|
+
# @param cli [GLI::App] the CLI application object
|
|
114
|
+
# @return [void]
|
|
115
|
+
def load_directory_plugins(cli)
|
|
116
|
+
return unless Dir.exist?(directory_plugins_path)
|
|
117
|
+
|
|
118
|
+
Dir.glob(File.join(directory_plugins_path, "*.rb")).sort.each do |plugin_file|
|
|
119
|
+
require plugin_file
|
|
120
|
+
rescue StandardError => e
|
|
121
|
+
warn "Warning: Failed to load plugin #{plugin_file}: #{e.message}"
|
|
122
|
+
warn e.backtrace.join("\n") if ENV["GLI_DEBUG"] == "true"
|
|
123
|
+
end
|
|
124
|
+
flush_registered_plugins(cli)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Returns the directory path for local plugins.
|
|
128
|
+
#
|
|
129
|
+
# @return [String] plugins directory path
|
|
130
|
+
def directory_plugins_path
|
|
131
|
+
File.expand_path("~/.pvectl/plugins")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Calls register on all queued plugins and clears the queue.
|
|
135
|
+
#
|
|
136
|
+
# @param cli [GLI::App] the CLI application object
|
|
137
|
+
# @return [void]
|
|
138
|
+
def flush_registered_plugins(cli)
|
|
139
|
+
@registered_plugins.each do |klass|
|
|
140
|
+
klass.register(cli)
|
|
141
|
+
rescue StandardError => e
|
|
142
|
+
warn "Warning: Failed to register plugin #{klass}: #{e.message}"
|
|
143
|
+
warn e.backtrace.join("\n") if ENV["GLI_DEBUG"] == "true"
|
|
144
|
+
end
|
|
145
|
+
@registered_plugins.clear
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Resets state (for testing).
|
|
149
|
+
#
|
|
150
|
+
# @return [void]
|
|
151
|
+
# @api private
|
|
152
|
+
def reset!
|
|
153
|
+
@registered_plugins = []
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|