proxcli 0.8.0__tar.gz → 0.8.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 (52) hide show
  1. {proxcli-0.8.0 → proxcli-0.8.1}/CHANGELOG.md +7 -0
  2. {proxcli-0.8.0 → proxcli-0.8.1}/PKG-INFO +1 -1
  3. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/vm.py +110 -0
  4. {proxcli-0.8.0 → proxcli-0.8.1}/pyproject.toml +1 -1
  5. {proxcli-0.8.0 → proxcli-0.8.1}/uv.lock +2 -2
  6. {proxcli-0.8.0 → proxcli-0.8.1}/.env.example +0 -0
  7. {proxcli-0.8.0 → proxcli-0.8.1}/.github/workflows/ci.yml +0 -0
  8. {proxcli-0.8.0 → proxcli-0.8.1}/.gitignore +0 -0
  9. {proxcli-0.8.0 → proxcli-0.8.1}/.python-version +0 -0
  10. {proxcli-0.8.0 → proxcli-0.8.1}/AGENTS.md +0 -0
  11. {proxcli-0.8.0 → proxcli-0.8.1}/PLAN.md +0 -0
  12. {proxcli-0.8.0 → proxcli-0.8.1}/PROJECT.md +0 -0
  13. {proxcli-0.8.0 → proxcli-0.8.1}/PROMPT.md +0 -0
  14. {proxcli-0.8.0 → proxcli-0.8.1}/README.md +0 -0
  15. {proxcli-0.8.0 → proxcli-0.8.1}/TODO.md +0 -0
  16. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/__init__.py +0 -0
  17. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/__init__.py +0 -0
  18. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/auth.py +0 -0
  19. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/cluster.py +0 -0
  20. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/completion.py +0 -0
  21. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/container.py +0 -0
  22. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/firewall_helpers.py +0 -0
  23. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/main.py +0 -0
  24. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/node.py +0 -0
  25. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/pool.py +0 -0
  26. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/storage.py +0 -0
  27. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/cli/tasks.py +0 -0
  28. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/client/__init__.py +0 -0
  29. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/client/auth.py +0 -0
  30. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/client/client.py +0 -0
  31. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/client/exceptions.py +0 -0
  32. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/config/__init__.py +0 -0
  33. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/config/config.py +0 -0
  34. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/config/models.py +0 -0
  35. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/output/__init__.py +0 -0
  36. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/output/formatter.py +0 -0
  37. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/output/json_fmt.py +0 -0
  38. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/output/table_fmt.py +0 -0
  39. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/output/yaml_fmt.py +0 -0
  40. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/utils/__init__.py +0 -0
  41. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/utils/helpers.py +0 -0
  42. {proxcli-0.8.0 → proxcli-0.8.1}/proxmox/utils/logging.py +0 -0
  43. {proxcli-0.8.0 → proxcli-0.8.1}/tests/__init__.py +0 -0
  44. {proxcli-0.8.0 → proxcli-0.8.1}/tests/conftest.py +0 -0
  45. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_auth.py +0 -0
  46. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_cli/__init__.py +0 -0
  47. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_cli/test_main.py +0 -0
  48. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_client.py +0 -0
  49. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_config.py +0 -0
  50. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_integration/__init__.py +0 -0
  51. {proxcli-0.8.0 → proxcli-0.8.1}/tests/test_output/__init__.py +0 -0
  52. {proxcli-0.8.0 → proxcli-0.8.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.8.1] - 2026-06-20
11
+
12
+ ### Added
13
+ - **vm snapshot** management: ``list``, ``create``, ``show``, ``rollback``,
14
+ ``delete``. Wraps ``/nodes/{node}/qemu/{vmid}/snapshot`` endpoints.
15
+
10
16
  ## [0.8.0] - 2026-06-20
11
17
 
12
18
  ### Fixed
@@ -118,6 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
118
124
  - CSRF ticket auto-refresh on 401.
119
125
  - AI-agent-friendly: default JSON output, strict exit codes, `--dry-run` mode.
120
126
 
127
+ [0.8.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.8.1
121
128
  [0.8.0]: https://github.com/xezpeleta/proxcli/releases/tag/v0.8.0
122
129
  [0.7.2]: https://github.com/xezpeleta/proxcli/releases/tag/v0.7.2
123
130
  [0.7.1]: https://github.com/xezpeleta/proxcli/releases/tag/v0.7.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxcli
3
- Version: 0.8.0
3
+ Version: 0.8.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
@@ -84,6 +84,46 @@ def register_vm_parser(subparsers: argparse._SubParsersAction) -> None:
84
84
  vm_delete.add_argument("--purge", action="store_true", help="Purge VM from all configurations")
85
85
  vm_delete.set_defaults(func=_vm_delete)
86
86
 
87
+ # --- snapshot ---
88
+ snap = vm_sub.add_parser("snapshot", help="Manage VM snapshots")
89
+ snap_sub = snap.add_subparsers(dest="snap_action", title="snapshot actions", required=True)
90
+
91
+ snap_list = snap_sub.add_parser("list", help="List snapshots")
92
+ snap_list.add_argument("vmid", type=vmid_type, help="VM ID")
93
+ snap_list.add_argument("--node", help="Node name (auto-detected if omitted)")
94
+ snap_list.set_defaults(func=_vm_snapshot_list)
95
+
96
+ snap_create = snap_sub.add_parser("create", help="Create a snapshot")
97
+ snap_create.add_argument("vmid", type=vmid_type, help="VM ID")
98
+ snap_create.add_argument("snapname", help="Snapshot name")
99
+ snap_create.add_argument("--node", help="Node name (auto-detected if omitted)")
100
+ snap_create.add_argument("--description", default=None, help="Snapshot description")
101
+ snap_create.add_argument("--vmstate", type=int, choices=[0, 1], default=0,
102
+ help="Include RAM state (1=yes, 0=no, default: 0)")
103
+ snap_create.set_defaults(func=_vm_snapshot_create)
104
+
105
+ snap_show = snap_sub.add_parser("show", help="Show snapshot details")
106
+ snap_show.add_argument("vmid", type=vmid_type, help="VM ID")
107
+ snap_show.add_argument("snapname", help="Snapshot name")
108
+ snap_show.add_argument("--node", help="Node name (auto-detected if omitted)")
109
+ snap_show.set_defaults(func=_vm_snapshot_show)
110
+
111
+ snap_rollback = snap_sub.add_parser("rollback", help="Rollback to a snapshot")
112
+ snap_rollback.add_argument("vmid", type=vmid_type, help="VM ID")
113
+ snap_rollback.add_argument("snapname", help="Snapshot name")
114
+ snap_rollback.add_argument("--node", help="Node name (auto-detected if omitted)")
115
+ snap_rollback.add_argument("--start", type=int, choices=[0, 1], default=0,
116
+ help="Start VM after rollback (1=yes, 0=no, default: 0)")
117
+ snap_rollback.set_defaults(func=_vm_snapshot_rollback)
118
+
119
+ snap_delete = snap_sub.add_parser("delete", help="Delete a snapshot")
120
+ snap_delete.add_argument("vmid", type=vmid_type, help="VM ID")
121
+ snap_delete.add_argument("snapname", help="Snapshot name")
122
+ snap_delete.add_argument("--node", help="Node name (auto-detected if omitted)")
123
+ snap_delete.add_argument("--force", type=int, choices=[0, 1], default=0,
124
+ help="Force removal (1=yes, 0=no, default: 0)")
125
+ snap_delete.set_defaults(func=_vm_snapshot_delete)
126
+
87
127
  # --- firewall ---
88
128
  fw = vm_sub.add_parser("firewall", help="Manage VM firewall")
89
129
  fw_sub = fw.add_subparsers(dest="fw_resource", title="resources", required=True)
@@ -325,6 +365,76 @@ def _vm_delete(args: argparse.Namespace, client: ProxmoxClient) -> dict:
325
365
  return result if isinstance(result, dict) else {"data": result}
326
366
 
327
367
 
368
+ # ---------------------------------------------------------------------------
369
+ # VM snapshot handlers
370
+ # ---------------------------------------------------------------------------
371
+
372
+ def _vm_snapshot_list(args: argparse.Namespace, client: ProxmoxClient) -> dict | list:
373
+ node = _resolve_node(client, args.node, args.vmid)
374
+ if not node:
375
+ return {"error": f"VM {args.vmid} not found"}
376
+ result = client.get(f"/nodes/{node}/qemu/{args.vmid}/snapshot")
377
+ # Add _node for consistency
378
+ if isinstance(result, list):
379
+ for snap in result:
380
+ if isinstance(snap, dict):
381
+ snap["_node"] = node
382
+ snap["_vmid"] = args.vmid
383
+ return result
384
+
385
+
386
+ def _vm_snapshot_create(args: argparse.Namespace, client: ProxmoxClient) -> dict:
387
+ node = _resolve_node(client, args.node, args.vmid)
388
+ if not node:
389
+ return {"error": f"VM {args.vmid} not found"}
390
+ data: dict = {"snapname": args.snapname}
391
+ if args.description:
392
+ data["description"] = args.description
393
+ if args.vmstate:
394
+ data["vmstate"] = args.vmstate
395
+ result = client.post(f"/nodes/{node}/qemu/{args.vmid}/snapshot", data=data)
396
+ return result if isinstance(result, dict) else {"data": result}
397
+
398
+
399
+ def _vm_snapshot_show(args: argparse.Namespace, client: ProxmoxClient) -> dict:
400
+ node = _resolve_node(client, args.node, args.vmid)
401
+ if not node:
402
+ return {"error": f"VM {args.vmid} not found"}
403
+ result = client.get(f"/nodes/{node}/qemu/{args.vmid}/snapshot/{args.snapname}")
404
+ if isinstance(result, dict):
405
+ result["_node"] = node
406
+ result["_vmid"] = args.vmid
407
+ return result
408
+
409
+
410
+ def _vm_snapshot_rollback(args: argparse.Namespace, client: ProxmoxClient) -> dict:
411
+ node = _resolve_node(client, args.node, args.vmid)
412
+ if not node:
413
+ return {"error": f"VM {args.vmid} not found"}
414
+ data: dict = {}
415
+ if args.start:
416
+ data["start"] = args.start
417
+ result = client.post(
418
+ f"/nodes/{node}/qemu/{args.vmid}/snapshot/{args.snapname}/rollback",
419
+ data=data or None,
420
+ )
421
+ return result if isinstance(result, dict) else {"data": result}
422
+
423
+
424
+ def _vm_snapshot_delete(args: argparse.Namespace, client: ProxmoxClient) -> dict:
425
+ node = _resolve_node(client, args.node, args.vmid)
426
+ if not node:
427
+ return {"error": f"VM {args.vmid} not found"}
428
+ params: dict = {}
429
+ if args.force:
430
+ params["force"] = args.force
431
+ result = client.delete(
432
+ f"/nodes/{node}/qemu/{args.vmid}/snapshot/{args.snapname}",
433
+ params=params or None,
434
+ )
435
+ return result if isinstance(result, dict) else {"data": result}
436
+
437
+
328
438
  # ---------------------------------------------------------------------------
329
439
  # VM firewall handlers
330
440
  # ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxcli"
3
- version = "0.8.0"
3
+ version = "0.8.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 = [
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 2
2
+ revision = 3
3
3
  requires-python = ">=3.10"
4
4
 
5
5
  [[package]]
@@ -254,7 +254,7 @@ wheels = [
254
254
 
255
255
  [[package]]
256
256
  name = "proxcli"
257
- version = "0.8.0"
257
+ version = "0.8.1"
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
File without changes
File without changes