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,329 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for Proxmox cluster storage pools.
6
+ #
7
+ # Defines column layout and formatting for table output.
8
+ # Standard columns show essential storage info.
9
+ # Wide columns add content types and shared status.
10
+ #
11
+ # @example Using with formatter
12
+ # presenter = Storage.new
13
+ # formatter = Formatters::Table.new
14
+ # output = formatter.format(storage_pools, presenter)
15
+ #
16
+ # @see Pvectl::Models::Storage Storage model
17
+ # @see Pvectl::Formatters::Table Table formatter
18
+ #
19
+ class Storage < Base
20
+ # Returns column headers for standard table output.
21
+ #
22
+ # @return [Array<String>] column headers
23
+ def columns
24
+ %w[NAME TYPE STATUS USED TOTAL %USED NODE]
25
+ end
26
+
27
+ # Returns additional column headers for wide output.
28
+ #
29
+ # @return [Array<String>] extra column headers
30
+ def extra_columns
31
+ %w[CONTENT SHARED]
32
+ end
33
+
34
+ # Converts Storage model to table row values.
35
+ #
36
+ # @param model [Models::Storage] Storage model
37
+ # @param context [Hash] optional context
38
+ # @return [Array<String>] row values matching columns order
39
+ def to_row(model, **_context)
40
+ @storage = model
41
+ [
42
+ storage.name,
43
+ type_display,
44
+ status_display,
45
+ used_display,
46
+ total_display,
47
+ usage_display,
48
+ node_display
49
+ ]
50
+ end
51
+
52
+ # Returns additional values for wide output.
53
+ #
54
+ # @param model [Models::Storage] Storage model
55
+ # @param context [Hash] optional context
56
+ # @return [Array<String>] extra values matching extra_columns order
57
+ def extra_values(model, **_context)
58
+ @storage = model
59
+ [
60
+ content_display,
61
+ shared_display
62
+ ]
63
+ end
64
+
65
+ # Converts Storage model to hash for JSON/YAML output.
66
+ #
67
+ # Returns a structured hash with nested objects for complex data.
68
+ #
69
+ # @param model [Models::Storage] Storage model
70
+ # @return [Hash] hash representation with string keys
71
+ def to_hash(model)
72
+ @storage = model
73
+ {
74
+ "name" => storage.name,
75
+ "type" => storage.plugintype,
76
+ "status" => storage.status,
77
+ "node" => storage.node,
78
+ "shared" => storage.shared?,
79
+ "content" => storage.content,
80
+ "disk" => {
81
+ "used_bytes" => storage.disk,
82
+ "total_bytes" => storage.maxdisk,
83
+ "used_gb" => disk_used_gb,
84
+ "total_gb" => disk_total_gb,
85
+ "usage_percent" => usage_percent
86
+ }
87
+ }
88
+ end
89
+
90
+ # Converts Storage model to description format for describe command.
91
+ #
92
+ # Returns a structured Hash with sections for kubectl-style vertical output.
93
+ # Nested Hashes create indented subsections.
94
+ # Arrays of Hashes render as inline tables.
95
+ #
96
+ # @param model [Models::Storage] Storage model with describe details
97
+ # @return [Hash] structured hash for describe formatter
98
+ def to_description(model)
99
+ @storage = model
100
+
101
+ {
102
+ "Name" => storage.name,
103
+ "Type" => type_display,
104
+ "Status" => status_display,
105
+ "Shared" => shared_display,
106
+ "Nodes" => nodes_display,
107
+
108
+ "Capacity" => build_capacity_section,
109
+
110
+ "Configuration" => build_configuration_section,
111
+
112
+ "Content Summary" => format_content_summary,
113
+
114
+ "Backup Retention" => format_backup_retention
115
+ }
116
+ end
117
+
118
+ # -----------------------------------------------------------------
119
+ # Display Methods (moved from Models::Storage)
120
+ # -----------------------------------------------------------------
121
+
122
+ # Returns storage type for display.
123
+ #
124
+ # @return [String] storage plugin type or "-"
125
+ def type_display
126
+ storage.plugintype || "-"
127
+ end
128
+
129
+ # Returns status for display.
130
+ # Maps "available" to "active" for consistency with kubectl style.
131
+ #
132
+ # @return [String] status (active, inactive)
133
+ def status_display
134
+ storage.active? ? "active" : "inactive"
135
+ end
136
+
137
+ # Returns node display.
138
+ # Shows "-" for shared storage (available on multiple nodes).
139
+ #
140
+ # @return [String] node name or "-" for shared storage
141
+ def node_display
142
+ storage.shared? ? "-" : (storage.node || "-")
143
+ end
144
+
145
+ # Returns disk used in GB.
146
+ #
147
+ # @return [Float, nil] disk used in GB, or nil if unavailable
148
+ def disk_used_gb
149
+ return nil if storage.disk.nil?
150
+
151
+ (storage.disk.to_f / 1024 / 1024 / 1024).round(1)
152
+ end
153
+
154
+ # Returns total disk in GB.
155
+ #
156
+ # @return [Float, nil] total disk in GB, or nil if unavailable
157
+ def disk_total_gb
158
+ return nil if storage.maxdisk.nil?
159
+
160
+ (storage.maxdisk.to_f / 1024 / 1024 / 1024).round(1)
161
+ end
162
+
163
+ # Returns storage usage percentage.
164
+ #
165
+ # @return [Integer, nil] usage percentage (0-100), or nil if unavailable
166
+ def usage_percent
167
+ return nil if storage.maxdisk.nil? || storage.maxdisk.zero? || storage.disk.nil?
168
+
169
+ ((storage.disk.to_f / storage.maxdisk) * 100).round
170
+ end
171
+
172
+ # Returns available bytes in GB.
173
+ #
174
+ # @return [Float, nil] available bytes in GB, or nil if unavailable
175
+ def avail_gb
176
+ return nil if storage.avail.nil?
177
+
178
+ (storage.avail.to_f / 1024 / 1024 / 1024).round(1)
179
+ end
180
+
181
+ # Returns available storage formatted with appropriate unit (GB or TB).
182
+ #
183
+ # @return [String] formatted available storage (e.g., "55 GB" or "2.0 TB") or "-"
184
+ def avail_display
185
+ return "-" unless storage.active? && avail_gb
186
+
187
+ format_size(avail_gb)
188
+ end
189
+
190
+ # Returns used storage formatted with appropriate unit (GB or TB).
191
+ #
192
+ # @return [String] formatted used storage (e.g., "45 GB" or "1.2 TB") or "-"
193
+ def used_display
194
+ return "-" unless storage.active? && disk_used_gb
195
+
196
+ format_size(disk_used_gb)
197
+ end
198
+
199
+ # Returns total storage formatted with appropriate unit (GB or TB).
200
+ #
201
+ # @return [String] formatted total storage (e.g., "100 GB" or "2.0 TB") or "-"
202
+ def total_display
203
+ return "-" if disk_total_gb.nil?
204
+
205
+ format_size(disk_total_gb)
206
+ end
207
+
208
+ # Returns usage percentage for display.
209
+ #
210
+ # @return [String] percentage (e.g., "45%") or "-" if unavailable
211
+ def usage_display
212
+ return "-" unless storage.active? && usage_percent
213
+
214
+ "#{usage_percent}%"
215
+ end
216
+
217
+ # Returns content types for display.
218
+ #
219
+ # @return [String] comma-separated content types or "-"
220
+ def content_display
221
+ storage.content.nil? || storage.content.empty? ? "-" : storage.content
222
+ end
223
+
224
+ # Returns shared status for display.
225
+ #
226
+ # @return [String] "yes" or "no"
227
+ def shared_display
228
+ storage.shared? ? "yes" : "no"
229
+ end
230
+
231
+ private
232
+
233
+ # @return [Models::Storage] the current storage being presented
234
+ attr_reader :storage
235
+
236
+ # Formats size with appropriate unit (GB or TB).
237
+ #
238
+ # @param gb [Float] size in GB
239
+ # @return [String] formatted size
240
+ def format_size(gb)
241
+ if gb >= 1024
242
+ "#{(gb / 1024).round(1)} TB"
243
+ else
244
+ "#{gb.round(0).to_i} GB"
245
+ end
246
+ end
247
+
248
+ # -----------------------------------------------------------------
249
+ # Helper methods for to_description
250
+ # -----------------------------------------------------------------
251
+
252
+ # Returns nodes display for describe output.
253
+ # Shows "all" if nodes_allowed is nil (available on all nodes).
254
+ #
255
+ # @return [String] nodes list or "all"
256
+ def nodes_display
257
+ storage.nodes_allowed.nil? ? "all" : storage.nodes_allowed
258
+ end
259
+
260
+ # Builds the capacity section for describe output.
261
+ #
262
+ # @return [Hash] capacity metrics
263
+ def build_capacity_section
264
+ {
265
+ "Total" => total_display,
266
+ "Used" => used_display,
267
+ "Available" => avail_display,
268
+ "Usage" => usage_display
269
+ }
270
+ end
271
+
272
+ # Builds the configuration section for describe output.
273
+ # Includes type-specific configuration fields.
274
+ #
275
+ # @return [Hash, String] configuration fields or "-" if empty
276
+ def build_configuration_section
277
+ config = {}
278
+ config["Path"] = storage.path if storage.path
279
+ config["Server"] = storage.server if storage.server
280
+ config["Export"] = storage.export if storage.export
281
+ config["Volume Group"] = storage.vgname if storage.vgname
282
+ config["Thin Pool"] = storage.thinpool if storage.thinpool
283
+ config["Pool"] = storage.pool if storage.pool
284
+ config["Content Types"] = storage.content if storage.content
285
+ config.empty? ? "-" : config
286
+ end
287
+
288
+ # Formats content summary from volumes list.
289
+ # Groups volumes by type and calculates counts and sizes.
290
+ #
291
+ # @return [Array<Hash>, String] formatted table data or "-" if no volumes
292
+ def format_content_summary
293
+ return "-" if storage.volumes.nil? || storage.volumes.empty?
294
+
295
+ # Group volumes by content type
296
+ grouped = storage.volumes.group_by { |v| v[:content] || v["content"] }
297
+
298
+ grouped.map do |content_type, volumes|
299
+ total_size = volumes.sum { |v| v[:size] || v["size"] || 0 }
300
+ size_gb = (total_size.to_f / 1024 / 1024 / 1024).round(1)
301
+ {
302
+ "TYPE" => content_type || "-",
303
+ "COUNT" => volumes.size.to_s,
304
+ "SIZE" => format_size(size_gb)
305
+ }
306
+ end
307
+ end
308
+
309
+ # Formats backup retention policy for describe output.
310
+ #
311
+ # @return [Hash, String] retention settings or "-" if not configured
312
+ def format_backup_retention
313
+ return "-" if storage.prune_backups.nil?
314
+
315
+ retention = {}
316
+ prune = storage.prune_backups
317
+
318
+ # Handle both string and symbol keys
319
+ retention["Keep Last"] = prune[:"keep-last"] || prune["keep-last"] if prune[:"keep-last"] || prune["keep-last"]
320
+ retention["Keep Daily"] = prune[:"keep-daily"] || prune["keep-daily"] if prune[:"keep-daily"] || prune["keep-daily"]
321
+ retention["Keep Weekly"] = prune[:"keep-weekly"] || prune["keep-weekly"] if prune[:"keep-weekly"] || prune["keep-weekly"]
322
+ retention["Keep Monthly"] = prune[:"keep-monthly"] || prune["keep-monthly"] if prune[:"keep-monthly"] || prune["keep-monthly"]
323
+ retention["Keep Yearly"] = prune[:"keep-yearly"] || prune["keep-yearly"] if prune[:"keep-yearly"] || prune["keep-yearly"]
324
+
325
+ retention.empty? ? "-" : retention
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for Proxmox subscription records.
6
+ #
7
+ # Default table view masks the license key to avoid leaking it in shared
8
+ # screenshots; the full key is only shown via `-o wide`, `-o json`, or
9
+ # `-o yaml` so revealing it is an explicit user choice.
10
+ #
11
+ # @see Pvectl::Models::Subscription
12
+ #
13
+ class Subscription < Base
14
+ # @return [Array<String>] standard column headers
15
+ def columns
16
+ %w[NODE LEVEL STATUS NEXTDUEDATE KEY]
17
+ end
18
+
19
+ # @return [Array<String>] extra column headers for wide output
20
+ def extra_columns
21
+ %w[PRODUCT REGDATE SERVERID]
22
+ end
23
+
24
+ # @param model [Models::Subscription]
25
+ # @return [Array<String>]
26
+ def to_row(model, **_context)
27
+ @subscription = model
28
+ [
29
+ node_display,
30
+ level_display,
31
+ status_display,
32
+ nextduedate_display,
33
+ masked_key
34
+ ]
35
+ end
36
+
37
+ # @param model [Models::Subscription]
38
+ # @return [Array<String>]
39
+ def extra_values(model, **_context)
40
+ @subscription = model
41
+ [
42
+ product_display,
43
+ regdate_display,
44
+ serverid_display
45
+ ]
46
+ end
47
+
48
+ # Wide row exposes the full key (after PRODUCT/REGDATE/SERVERID extras).
49
+ # We replace the masked KEY column with the full one to make `-o wide`
50
+ # useful for copying the license out without re-running with `-o json`.
51
+ #
52
+ # @param model [Models::Subscription]
53
+ # @return [Array<String>]
54
+ def to_wide_row(model, **context)
55
+ row = to_row(model, **context).dup
56
+ row[columns.index("KEY")] = full_key_display
57
+ row + extra_values(model, **context)
58
+ end
59
+
60
+ # Hash representation for JSON/YAML output. Includes the full key —
61
+ # JSON/YAML output is opt-in via `-o`, so the user already asked for
62
+ # the raw data.
63
+ #
64
+ # @param model [Models::Subscription]
65
+ # @return [Hash{String => Object}]
66
+ def to_hash(model)
67
+ @subscription = model
68
+ {
69
+ "node" => subscription.node,
70
+ "status" => subscription.status,
71
+ "level" => subscription.level,
72
+ "product" => subscription.productname,
73
+ "key" => subscription.key,
74
+ "next_due_date" => subscription.nextduedate,
75
+ "registration_date" => subscription.regdate,
76
+ "checktime" => subscription.checktime,
77
+ "server_id" => subscription.serverid,
78
+ "sockets" => subscription.sockets,
79
+ "url" => subscription.url,
80
+ "message" => subscription.message
81
+ }
82
+ end
83
+
84
+ # Description-style hash for `pvectl describe subscription` future use.
85
+ #
86
+ # @param model [Models::Subscription]
87
+ # @return [Hash{String => Object}]
88
+ def to_description(model)
89
+ @subscription = model
90
+ {
91
+ "Node" => node_display,
92
+ "Status" => status_display,
93
+ "Level" => level_display,
94
+ "Product" => product_display,
95
+ "Key" => masked_key,
96
+ "Next Due Date" => nextduedate_display,
97
+ "Registration Date" => regdate_display,
98
+ "Server ID" => serverid_display,
99
+ "Sockets" => sockets_display,
100
+ "Last Check" => checktime_display,
101
+ "Message" => message_display
102
+ }
103
+ end
104
+
105
+ # @return [String]
106
+ def node_display
107
+ subscription.node || "-"
108
+ end
109
+
110
+ # @return [String]
111
+ def status_display
112
+ subscription.status || "-"
113
+ end
114
+
115
+ # @return [String]
116
+ def level_display
117
+ subscription.level || "-"
118
+ end
119
+
120
+ # @return [String]
121
+ def product_display
122
+ subscription.productname || "-"
123
+ end
124
+
125
+ # @return [String]
126
+ def regdate_display
127
+ subscription.regdate || "-"
128
+ end
129
+
130
+ # @return [String]
131
+ def serverid_display
132
+ subscription.serverid || "-"
133
+ end
134
+
135
+ # @return [String]
136
+ def nextduedate_display
137
+ subscription.nextduedate || "-"
138
+ end
139
+
140
+ # @return [String]
141
+ def sockets_display
142
+ subscription.sockets ? subscription.sockets.to_s : "-"
143
+ end
144
+
145
+ # @return [String]
146
+ def message_display
147
+ subscription.message || "-"
148
+ end
149
+
150
+ # @return [String] formatted timestamp or "-"
151
+ def checktime_display
152
+ return "-" if subscription.checktime.nil? || subscription.checktime.to_i.zero?
153
+
154
+ Time.at(subscription.checktime.to_i).utc.strftime("%Y-%m-%d %H:%M:%S UTC")
155
+ end
156
+
157
+ # Returns the license key with the middle masked.
158
+ #
159
+ # Proxmox keys look like `pveXp-1234567890` (16 chars). We preserve the
160
+ # 6-char prefix (`pveXp-`) and the last 4 chars, masking the rest.
161
+ # Returns "-" when no key is set (community node).
162
+ #
163
+ # @return [String]
164
+ def masked_key
165
+ key = subscription.key
166
+ return "-" if key.nil? || key.empty?
167
+
168
+ if key.length <= 6
169
+ key
170
+ elsif key.length <= 10
171
+ "#{key[0, 4]}***#{key[-2..]}"
172
+ else
173
+ "#{key[0, 6]}****#{key[-4..]}"
174
+ end
175
+ end
176
+
177
+ # @return [String] full key or "-"
178
+ def full_key_display
179
+ key = subscription.key
180
+ key.nil? || key.empty? ? "-" : key
181
+ end
182
+
183
+ private
184
+
185
+ # @return [Models::Subscription]
186
+ attr_reader :subscription
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for syslog entries (pvectl logs node).
6
+ class SyslogEntry < Base
7
+ def columns
8
+ %w[LINE TEXT]
9
+ end
10
+
11
+ def to_row(model, **_context)
12
+ [model.n.to_s, model.t || ""]
13
+ end
14
+
15
+ def to_hash(model)
16
+ { "line" => model.n, "text" => model.t }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for task list entries (pvectl logs vm/ct).
6
+ class TaskEntry < Base
7
+ def columns
8
+ %w[STARTTIME TYPE STATUS USER DURATION NODE]
9
+ end
10
+
11
+ def extra_columns
12
+ %w[UPID ENDTIME ID PID]
13
+ end
14
+
15
+ def to_row(model, **_context)
16
+ [
17
+ format_time(model.starttime),
18
+ model.type || "-",
19
+ model.exitstatus || model.status || "-",
20
+ model.user || "-",
21
+ format_duration(model.duration),
22
+ model.node || "-"
23
+ ]
24
+ end
25
+
26
+ def extra_values(model, **_context)
27
+ [
28
+ model.upid || "-",
29
+ format_time(model.endtime),
30
+ model.id || "-",
31
+ model.pid&.to_s || "-"
32
+ ]
33
+ end
34
+
35
+ def to_hash(model)
36
+ {
37
+ "starttime" => model.starttime,
38
+ "endtime" => model.endtime,
39
+ "type" => model.type,
40
+ "status" => model.status,
41
+ "exitstatus" => model.exitstatus,
42
+ "user" => model.user,
43
+ "duration" => model.duration,
44
+ "node" => model.node,
45
+ "id" => model.id,
46
+ "upid" => model.upid
47
+ }
48
+ end
49
+
50
+ private
51
+
52
+ def format_time(timestamp)
53
+ return "-" if timestamp.nil?
54
+ Time.at(timestamp).strftime("%Y-%m-%d %H:%M:%S")
55
+ end
56
+
57
+ def format_duration(seconds)
58
+ return "-" if seconds.nil?
59
+ if seconds < 60
60
+ "#{seconds}s"
61
+ elsif seconds < 3600
62
+ "#{seconds / 60}m#{seconds % 60}s"
63
+ else
64
+ "#{seconds / 3600}h#{(seconds % 3600) / 60}m"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for task log lines (pvectl logs task UPID).
6
+ class TaskLogLine < Base
7
+ def columns
8
+ %w[LINE TEXT]
9
+ end
10
+
11
+ def to_row(model, **_context)
12
+ [model.n.to_s, model.t || ""]
13
+ end
14
+
15
+ def to_hash(model)
16
+ { "line" => model.n, "text" => model.t }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for template listing (mixed VM and Container templates).
6
+ #
7
+ # Handles both Models::Vm and Models::Container via duck typing.
8
+ # Both models share: vmid, name, type, node, maxdisk, tags.
9
+ #
10
+ # @example Using with formatter
11
+ # presenter = Template.new
12
+ # formatter = Formatters::Table.new
13
+ # output = formatter.format(templates, presenter)
14
+ #
15
+ # @see Pvectl::Models::Vm VM model
16
+ # @see Pvectl::Models::Container Container model
17
+ #
18
+ class Template < Base
19
+ # Returns column headers for template listing.
20
+ #
21
+ # @return [Array<String>] column names
22
+ def columns
23
+ %w[ID NAME TYPE NODE DISK TAGS]
24
+ end
25
+
26
+ # Converts a template model to table row.
27
+ #
28
+ # @param model [Models::Vm, Models::Container] template model
29
+ # @param context [Hash] unused
30
+ # @return [Array<String>] row values
31
+ def to_row(model, **_context)
32
+ [
33
+ model.vmid.to_s,
34
+ model.name || "-",
35
+ model.type || "-",
36
+ model.node || "-",
37
+ format_disk(model.maxdisk),
38
+ model.tags || "-"
39
+ ]
40
+ end
41
+
42
+ # Converts a template model to hash for JSON/YAML.
43
+ #
44
+ # @param model [Models::Vm, Models::Container] template model
45
+ # @return [Hash] hash representation
46
+ def to_hash(model)
47
+ {
48
+ "id" => model.vmid,
49
+ "name" => model.name,
50
+ "type" => model.type,
51
+ "node" => model.node,
52
+ "disk" => model.maxdisk,
53
+ "tags" => model.tags
54
+ }
55
+ end
56
+
57
+ private
58
+
59
+ # Formats disk size in human-readable format.
60
+ #
61
+ # @param bytes [Integer, nil] disk size in bytes
62
+ # @return [String] formatted size
63
+ def format_disk(bytes)
64
+ return "-" unless bytes && bytes.positive?
65
+
66
+ if bytes >= 1_073_741_824
67
+ "#{(bytes.to_f / 1_073_741_824).round(1)}G"
68
+ elsif bytes >= 1_048_576
69
+ "#{(bytes.to_f / 1_048_576).round(1)}M"
70
+ else
71
+ "#{bytes}B"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end