proxcli 0.4.0__tar.gz → 0.6.0__tar.gz

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 (52) hide show
  1. {proxcli-0.4.0 → proxcli-0.6.0}/CHANGELOG.md +15 -0
  2. {proxcli-0.4.0 → proxcli-0.6.0}/PKG-INFO +125 -1
  3. {proxcli-0.4.0 → proxcli-0.6.0}/README.md +124 -0
  4. proxcli-0.6.0/proxmox/cli/completion.py +214 -0
  5. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/main.py +12 -4
  6. proxcli-0.6.0/proxmox/cli/pool.py +74 -0
  7. {proxcli-0.4.0 → proxcli-0.6.0}/pyproject.toml +1 -1
  8. {proxcli-0.4.0 → proxcli-0.6.0}/uv.lock +1 -1
  9. {proxcli-0.4.0 → proxcli-0.6.0}/.env.example +0 -0
  10. {proxcli-0.4.0 → proxcli-0.6.0}/.github/workflows/ci.yml +0 -0
  11. {proxcli-0.4.0 → proxcli-0.6.0}/.gitignore +0 -0
  12. {proxcli-0.4.0 → proxcli-0.6.0}/.python-version +0 -0
  13. {proxcli-0.4.0 → proxcli-0.6.0}/AGENTS.md +0 -0
  14. {proxcli-0.4.0 → proxcli-0.6.0}/PLAN.md +0 -0
  15. {proxcli-0.4.0 → proxcli-0.6.0}/PROJECT.md +0 -0
  16. {proxcli-0.4.0 → proxcli-0.6.0}/PROMPT.md +0 -0
  17. {proxcli-0.4.0 → proxcli-0.6.0}/TODO.md +0 -0
  18. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/__init__.py +0 -0
  19. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/__init__.py +0 -0
  20. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/auth.py +0 -0
  21. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/cluster.py +0 -0
  22. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/container.py +0 -0
  23. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/firewall_helpers.py +0 -0
  24. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/node.py +0 -0
  25. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/storage.py +0 -0
  26. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/tasks.py +0 -0
  27. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/cli/vm.py +0 -0
  28. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/client/__init__.py +0 -0
  29. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/client/auth.py +0 -0
  30. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/client/client.py +0 -0
  31. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/client/exceptions.py +0 -0
  32. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/config/__init__.py +0 -0
  33. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/config/config.py +0 -0
  34. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/config/models.py +0 -0
  35. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/output/__init__.py +0 -0
  36. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/output/formatter.py +0 -0
  37. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/output/json_fmt.py +0 -0
  38. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/output/table_fmt.py +0 -0
  39. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/output/yaml_fmt.py +0 -0
  40. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/utils/__init__.py +0 -0
  41. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/utils/helpers.py +0 -0
  42. {proxcli-0.4.0 → proxcli-0.6.0}/proxmox/utils/logging.py +0 -0
  43. {proxcli-0.4.0 → proxcli-0.6.0}/tests/__init__.py +0 -0
  44. {proxcli-0.4.0 → proxcli-0.6.0}/tests/conftest.py +0 -0
  45. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_auth.py +0 -0
  46. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_cli/__init__.py +0 -0
  47. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_cli/test_main.py +0 -0
  48. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_client.py +0 -0
  49. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_config.py +0 -0
  50. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_integration/__init__.py +0 -0
  51. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_output/__init__.py +0 -0
  52. {proxcli-0.4.0 → proxcli-0.6.0}/tests/test_output/test_formatter.py +0 -0
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-06-20
11
+
12
+ ### Added
13
+ - Shell completion support: `proxmox completion bash|zsh|fish`.
14
+ Generated scripts introspect the parser tree and stay in sync
15
+ with all registered resources and actions.
16
+
17
+ ## [0.5.0] - 2026-06-20
18
+
19
+ ### Added
20
+ - Pool management (`proxmox pool`): list, show, create, update, delete.
21
+ Wraps `/pools` endpoints.
22
+
10
23
  ## [0.4.0] - 2026-06-20
11
24
 
12
25
  ### Added
@@ -61,6 +74,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
61
74
  - CSRF ticket auto-refresh on 401.
62
75
  - AI-agent-friendly: default JSON output, strict exit codes, `--dry-run` mode.
63
76
 
77
+ [0.6.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.6.0
78
+ [0.5.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.5.0
64
79
  [0.4.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.4.0
65
80
  [0.3.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.3.0
66
81
  [0.2.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.2.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxcli
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: A CLI tool to interact with Proxmox VE nodes and clusters via the REST API
5
5
  Author-email: Xabi Ezpeleta <xezpeleta@gmail.com>
6
6
  License: MIT
@@ -51,6 +51,11 @@ proxmox auth login --url https://192.168.1.10:8006 --username root@pam --passwor
51
51
  # Or with an API token
52
52
  proxmox auth login --url https://192.168.1.10:8006 --username root@pam --api-token 'root@pam!my-token=deadbeef...'
53
53
 
54
+ # Enable shell completions
55
+ source <(proxmox completion bash) # bash
56
+ source <(proxmox completion zsh) # zsh
57
+ proxmox completion fish | source # fish (or save to ~/.config/fish/completions/proxmox.fish)
58
+
54
59
  # Check auth status
55
60
  proxmox auth status
56
61
 
@@ -129,6 +134,27 @@ proxmox auth status # Show current auth context
129
134
  proxmox auth clear # Remove saved credentials
130
135
  ```
131
136
 
137
+ ### Completion
138
+
139
+ ```bash
140
+ proxmox completion bash # Emit bash completion script
141
+ proxmox completion zsh # Emit zsh completion script
142
+ proxmox completion fish # Emit fish completion script
143
+ ```
144
+
145
+ Add to your shell's rc file:
146
+
147
+ ```bash
148
+ # bash (~/.bashrc)
149
+ source <(proxmox completion bash)
150
+
151
+ # zsh (~/.zshrc)
152
+ source <(proxmox completion zsh)
153
+
154
+ # fish (~/.config/fish/completions/proxmox.fish)
155
+ proxmox completion fish > ~/.config/fish/completions/proxmox.fish
156
+ ```
157
+
132
158
  ### VM (QEMU)
133
159
 
134
160
  ```bash
@@ -141,6 +167,18 @@ proxmox vm reboot <vmid> [--node <node>]
141
167
  proxmox vm suspend <vmid> [--node <node>]
142
168
  proxmox vm resume <vmid> [--node <node>]
143
169
  proxmox vm delete <vmid> [--node <node>] [--force] [--purge]
170
+
171
+ # VM firewall
172
+ proxmox vm firewall options <vmid> [--node <node>]
173
+ proxmox vm firewall enable <vmid> [--node <node>]
174
+ proxmox vm firewall disable <vmid> [--node <node>]
175
+ proxmox vm firewall policy <vmid> --in-policy ACCEPT --out-policy DROP [--node <node>]
176
+ proxmox vm firewall rules list <vmid> [--node <node>]
177
+ proxmox vm firewall rules add <vmid> --action ACCEPT --dport 22 --proto tcp [--source <cidr>] [--comment <text>]
178
+ proxmox vm firewall rules show <vmid> <pos>
179
+ proxmox vm firewall rules update <vmid> <pos> --action DROP
180
+ proxmox vm firewall rules delete <vmid> <pos>
181
+ proxmox vm firewall refs <vmid> [--type alias|ipset|group]
144
182
  ```
145
183
 
146
184
  ### Container (LXC)
@@ -152,6 +190,18 @@ proxmox container create --node <node> --vmid <id> --ostemplate <tmpl> [--memory
152
190
  proxmox container start <vmid> [--node <node>]
153
191
  proxmox container stop <vmid> [--node <node>]
154
192
  proxmox container delete <vmid> [--node <node>] [--force] [--purge]
193
+
194
+ # Container firewall
195
+ proxmox container firewall options <vmid> [--node <node>]
196
+ proxmox container firewall enable <vmid> [--node <node>]
197
+ proxmox container firewall disable <vmid> [--node <node>]
198
+ proxmox container firewall policy <vmid> --in-policy ACCEPT --out-policy DROP
199
+ proxmox container firewall rules list <vmid> [--node <node>]
200
+ proxmox container firewall rules add <vmid> --action ACCEPT --dport 22 --proto tcp
201
+ proxmox container firewall rules show <vmid> <pos>
202
+ proxmox container firewall rules update <vmid> <pos> --action DROP
203
+ proxmox container firewall rules delete <vmid> <pos>
204
+ proxmox container firewall refs <vmid> [--type alias|ipset|group]
155
205
  ```
156
206
 
157
207
  ### Node
@@ -160,6 +210,18 @@ proxmox container delete <vmid> [--node <node>] [--force] [--purge]
160
210
  proxmox node list
161
211
  proxmox node show <node>
162
212
  proxmox node status [<node>]
213
+
214
+ # Node firewall
215
+ proxmox node firewall options <node>
216
+ proxmox node firewall enable <node>
217
+ proxmox node firewall disable <node>
218
+ proxmox node firewall policy <node> --in-policy ACCEPT --out-policy DROP
219
+ proxmox node firewall rules list <node>
220
+ proxmox node firewall rules add <node> --action ACCEPT --dport 22 --proto tcp
221
+ proxmox node firewall rules show <node> <pos>
222
+ proxmox node firewall rules update <node> <pos> --action DROP
223
+ proxmox node firewall rules delete <node> <pos>
224
+ proxmox node firewall refs <node> [--type alias|ipset|group]
163
225
  ```
164
226
 
165
227
  ### Storage
@@ -168,12 +230,45 @@ proxmox node status [<node>]
168
230
  proxmox storage list [--node <node>]
169
231
  proxmox storage show <storage>
170
232
  proxmox storage content <storage> [--node <node>]
233
+ proxmox storage upload --node <node> --storage <storage> --file <path> [--content-type iso|vztmpl|import]
234
+ ```
235
+
236
+ ### Pool
237
+
238
+ ```bash
239
+ proxmox pool list
240
+ proxmox pool show <poolid>
241
+ proxmox pool create <poolid> [--comment <text>]
242
+ proxmox pool update <poolid> [--comment <text>] [--allow-delete]
243
+ proxmox pool delete <poolid>
171
244
  ```
172
245
 
173
246
  ### Cluster
174
247
 
175
248
  ```bash
176
249
  proxmox cluster status
250
+
251
+ # Cluster firewall
252
+ proxmox cluster firewall options
253
+ proxmox cluster firewall enable
254
+ proxmox cluster firewall disable
255
+ proxmox cluster firewall policy --in-policy ACCEPT --out-policy DROP
256
+ proxmox cluster firewall rules # list (shorthand)
257
+ proxmox cluster firewall rules list # list (explicit)
258
+ proxmox cluster firewall rules add --action ACCEPT --dport 22 --source 10.0.0.0/8
259
+ proxmox cluster firewall rules show <pos>
260
+ proxmox cluster firewall rules update <pos> --action DROP
261
+ proxmox cluster firewall rules delete <pos>
262
+ proxmox cluster firewall aliases # list (shorthand)
263
+ proxmox cluster firewall aliases add <name> --cidr 10.0.0.0/24 --comment "web tier"
264
+ proxmox cluster firewall aliases delete <name>
265
+ proxmox cluster firewall ipsets # list (shorthand)
266
+ proxmox cluster firewall ipsets add <name> --comment "trusted hosts"
267
+ proxmox cluster firewall ipsets show <name>
268
+ proxmox cluster firewall ipsets delete <name>
269
+ proxmox cluster firewall ipsets add-cidr <name> --cidr 192.168.1.0/24
270
+ proxmox cluster firewall ipsets delete-cidr <name> --cidr 192.168.1.0/24
271
+ proxmox cluster firewall refs [--type alias|ipset|group]
177
272
  ```
178
273
 
179
274
  ### Task
@@ -260,3 +355,32 @@ uv build
260
355
  ## License
261
356
 
262
357
  MIT
358
+
359
+ ## Firewall Rule Options
360
+
361
+ Firewall rules share the same flags across cluster, node, VM, and container. The `--macro` flag can be used as a shortcut for common services (e.g., `--macro SSH` sets up port 22/tcp).
362
+
363
+ | Flag | Values | Description |
364
+ |---|---|---|
365
+ | `--action` | `ACCEPT`, `DENY`, `REJECT` | Rule action (required for `add`) |
366
+ | `--type` | `in`, `out` | Traffic direction (default: `in`) |
367
+ | `--iface` | e.g. `net0` | Network interface |
368
+ | `--source` | CIDR | Source IP/CIDR |
369
+ | `--dest` | CIDR | Destination IP/CIDR |
370
+ | `--dport` | e.g. `80` or `8000-9000` | Destination port |
371
+ | `--sport` | e.g. `1024-65535` | Source port |
372
+ | `--proto` | `tcp`, `udp`, `icmp`, `any` | Protocol |
373
+ | `--macro` | e.g. `SSH`, `HTTP`, `HTTPS`, `Ping` | Pre-defined service macro |
374
+ | `--comment` | text | Comment / description |
375
+ | `--enable` | `0`, `1` | Enable the rule (default: `1`) |
376
+ | `--log` | `emerg`..`debug`, `nolog` | Log level |
377
+
378
+ Example:
379
+
380
+ ```bash
381
+ # Allow SSH from a specific subnet
382
+ proxmox vm firewall rules add 100 --action ACCEPT --dport 22 --proto tcp --source 192.168.1.0/24 --comment "Admin SSH"
383
+
384
+ # Or use a macro
385
+ proxmox vm firewall rules add 100 --action ACCEPT --macro SSH --source 192.168.1.0/24
386
+ ```
@@ -28,6 +28,11 @@ proxmox auth login --url https://192.168.1.10:8006 --username root@pam --passwor
28
28
  # Or with an API token
29
29
  proxmox auth login --url https://192.168.1.10:8006 --username root@pam --api-token 'root@pam!my-token=deadbeef...'
30
30
 
31
+ # Enable shell completions
32
+ source <(proxmox completion bash) # bash
33
+ source <(proxmox completion zsh) # zsh
34
+ proxmox completion fish | source # fish (or save to ~/.config/fish/completions/proxmox.fish)
35
+
31
36
  # Check auth status
32
37
  proxmox auth status
33
38
 
@@ -106,6 +111,27 @@ proxmox auth status # Show current auth context
106
111
  proxmox auth clear # Remove saved credentials
107
112
  ```
108
113
 
114
+ ### Completion
115
+
116
+ ```bash
117
+ proxmox completion bash # Emit bash completion script
118
+ proxmox completion zsh # Emit zsh completion script
119
+ proxmox completion fish # Emit fish completion script
120
+ ```
121
+
122
+ Add to your shell's rc file:
123
+
124
+ ```bash
125
+ # bash (~/.bashrc)
126
+ source <(proxmox completion bash)
127
+
128
+ # zsh (~/.zshrc)
129
+ source <(proxmox completion zsh)
130
+
131
+ # fish (~/.config/fish/completions/proxmox.fish)
132
+ proxmox completion fish > ~/.config/fish/completions/proxmox.fish
133
+ ```
134
+
109
135
  ### VM (QEMU)
110
136
 
111
137
  ```bash
@@ -118,6 +144,18 @@ proxmox vm reboot <vmid> [--node <node>]
118
144
  proxmox vm suspend <vmid> [--node <node>]
119
145
  proxmox vm resume <vmid> [--node <node>]
120
146
  proxmox vm delete <vmid> [--node <node>] [--force] [--purge]
147
+
148
+ # VM firewall
149
+ proxmox vm firewall options <vmid> [--node <node>]
150
+ proxmox vm firewall enable <vmid> [--node <node>]
151
+ proxmox vm firewall disable <vmid> [--node <node>]
152
+ proxmox vm firewall policy <vmid> --in-policy ACCEPT --out-policy DROP [--node <node>]
153
+ proxmox vm firewall rules list <vmid> [--node <node>]
154
+ proxmox vm firewall rules add <vmid> --action ACCEPT --dport 22 --proto tcp [--source <cidr>] [--comment <text>]
155
+ proxmox vm firewall rules show <vmid> <pos>
156
+ proxmox vm firewall rules update <vmid> <pos> --action DROP
157
+ proxmox vm firewall rules delete <vmid> <pos>
158
+ proxmox vm firewall refs <vmid> [--type alias|ipset|group]
121
159
  ```
122
160
 
123
161
  ### Container (LXC)
@@ -129,6 +167,18 @@ proxmox container create --node <node> --vmid <id> --ostemplate <tmpl> [--memory
129
167
  proxmox container start <vmid> [--node <node>]
130
168
  proxmox container stop <vmid> [--node <node>]
131
169
  proxmox container delete <vmid> [--node <node>] [--force] [--purge]
170
+
171
+ # Container firewall
172
+ proxmox container firewall options <vmid> [--node <node>]
173
+ proxmox container firewall enable <vmid> [--node <node>]
174
+ proxmox container firewall disable <vmid> [--node <node>]
175
+ proxmox container firewall policy <vmid> --in-policy ACCEPT --out-policy DROP
176
+ proxmox container firewall rules list <vmid> [--node <node>]
177
+ proxmox container firewall rules add <vmid> --action ACCEPT --dport 22 --proto tcp
178
+ proxmox container firewall rules show <vmid> <pos>
179
+ proxmox container firewall rules update <vmid> <pos> --action DROP
180
+ proxmox container firewall rules delete <vmid> <pos>
181
+ proxmox container firewall refs <vmid> [--type alias|ipset|group]
132
182
  ```
133
183
 
134
184
  ### Node
@@ -137,6 +187,18 @@ proxmox container delete <vmid> [--node <node>] [--force] [--purge]
137
187
  proxmox node list
138
188
  proxmox node show <node>
139
189
  proxmox node status [<node>]
190
+
191
+ # Node firewall
192
+ proxmox node firewall options <node>
193
+ proxmox node firewall enable <node>
194
+ proxmox node firewall disable <node>
195
+ proxmox node firewall policy <node> --in-policy ACCEPT --out-policy DROP
196
+ proxmox node firewall rules list <node>
197
+ proxmox node firewall rules add <node> --action ACCEPT --dport 22 --proto tcp
198
+ proxmox node firewall rules show <node> <pos>
199
+ proxmox node firewall rules update <node> <pos> --action DROP
200
+ proxmox node firewall rules delete <node> <pos>
201
+ proxmox node firewall refs <node> [--type alias|ipset|group]
140
202
  ```
141
203
 
142
204
  ### Storage
@@ -145,12 +207,45 @@ proxmox node status [<node>]
145
207
  proxmox storage list [--node <node>]
146
208
  proxmox storage show <storage>
147
209
  proxmox storage content <storage> [--node <node>]
210
+ proxmox storage upload --node <node> --storage <storage> --file <path> [--content-type iso|vztmpl|import]
211
+ ```
212
+
213
+ ### Pool
214
+
215
+ ```bash
216
+ proxmox pool list
217
+ proxmox pool show <poolid>
218
+ proxmox pool create <poolid> [--comment <text>]
219
+ proxmox pool update <poolid> [--comment <text>] [--allow-delete]
220
+ proxmox pool delete <poolid>
148
221
  ```
149
222
 
150
223
  ### Cluster
151
224
 
152
225
  ```bash
153
226
  proxmox cluster status
227
+
228
+ # Cluster firewall
229
+ proxmox cluster firewall options
230
+ proxmox cluster firewall enable
231
+ proxmox cluster firewall disable
232
+ proxmox cluster firewall policy --in-policy ACCEPT --out-policy DROP
233
+ proxmox cluster firewall rules # list (shorthand)
234
+ proxmox cluster firewall rules list # list (explicit)
235
+ proxmox cluster firewall rules add --action ACCEPT --dport 22 --source 10.0.0.0/8
236
+ proxmox cluster firewall rules show <pos>
237
+ proxmox cluster firewall rules update <pos> --action DROP
238
+ proxmox cluster firewall rules delete <pos>
239
+ proxmox cluster firewall aliases # list (shorthand)
240
+ proxmox cluster firewall aliases add <name> --cidr 10.0.0.0/24 --comment "web tier"
241
+ proxmox cluster firewall aliases delete <name>
242
+ proxmox cluster firewall ipsets # list (shorthand)
243
+ proxmox cluster firewall ipsets add <name> --comment "trusted hosts"
244
+ proxmox cluster firewall ipsets show <name>
245
+ proxmox cluster firewall ipsets delete <name>
246
+ proxmox cluster firewall ipsets add-cidr <name> --cidr 192.168.1.0/24
247
+ proxmox cluster firewall ipsets delete-cidr <name> --cidr 192.168.1.0/24
248
+ proxmox cluster firewall refs [--type alias|ipset|group]
154
249
  ```
155
250
 
156
251
  ### Task
@@ -237,3 +332,32 @@ uv build
237
332
  ## License
238
333
 
239
334
  MIT
335
+
336
+ ## Firewall Rule Options
337
+
338
+ Firewall rules share the same flags across cluster, node, VM, and container. The `--macro` flag can be used as a shortcut for common services (e.g., `--macro SSH` sets up port 22/tcp).
339
+
340
+ | Flag | Values | Description |
341
+ |---|---|---|
342
+ | `--action` | `ACCEPT`, `DENY`, `REJECT` | Rule action (required for `add`) |
343
+ | `--type` | `in`, `out` | Traffic direction (default: `in`) |
344
+ | `--iface` | e.g. `net0` | Network interface |
345
+ | `--source` | CIDR | Source IP/CIDR |
346
+ | `--dest` | CIDR | Destination IP/CIDR |
347
+ | `--dport` | e.g. `80` or `8000-9000` | Destination port |
348
+ | `--sport` | e.g. `1024-65535` | Source port |
349
+ | `--proto` | `tcp`, `udp`, `icmp`, `any` | Protocol |
350
+ | `--macro` | e.g. `SSH`, `HTTP`, `HTTPS`, `Ping` | Pre-defined service macro |
351
+ | `--comment` | text | Comment / description |
352
+ | `--enable` | `0`, `1` | Enable the rule (default: `1`) |
353
+ | `--log` | `emerg`..`debug`, `nolog` | Log level |
354
+
355
+ Example:
356
+
357
+ ```bash
358
+ # Allow SSH from a specific subnet
359
+ proxmox vm firewall rules add 100 --action ACCEPT --dport 22 --proto tcp --source 192.168.1.0/24 --comment "Admin SSH"
360
+
361
+ # Or use a macro
362
+ proxmox vm firewall rules add 100 --action ACCEPT --macro SSH --source 192.168.1.0/24
363
+ ```
@@ -0,0 +1,214 @@
1
+ """Shell completion script generation for bash, zsh, and fish."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import textwrap
7
+
8
+ # Each resource has a known list of actions and optional flag completions.
9
+ # We introspect from the parser instead of hardcoding, keeping this DRY.
10
+
11
+ BASH_SCRIPT = textwrap.dedent("""\
12
+ _proxmox_completion() {{
13
+ local cur prev words cword
14
+ _init_completion || return
15
+
16
+ # Global flags
17
+ local global_flags="--url --username --password --password-stdin --api-token --output --dry-run --insecure --timeout --verbose --version --help"
18
+
19
+ # Resources and their actions (autogenerated)
20
+ declare -A _proxmox_resources
21
+ {resource_map}
22
+
23
+ # If we're at the first positional (after global flags), complete resources
24
+ if [[ $cword -eq 1 ]] || [[ "${{words[$((cword-1))]}}" =~ ^(--.*) ]]; then
25
+ COMPREPLY=($(compgen -W "${{!_proxmox_resources[*]}}" -- "$cur"))
26
+ return
27
+ fi
28
+
29
+ # Find the resource word
30
+ local resource=""
31
+ for w in "${{words[@]:1}}"; do
32
+ if [[ -n "${{_proxmox_resources[$w]}}" ]]; then
33
+ resource="$w"
34
+ break
35
+ fi
36
+ done
37
+
38
+ if [[ -n "$resource" ]]; then
39
+ local actions="${{_proxmox_resources[$resource]}}"
40
+ # Complete actions
41
+ COMPREPLY=($(compgen -W "$actions" -- "$cur"))
42
+ fi
43
+ }}
44
+
45
+ complete -F _proxmox_completion proxmox
46
+ """)
47
+
48
+
49
+ ZSH_SCRIPT = textwrap.dedent("""\
50
+ #compdef proxmox
51
+
52
+ _proxmox() {{
53
+ local -a resources
54
+ resources=({resources})
55
+
56
+ local -A resource_actions
57
+ {resource_zsh_map}
58
+
59
+ local curcontext="$curcontext" state line
60
+ typeset -A opt_args
61
+
62
+ _arguments -C \\
63
+ {resource_zsh_flags} \\
64
+ '1:resource:($resources)' \\
65
+ '*::arg:->args'
66
+
67
+ case $state in
68
+ args)
69
+ local resource="$line[1]"
70
+ if [[ -n "${{resource_actions[$resource]}}" ]]; then
71
+ _arguments "*:action:(${{resource_actions[$resource]}})"
72
+ fi
73
+ ;;
74
+ esac
75
+ }}
76
+
77
+ _proxmox "$@"
78
+ """)
79
+
80
+
81
+ FISH_SCRIPT = textwrap.dedent("""\
82
+ function __fish_proxmox_resources
83
+ echo {resources}
84
+ end
85
+
86
+ function __fish_proxmox_actions
87
+ set -l resource (commandline -opc)[1]
88
+ switch $resource
89
+ {fish_cases}
90
+ end
91
+ end
92
+
93
+ # Global flags
94
+ complete -c proxmox -f
95
+ complete -c proxmox -l url -d 'Proxmox API URL'
96
+ complete -c proxmox -l username -d 'Username'
97
+ complete -c proxmox -l password -d 'Password'
98
+ complete -c proxmox -l password-stdin -d 'Read password from stdin'
99
+ complete -c proxmox -l api-token -d 'API token'
100
+ complete -c proxmox -l output -a 'json table yaml' -d 'Output format'
101
+ complete -c proxmox -l dry-run -d 'Print request without executing'
102
+ complete -c proxmox -l insecure -d 'Skip TLS verification'
103
+ complete -c proxmox -l timeout -d 'Request timeout in seconds'
104
+ complete -c proxmox -l verbose -d 'Enable debug output'
105
+ complete -c proxmox -l version -d 'Show version'
106
+ complete -c proxmox -l help -d 'Show help'
107
+
108
+ # Resource completion at position 1
109
+ complete -c proxmox -n 'not __fish_seen_subcommand_from {resources}' \\
110
+ -a '(__fish_proxmox_resources)'
111
+
112
+ # Action completion per resource
113
+ {fish_action_completions}
114
+ """)
115
+
116
+
117
+ def _collect_parser_tree(parser: argparse.ArgumentParser) -> dict[str, list[str]]:
118
+ """Walk the root parser and collect resource -> action mapping."""
119
+ resource_actions: dict[str, list[str]] = {}
120
+
121
+ for action in parser._actions:
122
+ if not isinstance(action, argparse._SubParsersAction):
123
+ continue
124
+ for name, sub_parser in action.choices.items():
125
+ sub_actions: list[str] = []
126
+ for sa in sub_parser._actions:
127
+ if isinstance(sa, argparse._SubParsersAction):
128
+ sub_actions.extend(sa.choices.keys())
129
+ resource_actions[name] = sorted(sub_actions)
130
+
131
+ return resource_actions
132
+
133
+
134
+ def generate_bash(parser: argparse.ArgumentParser) -> str:
135
+ """Generate a bash completion script."""
136
+ resources = _collect_parser_tree(parser)
137
+ lines = []
138
+ for name, actions in sorted(resources.items()):
139
+ lines.append(f' _proxmox_resources[{name}]="{ " ".join(actions) }"')
140
+ return BASH_SCRIPT.format(resource_map="\n".join(lines))
141
+
142
+
143
+ def generate_zsh(parser: argparse.ArgumentParser) -> str:
144
+ """Generate a zsh completion script."""
145
+ resources = _collect_parser_tree(parser)
146
+ resources_list = " ".join(sorted(resources.keys()))
147
+
148
+ zsh_lines = []
149
+ for name, actions in sorted(resources.items()):
150
+ zsh_lines.append(f' resource_actions[{name}]="{ " ".join(actions) }"')
151
+
152
+ return ZSH_SCRIPT.format(
153
+ resources=resources_list,
154
+ resource_zsh_map="\n".join(zsh_lines),
155
+ resource_zsh_flags="",
156
+ )
157
+
158
+
159
+ def generate_fish(parser: argparse.ArgumentParser) -> str:
160
+ """Generate a fish completion script."""
161
+ resources = _collect_parser_tree(parser)
162
+ resources_list = " ".join(sorted(resources.keys()))
163
+
164
+ fish_cases = []
165
+ for name, actions in sorted(resources.items()):
166
+ fish_cases.append(f" case {name}\n echo {' '.join(actions)}")
167
+
168
+ fish_completions = []
169
+ for name, actions in sorted(resources.items()):
170
+ fish_completions.append(
171
+ f"complete -c proxmox -n '__fish_seen_subcommand_from {name}' "
172
+ f"-a '{' '.join(actions)}'"
173
+ )
174
+
175
+ return FISH_SCRIPT.format(
176
+ resources=resources_list,
177
+ fish_cases="\n".join(fish_cases),
178
+ fish_action_completions="\n".join(fish_completions),
179
+ )
180
+
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # CLI registration
184
+ # ---------------------------------------------------------------------------
185
+
186
+
187
+ def register_completion_parser(subparsers: argparse._SubParsersAction) -> None:
188
+ """Register the `proxmox completion` subcommand."""
189
+ comp_parser = subparsers.add_parser(
190
+ "completion", help="Generate shell completion script"
191
+ )
192
+ comp_parser.add_argument(
193
+ "shell",
194
+ choices=["bash", "zsh", "fish"],
195
+ help="Target shell",
196
+ )
197
+ comp_parser.set_defaults(func=_emit_completion)
198
+
199
+
200
+ def _emit_completion(args: argparse.Namespace, _client: object) -> str:
201
+ """Emit the completion script for the requested shell.
202
+
203
+ We need access to the root parser to introspect the subcommand tree,
204
+ so we import the builder here.
205
+ """
206
+ from proxmox.cli.main import build_root_parser
207
+
208
+ parser = build_root_parser()
209
+ generators = {
210
+ "bash": generate_bash,
211
+ "zsh": generate_zsh,
212
+ "fish": generate_fish,
213
+ }
214
+ return generators[args.shell](parser)
@@ -57,8 +57,10 @@ def build_root_parser() -> argparse.ArgumentParser:
57
57
  # Import and register subcommands
58
58
  from proxmox.cli.auth import register_auth_parser
59
59
  from proxmox.cli.cluster import register_cluster_parser
60
+ from proxmox.cli.completion import register_completion_parser
60
61
  from proxmox.cli.container import register_container_parser
61
62
  from proxmox.cli.node import register_node_parser
63
+ from proxmox.cli.pool import register_pool_parser
62
64
  from proxmox.cli.storage import register_storage_parser
63
65
  from proxmox.cli.tasks import register_task_parser
64
66
  from proxmox.cli.vm import register_vm_parser
@@ -66,9 +68,11 @@ def build_root_parser() -> argparse.ArgumentParser:
66
68
  register_auth_parser(subparsers)
67
69
  register_vm_parser(subparsers)
68
70
  register_node_parser(subparsers)
71
+ register_pool_parser(subparsers)
69
72
  register_container_parser(subparsers)
70
73
  register_storage_parser(subparsers)
71
74
  register_cluster_parser(subparsers)
75
+ register_completion_parser(subparsers)
72
76
  register_task_parser(subparsers)
73
77
 
74
78
  return parser
@@ -202,13 +206,17 @@ def main(argv: list[str] | None = None) -> None:
202
206
  return
203
207
 
204
208
  try:
205
- # auth status and clear don't need a client
206
- if args.resource == "auth" and args.action in ("status", "clear"):
209
+ # auth status, clear, and completion don't need a client
210
+ if (args.resource == "auth" and args.action in ("status", "clear")) or args.resource == "completion":
207
211
  if hasattr(args, "func"):
208
212
  result = args.func(args, None)
209
213
  if result is not None:
210
- output = format_output(result, args.output)
211
- print(output)
214
+ if args.resource == "completion":
215
+ # Completion scripts are raw shell code, not JSON
216
+ print(result)
217
+ else:
218
+ output = format_output(result, args.output)
219
+ print(output)
212
220
  return
213
221
 
214
222
  _, overrides = _merge_config(args)
@@ -0,0 +1,74 @@
1
+ """`proxmox pool` subcommand — resource pool management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+
7
+ from proxmox.client.client import ProxmoxClient
8
+
9
+
10
+ def register_pool_parser(subparsers: argparse._SubParsersAction) -> None:
11
+ """Register the `proxmox pool` subcommand tree."""
12
+ pool_parser = subparsers.add_parser("pool", help="Manage resource pools")
13
+ pool_sub = pool_parser.add_subparsers(dest="action", title="actions", required=True)
14
+
15
+ # --- pool list ---
16
+ pool_list = pool_sub.add_parser("list", help="List all pools")
17
+ pool_list.set_defaults(func=_pool_list)
18
+
19
+ # --- pool show ---
20
+ pool_show = pool_sub.add_parser("show", help="Show pool details and members")
21
+ pool_show.add_argument("poolid", help="Pool ID")
22
+ pool_show.set_defaults(func=_pool_show)
23
+
24
+ # --- pool create ---
25
+ pool_create = pool_sub.add_parser("create", help="Create a new pool")
26
+ pool_create.add_argument("poolid", help="Pool ID")
27
+ pool_create.add_argument("--comment", default=None, help="Pool description / comment")
28
+ pool_create.set_defaults(func=_pool_create)
29
+
30
+ # --- pool update ---
31
+ pool_update = pool_sub.add_parser("update", help="Update a pool's comment")
32
+ pool_update.add_argument("poolid", help="Pool ID")
33
+ pool_update.add_argument("--comment", default=None, help="New comment (omit to clear)")
34
+ pool_update.add_argument("--allow-delete", action="store_true", help="Allow deletion of pool comment")
35
+ pool_update.set_defaults(func=_pool_update)
36
+
37
+ # --- pool delete ---
38
+ pool_delete = pool_sub.add_parser("delete", help="Delete a pool")
39
+ pool_delete.add_argument("poolid", help="Pool ID")
40
+ pool_delete.set_defaults(func=_pool_delete)
41
+
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # Handlers
45
+ # ---------------------------------------------------------------------------
46
+
47
+ def _pool_list(_args: argparse.Namespace, client: ProxmoxClient) -> dict | list:
48
+ return client.get("/pools")
49
+
50
+
51
+ def _pool_show(args: argparse.Namespace, client: ProxmoxClient) -> dict:
52
+ return client.get(f"/pools/{args.poolid}")
53
+
54
+
55
+ def _pool_create(args: argparse.Namespace, client: ProxmoxClient) -> dict:
56
+ data: dict = {"poolid": args.poolid}
57
+ if args.comment:
58
+ data["comment"] = args.comment
59
+ return client.post("/pools", data=data)
60
+
61
+
62
+ def _pool_update(args: argparse.Namespace, client: ProxmoxClient) -> dict:
63
+ data: dict = {}
64
+ if args.comment is not None:
65
+ data["comment"] = args.comment
66
+ if args.allow_delete:
67
+ data["delete"] = 1
68
+ if not data:
69
+ return {"error": "Nothing to update. Use --comment or --allow-delete."}
70
+ return client.put(f"/pools/{args.poolid}", data=data)
71
+
72
+
73
+ def _pool_delete(args: argparse.Namespace, client: ProxmoxClient) -> dict:
74
+ return client.delete(f"/pools/{args.poolid}")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxcli"
3
- version = "0.4.0"
3
+ version = "0.6.0"
4
4
  description = "A CLI tool to interact with Proxmox VE nodes and clusters via the REST API"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -254,7 +254,7 @@ wheels = [
254
254
 
255
255
  [[package]]
256
256
  name = "proxcli"
257
- version = "0.4.0"
257
+ version = "0.6.0"
258
258
  source = { editable = "." }
259
259
  dependencies = [
260
260
  { name = "httpx" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes