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,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ module Config
6
+ # Handler for the `pvectl config use-context` command.
7
+ #
8
+ # Switches the active context in the configuration file.
9
+ # The context name must exist in the configuration.
10
+ #
11
+ # @example Usage
12
+ # pvectl config use-context production
13
+ # pvectl config use-context dev
14
+ #
15
+ class UseContext
16
+ # Registers the use-context subcommand.
17
+ #
18
+ # @param parent [GLI::Command] parent config command
19
+ # @return [void]
20
+ def self.register_subcommand(parent)
21
+ parent.desc "Switch to a different context"
22
+ parent.long_desc <<~HELP
23
+ Switch the active context. The context determines which Proxmox
24
+ cluster and credentials are used for subsequent commands.
25
+
26
+ EXAMPLES
27
+ Switch to production:
28
+ $ pvectl config use-context production
29
+
30
+ NOTES
31
+ Changes are persisted to the config file immediately.
32
+ Use PVECTL_CONTEXT env var for temporary overrides.
33
+ HELP
34
+ parent.command :"use-context" do |use_ctx|
35
+ use_ctx.arg_name "CONTEXT_NAME"
36
+ use_ctx.action do |global_options, _options, args|
37
+ if args.empty?
38
+ $stderr.puts "Error: context name is required"
39
+ exit ExitCodes::USAGE_ERROR
40
+ end
41
+ exit_code = execute(args[0], global_options)
42
+ exit exit_code if exit_code != 0
43
+ end
44
+ end
45
+ end
46
+
47
+ # Executes the use-context command.
48
+ #
49
+ # @param context_name [String] name of the context to switch to
50
+ # @param global_options [Hash] global CLI options (includes :config path)
51
+ # @return [Integer] exit code (0 for success)
52
+ # @raise [Config::ContextNotFoundError] if context doesn't exist
53
+ def self.execute(context_name, global_options)
54
+ config_path = global_options[:config]
55
+ service = Pvectl::Config::Service.new
56
+ service.load(config: config_path)
57
+
58
+ service.use_context(context_name)
59
+
60
+ puts "Switched to context \"#{context_name}\"."
61
+ 0
62
+ rescue Pvectl::Config::ContextNotFoundError => e
63
+ $stderr.puts "Error: #{e.message}"
64
+ ExitCodes::CONFIG_ERROR
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "yaml"
5
+
6
+ module Pvectl
7
+ module Commands
8
+ module Config
9
+ # Handler for the `pvectl config view` command.
10
+ #
11
+ # Displays the current configuration with secrets masked.
12
+ # Secrets (token-secret, password) are replaced with ********.
13
+ #
14
+ # @example Usage
15
+ # pvectl config view
16
+ # pvectl config view -o json
17
+ #
18
+ class View
19
+ # Registers the view subcommand.
20
+ #
21
+ # @param parent [GLI::Command] parent config command
22
+ # @return [void]
23
+ def self.register_subcommand(parent)
24
+ parent.desc "Display current configuration with masked secrets"
25
+ parent.long_desc <<~HELP
26
+ Display the current configuration file contents. Secrets (token
27
+ secrets, passwords) are masked for security.
28
+
29
+ EXAMPLES
30
+ $ pvectl config view
31
+ $ pvectl config view -o yaml
32
+ HELP
33
+ parent.command :view do |view|
34
+ view.action do |global_options, _options, _args|
35
+ exit_code = execute(global_options)
36
+ exit exit_code if exit_code != 0
37
+ end
38
+ end
39
+ end
40
+
41
+ # Executes the view command.
42
+ #
43
+ # @param global_options [Hash] global CLI options (includes :config, :output)
44
+ # @return [Integer] exit code (0 for success)
45
+ def self.execute(global_options)
46
+ config_path = global_options[:config]
47
+ output_format = global_options[:output] || "yaml"
48
+
49
+ service = Pvectl::Config::Service.new
50
+ service.load(config: config_path)
51
+
52
+ masked_config = service.masked_config
53
+
54
+ case output_format
55
+ when "json"
56
+ puts JSON.pretty_generate(masked_config)
57
+ else
58
+ # Default to YAML for human-readable output
59
+ puts masked_config.to_yaml
60
+ end
61
+
62
+ 0
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # CLI registration for the `pvectl console` command.
6
+ #
7
+ # Opens an interactive terminal console to a VM or container via
8
+ # WebSocket-based termproxy. Dispatches to {ConsoleVm} or {ConsoleCt}
9
+ # based on the resource type argument.
10
+ #
11
+ # @example Open console to a VM
12
+ # pvectl console vm 100
13
+ #
14
+ # @example Open console to a container
15
+ # pvectl console ct 200
16
+ #
17
+ # @example Open console with explicit credentials
18
+ # pvectl console vm 100 --user root@pam --password secret
19
+ #
20
+ class Console
21
+ # Supported resource type arguments.
22
+ SUPPORTED_RESOURCES = %w[vm ct container].freeze
23
+
24
+ # Registers the console command with the CLI.
25
+ #
26
+ # @param cli [GLI::App] the CLI application object
27
+ # @return [void]
28
+ def self.register(cli)
29
+ cli.desc "Open interactive terminal console to a VM or container"
30
+ cli.long_desc <<~HELP
31
+ Open an interactive terminal session to a VM or container via
32
+ WebSocket-based termproxy (xtermjs protocol).
33
+
34
+ EXAMPLES
35
+ Open console to a VM:
36
+ $ pvectl console vm 100
37
+
38
+ Open console to a container:
39
+ $ pvectl console ct 200
40
+
41
+ Specify node explicitly:
42
+ $ pvectl console vm 100 --node pve1
43
+
44
+ Provide credentials (prompted for password if omitted):
45
+ $ pvectl console vm 100 --user root@pam
46
+
47
+ NOTES
48
+ Console requires session authentication (username/password). If your
49
+ config only has an API token, pvectl will prompt for credentials.
50
+
51
+ Disconnect with Ctrl+].
52
+
53
+ The console uses a WebSocket connection. Network interruptions will
54
+ close the session.
55
+
56
+ SEE ALSO
57
+ pvectl help describe View VM/container configuration
58
+ pvectl help start Start a stopped VM before connecting
59
+ HELP
60
+ cli.arg_name "RESOURCE_TYPE ID"
61
+ cli.command :console do |c|
62
+ c.desc "Filter by node name"
63
+ c.flag [:node, :n], arg_name: "NODE"
64
+
65
+ c.desc "Username for session authentication"
66
+ c.flag [:user], arg_name: "USER"
67
+
68
+ c.desc "Password for session authentication"
69
+ c.flag [:password], arg_name: "PASSWORD"
70
+
71
+ c.action do |global_options, options, args|
72
+ resource_type = args.shift
73
+ resource_id = args.shift
74
+
75
+ unless resource_type && SUPPORTED_RESOURCES.include?(resource_type)
76
+ $stderr.puts "Error: Resource type required (vm, ct)"
77
+ exit Pvectl::ExitCodes::USAGE_ERROR
78
+ end
79
+
80
+ exit_code = case resource_type
81
+ when "ct", "container"
82
+ ConsoleCt.execute(resource_id, options, global_options)
83
+ else
84
+ ConsoleVm.execute(resource_id, options, global_options)
85
+ end
86
+
87
+ exit exit_code if exit_code != 0
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "io/console"
4
+
5
+ module Pvectl
6
+ module Commands
7
+ # Handler for the `pvectl console ct` command.
8
+ #
9
+ # Opens an interactive terminal console session to an LXC container
10
+ # via WebSocket-based termproxy. Requires a running container and
11
+ # interactive terminal (TTY).
12
+ #
13
+ # @example Open console to container 200
14
+ # pvectl console ct 200
15
+ #
16
+ # @example Open console with explicit credentials
17
+ # pvectl console ct 200 --user root@pam --password secret
18
+ #
19
+ class ConsoleCt
20
+ # Executes the console container command.
21
+ #
22
+ # @param ctid [String, nil] container identifier
23
+ # @param options [Hash] command options (:user, :password)
24
+ # @param global_options [Hash] global CLI options
25
+ # @return [Integer] exit code
26
+ def self.execute(ctid, options, global_options)
27
+ new(ctid, options, global_options).execute
28
+ end
29
+
30
+ # Initializes a console container command.
31
+ #
32
+ # @param ctid [String, nil] container identifier
33
+ # @param options [Hash] command options
34
+ # @param global_options [Hash] global CLI options
35
+ def initialize(ctid, options, global_options)
36
+ @ctid = ctid
37
+ @options = options
38
+ @global_options = global_options
39
+ end
40
+
41
+ # Executes the console flow.
42
+ #
43
+ # @return [Integer] exit code
44
+ def execute
45
+ return usage_error("CTID is required") unless @ctid
46
+ return usage_error("Console requires an interactive terminal (TTY)") unless $stdin.tty?
47
+
48
+ load_config
49
+ connection = Pvectl::Connection.new(@config)
50
+ repo = Pvectl::Repositories::Container.new(connection)
51
+
52
+ resource = repo.get(@ctid.to_i)
53
+ return not_found("Container #{@ctid} not found") unless resource
54
+
55
+ username, password = resolve_credentials
56
+ return ExitCodes::GENERAL_ERROR if username.nil? || password.nil?
57
+
58
+ $stderr.puts "Connecting to container #{resource.vmid} (#{resource.name || 'unnamed'}) on node #{resource.node}..."
59
+
60
+ Pvectl::Services::Console.new.run(
61
+ resource: resource,
62
+ resource_path: resource_path,
63
+ server: @config.server,
64
+ username: username,
65
+ password: password,
66
+ verify_ssl: @config.verify_ssl
67
+ )
68
+
69
+ ExitCodes::SUCCESS
70
+ rescue Services::Console::ResourceNotRunningError => e
71
+ $stderr.puts "Error: #{e.message}"
72
+ ExitCodes::GENERAL_ERROR
73
+ rescue Services::Console::AuthenticationError => e
74
+ $stderr.puts "Error: #{e.message}"
75
+ ExitCodes::PERMISSION_DENIED
76
+ rescue Pvectl::Config::ConfigNotFoundError,
77
+ Pvectl::Config::InvalidConfigError,
78
+ Pvectl::Config::ContextNotFoundError,
79
+ Pvectl::Config::ClusterNotFoundError,
80
+ Pvectl::Config::UserNotFoundError,
81
+ Pvectl::Config::MissingCredentialsError
82
+ raise # re-raise for CLI handler
83
+ rescue Errno::ECONNREFUSED, SocketError, Timeout::Error => e
84
+ $stderr.puts "Error: Cannot connect to console: #{e.message}"
85
+ ExitCodes::CONNECTION_ERROR
86
+ rescue StandardError => e
87
+ $stderr.puts "Error: #{e.message}"
88
+ ExitCodes::GENERAL_ERROR
89
+ end
90
+
91
+ private
92
+
93
+ # Returns the API resource path for an LXC container.
94
+ #
95
+ # @return [String] resource path segment
96
+ def resource_path
97
+ "lxc/#{@ctid}"
98
+ end
99
+
100
+ # Resolves authentication credentials for the console session.
101
+ #
102
+ # Priority: CLI flags > config file > interactive prompt.
103
+ # Extracts default username from token_id when available.
104
+ #
105
+ # @return [Array<String, String>, Array<nil, nil>] [username, password] or [nil, nil] if cancelled
106
+ def resolve_credentials
107
+ username = @options[:user]
108
+ password = @options[:password]
109
+
110
+ if username.nil? && password.nil? && @config.username && @config.password
111
+ username = @config.username
112
+ password = @config.password
113
+ end
114
+
115
+ if username.nil? && @config.token_id
116
+ # Extract default username from token_id (e.g., "root@pam!pvectl" -> "root@pam")
117
+ default_username = @config.token_id.split("!").first
118
+ end
119
+
120
+ # When credentials come from config (username+password pair), use them directly.
121
+ # Otherwise, prompt interactively — always show both prompts so the user knows
122
+ # which username will be used and can change it.
123
+ if password.nil?
124
+ username = prompt_username(username || default_username)
125
+ return [nil, nil] if username.nil?
126
+
127
+ password = prompt_password
128
+ end
129
+ return [nil, nil] if password.nil?
130
+
131
+ [username, password]
132
+ end
133
+
134
+ # Prompts the user for a username.
135
+ #
136
+ # @param default [String, nil] default username to suggest
137
+ # @return [String, nil] entered username or nil if cancelled
138
+ def prompt_username(default = nil)
139
+ prompt = default ? "Username [#{default}]: " : "Username: "
140
+ $stderr.print prompt
141
+ input = $stdin.gets&.strip
142
+ return nil if input.nil?
143
+
144
+ input.empty? ? default : input
145
+ end
146
+
147
+ # Prompts the user for a password (hidden input).
148
+ #
149
+ # @return [String, nil] entered password or nil if cancelled
150
+ def prompt_password
151
+ $stderr.print "Password: "
152
+ password = $stdin.noecho(&:gets)&.strip
153
+ $stderr.puts # newline after hidden input
154
+ return nil if password.nil? || password.empty?
155
+
156
+ password
157
+ end
158
+
159
+ # Loads configuration from file or environment.
160
+ #
161
+ # @return [void]
162
+ def load_config
163
+ service = Pvectl::Config::Service.new
164
+ service.load(config: @global_options[:config])
165
+ @config = service.current_config
166
+ end
167
+
168
+ # Outputs a usage error and returns the exit code.
169
+ #
170
+ # @param message [String] error message
171
+ # @return [Integer] USAGE_ERROR exit code
172
+ def usage_error(message)
173
+ $stderr.puts "Error: #{message}"
174
+ ExitCodes::USAGE_ERROR
175
+ end
176
+
177
+ # Outputs a not-found error and returns the exit code.
178
+ #
179
+ # @param message [String] error message
180
+ # @return [Integer] NOT_FOUND exit code
181
+ def not_found(message)
182
+ $stderr.puts "Error: #{message}"
183
+ ExitCodes::NOT_FOUND
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "io/console"
4
+
5
+ module Pvectl
6
+ module Commands
7
+ # Handler for the `pvectl console vm` command.
8
+ #
9
+ # Opens an interactive terminal console session to a QEMU virtual machine
10
+ # via WebSocket-based termproxy. Requires a running VM and interactive
11
+ # terminal (TTY).
12
+ #
13
+ # @example Open console to VM 100
14
+ # pvectl console vm 100
15
+ #
16
+ # @example Open console with explicit credentials
17
+ # pvectl console vm 100 --user root@pam --password secret
18
+ #
19
+ class ConsoleVm
20
+ # Executes the console VM command.
21
+ #
22
+ # @param vmid [String, nil] VM identifier
23
+ # @param options [Hash] command options (:user, :password)
24
+ # @param global_options [Hash] global CLI options
25
+ # @return [Integer] exit code
26
+ def self.execute(vmid, options, global_options)
27
+ new(vmid, options, global_options).execute
28
+ end
29
+
30
+ # Initializes a console VM command.
31
+ #
32
+ # @param vmid [String, nil] VM identifier
33
+ # @param options [Hash] command options
34
+ # @param global_options [Hash] global CLI options
35
+ def initialize(vmid, options, global_options)
36
+ @vmid = vmid
37
+ @options = options
38
+ @global_options = global_options
39
+ end
40
+
41
+ # Executes the console flow.
42
+ #
43
+ # @return [Integer] exit code
44
+ def execute
45
+ return usage_error("VMID is required") unless @vmid
46
+ return usage_error("Console requires an interactive terminal (TTY)") unless $stdin.tty?
47
+
48
+ load_config
49
+ connection = Pvectl::Connection.new(@config)
50
+ repo = Pvectl::Repositories::Vm.new(connection)
51
+
52
+ resource = repo.get(@vmid.to_i)
53
+ return not_found("VM #{@vmid} not found") unless resource
54
+
55
+ username, password = resolve_credentials
56
+ return ExitCodes::GENERAL_ERROR if username.nil? || password.nil?
57
+
58
+ $stderr.puts "Connecting to VM #{resource.vmid} (#{resource.name || 'unnamed'}) on node #{resource.node}..."
59
+
60
+ Pvectl::Services::Console.new.run(
61
+ resource: resource,
62
+ resource_path: resource_path,
63
+ server: @config.server,
64
+ username: username,
65
+ password: password,
66
+ verify_ssl: @config.verify_ssl
67
+ )
68
+
69
+ ExitCodes::SUCCESS
70
+ rescue Services::Console::ResourceNotRunningError => e
71
+ $stderr.puts "Error: #{e.message}"
72
+ ExitCodes::GENERAL_ERROR
73
+ rescue Services::Console::AuthenticationError => e
74
+ $stderr.puts "Error: #{e.message}"
75
+ ExitCodes::PERMISSION_DENIED
76
+ rescue Pvectl::Config::ConfigNotFoundError,
77
+ Pvectl::Config::InvalidConfigError,
78
+ Pvectl::Config::ContextNotFoundError,
79
+ Pvectl::Config::ClusterNotFoundError,
80
+ Pvectl::Config::UserNotFoundError,
81
+ Pvectl::Config::MissingCredentialsError
82
+ raise # re-raise for CLI handler
83
+ rescue Errno::ECONNREFUSED, SocketError, Timeout::Error => e
84
+ $stderr.puts "Error: Cannot connect to console: #{e.message}"
85
+ ExitCodes::CONNECTION_ERROR
86
+ rescue StandardError => e
87
+ $stderr.puts "Error: #{e.message}"
88
+ ExitCodes::GENERAL_ERROR
89
+ end
90
+
91
+ private
92
+
93
+ # Returns the API resource path for a QEMU VM.
94
+ #
95
+ # @return [String] resource path segment
96
+ def resource_path
97
+ "qemu/#{@vmid}"
98
+ end
99
+
100
+ # Resolves authentication credentials for the console session.
101
+ #
102
+ # Priority: CLI flags > config file > interactive prompt.
103
+ # Extracts default username from token_id when available.
104
+ #
105
+ # @return [Array<String, String>, Array<nil, nil>] [username, password] or [nil, nil] if cancelled
106
+ def resolve_credentials
107
+ username = @options[:user]
108
+ password = @options[:password]
109
+
110
+ if username.nil? && password.nil? && @config.username && @config.password
111
+ username = @config.username
112
+ password = @config.password
113
+ end
114
+
115
+ if username.nil? && @config.token_id
116
+ # Extract default username from token_id (e.g., "root@pam!pvectl" -> "root@pam")
117
+ default_username = @config.token_id.split("!").first
118
+ end
119
+
120
+ # When credentials come from config (username+password pair), use them directly.
121
+ # Otherwise, prompt interactively — always show both prompts so the user knows
122
+ # which username will be used and can change it.
123
+ if password.nil?
124
+ username = prompt_username(username || default_username)
125
+ return [nil, nil] if username.nil?
126
+
127
+ password = prompt_password
128
+ end
129
+ return [nil, nil] if password.nil?
130
+
131
+ [username, password]
132
+ end
133
+
134
+ # Prompts the user for a username.
135
+ #
136
+ # @param default [String, nil] default username to suggest
137
+ # @return [String, nil] entered username or nil if cancelled
138
+ def prompt_username(default = nil)
139
+ prompt = default ? "Username [#{default}]: " : "Username: "
140
+ $stderr.print prompt
141
+ input = $stdin.gets&.strip
142
+ return nil if input.nil?
143
+
144
+ input.empty? ? default : input
145
+ end
146
+
147
+ # Prompts the user for a password (hidden input).
148
+ #
149
+ # @return [String, nil] entered password or nil if cancelled
150
+ def prompt_password
151
+ $stderr.print "Password: "
152
+ password = $stdin.noecho(&:gets)&.strip
153
+ $stderr.puts # newline after hidden input
154
+ return nil if password.nil? || password.empty?
155
+
156
+ password
157
+ end
158
+
159
+ # Loads configuration from file or environment.
160
+ #
161
+ # @return [void]
162
+ def load_config
163
+ service = Pvectl::Config::Service.new
164
+ service.load(config: @global_options[:config])
165
+ @config = service.current_config
166
+ end
167
+
168
+ # Outputs a usage error and returns the exit code.
169
+ #
170
+ # @param message [String] error message
171
+ # @return [Integer] USAGE_ERROR exit code
172
+ def usage_error(message)
173
+ $stderr.puts "Error: #{message}"
174
+ ExitCodes::USAGE_ERROR
175
+ end
176
+
177
+ # Outputs a not-found error and returns the exit code.
178
+ #
179
+ # @param message [String] error message
180
+ # @return [Integer] NOT_FOUND exit code
181
+ def not_found(message)
182
+ $stderr.puts "Error: #{message}"
183
+ ExitCodes::NOT_FOUND
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pvectl
4
+ module Commands
5
+ # Container-specific lifecycle command hooks.
6
+ #
7
+ # Provides container repository, selector, service, and presenter
8
+ # to ResourceLifecycleCommand's template methods.
9
+ #
10
+ # @example Usage
11
+ # class StartContainer
12
+ # include ContainerLifecycleCommand
13
+ # OPERATION = :start
14
+ # end
15
+ #
16
+ module ContainerLifecycleCommand
17
+ include ResourceLifecycleCommand
18
+
19
+ # Extends the including class with ClassMethods from ResourceLifecycleCommand.
20
+ #
21
+ # @param base [Class] the class including this module
22
+ def self.included(base)
23
+ base.extend(ResourceLifecycleCommand::ClassMethods)
24
+ end
25
+
26
+ private
27
+
28
+ # @return [Array<String>] supported resource types
29
+ def supported_resources
30
+ %w[container ct]
31
+ end
32
+
33
+ # @return [String] human label for container resources
34
+ def resource_label
35
+ "container"
36
+ end
37
+
38
+ # @return [String] human label for container IDs
39
+ def resource_id_label
40
+ "CTID"
41
+ end
42
+
43
+ # Builds container repository.
44
+ #
45
+ # @param connection [Connection] API connection
46
+ # @return [Repositories::Container] container repository
47
+ def build_repository(connection)
48
+ Pvectl::Repositories::Container.new(connection)
49
+ end
50
+
51
+ # Builds container lifecycle service.
52
+ #
53
+ # @param repo [Repositories::Container] container repository
54
+ # @param task_repo [Repositories::Task] task repository
55
+ # @param options [Hash] service options
56
+ # @return [Services::ContainerLifecycle] container lifecycle service
57
+ def build_service(repo, task_repo, options)
58
+ Pvectl::Services::ContainerLifecycle.new(repo, task_repo, options)
59
+ end
60
+
61
+ # Builds container operation result presenter.
62
+ #
63
+ # @return [Presenters::ContainerOperationResult] presenter
64
+ def build_presenter
65
+ Pvectl::Presenters::ContainerOperationResult.new
66
+ end
67
+
68
+ # Builds container selector from strings.
69
+ #
70
+ # @param strings [Array<String>] selector strings
71
+ # @return [Selectors::Container] parsed selector
72
+ def build_selector(strings)
73
+ Pvectl::Selectors::Container.parse_all(strings)
74
+ end
75
+ end
76
+ end
77
+ end