Restore a container backup with a smaller root disk

Proxmox only grows disks. The restore dialog gets an optional "Root
disk GiB" field for container backups; with a size, the backup is
restored as a new guest with --rootfs <storage>:<N> (Proxmox's advanced
restore mode).

- A smaller disk is only allowed for a new VMID, checked in the dialog
  and again in cli.restore(): with force, Proxmox deletes the old disks
  before it unpacks, and a failed unpack then removes the guest.
- The size must be a positive integer (0 gives an unlimited subvol on
  ZFS/dir storage) and above the used root disk space of a running
  guest.
- The confirm screen is a separate DANGER screen with a red border: it
  says mount points are dropped and their backed-up files land in the
  root disk, raw lxc.* lines can come back under root@pam, root disk
  options are not carried over, and the used space is no fit check.
- Never --unique or ignore-unpack-errors.

Design: docs/plans/lxc-shrink.md (section A).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DuckbootandClaude Opus 5.5 committed 2026-11-04 19:58:02 +01:00
1 parent 9b472e8cf6
commit 82902edfc8
5 files changed
+457 -8

No files matched your search

+52
View File
@@ -1767,3 +1767,55 @@ def test_restore_refuses_a_backup_of_the_other_guest_type(fake, force):
ct, "local:backup/vzdump-qemu-100-x.vma.zst", 100, "local-lvm", force=force
)
assert fake.calls == []
# --- shrink restore: smaller rootfs (docs/plans/lxc-shrink.md, A) ------------
def test_restore_ct_with_rootfs_gb_adds_rootfs_on_the_chosen_storage(fake):
cli.restore(CT, CT_VOLID, 105, "zfs", rootfs_gb=4)
assert fake.argv == [
"cv4pve-cli", "do", "restore", "ct", "mainframe", "105", CT_VOLID,
"--storage", "zfs", "--rootfs", "zfs:4", "-y", "--wait",
] # fmt: skip
def test_restore_ct_with_rootfs_gb_and_start_exact_argv(fake):
cli.restore(CT, CT_VOLID, 105, "zfs", start=True, rootfs_gb=4)
assert fake.argv == [
"cv4pve-cli", "do", "restore", "ct", "mainframe", "105", CT_VOLID,
"--storage", "zfs", "--start", "1", "--rootfs", "zfs:4", "-y", "--wait",
] # fmt: skip
def test_restore_without_rootfs_gb_argv_is_unchanged(fake):
cli.restore(CT, CT_VOLID, 105, "zfs", rootfs_gb=None)
assert "--rootfs" not in fake.argv
assert fake.argv[-4:] == ["--storage", "zfs", "-y", "--wait"]
def test_restore_with_rootfs_gb_never_adds_unique_or_ignore_unpack_errors(fake):
cli.restore(CT, CT_VOLID, 105, "zfs", rootfs_gb=4)
joined = " ".join(fake.argv)
assert "--unique" not in joined and "ignore-unpack-errors" not in joined
@pytest.mark.parametrize("bad", [0, -1, True, "4", 1.5])
def test_restore_rootfs_gb_must_be_a_positive_int_and_never_runs(fake, bad):
# a size of 0 makes PVE allocate an unlimited subvol on ZFS/dir
with pytest.raises(ValueError):
cli.restore(CT, CT_VOLID, 105, "zfs", rootfs_gb=bad)
assert fake.calls == []
def test_restore_rootfs_gb_with_force_raises_and_never_runs(fake):
# force=1 destroys the old volumes first; a failed extraction would lose the guest
with pytest.raises(ValueError):
cli.restore(CT, CT_VOLID, 100, "zfs", force=True, rootfs_gb=4)
assert fake.calls == []
def test_restore_rootfs_gb_on_a_vm_backup_raises_and_never_runs(fake):
with pytest.raises(ValueError):
cli.restore(VM, VM_VOLID, 109, "local-lvm", rootfs_gb=4)
assert fake.calls == []