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,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Shared functionality for resource edit commands.
6
+ #
7
+ # Template method pattern: provides common edit workflow
8
+ # (config loading, editor session, diff display, dry-run)
9
+ # while specialization classes define resource-specific hooks.
10
+ #
11
+ # @abstract Include this module and implement template methods.
12
+ #
13
+ # @example Specialization
14
+ # class EditVm
15
+ # include EditResourceCommand
16
+ # private
17
+ # def resource_label = "VM"
18
+ # def resource_id_label = "VMID"
19
+ # # ...
20
+ # end
21
+ #
22
+ module EditResourceCommand
23
+ # Class methods added when the module is included.
24
+ module ClassMethods
25
+ # Executes the edit command.
26
+ #
27
+ # @param args [Array<String>] command arguments
28
+ # @param options [Hash] command options
29
+ # @param global_options [Hash] global CLI options
30
+ # @return [Integer] exit code
31
+ def execute(args, options, global_options)
32
+ new(args, options, global_options).execute
33
+ end
34
+ end
35
+
36
+ # Hook called when module is included.
37
+ #
38
+ # @param base [Class] the class including this module
39
+ def self.included(base)
40
+ base.extend(ClassMethods)
41
+ end
42
+
43
+ # Initializes an edit command.
44
+ #
45
+ # @param args [Array<String>] command arguments
46
+ # @param options [Hash] command options
47
+ # @param global_options [Hash] global CLI options
48
+ def initialize(args, options, global_options)
49
+ @args = args
50
+ @options = options
51
+ @global_options = global_options
52
+ end
53
+
54
+ # Executes the edit command.
55
+ #
56
+ # Loads configuration, builds the edit service, and runs it.
57
+ # Handles cancelled edits, successful updates with diff display,
58
+ # dry-run mode, and errors.
59
+ #
60
+ # @return [Integer] exit code
61
+ def execute
62
+ resource_id = @args.first
63
+ return usage_error("#{resource_id_label} is required") unless resource_id
64
+
65
+ load_config
66
+ connection = Pvectl::Connection.new(@config)
67
+ service = build_edit_service(connection)
68
+ result = service.execute(**execute_params(resource_id))
69
+
70
+ if result.nil?
71
+ $stdout.puts "Edit cancelled, no changes made."
72
+ return ExitCodes::SUCCESS
73
+ end
74
+
75
+ if result.successful?
76
+ display_diff(result)
77
+ if @options[:"dry-run"]
78
+ $stdout.puts "(dry-run mode — no changes applied)"
79
+ else
80
+ $stdout.puts "#{resource_label} #{resource_id} updated successfully."
81
+ end
82
+ ExitCodes::SUCCESS
83
+ else
84
+ $stderr.puts "Error: #{result.error}"
85
+ ExitCodes::GENERAL_ERROR
86
+ end
87
+ rescue Pvectl::Config::ConfigNotFoundError,
88
+ Pvectl::Config::InvalidConfigError,
89
+ Pvectl::Config::ContextNotFoundError,
90
+ Pvectl::Config::ClusterNotFoundError,
91
+ Pvectl::Config::UserNotFoundError
92
+ raise
93
+ rescue StandardError => e
94
+ $stderr.puts "Error: #{e.message}"
95
+ ExitCodes::GENERAL_ERROR
96
+ end
97
+
98
+ private
99
+
100
+ # @return [String] human label for resource ("VM" or "container")
101
+ def resource_label
102
+ raise NotImplementedError, "#{self.class} must implement #resource_label"
103
+ end
104
+
105
+ # @return [String] human label for resource ID ("VMID" or "CTID")
106
+ def resource_id_label
107
+ raise NotImplementedError, "#{self.class} must implement #resource_id_label"
108
+ end
109
+
110
+ # Builds execution parameters from resource ID.
111
+ #
112
+ # @param resource_id [String] resource identifier (converted by specializations)
113
+ # @return [Hash] parameters for the edit service (e.g. { vmid: 100 })
114
+ def execute_params(resource_id)
115
+ raise NotImplementedError, "#{self.class} must implement #execute_params"
116
+ end
117
+
118
+ # Builds the edit service for the given connection.
119
+ #
120
+ # @param connection [Connection] API connection
121
+ # @return [Object] edit service instance
122
+ def build_edit_service(connection)
123
+ raise NotImplementedError, "#{self.class} must implement #build_edit_service"
124
+ end
125
+
126
+ # Displays a formatted diff from the operation result.
127
+ #
128
+ # @param result [Models::OperationResult] operation result with diff in resource
129
+ # @return [void]
130
+ def display_diff(result)
131
+ diff = result.resource&.dig(:diff)
132
+ return unless diff
133
+ return if diff[:changed].empty? && diff[:added].empty? && diff[:removed].empty?
134
+
135
+ $stdout.puts "\nChanges:"
136
+ $stdout.puts Pvectl::ConfigSerializer.format_diff(diff)
137
+ $stdout.puts ""
138
+ end
139
+
140
+ # Loads configuration from file or environment.
141
+ #
142
+ # @return [void]
143
+ def load_config
144
+ service = Pvectl::Config::Service.new
145
+ service.load(config: @global_options[:config])
146
+ @config = service.current_config
147
+ end
148
+
149
+ # Builds service options from command options.
150
+ #
151
+ # @return [Hash] service options
152
+ def service_options
153
+ opts = {}
154
+ opts[:dry_run] = true if @options[:"dry-run"]
155
+ opts
156
+ end
157
+
158
+ # Builds an editor session from the --editor option.
159
+ #
160
+ # @return [EditorSession, nil] editor session or nil if no --editor flag
161
+ def build_editor_session
162
+ editor_cmd = @options[:editor]
163
+ return nil unless editor_cmd
164
+
165
+ Pvectl::EditorSession.new(
166
+ editor: ->(path) { system(editor_cmd, path) }
167
+ )
168
+ end
169
+
170
+ # Outputs usage error and returns exit code.
171
+ #
172
+ # @param message [String] error message
173
+ # @return [Integer] exit code
174
+ def usage_error(message)
175
+ $stderr.puts "Error: #{message}"
176
+ ExitCodes::USAGE_ERROR
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl edit vm` command.
6
+ #
7
+ # Includes EditResourceCommand for shared workflow and overrides
8
+ # template methods with VM-specific behavior.
9
+ #
10
+ # @example Basic usage
11
+ # pvectl edit vm 100
12
+ #
13
+ # @example With custom editor
14
+ # pvectl edit vm 100 --editor nano
15
+ #
16
+ # @example Dry-run mode
17
+ # pvectl edit vm 100 --dry-run
18
+ #
19
+ class EditVm
20
+ include EditResourceCommand
21
+
22
+ # Registers the edit command with the CLI.
23
+ #
24
+ # @param cli [GLI::App] the CLI application object
25
+ # @return [void]
26
+ def self.register(cli)
27
+ cli.desc "Edit a resource configuration"
28
+ cli.long_desc <<~HELP
29
+ Open a VM, container, node, or volume configuration in your text
30
+ editor. The configuration is presented as YAML for easy editing.
31
+ Changes are applied via the Proxmox API when you save and close
32
+ the editor.
33
+
34
+ EXAMPLES
35
+ Edit a VM configuration:
36
+ $ pvectl edit vm 100
37
+
38
+ Edit a container with a specific editor:
39
+ $ pvectl edit container 200 --editor nano
40
+
41
+ Edit a node configuration:
42
+ $ pvectl edit node pve1
43
+
44
+ Edit node DNS resolver settings:
45
+ $ pvectl edit dns pve1
46
+
47
+ Edit volume properties:
48
+ $ pvectl edit volume vm 100 scsi0
49
+
50
+ Edit /etc/hosts on a node:
51
+ $ pvectl edit hosts pve1
52
+
53
+ Preview changes without applying:
54
+ $ pvectl edit vm 100 --dry-run
55
+
56
+ NOTES
57
+ Uses $EDITOR environment variable by default, falling back to vi.
58
+ Override with --editor flag.
59
+
60
+ In --dry-run mode, shows the diff between current and edited
61
+ configuration without applying changes to Proxmox.
62
+
63
+ Supported resource types: vm, container (ct), node, volume, dns, hosts.
64
+
65
+ For DNS, the node identifier is given as a positional argument
66
+ (pvectl edit dns NODE). DNS settings include search domain and
67
+ up to three nameservers (dns1, dns2, dns3).
68
+
69
+ For volumes, syntax is: pvectl edit volume <vm|container> <id> <disk>
70
+
71
+ For hosts, the node identifier is given as a positional argument
72
+ (pvectl edit hosts NODE). The /etc/hosts file is edited as raw
73
+ text — Proxmox requires the pvelocalhost entry to remain present
74
+ and will reject updates removing it.
75
+
76
+ Not all configuration keys can be changed while a VM is running.
77
+ Proxmox will reject invalid changes with an error message.
78
+
79
+ SEE ALSO
80
+ pvectl help describe View current configuration (read-only)
81
+ pvectl help create Create a new resource
82
+ pvectl help set Set individual resource properties
83
+ HELP
84
+ cli.arg_name "RESOURCE_TYPE RESOURCE_ID"
85
+ cli.command :edit do |c|
86
+ c.desc "Override editor command (default: $EDITOR or vi)"
87
+ c.flag [:editor], arg_name: "CMD"
88
+
89
+ c.desc "Show diff without applying changes"
90
+ c.switch [:"dry-run"], negatable: false
91
+
92
+ c.action do |global_options, options, args|
93
+ resource_type = args.shift
94
+ resource_ids = args
95
+
96
+ exit_code = case resource_type
97
+ when "vm"
98
+ Commands::EditVm.execute(resource_ids, options, global_options)
99
+ when "container", "ct"
100
+ Commands::EditContainer.execute(resource_ids, options, global_options)
101
+ when "node"
102
+ Commands::EditNode.execute(resource_ids, options, global_options)
103
+ when "volume"
104
+ Commands::EditVolume.execute(resource_ids, options, global_options)
105
+ when "dns"
106
+ Commands::EditDns.execute(resource_ids, options, global_options)
107
+ when "hosts"
108
+ Commands::EditHosts.execute(resource_ids, options, global_options)
109
+ else
110
+ $stderr.puts "Error: Unknown resource type: #{resource_type}"
111
+ $stderr.puts "Valid types: vm, container, node, volume, dns, hosts"
112
+ ExitCodes::USAGE_ERROR
113
+ end
114
+
115
+ exit exit_code if exit_code != 0
116
+ end
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ # @return [String] human label for VM resources
123
+ def resource_label
124
+ "VM"
125
+ end
126
+
127
+ # @return [String] human label for VM IDs
128
+ def resource_id_label
129
+ "VMID"
130
+ end
131
+
132
+ # Builds execution parameters from a VM ID.
133
+ #
134
+ # @param resource_id [String] VMID (converted to Integer)
135
+ # @return [Hash] parameters for the edit service
136
+ def execute_params(resource_id)
137
+ { vmid: resource_id.to_i }
138
+ end
139
+
140
+ # Builds the VM edit service.
141
+ #
142
+ # @param connection [Connection] API connection
143
+ # @return [Services::EditVm] VM edit service
144
+ def build_edit_service(connection)
145
+ vm_repo = Pvectl::Repositories::Vm.new(connection)
146
+ Pvectl::Services::EditVm.new(
147
+ vm_repository: vm_repo,
148
+ editor_session: build_editor_session,
149
+ options: service_options
150
+ )
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl edit volume` command.
6
+ #
7
+ # Does NOT use EditResourceCommand template — has custom flow due to
8
+ # different argument structure (resource_type + id + disk).
9
+ #
10
+ # @example Edit volume properties
11
+ # pvectl edit volume vm 100 scsi0
12
+ #
13
+ # @example With custom editor
14
+ # pvectl edit volume vm 100 scsi0 --editor nano
15
+ #
16
+ # @example Dry-run mode
17
+ # pvectl edit volume vm 100 scsi0 --dry-run
18
+ #
19
+ class EditVolume
20
+ # Class methods for command execution.
21
+ module ClassMethods
22
+ # Executes the edit volume command.
23
+ #
24
+ # @param args [Array<String>] command arguments
25
+ # @param options [Hash] command options
26
+ # @param global_options [Hash] global CLI options
27
+ # @return [Integer] exit code
28
+ def execute(args, options, global_options)
29
+ new(args, options, global_options).execute
30
+ end
31
+ end
32
+
33
+ extend ClassMethods
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 edit volume command.
45
+ #
46
+ # @return [Integer] exit code
47
+ def execute
48
+ resource_type = @args[0]
49
+ resource_id = @args[1]
50
+ disk = @args[2]
51
+
52
+ return usage_error("Resource type required (vm, container)") unless resource_type
53
+ return usage_error("Resource ID is required") unless resource_id
54
+ return usage_error("Volume name is required (e.g., scsi0, rootfs)") unless disk
55
+
56
+ load_config
57
+ connection = Pvectl::Connection.new(@config)
58
+
59
+ node = resolve_node(resource_id.to_i, connection, resource_type)
60
+ return ExitCodes::NOT_FOUND unless node
61
+
62
+ repo = build_repository(connection, resource_type)
63
+ service = Pvectl::Services::EditVolume.new(
64
+ repository: repo,
65
+ resource_type: resource_type_symbol(resource_type),
66
+ editor_session: build_editor_session,
67
+ options: service_options
68
+ )
69
+ result = service.execute(id: resource_id.to_i, disk: disk, node: node)
70
+
71
+ if result.nil?
72
+ $stdout.puts "Edit cancelled, no changes made."
73
+ return ExitCodes::SUCCESS
74
+ end
75
+
76
+ if result.successful?
77
+ if @options[:"dry-run"]
78
+ $stdout.puts "(dry-run mode — no changes applied)"
79
+ else
80
+ $stdout.puts "Volume #{disk} on #{resource_type} #{resource_id} updated successfully."
81
+ end
82
+ ExitCodes::SUCCESS
83
+ else
84
+ $stderr.puts "Error: #{result.error}"
85
+ ExitCodes::GENERAL_ERROR
86
+ end
87
+ rescue Pvectl::Config::ConfigNotFoundError,
88
+ Pvectl::Config::InvalidConfigError,
89
+ Pvectl::Config::ContextNotFoundError,
90
+ Pvectl::Config::ClusterNotFoundError,
91
+ Pvectl::Config::UserNotFoundError
92
+ raise
93
+ rescue StandardError => e
94
+ $stderr.puts "Error: #{e.message}"
95
+ ExitCodes::GENERAL_ERROR
96
+ end
97
+
98
+ private
99
+
100
+ # Resolves node for a resource.
101
+ #
102
+ # @param resource_id [Integer] resource ID
103
+ # @param connection [Connection] API connection
104
+ # @param resource_type [String] resource type
105
+ # @return [String, nil] node name or nil
106
+ def resolve_node(resource_id, connection, resource_type)
107
+ return @options[:node] if @options[:node]
108
+
109
+ resolver = Pvectl::Utils::ResourceResolver.new(connection)
110
+ resolved = resolver.resolve(resource_id)
111
+
112
+ unless resolved
113
+ label = resource_type == "vm" ? "VM" : "container"
114
+ $stderr.puts "Error: #{label} #{resource_id} not found"
115
+ return nil
116
+ end
117
+
118
+ resolved[:node]
119
+ end
120
+
121
+ # Builds repository for resource type.
122
+ #
123
+ # @param connection [Connection] API connection
124
+ # @param resource_type [String] resource type
125
+ # @return [Repositories::Vm, Repositories::Container] repository
126
+ def build_repository(connection, resource_type)
127
+ case resource_type
128
+ when "vm"
129
+ Pvectl::Repositories::Vm.new(connection)
130
+ when "container", "ct"
131
+ Pvectl::Repositories::Container.new(connection)
132
+ else
133
+ raise ArgumentError, "Unknown resource type: #{resource_type}"
134
+ end
135
+ end
136
+
137
+ # Converts string resource type to symbol.
138
+ #
139
+ # @param resource_type [String] resource type
140
+ # @return [Symbol] :vm or :container
141
+ def resource_type_symbol(resource_type)
142
+ case resource_type
143
+ when "vm" then :vm
144
+ when "container", "ct" then :container
145
+ else :vm
146
+ end
147
+ end
148
+
149
+ # Builds an editor session from the --editor option.
150
+ #
151
+ # @return [EditorSession, nil] editor session or nil
152
+ def build_editor_session
153
+ editor_cmd = @options[:editor]
154
+ return nil unless editor_cmd
155
+
156
+ Pvectl::EditorSession.new(
157
+ editor: ->(path) { system(editor_cmd, path) }
158
+ )
159
+ end
160
+
161
+ # Builds service options from command options.
162
+ #
163
+ # @return [Hash] service options
164
+ def service_options
165
+ opts = {}
166
+ opts[:dry_run] = true if @options[:"dry-run"]
167
+ opts
168
+ end
169
+
170
+ # Loads configuration.
171
+ #
172
+ # @return [void]
173
+ def load_config
174
+ service = Pvectl::Config::Service.new
175
+ service.load(config: @global_options[:config])
176
+ @config = service.current_config
177
+ end
178
+
179
+ # Outputs usage error.
180
+ #
181
+ # @param message [String] error message
182
+ # @return [Integer] exit code
183
+ def usage_error(message)
184
+ $stderr.puts "Error: #{message}"
185
+ ExitCodes::USAGE_ERROR
186
+ end
187
+ end
188
+ end
189
+ end