Add snapshots to the info screen: take, roll back, delete
A "Snapshots" section on the info screen lists the snapshots, newest first, without Proxmox's "current" pseudo entry. Keys: - p takes a snapshot. The name defaults to tui_YYYYMMDD_HHMM, the description is optional, and VMs can include RAM. - R rolls back to the snapshot under the cursor. The confirm names the snapshot and its date. A running guest is started again; its status is read right before the rollback, and if it cannot be read the rollback does not run. - D deletes the snapshot under the cursor, after a confirm. `delete ct|vm <node> <vmid> -y` without the snapshot subcommand destroys the whole guest. delete_snapshot therefore validates the name first (letter first, then [A-Za-z0-9_-], not "current"), writes "snapshot" into the argv as a literal, and checks the argv shape before running it. A property test covers rejected names. The info screen now scrolls, because four sections do not fit short terminals. A description starting with "-" is refused, since it is a positional argument and could be read as an option. Live test on CT 104: take, roll back (started again) and delete. The snapshot list, status, config and IPv4/IPv6 match the baseline. Closes #8 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit was merged in pull request #21.
This commit is contained in:
1 parent
520de16cc6
commit
ec35a4c996
6 files changed
+1035
-14
No files matched your search
@@ -446,3 +446,69 @@ API facts (Proxmox VE 9.2, `api usage ... config set -v`):
|
||||
172.16.41.200/23 gw 172.16.40.1->DHCP. Bridge change cannot be tested live: the
|
||||
node has a single bridge (`vmbr0`). Final net0 string identical to the baseline.
|
||||
- [ ] Push `feat/network-edit`, merge to `main` (ask first).
|
||||
|
||||
---
|
||||
|
||||
# Snapshots in the info screen (issue #8)
|
||||
|
||||
## Decision (user-approved)
|
||||
|
||||
Info screen section "Snapshots"; keys `p` take, `R` roll back, `D` delete. LXC and VM.
|
||||
|
||||
CLI facts (cv4pve-cli 2.4.0, read-only probes):
|
||||
|
||||
- `get guest snapshots <node> <qemu|lxc> <vmid> -o json` -> list of dicts: `name`,
|
||||
`description`, `snaptime` (epoch or null), `parent`, VM also `vmstate`. Always
|
||||
contains a pseudo entry `name == "current"` ("You are here!") - not a snapshot.
|
||||
- `create ct|vm snapshot <node> <vmid> <snapname> <descr>` (+ `--vmstate 1` for VM RAM),
|
||||
no `--yes`; with `--wait`.
|
||||
- `do rollback ct|vm <node> <vmid> <snapname>` needs `-y`; API param `start` (bool)
|
||||
starts the guest after a successful rollback.
|
||||
- `delete ct|vm snapshot <node> <vmid> <snapname>` needs `-y`.
|
||||
DANGER: `delete ct|vm <node> <vmid> -y` (without `snapshot`) DESTROYS THE WHOLE GUEST.
|
||||
|
||||
### `cli.py`
|
||||
|
||||
- `valid_snapname(name) -> str` - ValueError unless it matches
|
||||
`[A-Za-z][A-Za-z0-9_-]{0,39}` and is not `current` (case-insensitive).
|
||||
- `snapshots(guest) -> list[dict]` - without `current`, newest first (`snaptime`
|
||||
desc, null last).
|
||||
- `take_snapshot(guest, name, description="", vmstate=False)` ->
|
||||
`create <kind> snapshot <node> <vmid> <name> <description or "cv4pve-tui">
|
||||
[--vmstate 1 (VM only)] --wait`; name validated first.
|
||||
- `rollback_snapshot(guest, name, start=False)` ->
|
||||
`do rollback <kind> <node> <vmid> <name> [--start 1] -y --wait`; name validated.
|
||||
- `delete_snapshot(guest, name)` ->
|
||||
`delete <kind> snapshot <node> <vmid> <name> -y --wait`; name validated first, and
|
||||
the argv is asserted to contain `snapshot` right after the kind and the name right
|
||||
after the vmid (defence against ever sending the guest-destroying form).
|
||||
|
||||
### `app.py` (UI contract)
|
||||
|
||||
- InfoScreen gets `Vertical#snapshots_section` (border_title "Snapshots") with
|
||||
`DataTable#snapshots` (Name, Date `YYYY-MM-DD HH:MM` or `-`, Description, and for VMs
|
||||
RAM `yes`/`no`) and `Static#snap_note`. Loaded with the rest; a failure of
|
||||
`cli.snapshots` is shown in `#snap_note` ("unavailable: <summary>"), the rest of the
|
||||
screen still works. Empty list -> `#snap_note` "No snapshots".
|
||||
- `p`: `SnapshotScreen` dialog (DialogScreen style, title "Take snapshot: <name>
|
||||
(<vmid>)"): `Input#snapname` prefilled `tui_YYYYMMDD_HHMM` (local time),
|
||||
`Input#snapdesc`, `Checkbox#vmstate` (VMs only; not shown for CTs), buttons
|
||||
`#yes`/`#no`, escape = no. Invalid name -> error notify, dialog stays, no call.
|
||||
Yes -> busy guard, worker `cli.take_snapshot`, done/error notify, info reloads.
|
||||
- `R`: roll back the snapshot under the cursor of `#snapshots`. `ConfirmScreen`
|
||||
titled "Roll back" naming snapshot and date and saying everything changed since then
|
||||
is lost; if the guest is running (live status) it says it will be started again and
|
||||
`start=True` is passed. Yes -> busy guard, worker, notify, info reloads.
|
||||
- `D`: delete the snapshot under the cursor. `ConfirmScreen` titled "Delete snapshot"
|
||||
naming it. Yes -> busy guard, worker `cli.delete_snapshot`, notify, info reloads.
|
||||
- `R`/`D` with no snapshot -> info notify, no dialog.
|
||||
|
||||
## Steps
|
||||
|
||||
- [x] Tests - red first. - [x] Implementation - suite green, ruff clean, README keys.
|
||||
- Review fixes: rollback reads the live status right before rolling back (if it cannot
|
||||
be read, no rollback); the info screen scrolls; a description starting with `-` is
|
||||
refused (positional argument).
|
||||
- [x] Live on CT 104: take `tui_live_test`, listed; roll back (CT running -> started
|
||||
again); delete; list back to the baseline (no snapshots); CT running.
|
||||
- [ ] PR with `Closes #8`, merge after user OK.
|
||||
Reference in new issue
Block a user