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,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Top-level `pvectl apt` command for managing APT packages on Proxmox nodes.
6
+ #
7
+ # Exposes four sub-commands:
8
+ #
9
+ # * `apt list` list pending package updates
10
+ # * `apt update` refresh the package index (apt-get update)
11
+ # * `apt changelog` show changelog for a package
12
+ # * `apt versions` list installed Proxmox-relevant package versions
13
+ #
14
+ # Each sub-command requires `--node NODE` (falls back to default-node from
15
+ # the active context configuration when not provided).
16
+ #
17
+ # @example Register with the CLI
18
+ # Commands::Apt.register(cli)
19
+ #
20
+ class Apt
21
+ # Registers the apt command and all sub-commands with the CLI.
22
+ #
23
+ # @param cli [GLI::App] the CLI application object
24
+ # @return [void]
25
+ def self.register(cli)
26
+ cli.desc "Manage APT packages on Proxmox nodes"
27
+ cli.long_desc <<~HELP
28
+ DESCRIPTION
29
+ Manage APT (Advanced Package Tool) packages on Proxmox VE nodes.
30
+ Wraps the Proxmox /nodes/{node}/apt API to inspect pending updates,
31
+ refresh the package index, read package changelogs, and report
32
+ installed Proxmox-relevant package versions.
33
+
34
+ SUB-COMMANDS
35
+ apt list List pending package updates
36
+ apt update Refresh the package index (apt-get update)
37
+ apt changelog PACKAGE Show changelog for a package
38
+ apt versions Show installed Proxmox package versions
39
+
40
+ EXAMPLES
41
+ List pending updates on a node:
42
+ $ pvectl apt list --node pve1
43
+
44
+ Refresh the package index on the default node:
45
+ $ pvectl apt update
46
+
47
+ Refresh quietly without sending update notifications:
48
+ $ pvectl apt update --node pve1 --quiet
49
+
50
+ Show changelog for a specific package version:
51
+ $ pvectl apt changelog pve-manager --node pve1 --version 8.2.4-1
52
+
53
+ Inspect installed Proxmox versions (parity with pveversion -v):
54
+ $ pvectl apt versions --node pve1 -o wide
55
+
56
+ NOTES
57
+ --node defaults to the context's default-node when configured.
58
+
59
+ Proxmox does NOT expose `apt upgrade` over the API (security
60
+ restriction). To actually install updates you must SSH to the
61
+ node and run apt full-upgrade manually.
62
+
63
+ `apt update` is asynchronous; the response includes the Proxmox
64
+ task UPID, inspect with `pvectl get tasks` and
65
+ `pvectl logs task UPID`.
66
+
67
+ SEE ALSO
68
+ pvectl help get List resources (try `get nodes`)
69
+ pvectl help logs Inspect task output
70
+ HELP
71
+
72
+ cli.command :apt do |c|
73
+ # Shared --node flag declared on the parent so every subcommand inherits it.
74
+ c.desc "Node name (defaults to context default-node)"
75
+ c.flag [:node], arg_name: "NODE"
76
+
77
+ register_list(c)
78
+ register_update(c)
79
+ register_changelog(c)
80
+ register_versions(c)
81
+ end
82
+ end
83
+
84
+ # Registers the `apt list` subcommand.
85
+ #
86
+ # @param parent [GLI::Command] parent apt command
87
+ # @return [void]
88
+ def self.register_list(parent)
89
+ parent.desc "List pending APT updates on a node"
90
+ parent.long_desc <<~HELP
91
+ List packages that have an available APT update on the target node.
92
+
93
+ EXAMPLES
94
+ $ pvectl apt list --node pve1
95
+ $ pvectl apt list --node pve1 -o wide
96
+ $ pvectl apt list --node pve1 -o json
97
+
98
+ SEE ALSO
99
+ pvectl help apt Parent command
100
+ HELP
101
+ parent.command :list do |sub|
102
+ sub.action do |global_options, options, args|
103
+ exit_code = execute(:list, args, options, global_options)
104
+ exit exit_code if exit_code != 0
105
+ end
106
+ end
107
+ end
108
+
109
+ # Registers the `apt update` subcommand.
110
+ #
111
+ # @param parent [GLI::Command] parent apt command
112
+ # @return [void]
113
+ def self.register_update(parent)
114
+ parent.desc "Refresh the APT package index on a node (apt-get update)"
115
+ parent.long_desc <<~HELP
116
+ Refresh the APT package index on the target node. Equivalent to
117
+ running `apt-get update` directly on the node.
118
+
119
+ This does NOT install updates — Proxmox does not expose apt upgrade
120
+ over the API for security reasons.
121
+
122
+ EXAMPLES
123
+ $ pvectl apt update --node pve1
124
+ $ pvectl apt update --node pve1 --quiet
125
+ $ pvectl apt update --node pve1 --notify
126
+
127
+ SEE ALSO
128
+ pvectl help apt Parent command
129
+ HELP
130
+ parent.command :update do |sub|
131
+ sub.desc "Send a notification about new packages"
132
+ sub.switch [:notify], negatable: false
133
+
134
+ sub.desc "Suppress progress output"
135
+ sub.switch [:quiet], negatable: false
136
+
137
+ sub.action do |global_options, options, args|
138
+ exit_code = execute(:update, args, options, global_options)
139
+ exit exit_code if exit_code != 0
140
+ end
141
+ end
142
+ end
143
+
144
+ # Registers the `apt changelog` subcommand.
145
+ #
146
+ # @param parent [GLI::Command] parent apt command
147
+ # @return [void]
148
+ def self.register_changelog(parent)
149
+ parent.desc "Show the changelog for an APT package on a node"
150
+ parent.long_desc <<~HELP
151
+ Show the upstream changelog for a package on the target node. When
152
+ --version is omitted the latest available version's changelog is
153
+ returned.
154
+
155
+ EXAMPLES
156
+ $ pvectl apt changelog pve-manager --node pve1
157
+ $ pvectl apt changelog pve-manager --node pve1 --version 8.2.4-1
158
+
159
+ SEE ALSO
160
+ pvectl help apt Parent command
161
+ HELP
162
+ parent.arg_name "PACKAGE"
163
+ parent.command :changelog do |sub|
164
+ sub.desc "Package version (optional, defaults to latest)"
165
+ sub.flag [:version], arg_name: "VERSION"
166
+
167
+ sub.action do |global_options, options, args|
168
+ exit_code = execute(:changelog, args, options, global_options)
169
+ exit exit_code if exit_code != 0
170
+ end
171
+ end
172
+ end
173
+
174
+ # Registers the `apt versions` subcommand.
175
+ #
176
+ # @param parent [GLI::Command] parent apt command
177
+ # @return [void]
178
+ def self.register_versions(parent)
179
+ parent.desc "Show installed Proxmox package versions on a node"
180
+ parent.long_desc <<~HELP
181
+ Show installed versions of important Proxmox VE packages on the
182
+ target node — parity with `pveversion -v` on the node itself.
183
+
184
+ EXAMPLES
185
+ $ pvectl apt versions --node pve1
186
+ $ pvectl apt versions --node pve1 -o wide
187
+ $ pvectl apt versions --node pve1 -o yaml
188
+
189
+ SEE ALSO
190
+ pvectl help apt Parent command
191
+ HELP
192
+ parent.command :versions do |sub|
193
+ sub.action do |global_options, options, args|
194
+ exit_code = execute(:versions, args, options, global_options)
195
+ exit exit_code if exit_code != 0
196
+ end
197
+ end
198
+ end
199
+
200
+ # Executes the command.
201
+ #
202
+ # @param operation [Symbol] :list, :update, :changelog, :versions
203
+ # @param args [Array<String>] positional CLI arguments
204
+ # @param options [Hash] command options
205
+ # @param global_options [Hash] global CLI options
206
+ # @return [Integer] exit code
207
+ def self.execute(operation, args, options, global_options)
208
+ new(operation, args, options, global_options).execute
209
+ end
210
+
211
+ # Initializes an apt command instance.
212
+ #
213
+ # @param operation [Symbol] operation
214
+ # @param args [Array<String>] positional CLI arguments
215
+ # @param options [Hash] command options
216
+ # @param global_options [Hash] global CLI options
217
+ # @param output [IO] IO for output (default: $stdout)
218
+ def initialize(operation, args, options, global_options, output: $stdout)
219
+ @operation = operation
220
+ @args = Array(args).compact
221
+ @options = options
222
+ @global_options = global_options
223
+ @output = output
224
+ end
225
+
226
+ # Executes the command.
227
+ #
228
+ # @return [Integer] exit code
229
+ def execute
230
+ load_config
231
+ node = resolve_node
232
+ return config_error("node is required (provide --node or configure default-node)") unless node
233
+
234
+ case @operation
235
+ when :list then run_list(node)
236
+ when :update then run_update(node)
237
+ when :changelog then run_changelog(node)
238
+ when :versions then run_versions(node)
239
+ else
240
+ usage_error("Unknown apt operation: #{@operation}")
241
+ end
242
+ rescue Pvectl::Config::ConfigNotFoundError,
243
+ Pvectl::Config::InvalidConfigError,
244
+ Pvectl::Config::ContextNotFoundError,
245
+ Pvectl::Config::ClusterNotFoundError,
246
+ Pvectl::Config::UserNotFoundError => e
247
+ $stderr.puts "Error: #{e.message}"
248
+ ExitCodes::CONFIG_ERROR
249
+ rescue StandardError => e
250
+ $stderr.puts "Error: #{e.message}"
251
+ ExitCodes::GENERAL_ERROR
252
+ end
253
+
254
+ private
255
+
256
+ # Loads configuration.
257
+ #
258
+ # @return [void]
259
+ def load_config
260
+ service = Pvectl::Config::Service.new
261
+ service.load(config: @global_options[:config])
262
+ @config = service.current_config
263
+ end
264
+
265
+ # Resolves the node from --node option or default-node config.
266
+ #
267
+ # @return [String, nil] node name or nil if unresolvable
268
+ def resolve_node
269
+ @options[:node] || @config&.default_node
270
+ end
271
+
272
+ # Returns the apt repository (lazy).
273
+ #
274
+ # @return [Repositories::Apt] APT repository
275
+ def repository
276
+ @repository ||= Pvectl::Repositories::Apt.new(Pvectl::Connection.new(@config))
277
+ end
278
+
279
+ # Runs `apt list` — prints pending updates as a table.
280
+ #
281
+ # @param node [String] node name
282
+ # @return [Integer] exit code
283
+ def run_list(node)
284
+ packages = repository.pending(node)
285
+ render_packages(packages)
286
+ ExitCodes::SUCCESS
287
+ end
288
+
289
+ # Runs `apt update` — triggers index refresh and reports the UPID.
290
+ #
291
+ # @param node [String] node name
292
+ # @return [Integer] exit code
293
+ def run_update(node)
294
+ upid = repository.refresh(node, notify: @options[:notify] ? true : false,
295
+ quiet: @options[:quiet] ? true : false)
296
+ result = build_update_result(node, upid)
297
+ render_operation_result(result)
298
+ ExitCodes::SUCCESS
299
+ end
300
+
301
+ # Runs `apt changelog` — prints raw changelog text to stdout.
302
+ #
303
+ # @param node [String] node name
304
+ # @return [Integer] exit code
305
+ def run_changelog(node)
306
+ return usage_error("package name is required") if @args.empty?
307
+
308
+ package = @args.first
309
+ version = @options[:version]
310
+ text = repository.changelog(node, package, version: version)
311
+ if text.nil? || text.empty?
312
+ @output.puts "(no changelog available)"
313
+ else
314
+ @output.puts text
315
+ end
316
+ ExitCodes::SUCCESS
317
+ end
318
+
319
+ # Runs `apt versions` — lists important Proxmox package versions.
320
+ #
321
+ # @param node [String] node name
322
+ # @return [Integer] exit code
323
+ def run_versions(node)
324
+ packages = repository.versions(node)
325
+ render_packages(packages)
326
+ ExitCodes::SUCCESS
327
+ end
328
+
329
+ # Renders an array of AptPackage models using the configured formatter.
330
+ #
331
+ # @param packages [Array<Models::AptPackage>] packages
332
+ # @return [void]
333
+ def render_packages(packages)
334
+ Pvectl::Formatters::OutputHelper.print(
335
+ data: packages,
336
+ presenter: Pvectl::Presenters::AptPackage.new,
337
+ format: @global_options[:output] || "table",
338
+ color_flag: @global_options[:color]
339
+ )
340
+ end
341
+
342
+ # Renders a NodeOperationResult (used by `apt update`).
343
+ #
344
+ # @param result [Models::NodeOperationResult] operation result
345
+ # @return [void]
346
+ def render_operation_result(result)
347
+ Pvectl::Formatters::OutputHelper.print(
348
+ data: [result],
349
+ presenter: Pvectl::Presenters::NodeOperationResult.new,
350
+ format: @global_options[:output] || "table",
351
+ color_flag: @global_options[:color]
352
+ )
353
+ end
354
+
355
+ # Builds a NodeOperationResult representing an apt update kick-off.
356
+ #
357
+ # @param node [String] node name
358
+ # @param upid [String] task UPID returned by the API
359
+ # @return [Models::NodeOperationResult]
360
+ def build_update_result(node, upid)
361
+ Pvectl::Models::NodeOperationResult.new(
362
+ operation: :update,
363
+ node_model: Pvectl::Models::Node.new(name: node),
364
+ resource: { node: node },
365
+ task_upid: upid,
366
+ success: :pending
367
+ )
368
+ end
369
+
370
+ # Outputs a usage error.
371
+ #
372
+ # @param message [String] error message
373
+ # @return [Integer] USAGE_ERROR exit code
374
+ def usage_error(message)
375
+ $stderr.puts "Error: #{message}"
376
+ ExitCodes::USAGE_ERROR
377
+ end
378
+
379
+ # Outputs a config error.
380
+ #
381
+ # @param message [String] error message
382
+ # @return [Integer] CONFIG_ERROR exit code
383
+ def config_error(message)
384
+ $stderr.puts "Error: #{message}"
385
+ ExitCodes::CONFIG_ERROR
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl clone container` command.
6
+ #
7
+ # Clones a container by CTID, supporting full and linked clones,
8
+ # custom hostname, target node, storage, pool, and description.
9
+ # No batch operations - clones exactly one container at a time.
10
+ #
11
+ # @example Full clone with auto-generated CTID
12
+ # pvectl clone container 100
13
+ #
14
+ # @example Clone with custom hostname and target CTID
15
+ # pvectl clone container 100 --vmid 200 --name web-clone
16
+ #
17
+ # @example Linked clone to different node
18
+ # pvectl clone container 100 --linked --target pve2
19
+ #
20
+ class CloneContainer
21
+ include SharedConfigParsers
22
+
23
+ # Executes the clone container command.
24
+ #
25
+ # @param args [Array<String>] command arguments (CTID)
26
+ # @param options [Hash] command options
27
+ # @param global_options [Hash] global CLI options
28
+ # @return [Integer] exit code
29
+ def self.execute(args, options, global_options)
30
+ new(args, options, global_options).execute
31
+ end
32
+
33
+ # Initializes a clone container command.
34
+ #
35
+ # @param args [Array<String>] command arguments
36
+ # @param options [Hash] command options
37
+ # @param global_options [Hash] global CLI options
38
+ def initialize(args, options, global_options)
39
+ @args = args
40
+ @options = options
41
+ @global_options = global_options
42
+ end
43
+
44
+ # Executes the clone container command.
45
+ #
46
+ # Builds config params from shared flags, validates async+config
47
+ # compatibility, and delegates to the clone operation.
48
+ #
49
+ # @return [Integer] exit code
50
+ def execute
51
+ ctid = @args.first
52
+ return usage_error("Source CTID required") unless ctid
53
+
54
+ config_params = build_ct_config_params
55
+
56
+ if @options[:async] && !config_params.empty?
57
+ return usage_error("Config flags require sync mode (remove --async)")
58
+ end
59
+
60
+ perform_clone(ctid.to_i, config_params)
61
+ end
62
+
63
+ private
64
+
65
+ # Performs the clone operation.
66
+ #
67
+ # When config params are present, displays a summary and prompts
68
+ # for confirmation before proceeding. Passes config_params to the
69
+ # service for the two-step clone+configure flow.
70
+ #
71
+ # @param ctid [Integer] source container identifier
72
+ # @param config_params [Hash] container config parameters to apply after clone
73
+ # @return [Integer] exit code
74
+ def perform_clone(ctid, config_params)
75
+ unless config_params.empty?
76
+ return ExitCodes::SUCCESS if display_clone_summary(ctid, config_params) == :cancelled
77
+ end
78
+
79
+ load_config
80
+ connection = Pvectl::Connection.new(@config)
81
+
82
+ ct_repo = Pvectl::Repositories::Container.new(connection)
83
+ task_repo = Pvectl::Repositories::Task.new(connection)
84
+
85
+ service = Pvectl::Services::CloneContainer.new(
86
+ container_repository: ct_repo,
87
+ task_repository: task_repo,
88
+ options: service_options
89
+ )
90
+
91
+ result = service.execute(
92
+ ctid: ctid,
93
+ new_ctid: @options[:newid]&.to_i,
94
+ hostname: @options[:name],
95
+ target_node: @options[:target],
96
+ storage: @options[:storage],
97
+ linked: @options[:linked],
98
+ pool: @options[:pool],
99
+ description: @options[:description],
100
+ config_params: config_params
101
+ )
102
+
103
+ print_progress(result) if !@options[:async] && result.container
104
+
105
+ output_result(result)
106
+ result.failed? ? ExitCodes::GENERAL_ERROR : ExitCodes::SUCCESS
107
+ rescue Pvectl::Config::ConfigNotFoundError,
108
+ Pvectl::Config::InvalidConfigError,
109
+ Pvectl::Config::ContextNotFoundError,
110
+ Pvectl::Config::ClusterNotFoundError,
111
+ Pvectl::Config::UserNotFoundError
112
+ raise
113
+ rescue StandardError => e
114
+ $stderr.puts "Error: #{e.message}"
115
+ ExitCodes::GENERAL_ERROR
116
+ end
117
+
118
+ # Prints progress message for sync mode.
119
+ #
120
+ # @param result [Models::ContainerOperationResult] clone result
121
+ # @return [void]
122
+ def print_progress(result)
123
+ source = result.container
124
+ new_hostname = result.resource&.dig(:hostname) || "clone"
125
+ new_id = result.resource&.dig(:new_ctid)
126
+ $stderr.puts "Cloning container #{source.vmid} (#{source.name || 'unnamed'}) to #{new_id} (#{new_hostname})..."
127
+ $stderr.puts ""
128
+ end
129
+
130
+ # Loads configuration from file or environment.
131
+ #
132
+ # @return [void]
133
+ def load_config
134
+ service = Pvectl::Config::Service.new
135
+ service.load(config: @global_options[:config])
136
+ @config = service.current_config
137
+ end
138
+
139
+ # Builds service options from command options.
140
+ #
141
+ # @return [Hash] service options
142
+ def service_options
143
+ opts = {}
144
+ opts[:timeout] = @options[:timeout] if @options[:timeout]
145
+ opts[:async] = true if @options[:async]
146
+ opts[:start] = true if @options[:start]
147
+ opts
148
+ end
149
+
150
+ # Outputs operation result using the configured formatter.
151
+ #
152
+ # @param result [Models::ContainerOperationResult] operation result
153
+ # @return [void]
154
+ def output_result(result)
155
+ presenter = Pvectl::Presenters::ContainerOperationResult.new
156
+ format = @global_options[:output] || "table"
157
+ color_flag = @global_options[:color]
158
+
159
+ formatter = Pvectl::Formatters::Registry.for(format)
160
+ output = formatter.format([result], presenter, color: color_flag)
161
+ puts output
162
+ end
163
+
164
+ # Displays clone summary with config changes and prompts for confirmation.
165
+ #
166
+ # Only called when config params are present. Shows source/target info
167
+ # and the config changes that will be applied after cloning.
168
+ #
169
+ # @param ctid [Integer] source container identifier
170
+ # @param config_params [Hash] config parameters to display
171
+ # @return [Symbol, nil] +:cancelled+ if user declines, +nil+ otherwise
172
+ def display_clone_summary(ctid, config_params)
173
+ $stdout.puts ""
174
+ $stdout.puts " Clone Container - Summary"
175
+ $stdout.puts " #{'─' * 40}"
176
+ $stdout.puts " Source: #{ctid}"
177
+ $stdout.puts " New ID: #{@options[:newid] || '(auto)'}"
178
+ $stdout.puts " Hostname: #{@options[:name] || '(auto)'}"
179
+ target_display = @options[:target] ? "→ #{@options[:target]}" : "(same)"
180
+ $stdout.puts " Node: #{target_display}"
181
+ $stdout.puts " Storage: #{@options[:storage]}" if @options[:storage]
182
+ display_config_changes(config_params)
183
+ $stdout.puts " #{'─' * 40}"
184
+ $stdout.puts ""
185
+
186
+ return nil if @options[:yes]
187
+
188
+ $stdout.print "Clone and configure this container? [y/N] "
189
+ $stdout.flush
190
+ answer = $stdin.gets&.strip&.downcase
191
+ answer == "y" ? nil : :cancelled
192
+ end
193
+
194
+ # Displays the config changes section of the clone summary.
195
+ #
196
+ # @param params [Hash] config parameters
197
+ # @return [void]
198
+ def display_config_changes(params)
199
+ $stdout.puts " ── Config changes #{'─' * 23}"
200
+ $stdout.puts " CPU: #{params[:cores]} cores" if params[:cores]
201
+ $stdout.puts " Memory: #{params[:memory]} MB" if params[:memory]
202
+ $stdout.puts " Swap: #{params[:swap]} MB" if params[:swap]
203
+ if params[:rootfs]
204
+ $stdout.puts " RootFS: #{params[:rootfs][:storage]}, #{params[:rootfs][:size]}"
205
+ end
206
+ if params[:mountpoints]
207
+ params[:mountpoints].each_with_index do |mp, i|
208
+ $stdout.puts " MP#{i}: #{mp[:mp]}, #{mp[:storage]}"
209
+ end
210
+ end
211
+ if params[:nets]
212
+ params[:nets].each_with_index do |n, i|
213
+ $stdout.puts " Net#{i}: #{n[:bridge]}"
214
+ end
215
+ end
216
+ $stdout.puts " Privileged: yes" if params[:privileged]
217
+ $stdout.puts " Tags: #{params[:tags]}" if params[:tags]
218
+ end
219
+
220
+ # Outputs usage error and returns exit code.
221
+ #
222
+ # @param message [String] error message
223
+ # @return [Integer] exit code
224
+ def usage_error(message)
225
+ $stderr.puts "Error: #{message}"
226
+ ExitCodes::USAGE_ERROR
227
+ end
228
+ end
229
+ end
230
+ end