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.
Files changed (558) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/rules/branch-before-changes.md +52 -0
  3. data/.claude/rules/documentation-updates.md +104 -0
  4. data/.claude/rules/git-workflow.md +84 -0
  5. data/.claude/rules/proxmox-api-docs.md +58 -0
  6. data/.claude/rules/rbs-signatures.md +80 -0
  7. data/.claude/rules/refactoring-as-design-option.md +35 -0
  8. data/.claude/scheduled_tasks.lock +1 -0
  9. data/.claude/settings.json +51 -0
  10. data/.mcp.json +8 -0
  11. data/.ruby-gemset +1 -0
  12. data/.ruby-version +1 -0
  13. data/CHANGELOG.md +138 -0
  14. data/CLAUDE.md +211 -0
  15. data/CODE_OF_CONDUCT.md +132 -0
  16. data/LICENSE.txt +21 -0
  17. data/README.md +143 -0
  18. data/Rakefile +8 -0
  19. data/docs/proxmox-api-update.sh +96 -0
  20. data/exe/pvectl +5 -0
  21. data/lib/pvectl/argv_preprocessor.rb +334 -0
  22. data/lib/pvectl/cli.rb +102 -0
  23. data/lib/pvectl/commands/apt.rb +389 -0
  24. data/lib/pvectl/commands/clone_container.rb +230 -0
  25. data/lib/pvectl/commands/clone_vm.rb +331 -0
  26. data/lib/pvectl/commands/cloudinit/command.rb +122 -0
  27. data/lib/pvectl/commands/cloudinit/dump.rb +94 -0
  28. data/lib/pvectl/commands/cloudinit/pending.rb +137 -0
  29. data/lib/pvectl/commands/cloudinit/regenerate.rb +79 -0
  30. data/lib/pvectl/commands/config/command.rb +65 -0
  31. data/lib/pvectl/commands/config/get_contexts.rb +68 -0
  32. data/lib/pvectl/commands/config/set_cluster.rb +103 -0
  33. data/lib/pvectl/commands/config/set_context.rb +136 -0
  34. data/lib/pvectl/commands/config/set_credentials.rb +181 -0
  35. data/lib/pvectl/commands/config/use_context.rb +69 -0
  36. data/lib/pvectl/commands/config/view.rb +67 -0
  37. data/lib/pvectl/commands/console.rb +93 -0
  38. data/lib/pvectl/commands/console_ct.rb +187 -0
  39. data/lib/pvectl/commands/console_vm.rb +187 -0
  40. data/lib/pvectl/commands/container_lifecycle_command.rb +77 -0
  41. data/lib/pvectl/commands/create_backup.rb +173 -0
  42. data/lib/pvectl/commands/create_container.rb +141 -0
  43. data/lib/pvectl/commands/create_resource_command.rb +244 -0
  44. data/lib/pvectl/commands/create_snapshot.rb +242 -0
  45. data/lib/pvectl/commands/create_vm.rb +267 -0
  46. data/lib/pvectl/commands/delete_backup.rb +139 -0
  47. data/lib/pvectl/commands/delete_command.rb +119 -0
  48. data/lib/pvectl/commands/delete_container.rb +30 -0
  49. data/lib/pvectl/commands/delete_snapshot.rb +248 -0
  50. data/lib/pvectl/commands/delete_vm.rb +127 -0
  51. data/lib/pvectl/commands/describe/command.rb +251 -0
  52. data/lib/pvectl/commands/edit_container.rb +56 -0
  53. data/lib/pvectl/commands/edit_dns.rb +149 -0
  54. data/lib/pvectl/commands/edit_hosts.rb +135 -0
  55. data/lib/pvectl/commands/edit_node.rb +54 -0
  56. data/lib/pvectl/commands/edit_resource_command.rb +180 -0
  57. data/lib/pvectl/commands/edit_vm.rb +154 -0
  58. data/lib/pvectl/commands/edit_volume.rb +189 -0
  59. data/lib/pvectl/commands/feature_command.rb +230 -0
  60. data/lib/pvectl/commands/feature_container.rb +21 -0
  61. data/lib/pvectl/commands/feature_vm.rb +94 -0
  62. data/lib/pvectl/commands/get/command.rb +360 -0
  63. data/lib/pvectl/commands/get/handlers/backups.rb +76 -0
  64. data/lib/pvectl/commands/get/handlers/capabilities.rb +107 -0
  65. data/lib/pvectl/commands/get/handlers/containers.rb +148 -0
  66. data/lib/pvectl/commands/get/handlers/disks.rb +107 -0
  67. data/lib/pvectl/commands/get/handlers/dns.rb +94 -0
  68. data/lib/pvectl/commands/get/handlers/hosts.rb +94 -0
  69. data/lib/pvectl/commands/get/handlers/nodes.rb +162 -0
  70. data/lib/pvectl/commands/get/handlers/services.rb +81 -0
  71. data/lib/pvectl/commands/get/handlers/snapshots.rb +97 -0
  72. data/lib/pvectl/commands/get/handlers/storage.rb +118 -0
  73. data/lib/pvectl/commands/get/handlers/subscription.rb +69 -0
  74. data/lib/pvectl/commands/get/handlers/tasks.rb +89 -0
  75. data/lib/pvectl/commands/get/handlers/templates.rb +175 -0
  76. data/lib/pvectl/commands/get/handlers/time.rb +118 -0
  77. data/lib/pvectl/commands/get/handlers/vms.rb +145 -0
  78. data/lib/pvectl/commands/get/handlers/volume.rb +134 -0
  79. data/lib/pvectl/commands/get/resource_handler.rb +63 -0
  80. data/lib/pvectl/commands/get/resource_registry.rb +18 -0
  81. data/lib/pvectl/commands/get/watch_loop.rb +129 -0
  82. data/lib/pvectl/commands/irreversible_command.rb +265 -0
  83. data/lib/pvectl/commands/logs/command.rb +275 -0
  84. data/lib/pvectl/commands/logs/handlers/journal.rb +46 -0
  85. data/lib/pvectl/commands/logs/handlers/syslog.rb +53 -0
  86. data/lib/pvectl/commands/logs/handlers/task_detail.rb +52 -0
  87. data/lib/pvectl/commands/logs/handlers/task_logs.rb +115 -0
  88. data/lib/pvectl/commands/logs/resource_handler.rb +46 -0
  89. data/lib/pvectl/commands/logs/resource_registry.rb +22 -0
  90. data/lib/pvectl/commands/migrate_command.rb +282 -0
  91. data/lib/pvectl/commands/migrate_container.rb +23 -0
  92. data/lib/pvectl/commands/migrate_vm.rb +122 -0
  93. data/lib/pvectl/commands/move_disk_command.rb +239 -0
  94. data/lib/pvectl/commands/move_disk_container.rb +21 -0
  95. data/lib/pvectl/commands/move_disk_vm.rb +127 -0
  96. data/lib/pvectl/commands/ping.rb +249 -0
  97. data/lib/pvectl/commands/pull.rb +342 -0
  98. data/lib/pvectl/commands/push.rb +352 -0
  99. data/lib/pvectl/commands/reset.rb +64 -0
  100. data/lib/pvectl/commands/resource_lifecycle_command.rb +277 -0
  101. data/lib/pvectl/commands/resource_registry.rb +73 -0
  102. data/lib/pvectl/commands/restart.rb +70 -0
  103. data/lib/pvectl/commands/restart_container.rb +18 -0
  104. data/lib/pvectl/commands/restore_backup.rb +236 -0
  105. data/lib/pvectl/commands/resume.rb +57 -0
  106. data/lib/pvectl/commands/rollback_snapshot.rb +228 -0
  107. data/lib/pvectl/commands/sendkey_vm.rb +205 -0
  108. data/lib/pvectl/commands/service.rb +293 -0
  109. data/lib/pvectl/commands/set_container.rb +50 -0
  110. data/lib/pvectl/commands/set_node.rb +52 -0
  111. data/lib/pvectl/commands/set_resource_command.rb +185 -0
  112. data/lib/pvectl/commands/set_vm.rb +136 -0
  113. data/lib/pvectl/commands/set_volume.rb +212 -0
  114. data/lib/pvectl/commands/shared_config_parsers.rb +126 -0
  115. data/lib/pvectl/commands/shared_flags.rb +155 -0
  116. data/lib/pvectl/commands/shutdown.rb +73 -0
  117. data/lib/pvectl/commands/shutdown_container.rb +18 -0
  118. data/lib/pvectl/commands/start.rb +79 -0
  119. data/lib/pvectl/commands/start_container.rb +18 -0
  120. data/lib/pvectl/commands/stop.rb +75 -0
  121. data/lib/pvectl/commands/stop_container.rb +18 -0
  122. data/lib/pvectl/commands/suspend.rb +64 -0
  123. data/lib/pvectl/commands/template_command.rb +205 -0
  124. data/lib/pvectl/commands/template_container.rb +27 -0
  125. data/lib/pvectl/commands/template_vm.rb +106 -0
  126. data/lib/pvectl/commands/top/command.rb +206 -0
  127. data/lib/pvectl/commands/top/handlers/containers.rb +61 -0
  128. data/lib/pvectl/commands/top/handlers/nodes.rb +61 -0
  129. data/lib/pvectl/commands/top/handlers/vms.rb +61 -0
  130. data/lib/pvectl/commands/top/resource_handler.rb +46 -0
  131. data/lib/pvectl/commands/top/resource_registry.rb +22 -0
  132. data/lib/pvectl/commands/unlink_disk_vm.rb +232 -0
  133. data/lib/pvectl/commands/vm_lifecycle_command.rb +77 -0
  134. data/lib/pvectl/commands/wakeonlan_node.rb +153 -0
  135. data/lib/pvectl/config/errors.rb +62 -0
  136. data/lib/pvectl/config/models/cluster.rb +180 -0
  137. data/lib/pvectl/config/models/context.rb +100 -0
  138. data/lib/pvectl/config/models/resolved_config.rb +171 -0
  139. data/lib/pvectl/config/models/user.rb +133 -0
  140. data/lib/pvectl/config/provider.rb +297 -0
  141. data/lib/pvectl/config/service.rb +300 -0
  142. data/lib/pvectl/config/store.rb +161 -0
  143. data/lib/pvectl/config/wizard.rb +309 -0
  144. data/lib/pvectl/config_serializer.rb +1034 -0
  145. data/lib/pvectl/connection/retry_handler.rb +161 -0
  146. data/lib/pvectl/connection.rb +157 -0
  147. data/lib/pvectl/console/terminal_session.rb +449 -0
  148. data/lib/pvectl/editor_session.rb +157 -0
  149. data/lib/pvectl/exit_codes.rb +43 -0
  150. data/lib/pvectl/formatters/base.rb +55 -0
  151. data/lib/pvectl/formatters/color_support.rb +90 -0
  152. data/lib/pvectl/formatters/json.rb +45 -0
  153. data/lib/pvectl/formatters/output_helper.rb +77 -0
  154. data/lib/pvectl/formatters/registry.rb +72 -0
  155. data/lib/pvectl/formatters/table.rb +235 -0
  156. data/lib/pvectl/formatters/wide.rb +93 -0
  157. data/lib/pvectl/formatters/yaml.rb +49 -0
  158. data/lib/pvectl/manifest_serializer.rb +142 -0
  159. data/lib/pvectl/models/apt_package.rb +107 -0
  160. data/lib/pvectl/models/backup.rb +173 -0
  161. data/lib/pvectl/models/base.rb +49 -0
  162. data/lib/pvectl/models/capability.rb +62 -0
  163. data/lib/pvectl/models/container.rb +205 -0
  164. data/lib/pvectl/models/container_operation_result.rb +27 -0
  165. data/lib/pvectl/models/dns_config.rb +54 -0
  166. data/lib/pvectl/models/hosts_file.rb +47 -0
  167. data/lib/pvectl/models/journal_entry.rb +16 -0
  168. data/lib/pvectl/models/network_interface.rb +85 -0
  169. data/lib/pvectl/models/node.rb +195 -0
  170. data/lib/pvectl/models/node_operation_result.rb +45 -0
  171. data/lib/pvectl/models/operation_result.rb +110 -0
  172. data/lib/pvectl/models/physical_disk.rb +193 -0
  173. data/lib/pvectl/models/service.rb +80 -0
  174. data/lib/pvectl/models/snapshot.rb +101 -0
  175. data/lib/pvectl/models/snapshot_description.rb +39 -0
  176. data/lib/pvectl/models/storage.rb +180 -0
  177. data/lib/pvectl/models/subscription.rb +87 -0
  178. data/lib/pvectl/models/syslog_entry.rb +17 -0
  179. data/lib/pvectl/models/task.rb +95 -0
  180. data/lib/pvectl/models/task_entry.rb +52 -0
  181. data/lib/pvectl/models/task_log_line.rb +17 -0
  182. data/lib/pvectl/models/time_config.rb +47 -0
  183. data/lib/pvectl/models/vm.rb +137 -0
  184. data/lib/pvectl/models/vm_operation_result.rb +27 -0
  185. data/lib/pvectl/models/volume.rb +133 -0
  186. data/lib/pvectl/models/volume_operation_result.rb +26 -0
  187. data/lib/pvectl/parsers/cloud_init_config.rb +92 -0
  188. data/lib/pvectl/parsers/disk_config.rb +97 -0
  189. data/lib/pvectl/parsers/lxc_mount_config.rb +98 -0
  190. data/lib/pvectl/parsers/lxc_net_config.rb +97 -0
  191. data/lib/pvectl/parsers/net_config.rb +95 -0
  192. data/lib/pvectl/parsers/smart_text.rb +42 -0
  193. data/lib/pvectl/plugin_loader.rb +157 -0
  194. data/lib/pvectl/presenters/apt_package.rb +99 -0
  195. data/lib/pvectl/presenters/backup.rb +128 -0
  196. data/lib/pvectl/presenters/base.rb +283 -0
  197. data/lib/pvectl/presenters/capability.rb +104 -0
  198. data/lib/pvectl/presenters/config/context.rb +80 -0
  199. data/lib/pvectl/presenters/container.rb +574 -0
  200. data/lib/pvectl/presenters/container_operation_result.rb +109 -0
  201. data/lib/pvectl/presenters/disk.rb +184 -0
  202. data/lib/pvectl/presenters/dns_config.rb +68 -0
  203. data/lib/pvectl/presenters/hosts_file.rb +61 -0
  204. data/lib/pvectl/presenters/journal_entry.rb +20 -0
  205. data/lib/pvectl/presenters/node.rb +762 -0
  206. data/lib/pvectl/presenters/node_operation_result.rb +50 -0
  207. data/lib/pvectl/presenters/operation_result.rb +61 -0
  208. data/lib/pvectl/presenters/service.rb +76 -0
  209. data/lib/pvectl/presenters/snapshot.rb +239 -0
  210. data/lib/pvectl/presenters/snapshot_operation_result.rb +125 -0
  211. data/lib/pvectl/presenters/storage.rb +329 -0
  212. data/lib/pvectl/presenters/subscription.rb +189 -0
  213. data/lib/pvectl/presenters/syslog_entry.rb +20 -0
  214. data/lib/pvectl/presenters/task_entry.rb +69 -0
  215. data/lib/pvectl/presenters/task_log_line.rb +20 -0
  216. data/lib/pvectl/presenters/template.rb +76 -0
  217. data/lib/pvectl/presenters/time_config.rb +86 -0
  218. data/lib/pvectl/presenters/top_container.rb +112 -0
  219. data/lib/pvectl/presenters/top_node.rb +115 -0
  220. data/lib/pvectl/presenters/top_presenter.rb +59 -0
  221. data/lib/pvectl/presenters/top_vm.rb +105 -0
  222. data/lib/pvectl/presenters/vm.rb +853 -0
  223. data/lib/pvectl/presenters/vm_operation_result.rb +109 -0
  224. data/lib/pvectl/presenters/volume.rb +136 -0
  225. data/lib/pvectl/presenters/volume_operation_result.rb +58 -0
  226. data/lib/pvectl/repositories/apt.rb +93 -0
  227. data/lib/pvectl/repositories/backup.rb +186 -0
  228. data/lib/pvectl/repositories/base.rb +110 -0
  229. data/lib/pvectl/repositories/capabilities.rb +96 -0
  230. data/lib/pvectl/repositories/container.rb +503 -0
  231. data/lib/pvectl/repositories/disk.rb +87 -0
  232. data/lib/pvectl/repositories/dns.rb +54 -0
  233. data/lib/pvectl/repositories/hosts.rb +63 -0
  234. data/lib/pvectl/repositories/journal.rb +23 -0
  235. data/lib/pvectl/repositories/node.rb +537 -0
  236. data/lib/pvectl/repositories/service.rb +139 -0
  237. data/lib/pvectl/repositories/snapshot.rb +133 -0
  238. data/lib/pvectl/repositories/storage.rb +302 -0
  239. data/lib/pvectl/repositories/subscription.rb +77 -0
  240. data/lib/pvectl/repositories/syslog.rb +25 -0
  241. data/lib/pvectl/repositories/task.rb +82 -0
  242. data/lib/pvectl/repositories/task_list.rb +30 -0
  243. data/lib/pvectl/repositories/task_log.rb +31 -0
  244. data/lib/pvectl/repositories/time_config.rb +53 -0
  245. data/lib/pvectl/repositories/vm.rb +616 -0
  246. data/lib/pvectl/repositories/volume.rb +306 -0
  247. data/lib/pvectl/selectors/base.rb +201 -0
  248. data/lib/pvectl/selectors/container.rb +116 -0
  249. data/lib/pvectl/selectors/disk.rb +59 -0
  250. data/lib/pvectl/selectors/vm.rb +116 -0
  251. data/lib/pvectl/selectors/volume.rb +59 -0
  252. data/lib/pvectl/services/backup.rb +209 -0
  253. data/lib/pvectl/services/clone_container.rb +260 -0
  254. data/lib/pvectl/services/clone_vm.rb +265 -0
  255. data/lib/pvectl/services/cloudinit.rb +96 -0
  256. data/lib/pvectl/services/console.rb +152 -0
  257. data/lib/pvectl/services/container_lifecycle.rb +124 -0
  258. data/lib/pvectl/services/create_container.rb +179 -0
  259. data/lib/pvectl/services/create_vm.rb +191 -0
  260. data/lib/pvectl/services/edit_container.rb +125 -0
  261. data/lib/pvectl/services/edit_dns.rb +159 -0
  262. data/lib/pvectl/services/edit_hosts.rb +78 -0
  263. data/lib/pvectl/services/edit_node.rb +147 -0
  264. data/lib/pvectl/services/edit_vm.rb +125 -0
  265. data/lib/pvectl/services/edit_volume.rb +224 -0
  266. data/lib/pvectl/services/get/resource_service.rb +98 -0
  267. data/lib/pvectl/services/move_disk.rb +132 -0
  268. data/lib/pvectl/services/pull_config.rb +94 -0
  269. data/lib/pvectl/services/push_config.rb +524 -0
  270. data/lib/pvectl/services/resize_volume.rb +253 -0
  271. data/lib/pvectl/services/resource_delete.rb +169 -0
  272. data/lib/pvectl/services/resource_migration.rb +170 -0
  273. data/lib/pvectl/services/sendkey.rb +108 -0
  274. data/lib/pvectl/services/service_lifecycle.rb +89 -0
  275. data/lib/pvectl/services/set_container.rb +128 -0
  276. data/lib/pvectl/services/set_node.rb +236 -0
  277. data/lib/pvectl/services/set_vm.rb +128 -0
  278. data/lib/pvectl/services/set_volume.rb +126 -0
  279. data/lib/pvectl/services/snapshot.rb +261 -0
  280. data/lib/pvectl/services/task_listing.rb +75 -0
  281. data/lib/pvectl/services/unlink_disk.rb +86 -0
  282. data/lib/pvectl/services/vm_lifecycle.rb +124 -0
  283. data/lib/pvectl/services/wakeonlan.rb +79 -0
  284. data/lib/pvectl/utils/resource_resolver.rb +80 -0
  285. data/lib/pvectl/version.rb +13 -0
  286. data/lib/pvectl/wizards/create_container.rb +105 -0
  287. data/lib/pvectl/wizards/create_vm.rb +98 -0
  288. data/lib/pvectl.rb +439 -0
  289. data/sig/external/gli.rbs +16 -0
  290. data/sig/external/proxmox_api.rbs +10 -0
  291. data/sig/pvectl/argv_preprocessor.rbs +53 -0
  292. data/sig/pvectl/cli.rbs +26 -0
  293. data/sig/pvectl/commands/apt.rbs +47 -0
  294. data/sig/pvectl/commands/clone_container.rbs +31 -0
  295. data/sig/pvectl/commands/clone_vm.rbs +33 -0
  296. data/sig/pvectl/commands/cloudinit/command.rbs +13 -0
  297. data/sig/pvectl/commands/cloudinit/dump.rbs +13 -0
  298. data/sig/pvectl/commands/cloudinit/pending.rbs +17 -0
  299. data/sig/pvectl/commands/cloudinit/regenerate.rbs +11 -0
  300. data/sig/pvectl/commands/config/command.rbs +9 -0
  301. data/sig/pvectl/commands/config/get_contexts.rbs +11 -0
  302. data/sig/pvectl/commands/config/set_cluster.rbs +11 -0
  303. data/sig/pvectl/commands/config/set_context.rbs +15 -0
  304. data/sig/pvectl/commands/config/set_credentials.rbs +15 -0
  305. data/sig/pvectl/commands/config/use_context.rbs +11 -0
  306. data/sig/pvectl/commands/config/view.rbs +11 -0
  307. data/sig/pvectl/commands/console.rbs +9 -0
  308. data/sig/pvectl/commands/console_ct.rbs +27 -0
  309. data/sig/pvectl/commands/console_vm.rbs +27 -0
  310. data/sig/pvectl/commands/container_lifecycle_command.rbs +25 -0
  311. data/sig/pvectl/commands/create_backup.rbs +29 -0
  312. data/sig/pvectl/commands/create_container.rbs +30 -0
  313. data/sig/pvectl/commands/create_resource_command.rbs +53 -0
  314. data/sig/pvectl/commands/create_snapshot.rbs +35 -0
  315. data/sig/pvectl/commands/create_vm.rbs +30 -0
  316. data/sig/pvectl/commands/delete_backup.rbs +25 -0
  317. data/sig/pvectl/commands/delete_command.rbs +45 -0
  318. data/sig/pvectl/commands/delete_container.rbs +11 -0
  319. data/sig/pvectl/commands/delete_snapshot.rbs +35 -0
  320. data/sig/pvectl/commands/delete_vm.rbs +13 -0
  321. data/sig/pvectl/commands/describe/command.rbs +27 -0
  322. data/sig/pvectl/commands/edit_container.rbs +17 -0
  323. data/sig/pvectl/commands/edit_dns.rbs +25 -0
  324. data/sig/pvectl/commands/edit_hosts.rbs +23 -0
  325. data/sig/pvectl/commands/edit_node.rbs +17 -0
  326. data/sig/pvectl/commands/edit_resource_command.rbs +35 -0
  327. data/sig/pvectl/commands/edit_vm.rbs +19 -0
  328. data/sig/pvectl/commands/edit_volume.rbs +24 -0
  329. data/sig/pvectl/commands/feature_command.rbs +43 -0
  330. data/sig/pvectl/commands/feature_container.rbs +10 -0
  331. data/sig/pvectl/commands/feature_vm.rbs +12 -0
  332. data/sig/pvectl/commands/get/command.rbs +42 -0
  333. data/sig/pvectl/commands/get/handlers/backups.rbs +23 -0
  334. data/sig/pvectl/commands/get/handlers/capabilities.rbs +29 -0
  335. data/sig/pvectl/commands/get/handlers/containers.rbs +35 -0
  336. data/sig/pvectl/commands/get/handlers/disks.rbs +27 -0
  337. data/sig/pvectl/commands/get/handlers/dns.rbs +25 -0
  338. data/sig/pvectl/commands/get/handlers/hosts.rbs +25 -0
  339. data/sig/pvectl/commands/get/handlers/nodes.rbs +33 -0
  340. data/sig/pvectl/commands/get/handlers/services.rbs +23 -0
  341. data/sig/pvectl/commands/get/handlers/snapshots.rbs +27 -0
  342. data/sig/pvectl/commands/get/handlers/storage.rbs +25 -0
  343. data/sig/pvectl/commands/get/handlers/subscription.rbs +25 -0
  344. data/sig/pvectl/commands/get/handlers/tasks.rbs +28 -0
  345. data/sig/pvectl/commands/get/handlers/templates.rbs +35 -0
  346. data/sig/pvectl/commands/get/handlers/time.rbs +29 -0
  347. data/sig/pvectl/commands/get/handlers/vms.rbs +35 -0
  348. data/sig/pvectl/commands/get/handlers/volume.rbs +27 -0
  349. data/sig/pvectl/commands/get/resource_handler.rbs +13 -0
  350. data/sig/pvectl/commands/get/resource_registry.rbs +8 -0
  351. data/sig/pvectl/commands/get/watch_loop.rbs +33 -0
  352. data/sig/pvectl/commands/irreversible_command.rbs +32 -0
  353. data/sig/pvectl/commands/logs/command.rbs +35 -0
  354. data/sig/pvectl/commands/logs/handlers/journal.rbs +21 -0
  355. data/sig/pvectl/commands/logs/handlers/syslog.rbs +21 -0
  356. data/sig/pvectl/commands/logs/handlers/task_detail.rbs +21 -0
  357. data/sig/pvectl/commands/logs/handlers/task_logs.rbs +35 -0
  358. data/sig/pvectl/commands/logs/resource_handler.rbs +11 -0
  359. data/sig/pvectl/commands/logs/resource_registry.rbs +8 -0
  360. data/sig/pvectl/commands/migrate_command.rbs +45 -0
  361. data/sig/pvectl/commands/migrate_container.rbs +11 -0
  362. data/sig/pvectl/commands/migrate_vm.rbs +13 -0
  363. data/sig/pvectl/commands/move_disk_command.rbs +43 -0
  364. data/sig/pvectl/commands/move_disk_container.rbs +11 -0
  365. data/sig/pvectl/commands/move_disk_vm.rbs +13 -0
  366. data/sig/pvectl/commands/ping.rbs +39 -0
  367. data/sig/pvectl/commands/pull.rbs +33 -0
  368. data/sig/pvectl/commands/push.rbs +32 -0
  369. data/sig/pvectl/commands/reset.rbs +11 -0
  370. data/sig/pvectl/commands/resource_lifecycle_command.rbs +55 -0
  371. data/sig/pvectl/commands/resource_registry.rbs +19 -0
  372. data/sig/pvectl/commands/restart.rbs +11 -0
  373. data/sig/pvectl/commands/restart_container.rbs +9 -0
  374. data/sig/pvectl/commands/restore_backup.rbs +27 -0
  375. data/sig/pvectl/commands/resume.rbs +11 -0
  376. data/sig/pvectl/commands/rollback_snapshot.rbs +31 -0
  377. data/sig/pvectl/commands/sendkey_vm.rbs +25 -0
  378. data/sig/pvectl/commands/service.rbs +38 -0
  379. data/sig/pvectl/commands/set_container.rbs +13 -0
  380. data/sig/pvectl/commands/set_node.rbs +13 -0
  381. data/sig/pvectl/commands/set_resource_command.rbs +25 -0
  382. data/sig/pvectl/commands/set_vm.rbs +15 -0
  383. data/sig/pvectl/commands/set_volume.rbs +24 -0
  384. data/sig/pvectl/commands/shared_config_parsers.rbs +19 -0
  385. data/sig/pvectl/commands/shared_flags.rbs +10 -0
  386. data/sig/pvectl/commands/shutdown.rbs +11 -0
  387. data/sig/pvectl/commands/shutdown_container.rbs +9 -0
  388. data/sig/pvectl/commands/start.rbs +11 -0
  389. data/sig/pvectl/commands/start_container.rbs +9 -0
  390. data/sig/pvectl/commands/stop.rbs +11 -0
  391. data/sig/pvectl/commands/stop_container.rbs +9 -0
  392. data/sig/pvectl/commands/suspend.rbs +11 -0
  393. data/sig/pvectl/commands/template_command.rbs +21 -0
  394. data/sig/pvectl/commands/template_container.rbs +10 -0
  395. data/sig/pvectl/commands/template_vm.rbs +12 -0
  396. data/sig/pvectl/commands/top/command.rbs +31 -0
  397. data/sig/pvectl/commands/top/handlers/containers.rbs +21 -0
  398. data/sig/pvectl/commands/top/handlers/nodes.rbs +21 -0
  399. data/sig/pvectl/commands/top/handlers/vms.rbs +21 -0
  400. data/sig/pvectl/commands/top/resource_handler.rbs +11 -0
  401. data/sig/pvectl/commands/top/resource_registry.rbs +8 -0
  402. data/sig/pvectl/commands/unlink_disk_vm.rbs +27 -0
  403. data/sig/pvectl/commands/vm_lifecycle_command.rbs +25 -0
  404. data/sig/pvectl/commands/wakeonlan_node.rbs +21 -0
  405. data/sig/pvectl/config/errors.rbs +24 -0
  406. data/sig/pvectl/config/models/cluster.rbs +39 -0
  407. data/sig/pvectl/config/models/context.rbs +23 -0
  408. data/sig/pvectl/config/models/resolved_config.rbs +51 -0
  409. data/sig/pvectl/config/models/user.rbs +31 -0
  410. data/sig/pvectl/config/provider.rbs +40 -0
  411. data/sig/pvectl/config/service.rbs +65 -0
  412. data/sig/pvectl/config/store.rbs +14 -0
  413. data/sig/pvectl/config/wizard.rbs +48 -0
  414. data/sig/pvectl/config_serializer.rbs +121 -0
  415. data/sig/pvectl/connection/retry_handler.rbs +31 -0
  416. data/sig/pvectl/connection.rbs +35 -0
  417. data/sig/pvectl/console/terminal_session.rbs +63 -0
  418. data/sig/pvectl/editor_session.rbs +33 -0
  419. data/sig/pvectl/exit_codes.rbs +19 -0
  420. data/sig/pvectl/formatters/base.rbs +13 -0
  421. data/sig/pvectl/formatters/color_support.rbs +13 -0
  422. data/sig/pvectl/formatters/json.rbs +7 -0
  423. data/sig/pvectl/formatters/output_helper.rbs +9 -0
  424. data/sig/pvectl/formatters/registry.rbs +13 -0
  425. data/sig/pvectl/formatters/table.rbs +25 -0
  426. data/sig/pvectl/formatters/wide.rbs +15 -0
  427. data/sig/pvectl/formatters/yaml.rbs +7 -0
  428. data/sig/pvectl/manifest_serializer.rbs +18 -0
  429. data/sig/pvectl/models/apt_package.rbs +26 -0
  430. data/sig/pvectl/models/backup.rbs +31 -0
  431. data/sig/pvectl/models/base.rbs +11 -0
  432. data/sig/pvectl/models/capability.rbs +16 -0
  433. data/sig/pvectl/models/container.rbs +44 -0
  434. data/sig/pvectl/models/container_operation_result.rbs +9 -0
  435. data/sig/pvectl/models/dns_config.rbs +15 -0
  436. data/sig/pvectl/models/hosts_file.rbs +13 -0
  437. data/sig/pvectl/models/journal_entry.rbs +10 -0
  438. data/sig/pvectl/models/network_interface.rbs +20 -0
  439. data/sig/pvectl/models/node.rbs +47 -0
  440. data/sig/pvectl/models/node_operation_result.rbs +12 -0
  441. data/sig/pvectl/models/operation_result.rbs +21 -0
  442. data/sig/pvectl/models/physical_disk.rbs +35 -0
  443. data/sig/pvectl/models/service.rbs +18 -0
  444. data/sig/pvectl/models/snapshot.rbs +21 -0
  445. data/sig/pvectl/models/snapshot_description.rbs +18 -0
  446. data/sig/pvectl/models/storage.rbs +39 -0
  447. data/sig/pvectl/models/subscription.rbs +24 -0
  448. data/sig/pvectl/models/syslog_entry.rbs +10 -0
  449. data/sig/pvectl/models/task.rbs +22 -0
  450. data/sig/pvectl/models/task_entry.rbs +24 -0
  451. data/sig/pvectl/models/task_log_line.rbs +10 -0
  452. data/sig/pvectl/models/time_config.rbs +12 -0
  453. data/sig/pvectl/models/vm.rbs +32 -0
  454. data/sig/pvectl/models/vm_operation_result.rbs +9 -0
  455. data/sig/pvectl/models/volume.rbs +29 -0
  456. data/sig/pvectl/models/volume_operation_result.rbs +9 -0
  457. data/sig/pvectl/parsers/cloud_init_config.rbs +15 -0
  458. data/sig/pvectl/parsers/disk_config.rbs +19 -0
  459. data/sig/pvectl/parsers/lxc_mount_config.rbs +19 -0
  460. data/sig/pvectl/parsers/lxc_net_config.rbs +19 -0
  461. data/sig/pvectl/parsers/net_config.rbs +19 -0
  462. data/sig/pvectl/parsers/smart_text.rbs +7 -0
  463. data/sig/pvectl/plugin_loader.rbs +25 -0
  464. data/sig/pvectl/presenters/apt_package.rbs +19 -0
  465. data/sig/pvectl/presenters/backup.rbs +25 -0
  466. data/sig/pvectl/presenters/base.rbs +41 -0
  467. data/sig/pvectl/presenters/capability.rbs +19 -0
  468. data/sig/pvectl/presenters/config/context.rbs +17 -0
  469. data/sig/pvectl/presenters/container.rbs +78 -0
  470. data/sig/pvectl/presenters/container_operation_result.rbs +19 -0
  471. data/sig/pvectl/presenters/disk.rbs +31 -0
  472. data/sig/pvectl/presenters/dns_config.rbs +13 -0
  473. data/sig/pvectl/presenters/hosts_file.rbs +13 -0
  474. data/sig/pvectl/presenters/journal_entry.rbs +11 -0
  475. data/sig/pvectl/presenters/node.rbs +118 -0
  476. data/sig/pvectl/presenters/node_operation_result.rbs +11 -0
  477. data/sig/pvectl/presenters/operation_result.rbs +17 -0
  478. data/sig/pvectl/presenters/service.rbs +15 -0
  479. data/sig/pvectl/presenters/snapshot.rbs +35 -0
  480. data/sig/pvectl/presenters/snapshot_operation_result.rbs +27 -0
  481. data/sig/pvectl/presenters/storage.rbs +59 -0
  482. data/sig/pvectl/presenters/subscription.rbs +36 -0
  483. data/sig/pvectl/presenters/syslog_entry.rbs +11 -0
  484. data/sig/pvectl/presenters/task_entry.rbs +21 -0
  485. data/sig/pvectl/presenters/task_log_line.rbs +11 -0
  486. data/sig/pvectl/presenters/template.rbs +15 -0
  487. data/sig/pvectl/presenters/time_config.rbs +19 -0
  488. data/sig/pvectl/presenters/top_container.rbs +17 -0
  489. data/sig/pvectl/presenters/top_node.rbs +17 -0
  490. data/sig/pvectl/presenters/top_presenter.rbs +13 -0
  491. data/sig/pvectl/presenters/top_vm.rbs +17 -0
  492. data/sig/pvectl/presenters/vm.rbs +91 -0
  493. data/sig/pvectl/presenters/vm_operation_result.rbs +19 -0
  494. data/sig/pvectl/presenters/volume.rbs +23 -0
  495. data/sig/pvectl/presenters/volume_operation_result.rbs +11 -0
  496. data/sig/pvectl/repositories/apt.rbs +17 -0
  497. data/sig/pvectl/repositories/backup.rbs +27 -0
  498. data/sig/pvectl/repositories/base.rbs +23 -0
  499. data/sig/pvectl/repositories/capabilities.rbs +20 -0
  500. data/sig/pvectl/repositories/container.rbs +63 -0
  501. data/sig/pvectl/repositories/disk.rbs +17 -0
  502. data/sig/pvectl/repositories/dns.rbs +13 -0
  503. data/sig/pvectl/repositories/hosts.rbs +13 -0
  504. data/sig/pvectl/repositories/journal.rbs +7 -0
  505. data/sig/pvectl/repositories/node.rbs +68 -0
  506. data/sig/pvectl/repositories/service.rbs +27 -0
  507. data/sig/pvectl/repositories/snapshot.rbs +19 -0
  508. data/sig/pvectl/repositories/storage.rbs +37 -0
  509. data/sig/pvectl/repositories/subscription.rbs +17 -0
  510. data/sig/pvectl/repositories/syslog.rbs +7 -0
  511. data/sig/pvectl/repositories/task.rbs +19 -0
  512. data/sig/pvectl/repositories/task_list.rbs +7 -0
  513. data/sig/pvectl/repositories/task_log.rbs +11 -0
  514. data/sig/pvectl/repositories/time_config.rbs +13 -0
  515. data/sig/pvectl/repositories/vm.rbs +85 -0
  516. data/sig/pvectl/repositories/volume.rbs +43 -0
  517. data/sig/pvectl/selectors/base.rbs +37 -0
  518. data/sig/pvectl/selectors/container.rbs +19 -0
  519. data/sig/pvectl/selectors/disk.rbs +13 -0
  520. data/sig/pvectl/selectors/vm.rbs +19 -0
  521. data/sig/pvectl/selectors/volume.rbs +13 -0
  522. data/sig/pvectl/services/backup.rbs +27 -0
  523. data/sig/pvectl/services/clone_container.rbs +35 -0
  524. data/sig/pvectl/services/clone_vm.rbs +35 -0
  525. data/sig/pvectl/services/cloudinit.rbs +19 -0
  526. data/sig/pvectl/services/console.rbs +23 -0
  527. data/sig/pvectl/services/container_lifecycle.rbs +26 -0
  528. data/sig/pvectl/services/create_container.rbs +64 -0
  529. data/sig/pvectl/services/create_vm.rbs +72 -0
  530. data/sig/pvectl/services/edit_container.rbs +17 -0
  531. data/sig/pvectl/services/edit_dns.rbs +23 -0
  532. data/sig/pvectl/services/edit_hosts.rbs +13 -0
  533. data/sig/pvectl/services/edit_node.rbs +21 -0
  534. data/sig/pvectl/services/edit_vm.rbs +17 -0
  535. data/sig/pvectl/services/edit_volume.rbs +18 -0
  536. data/sig/pvectl/services/get/resource_service.rbs +23 -0
  537. data/sig/pvectl/services/move_disk.rbs +21 -0
  538. data/sig/pvectl/services/pull_config.rbs +18 -0
  539. data/sig/pvectl/services/push_config.rbs +37 -0
  540. data/sig/pvectl/services/resize_volume.rbs +47 -0
  541. data/sig/pvectl/services/resource_delete.rbs +27 -0
  542. data/sig/pvectl/services/resource_migration.rbs +29 -0
  543. data/sig/pvectl/services/sendkey.rbs +19 -0
  544. data/sig/pvectl/services/service_lifecycle.rbs +17 -0
  545. data/sig/pvectl/services/set_container.rbs +14 -0
  546. data/sig/pvectl/services/set_node.rbs +23 -0
  547. data/sig/pvectl/services/set_vm.rbs +14 -0
  548. data/sig/pvectl/services/set_volume.rbs +12 -0
  549. data/sig/pvectl/services/snapshot.rbs +35 -0
  550. data/sig/pvectl/services/task_listing.rbs +13 -0
  551. data/sig/pvectl/services/unlink_disk.rbs +17 -0
  552. data/sig/pvectl/services/vm_lifecycle.rbs +26 -0
  553. data/sig/pvectl/services/wakeonlan.rbs +17 -0
  554. data/sig/pvectl/utils/resource_resolver.rbs +17 -0
  555. data/sig/pvectl/wizards/create_container.rbs +21 -0
  556. data/sig/pvectl/wizards/create_vm.rbs +21 -0
  557. data/sig/pvectl.rbs +9 -0
  558. metadata +675 -0
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Shared functionality for the feature query commands (feature vm, feature ct).
6
+ #
7
+ # Queries whether a Proxmox feature (clone, snapshot, copy) is available for
8
+ # a given VM or container. Availability depends on storage type, snapshot
9
+ # state, and other server-side conditions.
10
+ #
11
+ # Designed with the hybrid Template Method pattern (MoveDiskCommand /
12
+ # MigrateCommand) — specializations only set RESOURCE_TYPE and
13
+ # SUPPORTED_RESOURCES; the FeatureVm specialization additionally implements
14
+ # +.register+ which wires the GLI command.
15
+ #
16
+ # @example Including in a command class
17
+ # class FeatureVm
18
+ # include FeatureCommand
19
+ # RESOURCE_TYPE = :vm
20
+ # SUPPORTED_RESOURCES = %w[vm].freeze
21
+ # end
22
+ #
23
+ module FeatureCommand
24
+ # Valid feature names per Proxmox API (both QEMU and LXC endpoints).
25
+ VALID_FEATURES = %w[clone snapshot copy].freeze
26
+
27
+ # Class methods added when the module is included.
28
+ module ClassMethods
29
+ # Executes the feature query command.
30
+ #
31
+ # @param args [Array<String>] command-positional args ([id, feature])
32
+ # @param options [Hash] command options
33
+ # @param global_options [Hash] global CLI options
34
+ # @return [Integer] exit code
35
+ def execute(args, options, global_options)
36
+ new(args, options, global_options).execute
37
+ end
38
+ end
39
+
40
+ # Hook called when the module is included.
41
+ #
42
+ # @param base [Class] the class including this module
43
+ def self.included(base)
44
+ base.extend(ClassMethods)
45
+ end
46
+
47
+ # Initializes a feature query command.
48
+ #
49
+ # @param args [Array<String>] command-positional args ([id, feature])
50
+ # @param options [Hash] command options
51
+ # @param global_options [Hash] global CLI options
52
+ def initialize(args, options, global_options)
53
+ @args = Array(args).compact
54
+ @options = options
55
+ @global_options = global_options
56
+ end
57
+
58
+ # Executes the feature query.
59
+ #
60
+ # @return [Integer] exit code (0 if available, 1 if unavailable, 2 on usage)
61
+ def execute
62
+ return usage_error("#{id_label} is required") if @args.empty?
63
+ return usage_error("FEATURE is required") if @args.size < 2
64
+
65
+ id_str = @args[0]
66
+ feature = @args[1]
67
+
68
+ unless id_str.to_s.match?(/\A\d+\z/)
69
+ return usage_error("Invalid #{id_label}: #{id_str}")
70
+ end
71
+
72
+ unless VALID_FEATURES.include?(feature)
73
+ return usage_error(
74
+ "Invalid feature: #{feature} (allowed: #{VALID_FEATURES.join(', ')})"
75
+ )
76
+ end
77
+
78
+ perform_operation(id_str.to_i, feature)
79
+ end
80
+
81
+ private
82
+
83
+ # Returns the resource type symbol (:vm or :container).
84
+ #
85
+ # @return [Symbol]
86
+ def resource_type_symbol
87
+ self.class::RESOURCE_TYPE
88
+ end
89
+
90
+ # Returns the human label for the resource ID ("VMID" or "CTID").
91
+ #
92
+ # @return [String]
93
+ def id_label
94
+ resource_type_symbol == :vm ? "VMID" : "CTID"
95
+ end
96
+
97
+ # Returns the singular type name ("VM" or "container") used in error messages.
98
+ #
99
+ # @return [String]
100
+ def type_name
101
+ resource_type_symbol == :vm ? "VM" : "container"
102
+ end
103
+
104
+ # Performs the feature availability check.
105
+ #
106
+ # @param id [Integer] resource identifier
107
+ # @param feature [String] feature name
108
+ # @return [Integer] exit code
109
+ def perform_operation(id, feature)
110
+ load_config
111
+ connection = Pvectl::Connection.new(@config)
112
+
113
+ resource = resolve_resource(connection, id)
114
+ return resource_not_found(id) if resource.nil?
115
+
116
+ result = build_repository(connection)
117
+ .feature_available?(id, resource.node, feature, snapname: @options[:snapname])
118
+
119
+ output_result(result, feature)
120
+ result[:available] ? ExitCodes::SUCCESS : ExitCodes::GENERAL_ERROR
121
+ rescue Pvectl::Config::ConfigNotFoundError,
122
+ Pvectl::Config::InvalidConfigError,
123
+ Pvectl::Config::ContextNotFoundError,
124
+ Pvectl::Config::ClusterNotFoundError,
125
+ Pvectl::Config::UserNotFoundError
126
+ raise
127
+ rescue StandardError => e
128
+ $stderr.puts "Error: #{e.message}"
129
+ ExitCodes::GENERAL_ERROR
130
+ end
131
+
132
+ # Resolves the resource (VM or container) by ID using the appropriate repository.
133
+ #
134
+ # @param connection [Connection] API connection
135
+ # @param id [Integer] resource ID
136
+ # @return [Models::Vm, Models::Container, nil]
137
+ def resolve_resource(connection, id)
138
+ build_repository(connection).get(id)
139
+ end
140
+
141
+ # Builds the resource repository (VM or Container) for the current type.
142
+ #
143
+ # @param connection [Connection] API connection
144
+ # @return [Repositories::Vm, Repositories::Container]
145
+ def build_repository(connection)
146
+ if resource_type_symbol == :vm
147
+ Pvectl::Repositories::Vm.new(connection)
148
+ else
149
+ Pvectl::Repositories::Container.new(connection)
150
+ end
151
+ end
152
+
153
+ # Loads configuration from file or environment.
154
+ #
155
+ # @return [void]
156
+ def load_config
157
+ service = Pvectl::Config::Service.new
158
+ service.load(config: @global_options[:config])
159
+ @config = service.current_config
160
+ end
161
+
162
+ # Outputs the result in the requested format.
163
+ #
164
+ # Plain output: "available"/"unavailable" + optional list of capable nodes.
165
+ # JSON/YAML output: structured hash with feature metadata.
166
+ #
167
+ # @param result [Hash] result from +feature_available?+ (:available, :nodes)
168
+ # @param feature [String] feature name
169
+ # @return [void]
170
+ def output_result(result, feature)
171
+ format = (@global_options[:output] || "plain").to_s
172
+
173
+ case format
174
+ when "json"
175
+ require "json"
176
+ $stdout.puts JSON.pretty_generate(payload(result, feature))
177
+ when "yaml"
178
+ require "yaml"
179
+ $stdout.puts YAML.dump(stringify_keys(payload(result, feature)))
180
+ else
181
+ status = result[:available] ? "available" : "unavailable"
182
+ $stdout.puts status
183
+ if result[:available] && !result[:nodes].empty?
184
+ $stdout.puts "nodes: #{result[:nodes].join(', ')}"
185
+ end
186
+ end
187
+ end
188
+
189
+ # Builds the structured result payload for JSON/YAML output.
190
+ #
191
+ # @param result [Hash] feature_available? result
192
+ # @param feature [String] feature name
193
+ # @return [Hash]
194
+ def payload(result, feature)
195
+ {
196
+ available: result[:available],
197
+ feature: feature,
198
+ snapname: @options[:snapname],
199
+ nodes: result[:nodes]
200
+ }
201
+ end
202
+
203
+ # Converts symbol keys to strings for YAML output.
204
+ #
205
+ # @param hash [Hash]
206
+ # @return [Hash]
207
+ def stringify_keys(hash)
208
+ hash.each_with_object({}) { |(k, v), acc| acc[k.to_s] = v }
209
+ end
210
+
211
+ # Outputs usage error and returns the usage exit code.
212
+ #
213
+ # @param message [String]
214
+ # @return [Integer]
215
+ def usage_error(message)
216
+ $stderr.puts "Error: #{message}"
217
+ ExitCodes::USAGE_ERROR
218
+ end
219
+
220
+ # Outputs "not found" error and returns the not-found exit code.
221
+ #
222
+ # @param id [Integer]
223
+ # @return [Integer]
224
+ def resource_not_found(id)
225
+ $stderr.puts "Error: No #{type_name} found with ID #{id}"
226
+ ExitCodes::NOT_FOUND
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl feature ct` command.
6
+ #
7
+ # Queries whether a Proxmox feature (clone, snapshot, copy) is available
8
+ # for an LXC container. The LXC feature endpoint returns only +hasFeature+
9
+ # (no +nodes+ list), so the +nodes+ column is always empty.
10
+ #
11
+ # @example Check whether container 200 supports snapshot
12
+ # pvectl feature ct 200 snapshot
13
+ #
14
+ class FeatureContainer
15
+ include FeatureCommand
16
+
17
+ RESOURCE_TYPE = :container
18
+ SUPPORTED_RESOURCES = %w[container ct].freeze
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl feature vm` command.
6
+ #
7
+ # Queries whether a Proxmox feature (clone, snapshot, copy) is available
8
+ # for a virtual machine. Availability depends on storage type, snapshot
9
+ # state, and other server-side conditions.
10
+ #
11
+ # Also registers the top-level `feature` GLI command and dispatches
12
+ # to FeatureVm or FeatureContainer based on the resource type.
13
+ #
14
+ # @example Check whether VM 100 can be cloned
15
+ # pvectl feature vm 100 clone
16
+ #
17
+ # @example Check snapshot feature against a specific snapshot
18
+ # pvectl feature vm 100 snapshot --snapname pre-upgrade
19
+ #
20
+ class FeatureVm
21
+ include FeatureCommand
22
+
23
+ # Registers the feature command with the CLI.
24
+ #
25
+ # @param cli [GLI::App] the CLI application object
26
+ # @return [void]
27
+ def self.register(cli)
28
+ cli.desc "Query whether a feature (clone/snapshot/copy) is available"
29
+ cli.long_desc <<~HELP
30
+ Check whether a Proxmox feature is available for a VM or container.
31
+ Availability depends on the storage type backing the disks, current
32
+ snapshot state, and other server-side conditions.
33
+
34
+ Exit code is 0 when the feature is available, 1 when unavailable,
35
+ 5 when the resource is not found, and 2 on invalid usage.
36
+
37
+ EXAMPLES
38
+ Check whether VM 100 can be cloned:
39
+ $ pvectl feature vm 100 clone
40
+
41
+ Check snapshot feature on a container with a specific snapshot:
42
+ $ pvectl feature ct 200 snapshot --snapname pre-upgrade
43
+
44
+ Check copy feature for a VM:
45
+ $ pvectl feature vm 100 copy
46
+
47
+ JSON output (machine-readable):
48
+ $ pvectl feature vm 100 clone -o json
49
+
50
+ NOTES
51
+ Supported features: clone, snapshot, copy.
52
+
53
+ The Proxmox API returns the list of cluster nodes that can host
54
+ the resource after the requested feature is applied. For VMs the
55
+ list is printed under the "nodes:" line; for containers the LXC
56
+ endpoint does not return this list.
57
+
58
+ --snapname is required for some checks (e.g. checking whether a
59
+ specific snapshot can be removed or cloned from).
60
+
61
+ SEE ALSO
62
+ pvectl help clone Clone a VM or container
63
+ pvectl help create snapshot Create a snapshot
64
+ pvectl help get snapshots List snapshots
65
+ HELP
66
+ cli.arg_name "RESOURCE_TYPE ID FEATURE"
67
+ cli.command :feature do |c|
68
+ c.desc "Snapshot name (optional, required for some checks)"
69
+ c.flag [:snapname], arg_name: "NAME"
70
+
71
+ c.action do |global_options, options, args|
72
+ resource_type = args.shift
73
+
74
+ exit_code = case resource_type
75
+ when "vm"
76
+ Commands::FeatureVm.execute(args, options, global_options)
77
+ when "container", "ct"
78
+ Commands::FeatureContainer.execute(args, options, global_options)
79
+ else
80
+ $stderr.puts "Error: Unknown resource type: #{resource_type}"
81
+ $stderr.puts "Valid types: vm, container, ct"
82
+ ExitCodes::USAGE_ERROR
83
+ end
84
+
85
+ exit exit_code if exit_code != 0
86
+ end
87
+ end
88
+ end
89
+
90
+ RESOURCE_TYPE = :vm
91
+ SUPPORTED_RESOURCES = %w[vm].freeze
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,360 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ module Get
6
+ # Dispatcher for the `pvectl get <resource_type>` command.
7
+ #
8
+ # Routes requests to appropriate resource handlers based on the
9
+ # resource type argument. Supports watch mode for continuous
10
+ # monitoring and node filtering.
11
+ #
12
+ # Responsibilities:
13
+ # - Control flow (watch vs single execution)
14
+ # - Handler lookup via ResourceRegistry
15
+ # - Error handling for unknown resources and handler exceptions
16
+ #
17
+ # Delegates to:
18
+ # - Services::Get::ResourceService for data fetching and formatting
19
+ #
20
+ # @example Basic usage
21
+ # Commands::Get::Command.execute("nodes", nil, options, global_options)
22
+ #
23
+ # @example With watch mode
24
+ # options = { watch: true, :"watch-interval" => 5 }
25
+ # Commands::Get::Command.execute("vms", nil, options, global_options)
26
+ #
27
+ class Command
28
+ # Registers the get command with the CLI.
29
+ #
30
+ # @param cli [GLI::App] the CLI application object
31
+ # @return [void]
32
+ def self.register(cli)
33
+ cli.desc "List resources in cluster"
34
+ cli.long_desc <<~HELP
35
+ List resources across the Proxmox cluster. Supports multiple resource
36
+ types including nodes, VMs, containers, storage, snapshots, backups,
37
+ templates, and tasks.
38
+
39
+ Results can be filtered by node (--node), formatted in different output
40
+ modes (-o), and auto-refreshed with watch mode (-w).
41
+
42
+ RESOURCE TYPES
43
+ nodes (node) Cluster nodes
44
+ vms (vm) Virtual machines
45
+ containers (ct, cts) LXC containers
46
+ storage (stor) Storage pools
47
+ snapshots (snap) VM/CT snapshots
48
+ backups (backup) Backup volumes
49
+ templates (template) VM and container templates
50
+ tasks (task) Task history
51
+ disks (disk) Physical disks (block devices)
52
+ volumes (volume, vol) Virtual disks attached to VMs/containers
53
+ time Node time and timezone settings
54
+ node-capabilities (caps) Supported QEMU CPU models and machine types
55
+ subscription (sub) Proxmox subscription status per node
56
+
57
+ EXAMPLES
58
+ List all VMs in table format:
59
+ $ pvectl get vms
60
+
61
+ List containers on a specific node as JSON:
62
+ $ pvectl get containers --node pve1 -o json
63
+
64
+ List snapshots for specific VMs:
65
+ $ pvectl get snapshots --vmid 100 --vmid 101
66
+
67
+ Watch cluster nodes with 5-second refresh:
68
+ $ pvectl get nodes -w --watch-interval 5
69
+
70
+ Filter tasks by type and date:
71
+ $ pvectl get tasks --type vzdump --since 2026-01-01
72
+
73
+ Wide output with extra columns:
74
+ $ pvectl get vms -o wide
75
+
76
+ Filter VMs by status (shortcut):
77
+ $ pvectl get vms --status running
78
+
79
+ Filter VMs by selector (supports status, name, tags, pool):
80
+ $ pvectl get vms -l status=running
81
+ $ pvectl get vms -l status=running,tags=prod
82
+ $ pvectl get vms -l name=~web-*
83
+
84
+ List VM volumes:
85
+ $ pvectl get volume vm 100
86
+
87
+ List volumes from storage:
88
+ $ pvectl get volume --storage local-lvm
89
+
90
+ List volumes with filtering:
91
+ $ pvectl get volume vm 100 -l format=raw
92
+
93
+ Show node time and timezone for a single node:
94
+ $ pvectl get time --node pve1
95
+
96
+ Show time and timezone for all online nodes:
97
+ $ pvectl get time
98
+
99
+ List supported CPU models / machine types for a node:
100
+ $ pvectl get node-capabilities --node pve1
101
+ $ pvectl get caps --node pve1 -o json
102
+
103
+ List subscription status across the cluster:
104
+ $ pvectl get subscription
105
+
106
+ Show full license key for one node (key is masked by default):
107
+ $ pvectl get subscription --node pve1 -o wide
108
+
109
+ NOTES
110
+ Use selectors (-l) to filter VMs/containers by status, name, tags, or
111
+ pool. Multiple selectors use AND logic. The --status flag is a shortcut
112
+ for -l status=VALUE and can be combined with other selectors.
113
+
114
+ Task listing defaults to 50 entries; use --limit to change.
115
+
116
+ Watch mode clears the screen on each refresh. Press Ctrl+C to stop.
117
+
118
+ SEE ALSO
119
+ pvectl help describe Show detailed info about a single resource
120
+ pvectl help top Display real-time resource usage metrics
121
+ pvectl help logs Show logs and task history
122
+ HELP
123
+ cli.command :get do |c|
124
+ c.desc "Filter by node name"
125
+ c.flag [:node], arg_name: "NODE"
126
+
127
+ c.desc "Filter by VM/CT ID (repeatable)"
128
+ c.flag [:vmid], arg_name: "VMID", multiple: true
129
+
130
+ c.desc "Filter by storage (for backups)"
131
+ c.flag [:storage], arg_name: "STORAGE"
132
+
133
+ c.desc "Watch for changes with auto-refresh"
134
+ c.switch [:watch, :w], negatable: false
135
+
136
+ c.desc "Watch refresh interval in seconds (default: 2, minimum: 1)"
137
+ c.default_value 2
138
+ c.flag [:"watch-interval"], arg_name: "SECONDS", type: Integer
139
+
140
+ c.desc "Maximum number of entries to show (for tasks)"
141
+ c.default_value 50
142
+ c.flag [:limit], type: Integer, arg_name: "N"
143
+
144
+ c.desc "Show entries since timestamp (YYYY-MM-DD or epoch, for tasks)"
145
+ c.flag [:since], arg_name: "TIMESTAMP"
146
+
147
+ c.desc "Show entries until timestamp (YYYY-MM-DD or epoch, for tasks)"
148
+ c.flag [:until], arg_name: "TIMESTAMP"
149
+
150
+ c.desc "Filter by task type (e.g., qmstart, qmstop, vzdump)"
151
+ c.flag [:type], arg_name: "TYPE"
152
+
153
+ c.desc "Filter by status (running, stopped for VMs/CTs; running, ok, error for tasks)"
154
+ c.flag [:status], arg_name: "STATUS"
155
+
156
+ c.desc "Filter by selector (e.g., status=running,tags=prod)"
157
+ c.flag [:l, :selector], arg_name: "SELECTOR", multiple: true
158
+
159
+ c.desc "Search across all cluster nodes (default for tasks)"
160
+ c.switch [:"all-nodes"], negatable: false
161
+
162
+ c.action do |global_options, options, args|
163
+ resource_type = args[0]
164
+ resource_args = args[1..] || []
165
+ exit_code = execute(resource_type, resource_args, options, global_options)
166
+ exit exit_code if exit_code != 0
167
+ end
168
+ end
169
+ end
170
+
171
+ # Executes the get command.
172
+ #
173
+ # @param resource_type [String, nil] type of resource to list (e.g., "nodes", "vms")
174
+ # @param args [String, Array, nil] positional arguments (VMIDs for snapshots, or name filter)
175
+ # @param options [Hash] command-specific options
176
+ # - :watch [Boolean] enable continuous monitoring
177
+ # - :"watch-interval" [Integer] refresh interval in seconds
178
+ # - :node [String] filter by node name
179
+ # @param global_options [Hash] global CLI options
180
+ # - :output [String] output format (table, json, yaml, wide)
181
+ # - :color [Boolean, nil] explicit color setting
182
+ # @return [Integer] exit code (0 for success, 2 for unknown resource)
183
+ def self.execute(resource_type, args, options, global_options)
184
+ new(resource_type, args, options, global_options).execute
185
+ end
186
+
187
+ # Creates a new Get command instance.
188
+ #
189
+ # @param resource_type [String, nil] type of resource to list
190
+ # @param args [String, Array, nil] positional arguments
191
+ # @param options [Hash] command options
192
+ # @param global_options [Hash] global CLI options
193
+ # @param registry [Class] registry class for dependency injection (default: ResourceRegistry)
194
+ def initialize(resource_type, args, options, global_options,
195
+ registry: ResourceRegistry)
196
+ @resource_type = resource_type
197
+ @args = normalize_args(args)
198
+ @options = options
199
+ @global_options = global_options
200
+ @registry = registry
201
+ end
202
+
203
+ # Executes the get operation.
204
+ #
205
+ # @return [Integer] exit code
206
+ def execute
207
+ return missing_resource_type_error if @resource_type.nil?
208
+
209
+ handler = @registry.for(@resource_type)
210
+ return unknown_resource_error unless handler
211
+
212
+ if @options[:watch]
213
+ run_watch_mode(handler)
214
+ else
215
+ run_once(handler)
216
+ end
217
+
218
+ ExitCodes::SUCCESS
219
+ rescue Timeout::Error => e
220
+ output_connection_error(e.message)
221
+ ExitCodes::CONNECTION_ERROR
222
+ rescue Errno::ECONNREFUSED => e
223
+ output_connection_error(e.message)
224
+ ExitCodes::CONNECTION_ERROR
225
+ rescue SocketError => e
226
+ output_connection_error(e.message)
227
+ ExitCodes::CONNECTION_ERROR
228
+ rescue ArgumentError => e
229
+ output_usage_error(e.message)
230
+ ExitCodes::USAGE_ERROR
231
+ end
232
+
233
+ private
234
+
235
+ attr_reader :resource_type, :args, :options, :global_options
236
+
237
+ # Normalizes args to an array.
238
+ #
239
+ # @param args [String, Array, nil] input args
240
+ # @return [Array<String>] normalized args array
241
+ def normalize_args(args)
242
+ case args
243
+ when nil then []
244
+ when Array then args
245
+ else [args.to_s]
246
+ end
247
+ end
248
+
249
+ # Outputs error for missing resource type argument.
250
+ #
251
+ # @return [Integer] USAGE_ERROR exit code
252
+ def missing_resource_type_error
253
+ $stderr.puts "Error: resource type is required"
254
+ $stderr.puts "Usage: pvectl get RESOURCE_TYPE [NAME] [options]"
255
+ ExitCodes::USAGE_ERROR
256
+ end
257
+
258
+ # Outputs error for unknown resource type.
259
+ #
260
+ # @return [Integer] USAGE_ERROR exit code
261
+ def unknown_resource_error
262
+ $stderr.puts "Unknown resource type: #{@resource_type}"
263
+ ExitCodes::USAGE_ERROR
264
+ end
265
+
266
+ # Outputs connection error message.
267
+ #
268
+ # @param message [String] the error message
269
+ # @return [void]
270
+ def output_connection_error(message)
271
+ $stderr.puts "Error: #{message}"
272
+ end
273
+
274
+ # Outputs usage error message.
275
+ #
276
+ # @param message [String] the error message
277
+ # @return [void]
278
+ def output_usage_error(message)
279
+ $stderr.puts "Error: #{message}"
280
+ end
281
+
282
+ # Executes a single fetch and display operation.
283
+ #
284
+ # @param handler [ResourceHandler] the resource handler
285
+ # @return [void]
286
+ def run_once(handler)
287
+ service = build_service(handler)
288
+ selector = build_selector(handler)
289
+ output = service.list(
290
+ node: options[:node],
291
+ name: nil,
292
+ args: args,
293
+ storage: options[:storage],
294
+ vmid: options[:vmid],
295
+ limit: options[:limit],
296
+ since: options[:since],
297
+ until_time: options[:until],
298
+ type_filter: options[:type],
299
+ status_filter: options[:status],
300
+ all_nodes: options[:"all-nodes"] || false,
301
+ selector: selector
302
+ )
303
+ puts output
304
+ end
305
+
306
+ # Executes watch mode with continuous refresh.
307
+ #
308
+ # @param handler [ResourceHandler] the resource handler
309
+ # @return [void]
310
+ def run_watch_mode(handler)
311
+ interval = options[:"watch-interval"] || WatchLoop::DEFAULT_INTERVAL
312
+ watch_loop = WatchLoop.new(interval: interval)
313
+ watch_loop.run { run_once(handler) }
314
+ end
315
+
316
+ # Builds a selector from -l and --status flags for client-side filtering.
317
+ #
318
+ # For handlers that declare a selector_class (VMs, Containers), --status
319
+ # is translated into a selector condition. For handlers without selector
320
+ # support (e.g., tasks), --status is passed through as status_filter.
321
+ #
322
+ # @param handler [ResourceHandler] the resource handler
323
+ # @return [Selectors::Base, nil] parsed selector or nil if not applicable
324
+ def build_selector(handler)
325
+ selector_class = handler.selector_class
326
+ return nil unless selector_class
327
+
328
+ selector_strings = Array(options[:selector])
329
+ selector_strings << "status=#{options[:status]}" if options[:status]
330
+
331
+ return nil if selector_strings.empty?
332
+
333
+ selector_class.parse_all(selector_strings)
334
+ end
335
+
336
+ # Builds the service for the given handler.
337
+ #
338
+ # @param handler [ResourceHandler] the resource handler
339
+ # @return [Services::Get::ResourceService] the service instance
340
+ def build_service(handler)
341
+ Services::Get::ResourceService.new(
342
+ handler: handler,
343
+ format: global_options[:output] || "table",
344
+ color_enabled: determine_color_enabled
345
+ )
346
+ end
347
+
348
+ # Determines if color output should be enabled.
349
+ #
350
+ # @return [Boolean] true if color should be enabled
351
+ def determine_color_enabled
352
+ explicit = global_options[:color]
353
+ return explicit unless explicit.nil?
354
+
355
+ $stdout.tty?
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end