proxcli 0.1.0__tar.gz → 0.1.1__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 (48) hide show
  1. {proxcli-0.1.0 → proxcli-0.1.1}/CHANGELOG.md +8 -1
  2. {proxcli-0.1.0 → proxcli-0.1.1}/PKG-INFO +4 -4
  3. {proxcli-0.1.0 → proxcli-0.1.1}/README.md +3 -3
  4. proxcli-0.1.1/TODO.md +85 -0
  5. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/container.py +3 -3
  6. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/vm.py +3 -3
  7. proxcli-0.1.1/proxmox/utils/helpers.py +32 -0
  8. {proxcli-0.1.0 → proxcli-0.1.1}/pyproject.toml +1 -1
  9. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_client.py +41 -0
  10. {proxcli-0.1.0 → proxcli-0.1.1}/uv.lock +1 -1
  11. proxcli-0.1.0/proxmox/utils/helpers.py +0 -14
  12. {proxcli-0.1.0 → proxcli-0.1.1}/.github/workflows/ci.yml +0 -0
  13. {proxcli-0.1.0 → proxcli-0.1.1}/.gitignore +0 -0
  14. {proxcli-0.1.0 → proxcli-0.1.1}/.python-version +0 -0
  15. {proxcli-0.1.0 → proxcli-0.1.1}/PLAN.md +0 -0
  16. {proxcli-0.1.0 → proxcli-0.1.1}/PROJECT.md +0 -0
  17. {proxcli-0.1.0 → proxcli-0.1.1}/PROMPT.md +0 -0
  18. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/__init__.py +0 -0
  19. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/__init__.py +0 -0
  20. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/auth.py +0 -0
  21. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/cluster.py +0 -0
  22. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/main.py +0 -0
  23. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/node.py +0 -0
  24. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/storage.py +0 -0
  25. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/cli/tasks.py +0 -0
  26. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/client/__init__.py +0 -0
  27. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/client/auth.py +0 -0
  28. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/client/client.py +0 -0
  29. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/client/exceptions.py +0 -0
  30. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/config/__init__.py +0 -0
  31. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/config/config.py +0 -0
  32. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/config/models.py +0 -0
  33. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/output/__init__.py +0 -0
  34. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/output/formatter.py +0 -0
  35. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/output/json_fmt.py +0 -0
  36. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/output/table_fmt.py +0 -0
  37. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/output/yaml_fmt.py +0 -0
  38. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/utils/__init__.py +0 -0
  39. {proxcli-0.1.0 → proxcli-0.1.1}/proxmox/utils/logging.py +0 -0
  40. {proxcli-0.1.0 → proxcli-0.1.1}/tests/__init__.py +0 -0
  41. {proxcli-0.1.0 → proxcli-0.1.1}/tests/conftest.py +0 -0
  42. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_auth.py +0 -0
  43. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_cli/__init__.py +0 -0
  44. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_cli/test_main.py +0 -0
  45. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_config.py +0 -0
  46. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_integration/__init__.py +0 -0
  47. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_output/__init__.py +0 -0
  48. {proxcli-0.1.0 → proxcli-0.1.1}/tests/test_output/test_formatter.py +0 -0
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.1] - 2026-06-20
11
+
12
+ ### Changed
13
+ - `proxmox vm create --vmid` and `proxmox container create --vmid` are now optional.
14
+ The next free VMID is auto-assigned via the `/cluster/nextid` API when omitted.
15
+
10
16
  ## [0.1.0] - 2026-06-20
11
17
 
12
18
  ### Added
@@ -26,4 +32,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
32
  - CSRF ticket auto-refresh on 401.
27
33
  - AI-agent-friendly: default JSON output, strict exit codes, `--dry-run` mode.
28
34
 
29
- [0.1.0]: https://github.com/xezpeleta/proxmox-cli/releases/tag/v0.1.0
35
+ [0.1.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.1.1
36
+ [0.1.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxcli
3
- Version: 0.1.0
3
+ Version: 0.1.1
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
@@ -36,7 +36,7 @@ Requires Python 3.10+ and [uv](https://docs.astral.sh/uv/).
36
36
  uv tool install proxmox
37
37
 
38
38
  # From Git
39
- uv tool install git+https://github.com/xezpeleta/proxmox-cli.git
39
+ uv tool install git+https://github.com/xezpeleta/proxcli.git
40
40
 
41
41
  # From local checkout
42
42
  uv tool install .
@@ -238,8 +238,8 @@ proxmox vm show 999 || echo "VM not found"
238
238
 
239
239
  ```bash
240
240
  # Clone
241
- git clone https://github.com/xezpeleta/proxmox-cli.git
242
- cd proxmox-cli
241
+ git clone https://github.com/xezpeleta/proxcli.git
242
+ cd proxcli
243
243
 
244
244
  # Install dev dependencies
245
245
  uv sync
@@ -13,7 +13,7 @@ Requires Python 3.10+ and [uv](https://docs.astral.sh/uv/).
13
13
  uv tool install proxmox
14
14
 
15
15
  # From Git
16
- uv tool install git+https://github.com/xezpeleta/proxmox-cli.git
16
+ uv tool install git+https://github.com/xezpeleta/proxcli.git
17
17
 
18
18
  # From local checkout
19
19
  uv tool install .
@@ -215,8 +215,8 @@ proxmox vm show 999 || echo "VM not found"
215
215
 
216
216
  ```bash
217
217
  # Clone
218
- git clone https://github.com/xezpeleta/proxmox-cli.git
219
- cd proxmox-cli
218
+ git clone https://github.com/xezpeleta/proxcli.git
219
+ cd proxcli
220
220
 
221
221
  # Install dev dependencies
222
222
  uv sync
proxcli-0.1.1/TODO.md ADDED
@@ -0,0 +1,85 @@
1
+ # TODO
2
+
3
+ Planned improvements for future releases. Items are roughly ordered by priority.
4
+
5
+ ---
6
+
7
+ ## v1.1 — Polish & Usability
8
+
9
+ - [ ] **Shell completions**
10
+ - Add `proxmox completion bash|zsh|fish` subcommand that emits a completion script. Use argparse's built-in completion or a lightweight generator. Makes tab-completion work for all subcommands and flags.
11
+
12
+ - [ ] **Streaming task logs (`--follow`)**
13
+ - `proxmox task log <upid>` that streams task output in real time (like `tail -f`). Requires httpx streaming support (already viable — httpx is the chosen HTTP client).
14
+
15
+ - [ ] **Startup time optimization**
16
+ - Current `proxmox --help` takes ~350ms. Lazy-load subcommand modules so only the requested resource's code is imported. Move `import rich`, `import yaml` inside formatter functions. Target: <200ms.
17
+
18
+ - [ ] **`--output table` column selection**
19
+ - Allow `proxmox vm list --output table --columns vmid,name,status,mem` to pick which columns appear in the table.
20
+
21
+ - [ ] **Color support in table output**
22
+ - Use `rich` styling for status values (green= running, red= stopped, yellow= suspended).
23
+
24
+ ---
25
+
26
+ ## v1.2 — Resource Coverage
27
+
28
+ - [ ] **Backup (`vzdump`) management**
29
+ - `proxmox backup` subcommand: `list`, `create`, `show`, `delete`. Wrap `/nodes/{node}/vzdump` and `/nodes/{node}/storage/{storage}/content` for backup files.
30
+
31
+ - [ ] **User & permission management**
32
+ - `proxmox user` subcommand: `list`, `show`, `create`, `update`, `delete`. Wraps `/access/users`, `/access/acl`, `/access/roles`.
33
+
34
+ - [ ] **Pool management**
35
+ - `proxmox pool` subcommand: `list`, `show`, `create`, `update`, `delete`. Wraps `/pools`.
36
+
37
+ - [ ] **Network management**
38
+ - `proxmox network` subcommand: `list`, `show`, `update` for bridges, bonds, VLANs. Wraps `/nodes/{node}/network`.
39
+
40
+ - [ ] **Firewall management**
41
+ - `proxmox firewall` subcommand: `list`, `show`, `enable`, `disable`, `create rule`, `delete rule`. Wraps `/nodes/{node}/firewall`, `/cluster/firewall`.
42
+
43
+ - [ ] **SDN (Software-Defined Networking)**
44
+ - `proxmox sdn` subcommand: `zones`, `vnets`, `subnets`. Wraps `/cluster/sdn/*` endpoints.
45
+
46
+ - [ ] **HA (High Availability)**
47
+ - `proxmox ha` subcommand: `status`, `groups`, `resources`. Wraps `/cluster/ha/*` endpoints.
48
+
49
+ ---
50
+
51
+ ## v2.0 — Multi-Cluster & Advanced
52
+
53
+ - [ ] **Multi-profile / multi-cluster support**
54
+ - Support `--profile <name>` global flag to switch between multiple saved Proxmox endpoints. Config file format extended from single endpoint to a profiles dict. `proxmox auth login --profile homelab` and `proxmox auth login --profile work` coexist.
55
+
56
+ - [ ] **Batch / bulk operations**
57
+ - `proxmox vm start --all-on-node pve01` (start all VMs on a node). `proxmox vm snapshot --vmid 100,101,102` (apply to multiple IDs).
58
+
59
+ - [ ] **Config file templating**
60
+ - Ability to define VM/container specs in a YAML/JSON file and create from it: `proxmox vm create --file my-vm.yaml`.
61
+
62
+ - [ ] **Plugin system for custom commands**
63
+ - Allow users to extend the CLI with custom subcommands via a plugins directory.
64
+
65
+ - [ ] **Dry-run diff mode**
66
+ - `--dry-run` that shows what *would change* on the Proxmox side (e.g., before/after VM config diff) rather than just the HTTP request.
67
+
68
+ ---
69
+
70
+ ## Ideas (not yet scheduled)
71
+
72
+ - [ ] **Interactive mode / TUI**
73
+ - A `proxmox tui` command that opens a terminal UI (like `htop` but for Proxmox resources). Low priority — the CLI is designed for automation first.
74
+
75
+ - [ ] **Webhook / event listener**
76
+ - Subscribe to Proxmox cluster events and pipe them to a webhook or stdout for external monitoring.
77
+
78
+ - [ ] **VM migration wizard**
79
+ - `proxmox vm migrate <vmid> --to <target-node>` with progress tracking and live migration support.
80
+
81
+ - [ ] **Proxmox Backup Server (PBS) integration**
82
+ - Separate subcommand or a companion tool (`proxbackup`?) for managing PBS instances via their API.
83
+
84
+ - [ ] **Terraform / Pulumi bridge**
85
+ - Export current Proxmox state as Terraform HCL or Pulumi Python/TypeScript, enabling import into IaC.
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import argparse
6
6
 
7
7
  from proxmox.client.client import ProxmoxClient
8
- from proxmox.utils.helpers import vmid_type
8
+ from proxmox.utils.helpers import resolve_vmid, vmid_type
9
9
 
10
10
 
11
11
  def register_container_parser(subparsers: argparse._SubParsersAction) -> None:
@@ -27,7 +27,7 @@ def register_container_parser(subparsers: argparse._SubParsersAction) -> None:
27
27
  # --- container create ---
28
28
  ct_create = ct_sub.add_parser("create", help="Create a new container")
29
29
  ct_create.add_argument("--node", required=True, help="Target node")
30
- ct_create.add_argument("--vmid", type=vmid_type, required=True, help="Container ID")
30
+ ct_create.add_argument("--vmid", type=vmid_type, default=None, help="Container ID (auto-assigned if omitted)")
31
31
  ct_create.add_argument("--ostemplate", required=True, help="OS template")
32
32
  ct_create.add_argument("--storage", default=None, help="Storage for the container")
33
33
  ct_create.add_argument("--memory", type=int, default=512, help="Memory in MB")
@@ -117,7 +117,7 @@ def _ct_show(args: argparse.Namespace, client: ProxmoxClient) -> dict:
117
117
 
118
118
  def _ct_create(args: argparse.Namespace, client: ProxmoxClient) -> dict:
119
119
  data: dict = {
120
- "vmid": args.vmid,
120
+ "vmid": resolve_vmid(client, args.vmid),
121
121
  "ostemplate": args.ostemplate,
122
122
  "memory": args.memory,
123
123
  "cores": args.cores,
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import argparse
6
6
 
7
7
  from proxmox.client.client import ProxmoxClient
8
- from proxmox.utils.helpers import vmid_type
8
+ from proxmox.utils.helpers import resolve_vmid, vmid_type
9
9
 
10
10
 
11
11
  def register_vm_parser(subparsers: argparse._SubParsersAction) -> None:
@@ -27,7 +27,7 @@ def register_vm_parser(subparsers: argparse._SubParsersAction) -> None:
27
27
  # --- vm create ---
28
28
  vm_create = vm_sub.add_parser("create", help="Create a new VM")
29
29
  vm_create.add_argument("--node", required=True, help="Target node")
30
- vm_create.add_argument("--vmid", type=vmid_type, required=True, help="VM ID")
30
+ vm_create.add_argument("--vmid", type=vmid_type, default=None, help="VM ID (auto-assigned if omitted)")
31
31
  vm_create.add_argument("--memory", type=int, required=True, help="Memory in MB")
32
32
  vm_create.add_argument("--cores", type=int, default=1, help="CPU cores (default: 1)")
33
33
  vm_create.add_argument("--net", default=None, help="Network config (e.g. model=virtio,bridge=vmbr0)")
@@ -141,7 +141,7 @@ def _vm_show(args: argparse.Namespace, client: ProxmoxClient) -> dict:
141
141
 
142
142
  def _vm_create(args: argparse.Namespace, client: ProxmoxClient) -> dict:
143
143
  data: dict = {
144
- "vmid": args.vmid,
144
+ "vmid": resolve_vmid(client, args.vmid),
145
145
  "memory": args.memory,
146
146
  "cores": args.cores,
147
147
  }
@@ -0,0 +1,32 @@
1
+ """Shared helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from proxmox.client.client import ProxmoxClient
9
+
10
+
11
+ def vmid_type(value: str) -> int:
12
+ """Argparse type for validating VMID (positive integer)."""
13
+ try:
14
+ v = int(value)
15
+ except ValueError:
16
+ raise ValueError(f"Invalid VMID '{value}': must be an integer")
17
+ if v <= 0:
18
+ raise ValueError(f"Invalid VMID '{value}': must be positive")
19
+ return v
20
+
21
+
22
+ def resolve_vmid(client: ProxmoxClient, vmid: int | None) -> int:
23
+ """Return *vmid* if provided, otherwise fetch the next free VMID from the cluster."""
24
+ if vmid is not None and vmid > 0:
25
+ return vmid
26
+ result = client.get("/cluster/nextid")
27
+ if isinstance(result, dict):
28
+ # Proxmox returns {"data": "<nextid>"} or just "<nextid>" as string
29
+ raw = result.get("data", result)
30
+ if isinstance(raw, str):
31
+ return int(raw)
32
+ return int(result) if isinstance(result, (str, int)) else 0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxcli"
3
- version = "0.1.0"
3
+ version = "0.1.1"
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 = [
@@ -8,6 +8,7 @@ import pytest
8
8
  from proxmox.client.auth import AuthManager
9
9
  from proxmox.client.client import ProxmoxClient
10
10
  from proxmox.client.exceptions import ProxmoxAPIError
11
+ from proxmox.utils.helpers import resolve_vmid
11
12
 
12
13
 
13
14
  class TestProxmoxClient:
@@ -119,3 +120,43 @@ class TestProxmoxClient:
119
120
  client = ProxmoxClient("https://pve:8006/", AuthManager())
120
121
  result = client.get("/nodes")
121
122
  assert result == []
123
+
124
+
125
+ class TestResolveVmid:
126
+ def test_returns_provided_vmid(self, mock_httpx_client):
127
+ """When a vmid is provided, return it without calling the API."""
128
+ client = ProxmoxClient("https://pve:8006", AuthManager())
129
+ result = resolve_vmid(client, 110)
130
+ assert result == 110
131
+ assert len(mock_httpx_client.get_requests()) == 0
132
+
133
+ def test_fetches_nextid_when_vmid_is_none(self, mock_httpx_client):
134
+ """When vmid is None, call /cluster/nextid."""
135
+ mock_httpx_client.add_response(
136
+ url="https://pve:8006/api2/json/cluster/nextid",
137
+ json={"data": "102"},
138
+ )
139
+ client = ProxmoxClient("https://pve:8006", AuthManager())
140
+ result = resolve_vmid(client, None)
141
+ assert result == 102
142
+ assert len(mock_httpx_client.get_requests()) == 1
143
+
144
+ def test_fetches_nextid_when_vmid_is_zero(self, mock_httpx_client):
145
+ """When vmid is 0, also fetch nextid (0 is not a valid VMID)."""
146
+ mock_httpx_client.add_response(
147
+ url="https://pve:8006/api2/json/cluster/nextid",
148
+ json={"data": "105"},
149
+ )
150
+ client = ProxmoxClient("https://pve:8006", AuthManager())
151
+ result = resolve_vmid(client, 0)
152
+ assert result == 105
153
+
154
+ def test_nextid_returns_integer(self, mock_httpx_client):
155
+ """Some Proxmox versions return nextid as integer."""
156
+ mock_httpx_client.add_response(
157
+ url="https://pve:8006/api2/json/cluster/nextid",
158
+ json={"data": 108},
159
+ )
160
+ client = ProxmoxClient("https://pve:8006", AuthManager())
161
+ result = resolve_vmid(client, None)
162
+ assert result == 108
@@ -253,7 +253,7 @@ wheels = [
253
253
  ]
254
254
 
255
255
  [[package]]
256
- name = "proxmox"
256
+ name = "proxcli"
257
257
  version = "0.1.0"
258
258
  source = { editable = "." }
259
259
  dependencies = [
@@ -1,14 +0,0 @@
1
- """Shared helpers."""
2
-
3
- from __future__ import annotations
4
-
5
-
6
- def vmid_type(value: str) -> int:
7
- """Argparse type for validating VMID (positive integer)."""
8
- try:
9
- v = int(value)
10
- except ValueError:
11
- raise ValueError(f"Invalid VMID '{value}': must be an integer")
12
- if v <= 0:
13
- raise ValueError(f"Invalid VMID '{value}': must be positive")
14
- return v
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