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,574 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Pvectl
6
+ module Presenters
7
+ # Presenter for LXC containers.
8
+ #
9
+ # Defines column layout and formatting for table output.
10
+ # Used by formatters to render container data in various formats.
11
+ #
12
+ # Standard columns: NAME, CTID, STATUS, NODE, CPU, MEMORY
13
+ # Wide columns add: UPTIME, TEMPLATE, TAGS, SWAP, DISK, NETIN, NETOUT, POOL
14
+ #
15
+ # Description output is organized by Proxmox VE web UI tabs:
16
+ # Summary, Resources, Network, DNS, Options, Task History, Snapshots, HA.
17
+ #
18
+ # @example Using with formatter
19
+ # presenter = Container.new
20
+ # formatter = Formatters::Table.new
21
+ # output = formatter.format(containers, presenter)
22
+ #
23
+ # @see Pvectl::Models::Container Container model
24
+ # @see Pvectl::Formatters::Table Table formatter
25
+ #
26
+ class Container < Base
27
+ # Returns column headers for standard table output.
28
+ #
29
+ # @return [Array<String>] column headers
30
+ def columns
31
+ %w[NAME CTID STATUS NODE CPU MEMORY]
32
+ end
33
+
34
+ # Returns additional column headers for wide output.
35
+ #
36
+ # @return [Array<String>] extra column headers
37
+ def extra_columns
38
+ %w[UPTIME TEMPLATE TAGS SWAP DISK NETIN NETOUT POOL]
39
+ end
40
+
41
+ # Converts Container model to table row values.
42
+ #
43
+ # @param model [Models::Container] Container model
44
+ # @param context [Hash] optional context
45
+ # @return [Array<String>] row values matching columns order
46
+ def to_row(model, **_context)
47
+ @container = model
48
+ [
49
+ display_name,
50
+ container.vmid.to_s,
51
+ container.status,
52
+ container.node,
53
+ cpu_percent,
54
+ memory_display
55
+ ]
56
+ end
57
+
58
+ # Returns additional values for wide output.
59
+ #
60
+ # @param model [Models::Container] Container model
61
+ # @param context [Hash] optional context
62
+ # @return [Array<String>] extra values matching extra_columns order
63
+ def extra_values(model, **_context)
64
+ @container = model
65
+ [
66
+ uptime_human,
67
+ template_display,
68
+ tags_display,
69
+ swap_display,
70
+ disk_display,
71
+ netin_display,
72
+ netout_display,
73
+ pool_display
74
+ ]
75
+ end
76
+
77
+ # Converts Container model to hash for JSON/YAML output.
78
+ #
79
+ # Returns a structured hash with nested objects for complex data
80
+ # like CPU, memory, swap, disk, uptime, and network.
81
+ #
82
+ # @param model [Models::Container] Container model
83
+ # @return [Hash] hash representation with string keys
84
+ def to_hash(model)
85
+ @container = model
86
+ {
87
+ "ctid" => container.vmid,
88
+ "name" => container.name,
89
+ "status" => container.status,
90
+ "node" => container.node,
91
+ "template" => container.template?,
92
+ "pool" => container.pool,
93
+ "cpu" => {
94
+ "usage_percent" => container.cpu.nil? ? nil : (container.cpu * 100).round,
95
+ "cores" => container.maxcpu
96
+ },
97
+ "memory" => {
98
+ "used_gib" => memory_used_gib,
99
+ "total_gib" => memory_total_gib,
100
+ "used_bytes" => container.mem,
101
+ "total_bytes" => container.maxmem
102
+ },
103
+ "swap" => {
104
+ "used_mib" => swap_used_mib,
105
+ "total_mib" => swap_total_mib,
106
+ "used_bytes" => container.swap,
107
+ "total_bytes" => container.maxswap
108
+ },
109
+ "disk" => {
110
+ "used_gib" => disk_used_gib,
111
+ "total_gib" => disk_total_gib,
112
+ "used_bytes" => container.disk,
113
+ "total_bytes" => container.maxdisk
114
+ },
115
+ "uptime" => {
116
+ "seconds" => container.uptime,
117
+ "human" => uptime_human
118
+ },
119
+ "network" => {
120
+ "in_bytes" => container.netin,
121
+ "out_bytes" => container.netout
122
+ },
123
+ "tags" => tags_array
124
+ }
125
+ end
126
+
127
+ # Converts Container model to description format for describe command.
128
+ #
129
+ # Returns a structured Hash organized by Proxmox VE web UI tabs:
130
+ # Summary, Resources, Network, DNS, Options, Task History, Snapshots,
131
+ # High Availability. Nested Hashes create indented subsections.
132
+ # Arrays of Hashes render as inline tables.
133
+ #
134
+ # @param model [Models::Container] Container model with describe details
135
+ # @return [Hash] structured hash for describe formatter
136
+ def to_description(model)
137
+ @container = model
138
+ @consumed_keys = Set.new
139
+ data = container.describe_data || {}
140
+ config = data[:config] || {}
141
+
142
+ consume(:hostname, :description, :tags, :pool, :template, :lxc)
143
+
144
+ {
145
+ "Name" => display_name,
146
+ "CTID" => container.vmid,
147
+ "Status" => container.status,
148
+ "Node" => container.node,
149
+ "Tags" => tags_display,
150
+ "Description" => container.description || config[:description] || "-",
151
+ "Summary" => format_summary,
152
+ "Resources" => format_resources(config),
153
+ "Network" => format_network_interfaces(config),
154
+ "DNS" => format_dns(config),
155
+ "Options" => format_options(config),
156
+ "Firewall" => format_firewall(data[:firewall]),
157
+ "Firewall Rules" => format_firewall_rules(data[:firewall]),
158
+ "Task History" => format_task_history(data[:tasks]),
159
+ "Snapshots" => format_snapshots(data[:snapshots]),
160
+ "High Availability" => format_ha,
161
+ "Additional Configuration" => format_remaining(config)
162
+ }
163
+ end
164
+
165
+ # ---------------------------
166
+ # Display Methods
167
+ # ---------------------------
168
+
169
+ # Returns display name, falling back to "CT-{ctid}" if name is nil.
170
+ #
171
+ # @return [String] display name
172
+ def display_name
173
+ container.name || "CT-#{container.vmid}"
174
+ end
175
+
176
+ # Returns CPU usage as percentage string.
177
+ #
178
+ # For running containers, shows actual usage percentage.
179
+ # For stopped containers, shows "-" for usage but includes core count if available.
180
+ #
181
+ # @return [String] CPU percentage (e.g., "12%/4") or "-/4" for stopped containers
182
+ def cpu_percent
183
+ return "-" if container.maxcpu.nil?
184
+ return "-/#{container.maxcpu}" unless container.running?
185
+ return "-/#{container.maxcpu}" if container.cpu.nil?
186
+
187
+ "#{(container.cpu * 100).round}%/#{container.maxcpu}"
188
+ end
189
+
190
+ # Returns memory used in GiB.
191
+ #
192
+ # @return [Float, nil] memory used in GiB, or nil if unavailable
193
+ def memory_used_gib
194
+ return nil if container.mem.nil?
195
+
196
+ (container.mem.to_f / 1024 / 1024 / 1024).round(1)
197
+ end
198
+
199
+ # Returns total memory in GiB.
200
+ #
201
+ # @return [Float, nil] total memory in GiB, or nil if unavailable
202
+ def memory_total_gib
203
+ return nil if container.maxmem.nil?
204
+
205
+ (container.maxmem.to_f / 1024 / 1024 / 1024).round(1)
206
+ end
207
+
208
+ # Returns memory formatted as "used/total GiB".
209
+ #
210
+ # For running containers, shows actual usage and total.
211
+ # For stopped containers, shows "-" for usage but includes total if available.
212
+ #
213
+ # @return [String] formatted memory (e.g., "2.1/4.0 GiB") or "-/4.0 GiB" for stopped
214
+ def memory_display
215
+ return "-" if memory_total_gib.nil?
216
+ return "-/#{memory_total_gib} GiB" unless container.running?
217
+ return "-/#{memory_total_gib} GiB" if memory_used_gib.nil?
218
+
219
+ "#{memory_used_gib}/#{memory_total_gib} GiB"
220
+ end
221
+
222
+ # Returns swap used in MiB.
223
+ #
224
+ # @return [Float, nil] swap used in MiB, or nil if unavailable
225
+ def swap_used_mib
226
+ return nil if container.swap.nil?
227
+
228
+ (container.swap.to_f / 1024 / 1024).round(1)
229
+ end
230
+
231
+ # Returns total swap in MiB.
232
+ #
233
+ # @return [Float, nil] total swap in MiB, or nil if unavailable
234
+ def swap_total_mib
235
+ return nil if container.maxswap.nil?
236
+
237
+ (container.maxswap.to_f / 1024 / 1024).round(1)
238
+ end
239
+
240
+ # Returns swap formatted as "used/total MiB".
241
+ #
242
+ # @return [String] formatted swap (e.g., "128/512 MiB") or "-" if unavailable
243
+ def swap_display
244
+ return "-" if swap_total_mib.nil? || swap_total_mib.zero?
245
+ return "-/#{swap_total_mib.round} MiB" unless container.running?
246
+ return "-/#{swap_total_mib.round} MiB" if swap_used_mib.nil?
247
+
248
+ "#{swap_used_mib.round}/#{swap_total_mib.round} MiB"
249
+ end
250
+
251
+ # Returns disk used in GiB.
252
+ #
253
+ # @return [Float, nil] disk used in GiB, or nil if unavailable
254
+ def disk_used_gib
255
+ return nil if container.disk.nil?
256
+
257
+ (container.disk.to_f / 1024 / 1024 / 1024).round(1)
258
+ end
259
+
260
+ # Returns total disk in GiB.
261
+ #
262
+ # @return [Float, nil] total disk in GiB, or nil if unavailable
263
+ def disk_total_gib
264
+ return nil if container.maxdisk.nil?
265
+
266
+ (container.maxdisk.to_f / 1024 / 1024 / 1024).round(1)
267
+ end
268
+
269
+ # Returns disk formatted as "used/total GiB".
270
+ #
271
+ # @return [String] formatted disk (e.g., "15.0/50.0 GiB") or "-" if unavailable
272
+ def disk_display
273
+ return "-" if disk_used_gib.nil?
274
+
275
+ "#{disk_used_gib}/#{disk_total_gib} GiB"
276
+ end
277
+
278
+ # Returns pool display string.
279
+ #
280
+ # @return [String] pool name or "-" if no pool
281
+ def pool_display
282
+ container.pool || "-"
283
+ end
284
+
285
+ # Returns network in bytes formatted.
286
+ #
287
+ # @return [String] formatted network in (e.g., "117.7 MiB") or "-"
288
+ def netin_display
289
+ format_bytes(container.netin)
290
+ end
291
+
292
+ # Returns network out bytes formatted.
293
+ #
294
+ # @return [String] formatted network out (e.g., "941.9 MiB") or "-"
295
+ def netout_display
296
+ format_bytes(container.netout)
297
+ end
298
+
299
+ private
300
+
301
+ # @return [Models::Container] current container model
302
+ attr_reader :container
303
+ alias resource container
304
+
305
+ # Formats Summary section (PVE Summary tab).
306
+ #
307
+ # Shows resource usage and (for running containers) runtime info
308
+ # including uptime, PID, and network I/O statistics.
309
+ #
310
+ # @return [Hash] summary info
311
+ def format_summary
312
+ cpu_usage = if container.running? && container.cpu
313
+ "#{(container.cpu * 100).round(2)}% of #{container.maxcpu || '-'} core(s)"
314
+ else
315
+ "-"
316
+ end
317
+
318
+ mem_usage = if container.running? && container.mem && container.maxmem && container.maxmem > 0
319
+ pct = ((container.mem.to_f / container.maxmem) * 100).round(2)
320
+ "#{pct}% (#{format_bytes(container.mem)} of #{format_bytes(container.maxmem)})"
321
+ else
322
+ "-"
323
+ end
324
+
325
+ swap_usage = if container.running? && container.swap && container.maxswap && container.maxswap > 0
326
+ "#{format_bytes(container.swap)} / #{format_bytes(container.maxswap)}"
327
+ else
328
+ "-"
329
+ end
330
+
331
+ rootfs_usage = if container.disk && container.maxdisk && container.maxdisk > 0
332
+ "#{format_bytes(container.disk)} / #{format_bytes(container.maxdisk)}"
333
+ else
334
+ "-"
335
+ end
336
+
337
+ result = {
338
+ "CPU Usage" => cpu_usage,
339
+ "Memory Usage" => mem_usage,
340
+ "Swap Usage" => swap_usage,
341
+ "Root FS Usage" => rootfs_usage
342
+ }
343
+
344
+ if container.running?
345
+ result["Uptime"] = uptime_human
346
+ result["PID"] = (container.pid || "-").to_s
347
+ result["Network In"] = format_bytes(container.netin)
348
+ result["Network Out"] = format_bytes(container.netout)
349
+ end
350
+
351
+ result
352
+ end
353
+
354
+ # Formats Resources section (PVE Resources tab).
355
+ #
356
+ # Shows configured memory, swap, cores, rootfs, and mountpoints.
357
+ #
358
+ # @param config [Hash] raw config hash for key consumption
359
+ # @return [Hash] resources info
360
+ def format_resources(config)
361
+ consume(:memory, :swap, :cores, :rootfs)
362
+
363
+ mem_mb = config[:memory]
364
+ memory_str = if mem_mb
365
+ "#{(mem_mb.to_f / 1024).round(2)} GiB"
366
+ else
367
+ container.maxmem ? format_bytes(container.maxmem) : "-"
368
+ end
369
+
370
+ swap_mb = config[:swap]
371
+ swap_str = if swap_mb
372
+ "#{swap_mb} MiB"
373
+ else
374
+ container.maxswap ? format_bytes(container.maxswap) : "-"
375
+ end
376
+
377
+ {
378
+ "Memory" => memory_str,
379
+ "Swap" => swap_str,
380
+ "Cores" => (config[:cores] || container.maxcpu || "-").to_s,
381
+ "Root Filesystem" => format_rootfs(config),
382
+ "Mountpoints" => format_mountpoints(config)
383
+ }
384
+ end
385
+
386
+ # Formats rootfs section.
387
+ #
388
+ # @param config [Hash] raw config hash for key consumption
389
+ # @return [Hash] rootfs info
390
+ def format_rootfs(config = {})
391
+ size_gib = disk_total_gib ? "#{disk_total_gib} GiB" : "-"
392
+ used_gib = disk_used_gib ? "#{disk_used_gib} GiB" : "-"
393
+
394
+ {
395
+ "Size" => size_gib,
396
+ "Used" => used_gib
397
+ }
398
+ end
399
+
400
+ # Formats network interfaces for table display.
401
+ #
402
+ # @param config [Hash] raw config hash for key consumption
403
+ # @return [Array<Hash>, String] network interfaces or "-"
404
+ def format_network_interfaces(config = {})
405
+ consume_matching(config, /^net\d+$/)
406
+ interfaces = container.network_interfaces
407
+ return "-" if interfaces.nil? || interfaces.empty?
408
+
409
+ interfaces.map do |iface|
410
+ {
411
+ "NAME" => iface[:name] || "-",
412
+ "BRIDGE" => iface[:bridge] || "-",
413
+ "IP" => iface[:ip] || "-",
414
+ "MAC" => iface[:hwaddr] || "-"
415
+ }
416
+ end
417
+ end
418
+
419
+ # Formats features for display.
420
+ #
421
+ # @param config [Hash] raw config hash for key consumption
422
+ # @return [String] formatted features or "-"
423
+ def format_features(config = {})
424
+ consume(:features)
425
+ features_str = container.features || config[:features]
426
+ return "-" if features_str.nil? || features_str.empty?
427
+
428
+ # Parse "nesting=1,keyctl=1" to "nesting, keyctl"
429
+ features_str.split(",").map do |f|
430
+ key, value = f.split("=")
431
+ value == "1" ? key : nil
432
+ end.compact.join(", ")
433
+ end
434
+
435
+ # Formats mountpoints section (mp0-mp255).
436
+ #
437
+ # @param config [Hash] container config
438
+ # @return [Array<Hash>, String] mountpoints table or "-"
439
+ def format_mountpoints(config)
440
+ mp_keys = config.keys.select { |k| k.to_s.match?(/^mp\d+$/) }
441
+ consume_matching(config, /^mp\d+$/)
442
+ consume_matching(config, /^unused\d+$/)
443
+ return "-" if mp_keys.empty?
444
+
445
+ mp_keys.sort.map do |key|
446
+ parts = config[key].to_s.split(",")
447
+ storage_part = parts.first
448
+ storage = storage_part.include?(":") ? storage_part.split(":").first : storage_part
449
+ mp_path = nil
450
+ size = nil
451
+ parts[1..].each do |part|
452
+ k, v = part.split("=", 2)
453
+ case k
454
+ when "mp" then mp_path = v
455
+ when "size" then size = v
456
+ end
457
+ end
458
+ { "NAME" => key.to_s, "PATH" => mp_path || "-", "STORAGE" => storage, "SIZE" => size || "-" }
459
+ end
460
+ end
461
+
462
+ # Formats DNS section.
463
+ #
464
+ # @param config [Hash] container config
465
+ # @return [Hash, String] DNS info or "-"
466
+ def format_dns(config)
467
+ consume(:nameserver, :searchdomain)
468
+ ns = config[:nameserver]
469
+ sd = config[:searchdomain]
470
+ return "-" if ns.nil? && sd.nil?
471
+
472
+ { "Nameserver" => ns || "-", "Search Domain" => sd || "-" }
473
+ end
474
+
475
+ # Formats Options section (PVE Options tab).
476
+ #
477
+ # Shows boot, startup, OS type, architecture, security, and
478
+ # other container options.
479
+ #
480
+ # @param config [Hash] container config
481
+ # @return [Hash] options info
482
+ def format_options(config)
483
+ consume(:onboot, :startup, :ostype, :arch, :unprivileged,
484
+ :features, :cmode, :tty, :protection, :lock, :hookscript)
485
+
486
+ on_boot = config[:onboot] == 1 ? "Yes" : "No"
487
+ startup = config[:startup]
488
+ startup_display = startup ? startup.to_s : "-"
489
+ ostype = container.ostype || config[:ostype] || "-"
490
+ arch = container.arch || config[:arch] || "-"
491
+ unpriv = container.unprivileged? ? "Yes" : "No"
492
+ features = format_features(config)
493
+ cmode = config[:cmode] || "tty"
494
+ tty_count = (config[:tty] || 2).to_s
495
+ protection = config[:protection] == 1 ? "Yes" : "No"
496
+ hookscript = config[:hookscript] || "-"
497
+
498
+ {
499
+ "Start at Boot" => on_boot,
500
+ "Startup Order" => startup_display,
501
+ "OS Type" => ostype,
502
+ "Architecture" => arch,
503
+ "Unprivileged" => unpriv,
504
+ "Features" => features,
505
+ "Console Mode" => cmode,
506
+ "TTY" => tty_count,
507
+ "Protection" => protection,
508
+ "Hookscript" => hookscript
509
+ }
510
+ end
511
+
512
+ # Formats snapshots section.
513
+ #
514
+ # @param snapshots [Array<Hash>, nil] snapshots from API
515
+ # @return [Array<Hash>, String] snapshots table or "No snapshots"
516
+ def format_snapshots(snapshots)
517
+ return "No snapshots" if snapshots.nil? || snapshots.empty?
518
+
519
+ snapshots.map do |snap|
520
+ snaptime = snap[:snaptime]
521
+ date = snaptime ? Time.at(snaptime).strftime("%Y-%m-%d %H:%M:%S") : "-"
522
+ {
523
+ "NAME" => snap[:name],
524
+ "DATE" => date,
525
+ "DESCRIPTION" => snap[:description] || "-"
526
+ }
527
+ end
528
+ end
529
+
530
+ # Formats High Availability section.
531
+ #
532
+ # @return [Hash] HA info
533
+ def format_ha
534
+ ha = container.ha
535
+ return { "State" => "-", "Group" => "-" } if ha.nil?
536
+
537
+ {
538
+ "State" => ha[:managed] == 1 ? "managed" : "-",
539
+ "Group" => ha[:group] || "-"
540
+ }
541
+ end
542
+
543
+ # Registers config keys as consumed by a format method.
544
+ #
545
+ # @param keys [Array<Symbol>] config keys to mark as consumed
546
+ # @return [void]
547
+ def consume(*keys)
548
+ @consumed_keys.merge(keys.map(&:to_sym))
549
+ end
550
+
551
+ # Consumes all config keys matching a pattern.
552
+ #
553
+ # @param config [Hash] config hash
554
+ # @param pattern [Regexp] pattern to match key names
555
+ # @return [void]
556
+ def consume_matching(config, pattern)
557
+ config.keys.select { |k| k.to_s.match?(pattern) }.each { |k| consume(k) }
558
+ end
559
+
560
+ # Formats remaining unconsumed config keys as catch-all table.
561
+ #
562
+ # @param config [Hash] full config hash
563
+ # @return [Array<Hash>, String] remaining keys table or "-"
564
+ def format_remaining(config)
565
+ excluded = %i[digest]
566
+ remaining = config.keys.map(&:to_sym) - @consumed_keys.to_a - excluded
567
+ return "-" if remaining.empty?
568
+
569
+ remaining.sort.map { |k| { "KEY" => k.to_s, "VALUE" => config[k].to_s } }
570
+ end
571
+
572
+ end
573
+ end
574
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Presenters
5
+ # Presenter for container lifecycle operation results.
6
+ #
7
+ # Formats ContainerOperationResult models for table/JSON/YAML output.
8
+ #
9
+ # @example Using with formatter
10
+ # presenter = ContainerOperationResult.new
11
+ # formatter = Formatters::Table.new
12
+ # output = formatter.format(results, presenter)
13
+ #
14
+ class ContainerOperationResult < OperationResult
15
+ # Returns column headers for standard table output.
16
+ #
17
+ # @return [Array<String>] column headers
18
+ def columns
19
+ %w[CTID NAME NODE STATUS MESSAGE]
20
+ end
21
+
22
+ # Returns additional columns for wide output.
23
+ #
24
+ # @return [Array<String>] extra column headers
25
+ def extra_columns
26
+ %w[TASK DURATION]
27
+ end
28
+
29
+ # Converts result to table row values.
30
+ #
31
+ # For clone operations, displays the new (cloned) container data.
32
+ # For all other operations, displays the source container data.
33
+ #
34
+ # @param model [Models::ContainerOperationResult] result model
35
+ # @param context [Hash] optional context
36
+ # @return [Array<String>] row values
37
+ def to_row(model, **_context)
38
+ if model.operation == :clone && model.resource
39
+ [
40
+ model.resource[:new_ctid].to_s,
41
+ model.resource[:hostname] || "CT-#{model.resource[:new_ctid]}",
42
+ model.resource[:node] || model.container&.node,
43
+ status_display(model),
44
+ model.message
45
+ ]
46
+ else
47
+ [
48
+ model.container.vmid.to_s,
49
+ display_name(model.container),
50
+ model.container.node,
51
+ status_display(model),
52
+ model.message
53
+ ]
54
+ end
55
+ end
56
+
57
+ # Returns additional values for wide output.
58
+ #
59
+ # @param model [Models::ContainerOperationResult] result model
60
+ # @param context [Hash] optional context
61
+ # @return [Array<String>] extra values
62
+ def extra_values(model, **_context)
63
+ [
64
+ task_upid(model),
65
+ duration_display(model)
66
+ ]
67
+ end
68
+
69
+ # Converts result to hash for JSON/YAML output.
70
+ #
71
+ # For clone operations, displays the new (cloned) container data.
72
+ # For all other operations, displays the source container data.
73
+ #
74
+ # @param model [Models::ContainerOperationResult] result model
75
+ # @return [Hash] hash representation
76
+ def to_hash(model)
77
+ if model.operation == :clone && model.resource
78
+ {
79
+ "ctid" => model.resource[:new_ctid],
80
+ "name" => model.resource[:hostname],
81
+ "node" => model.resource[:node] || model.container&.node,
82
+ "status" => model.status_text,
83
+ "message" => model.message,
84
+ "task_upid" => model.task_upid || model.task&.upid
85
+ }
86
+ else
87
+ {
88
+ "ctid" => model.container.vmid,
89
+ "name" => model.container.name,
90
+ "node" => model.container.node,
91
+ "status" => model.status_text,
92
+ "message" => model.message,
93
+ "task_upid" => model.task_upid || model.task&.upid
94
+ }
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # Returns display name for container.
101
+ #
102
+ # @param container [Models::Container] container model
103
+ # @return [String] display name
104
+ def display_name(container)
105
+ container.name || "CT-#{container.vmid}"
106
+ end
107
+ end
108
+ end
109
+ end