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,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Shared functionality for resource set commands.
6
+ #
7
+ # Template method pattern: provides common set workflow
8
+ # (config loading, key-value parsing, 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 SetVm
15
+ # include SetResourceCommand
16
+ # private
17
+ # def resource_label = "VM"
18
+ # def resource_id_label = "VMID"
19
+ # # ...
20
+ # end
21
+ #
22
+ module SetResourceCommand
23
+ # Class methods added when the module is included.
24
+ module ClassMethods
25
+ # Executes the set 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 a set 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 set command.
55
+ #
56
+ # @return [Integer] exit code
57
+ def execute
58
+ resource_id = @args.first
59
+ return usage_error("#{resource_id_label} is required") unless resource_id
60
+
61
+ key_values = parse_key_values(@args[1..])
62
+ return usage_error("At least one key=value pair is required") if key_values.empty?
63
+
64
+ load_config
65
+ connection = Pvectl::Connection.new(@config)
66
+ service = build_set_service(connection)
67
+ result = service.execute(**execute_params(resource_id, key_values))
68
+
69
+ if result.nil?
70
+ $stdout.puts "No changes detected."
71
+ return ExitCodes::SUCCESS
72
+ end
73
+
74
+ if result.successful?
75
+ display_diff(result)
76
+ if @options[:"dry-run"]
77
+ $stdout.puts "(dry-run mode — no changes applied)"
78
+ else
79
+ $stdout.puts "#{resource_label} #{resource_id} updated successfully."
80
+ end
81
+ ExitCodes::SUCCESS
82
+ else
83
+ $stderr.puts "Error: #{result.error}"
84
+ ExitCodes::GENERAL_ERROR
85
+ end
86
+ rescue Pvectl::Config::ConfigNotFoundError,
87
+ Pvectl::Config::InvalidConfigError,
88
+ Pvectl::Config::ContextNotFoundError,
89
+ Pvectl::Config::ClusterNotFoundError,
90
+ Pvectl::Config::UserNotFoundError
91
+ raise
92
+ rescue StandardError => e
93
+ $stderr.puts "Error: #{e.message}"
94
+ ExitCodes::GENERAL_ERROR
95
+ end
96
+
97
+ private
98
+
99
+ # @return [String] human label for resource
100
+ def resource_label
101
+ raise NotImplementedError, "#{self.class} must implement #resource_label"
102
+ end
103
+
104
+ # @return [String] human label for resource ID
105
+ def resource_id_label
106
+ raise NotImplementedError, "#{self.class} must implement #resource_id_label"
107
+ end
108
+
109
+ # Builds execution parameters.
110
+ #
111
+ # @param resource_id [String] resource identifier
112
+ # @param key_values [Hash] parsed key-value pairs
113
+ # @return [Hash] parameters for the service
114
+ def execute_params(resource_id, key_values)
115
+ raise NotImplementedError, "#{self.class} must implement #execute_params"
116
+ end
117
+
118
+ # Builds the set service.
119
+ #
120
+ # @param connection [Connection] API connection
121
+ # @return [Object] set service instance
122
+ def build_set_service(connection)
123
+ raise NotImplementedError, "#{self.class} must implement #build_set_service"
124
+ end
125
+
126
+ # Parses key=value pairs from argument list.
127
+ #
128
+ # @param args [Array<String>] arguments like ["memory=4096", "cores=2"]
129
+ # @return [Hash] parsed pairs { "memory" => "4096", "cores" => "2" }
130
+ def parse_key_values(args)
131
+ pairs = {}
132
+ (args || []).each do |arg|
133
+ unless arg.include?("=")
134
+ $stderr.puts "Warning: Ignoring argument without '=': #{arg}"
135
+ next
136
+ end
137
+ key, value = arg.split("=", 2)
138
+ pairs[key] = value
139
+ end
140
+ pairs
141
+ end
142
+
143
+ # Displays a formatted diff from the operation result.
144
+ #
145
+ # @param result [Models::OperationResult] operation result
146
+ # @return [void]
147
+ def display_diff(result)
148
+ diff = result.resource&.dig(:diff)
149
+ return unless diff
150
+ return if diff[:changed].empty? && diff[:added].empty? && diff[:removed].empty?
151
+
152
+ $stdout.puts "\nChanges:"
153
+ $stdout.puts Pvectl::ConfigSerializer.format_diff(diff)
154
+ $stdout.puts ""
155
+ end
156
+
157
+ # Loads configuration.
158
+ #
159
+ # @return [void]
160
+ def load_config
161
+ service = Pvectl::Config::Service.new
162
+ service.load(config: @global_options[:config])
163
+ @config = service.current_config
164
+ end
165
+
166
+ # Outputs usage error.
167
+ #
168
+ # @param message [String] error message
169
+ # @return [Integer] exit code
170
+ def usage_error(message)
171
+ $stderr.puts "Error: #{message}"
172
+ ExitCodes::USAGE_ERROR
173
+ end
174
+
175
+ # Returns service options.
176
+ #
177
+ # @return [Hash] service options
178
+ def service_options
179
+ opts = {}
180
+ opts[:dry_run] = true if @options[:"dry-run"]
181
+ opts
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl set vm` command.
6
+ #
7
+ # Includes SetResourceCommand for shared workflow and overrides
8
+ # template methods with VM-specific behavior.
9
+ #
10
+ # Also registers the top-level `set` command with all resource types.
11
+ #
12
+ # @example Basic usage
13
+ # pvectl set vm 100 memory=4096 cores=2
14
+ #
15
+ # @example Dry-run mode
16
+ # pvectl set vm 100 memory=8192 --dry-run
17
+ #
18
+ class SetVm
19
+ include SetResourceCommand
20
+
21
+ # Registers the set command with the CLI.
22
+ #
23
+ # @param cli [GLI::App] the CLI application object
24
+ # @return [void]
25
+ def self.register(cli)
26
+ cli.desc "Set a resource property (non-interactive)"
27
+ cli.long_desc <<~HELP
28
+ Set configuration properties on a resource without opening an editor.
29
+ Accepts key=value pairs as arguments. This is the non-interactive
30
+ counterpart to the edit command.
31
+
32
+ EXAMPLES
33
+ Set VM memory and CPU:
34
+ $ pvectl set vm 100 memory=4096 cores=2
35
+
36
+ Set container hostname:
37
+ $ pvectl set container 200 hostname=web01
38
+
39
+ Resize a volume:
40
+ $ pvectl set volume vm 100 scsi0 size=+10G
41
+
42
+ Set volume cache mode:
43
+ $ pvectl set volume vm 100 scsi0 cache=writeback
44
+
45
+ Set node description:
46
+ $ pvectl set node pve1 description="Production node"
47
+
48
+ Change node timezone:
49
+ $ pvectl set node pve1 timezone=Europe/Warsaw
50
+
51
+ Preview changes without applying:
52
+ $ pvectl set vm 100 memory=8192 --dry-run
53
+
54
+ NOTES
55
+ Volume resize (size=) is irreversible. A confirmation prompt is shown
56
+ unless --yes is specified.
57
+
58
+ Keys are passed directly to the Proxmox API. Use Proxmox documentation
59
+ to find valid configuration keys for each resource type.
60
+
61
+ SEE ALSO
62
+ pvectl help edit Interactive configuration editor
63
+ pvectl help describe View current configuration
64
+ HELP
65
+ cli.arg_name "RESOURCE_TYPE RESOURCE_ID [SUB_ID] KEY=VALUE [KEY=VALUE ...]"
66
+ cli.command :set do |c|
67
+ c.desc "Skip confirmation prompt"
68
+ c.switch [:yes, :y], negatable: false
69
+
70
+ c.desc "Target node name"
71
+ c.flag [:node, :n], arg_name: "NODE"
72
+
73
+ c.desc "Show diff without applying changes"
74
+ c.switch [:"dry-run"], negatable: false
75
+
76
+ c.action do |global_options, options, args|
77
+ resource_type = args.shift
78
+
79
+ exit_code = case resource_type
80
+ when "vm"
81
+ Commands::SetVm.execute(args, options, global_options)
82
+ when "container", "ct"
83
+ Commands::SetContainer.execute(args, options, global_options)
84
+ when "volume", "vol"
85
+ Commands::SetVolume.execute(args, options, global_options)
86
+ when "node"
87
+ Commands::SetNode.execute(args, options, global_options)
88
+ when nil
89
+ $stderr.puts "Error: Resource type required (vm, container, volume, node)"
90
+ ExitCodes::USAGE_ERROR
91
+ else
92
+ $stderr.puts "Error: Unknown resource type: #{resource_type}"
93
+ $stderr.puts "Valid types: vm, container, volume, node"
94
+ ExitCodes::USAGE_ERROR
95
+ end
96
+
97
+ exit exit_code if exit_code != 0
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ # @return [String] human label for VM resources
105
+ def resource_label
106
+ "VM"
107
+ end
108
+
109
+ # @return [String] human label for VM IDs
110
+ def resource_id_label
111
+ "VMID"
112
+ end
113
+
114
+ # Builds execution parameters from a VM ID.
115
+ #
116
+ # @param resource_id [String] VMID
117
+ # @param key_values [Hash] parsed key-value pairs
118
+ # @return [Hash] parameters for the set service
119
+ def execute_params(resource_id, key_values)
120
+ { vmid: resource_id.to_i, params: key_values }
121
+ end
122
+
123
+ # Builds the VM set service.
124
+ #
125
+ # @param connection [Connection] API connection
126
+ # @return [Services::SetVm] VM set service
127
+ def build_set_service(connection)
128
+ vm_repo = Pvectl::Repositories::Vm.new(connection)
129
+ Pvectl::Services::SetVm.new(
130
+ vm_repository: vm_repo,
131
+ options: service_options
132
+ )
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Handler for the `pvectl set volume` command.
6
+ #
7
+ # Does NOT use SetResourceCommand template — has custom flow due to
8
+ # different argument structure (resource_type + id + disk + key=value).
9
+ #
10
+ # @example Resize volume
11
+ # pvectl set volume vm 100 scsi0 size=+10G
12
+ #
13
+ # @example Set cache mode
14
+ # pvectl set volume vm 100 scsi0 cache=writeback
15
+ #
16
+ # @example Mixed operations
17
+ # pvectl set volume vm 100 scsi0 size=+10G cache=writeback --yes
18
+ #
19
+ class SetVolume
20
+ # Class methods for command execution.
21
+ module ClassMethods
22
+ # Executes the set 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
+ # Initializes a set volume command.
36
+ #
37
+ # @param args [Array<String>] command arguments
38
+ # @param options [Hash] command options
39
+ # @param global_options [Hash] global CLI options
40
+ def initialize(args, options, global_options)
41
+ @args = args
42
+ @options = options
43
+ @global_options = global_options
44
+ end
45
+
46
+ # Executes the set volume command.
47
+ #
48
+ # @return [Integer] exit code
49
+ def execute
50
+ resource_type = @args[0]
51
+ resource_id = @args[1]
52
+ disk = @args[2]
53
+ key_value_args = @args[3..] || []
54
+
55
+ return usage_error("Resource type required (vm, container)") unless resource_type
56
+ return usage_error("Resource ID is required") unless resource_id
57
+ return usage_error("Volume name is required (e.g., scsi0, rootfs)") unless disk
58
+
59
+ key_values = parse_key_values(key_value_args)
60
+ return usage_error("At least one key=value pair is required") if key_values.empty?
61
+
62
+ load_config
63
+ connection = Pvectl::Connection.new(@config)
64
+
65
+ node = resolve_node(resource_id.to_i, connection, resource_type)
66
+ return ExitCodes::NOT_FOUND unless node
67
+
68
+ # Confirmation for resize operations
69
+ if key_values.key?("size") && !@options[:yes]
70
+ return ExitCodes::SUCCESS unless confirm_resize(resource_id, disk, key_values["size"])
71
+ end
72
+
73
+ repo = build_repository(connection, resource_type)
74
+ service = Pvectl::Services::SetVolume.new(
75
+ repository: repo,
76
+ resource_type: resource_type_symbol(resource_type)
77
+ )
78
+ result = service.execute(id: resource_id.to_i, disk: disk, params: key_values, node: node)
79
+
80
+ output_result(result)
81
+
82
+ result.successful? ? ExitCodes::SUCCESS : ExitCodes::GENERAL_ERROR
83
+ rescue Pvectl::Config::ConfigNotFoundError,
84
+ Pvectl::Config::InvalidConfigError,
85
+ Pvectl::Config::ContextNotFoundError,
86
+ Pvectl::Config::ClusterNotFoundError,
87
+ Pvectl::Config::UserNotFoundError
88
+ raise
89
+ rescue StandardError => e
90
+ $stderr.puts "Error: #{e.message}"
91
+ ExitCodes::GENERAL_ERROR
92
+ end
93
+
94
+ private
95
+
96
+ # Parses key=value pairs from argument list.
97
+ #
98
+ # @param args [Array<String>] arguments
99
+ # @return [Hash] parsed pairs
100
+ def parse_key_values(args)
101
+ pairs = {}
102
+ args.each do |arg|
103
+ unless arg.include?("=")
104
+ $stderr.puts "Warning: Ignoring argument without '=': #{arg}"
105
+ next
106
+ end
107
+ key, value = arg.split("=", 2)
108
+ pairs[key] = value
109
+ end
110
+ pairs
111
+ end
112
+
113
+ # Resolves node for a resource.
114
+ #
115
+ # @param resource_id [Integer] resource ID
116
+ # @param connection [Connection] API connection
117
+ # @param resource_type [String] resource type
118
+ # @return [String, nil] node name or nil
119
+ def resolve_node(resource_id, connection, resource_type)
120
+ return @options[:node] if @options[:node]
121
+
122
+ resolver = Pvectl::Utils::ResourceResolver.new(connection)
123
+ resolved = resolver.resolve(resource_id)
124
+
125
+ unless resolved
126
+ label = resource_type == "vm" ? "VM" : "container"
127
+ $stderr.puts "Error: #{label} #{resource_id} not found"
128
+ return nil
129
+ end
130
+
131
+ resolved[:node]
132
+ end
133
+
134
+ # Confirms resize operation.
135
+ #
136
+ # @param resource_id [String] resource ID
137
+ # @param disk [String] disk name
138
+ # @param size [String] size value
139
+ # @return [Boolean] true if should proceed
140
+ def confirm_resize(resource_id, disk, size)
141
+ $stdout.puts "Resize volume #{disk} on resource #{resource_id}:"
142
+ $stdout.puts " New size: #{size}"
143
+ $stdout.puts ""
144
+ $stdout.puts "This action is IRREVERSIBLE — volumes cannot be shrunk via API."
145
+ $stdout.print "Proceed? [y/N]: "
146
+
147
+ response = $stdin.gets&.strip&.downcase
148
+ %w[y yes].include?(response)
149
+ end
150
+
151
+ # Builds repository for resource type.
152
+ #
153
+ # @param connection [Connection] API connection
154
+ # @param resource_type [String] resource type
155
+ # @return [Repositories::Vm, Repositories::Container] repository
156
+ def build_repository(connection, resource_type)
157
+ case resource_type
158
+ when "vm"
159
+ Pvectl::Repositories::Vm.new(connection)
160
+ when "container", "ct"
161
+ Pvectl::Repositories::Container.new(connection)
162
+ else
163
+ raise ArgumentError, "Unknown resource type: #{resource_type}"
164
+ end
165
+ end
166
+
167
+ # Converts string resource type to symbol.
168
+ #
169
+ # @param resource_type [String] resource type
170
+ # @return [Symbol] :vm or :container
171
+ def resource_type_symbol(resource_type)
172
+ case resource_type
173
+ when "vm" then :vm
174
+ when "container", "ct" then :container
175
+ else :vm
176
+ end
177
+ end
178
+
179
+ # Outputs operation result.
180
+ #
181
+ # @param result [Models::VolumeOperationResult] operation result
182
+ # @return [void]
183
+ def output_result(result)
184
+ presenter = Pvectl::Presenters::VolumeOperationResult.new
185
+ format = @global_options[:output] || "table"
186
+ color_flag = @global_options[:color]
187
+
188
+ formatter = Pvectl::Formatters::Registry.for(format)
189
+ output = formatter.format([result], presenter, color: color_flag)
190
+ puts output
191
+ end
192
+
193
+ # Loads configuration.
194
+ #
195
+ # @return [void]
196
+ def load_config
197
+ service = Pvectl::Config::Service.new
198
+ service.load(config: @global_options[:config])
199
+ @config = service.current_config
200
+ end
201
+
202
+ # Outputs usage error.
203
+ #
204
+ # @param message [String] error message
205
+ # @return [Integer] exit code
206
+ def usage_error(message)
207
+ $stderr.puts "Error: #{message}"
208
+ ExitCodes::USAGE_ERROR
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Shared parser methods for CLI flag values.
6
+ #
7
+ # Provides reusable methods that parse --disk, --net, --cloud-init,
8
+ # --mp flags through their respective Parsers. Included by create
9
+ # and clone commands to avoid parser logic duplication.
10
+ #
11
+ # Expects +@options+ (a Hash of CLI flag values) to be available
12
+ # in the including class.
13
+ #
14
+ # @example Including in a command class
15
+ # class CreateVm
16
+ # include SharedConfigParsers
17
+ # # ...
18
+ # end
19
+ #
20
+ module SharedConfigParsers
21
+ # Parses VM disk configuration strings through the DiskConfig parser.
22
+ #
23
+ # @return [Array<Hash<Symbol, String>>] parsed disk configurations
24
+ # @raise [ArgumentError] if any disk string is invalid
25
+ def parse_vm_disks
26
+ Array(@options[:disk]).map { |d| Parsers::DiskConfig.parse(d) }
27
+ end
28
+
29
+ # Parses VM network configuration strings through the NetConfig parser.
30
+ #
31
+ # @return [Array<Hash<Symbol, String>>] parsed network configurations
32
+ # @raise [ArgumentError] if any net string is invalid
33
+ def parse_vm_nets
34
+ Array(@options[:net]).map { |n| Parsers::NetConfig.parse(n) }
35
+ end
36
+
37
+ # Parses cloud-init configuration string and converts to Proxmox params.
38
+ #
39
+ # @return [Hash<Symbol, untyped>] Proxmox-compatible cloud-init parameters
40
+ # @raise [ArgumentError] if the cloud-init string is invalid
41
+ def parse_vm_cloud_init
42
+ config = Parsers::CloudInitConfig.parse(@options[:"cloud-init"])
43
+ Parsers::CloudInitConfig.to_proxmox_params(config)
44
+ end
45
+
46
+ # Parses LXC mountpoint configuration strings through the LxcMountConfig parser.
47
+ #
48
+ # @return [Array<Hash<Symbol, String>>] parsed mountpoint configurations
49
+ # @raise [ArgumentError] if any mountpoint string is invalid
50
+ def parse_ct_mountpoints
51
+ Array(@options[:mp]).map { |m| Parsers::LxcMountConfig.parse(m) }
52
+ end
53
+
54
+ # Parses LXC network configuration strings through the LxcNetConfig parser.
55
+ #
56
+ # @return [Array<Hash<Symbol, String>>] parsed network configurations
57
+ # @raise [ArgumentError] if any net string is invalid
58
+ def parse_ct_nets
59
+ Array(@options[:net]).map { |n| Parsers::LxcNetConfig.parse(n) }
60
+ end
61
+
62
+ # Builds a VM config parameters hash from CLI options.
63
+ #
64
+ # Extracts VM-specific configuration keys from @options, parsing
65
+ # disk, network, and cloud-init flags through their respective parsers.
66
+ # Nil values are compacted out.
67
+ #
68
+ # @return [Hash<Symbol, untyped>] VM configuration parameters
69
+ # @raise [ArgumentError] if any parser validation fails
70
+ def build_vm_config_params
71
+ params = {
72
+ cores: @options[:cores],
73
+ sockets: @options[:sockets],
74
+ cpu_type: @options[:"cpu-type"],
75
+ numa: @options[:numa],
76
+ memory: @options[:memory],
77
+ balloon: @options[:balloon],
78
+ scsihw: @options[:scsihw],
79
+ cdrom: @options[:cdrom],
80
+ bios: @options[:bios],
81
+ boot_order: @options[:"boot-order"],
82
+ machine: @options[:machine],
83
+ efidisk: @options[:efidisk],
84
+ agent: @options[:agent],
85
+ ostype: @options[:ostype],
86
+ tags: @options[:tags]
87
+ }
88
+
89
+ params[:disks] = parse_vm_disks if @options[:disk]
90
+ params[:nets] = parse_vm_nets if @options[:net]
91
+ params[:cloud_init] = parse_vm_cloud_init if @options[:"cloud-init"]
92
+
93
+ params.compact
94
+ end
95
+
96
+ # Builds a container config parameters hash from CLI options.
97
+ #
98
+ # Extracts container-specific configuration keys from @options, parsing
99
+ # rootfs, mountpoint, and network flags through their respective parsers.
100
+ # Nil values are compacted out.
101
+ #
102
+ # @return [Hash<Symbol, untyped>] container configuration parameters
103
+ # @raise [ArgumentError] if any parser validation fails
104
+ def build_ct_config_params
105
+ params = {
106
+ cores: @options[:cores],
107
+ memory: @options[:memory],
108
+ swap: @options[:swap],
109
+ tags: @options[:tags],
110
+ features: @options[:features],
111
+ password: @options[:password],
112
+ ssh_public_keys: @options[:"ssh-public-keys"],
113
+ onboot: @options[:onboot],
114
+ startup: @options[:startup]
115
+ }
116
+
117
+ params[:rootfs] = Parsers::LxcMountConfig.parse(@options[:rootfs]) if @options[:rootfs]
118
+ params[:mountpoints] = parse_ct_mountpoints if @options[:mp]
119
+ params[:nets] = parse_ct_nets if @options[:net]
120
+ params[:privileged] = @options[:privileged]
121
+
122
+ params.compact
123
+ end
124
+ end
125
+ end
126
+ end