From 5ef4888910773f5b7eafeeef65491e1130ccddac Mon Sep 17 00:00:00 2001 From: Michal Fox Date: Sat, 3 Oct 2026 01:40:02 +0200 Subject: [PATCH] fix #6391: restore: fall back to automatic VM state storage if unavailable Restoring a backup keeps the 'vmstatestorage' option of the backed up VM. When the backup is restored on another node or cluster, that storage might not exist there or not be enabled for the node, so that hibernating the VM or taking a snapshot including RAM fails later on, for example with "storage 'local-zfs-pve1' does not exist". If the configured VM state storage is not available on the node the backup is restored to, drop the option, so that the storage is selected automatically, like for VMs without it, and log that in the restore task log. A 'vmstatestorage' passed explicitly when restoring is applied after this and is not affected. Signed-off-by: Michal Fox --- src/PVE/QemuServer.pm | 10 +++++++++ src/test/restore-config-expected/201.conf | 13 ++++++++++++ src/test/restore-config-expected/202.conf | 12 +++++++++++ src/test/restore-config-expected/203.conf | 12 +++++++++++ src/test/restore-config-expected/204.conf | 12 +++++++++++ src/test/restore-config-input/201.conf | 14 +++++++++++++ src/test/restore-config-input/202.conf | 14 +++++++++++++ src/test/restore-config-input/203.conf | 14 +++++++++++++ src/test/restore-config-input/204.conf | 14 +++++++++++++ src/test/run_qemu_restore_config_tests.pl | 25 ++++++++++++++++++++++- 10 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/test/restore-config-expected/201.conf create mode 100644 src/test/restore-config-expected/202.conf create mode 100644 src/test/restore-config-expected/203.conf create mode 100644 src/test/restore-config-expected/204.conf create mode 100644 src/test/restore-config-input/201.conf create mode 100644 src/test/restore-config-input/202.conf create mode 100644 src/test/restore-config-input/203.conf create mode 100644 src/test/restore-config-input/204.conf diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm index f199371..62fb72e 100644 --- a/src/PVE/QemuServer.pm +++ b/src/PVE/QemuServer.pm @@ -6697,6 +6697,16 @@ sub restore_update_config_line { } else { $res .= $line; } + } elsif ($line =~ m/^vmstatestorage:\s*(\S+)\s*$/) { + my $storeid = $1; + my $storecfg = PVE::Storage::config(); + my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1); + if ($scfg && PVE::Storage::storage_check_enabled($storecfg, $storeid, undef, 1)) { + $res .= $line; + } else { + print "VM state storage '$storeid' is not available on this node, using" + . " automatic selection instead\n"; + } } elsif (($line =~ m/^vmgenid: (.*)/)) { my $vmgenid = $1; if ($vmgenid ne '0') { diff --git a/src/test/restore-config-expected/201.conf b/src/test/restore-config-expected/201.conf new file mode 100644 index 0000000..c76a010 --- /dev/null +++ b/src/test/restore-config-expected/201.conf @@ -0,0 +1,13 @@ +# VM state storage available on the target is kept +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: target:201/vm-201-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 +vmstatestorage: mydir diff --git a/src/test/restore-config-expected/202.conf b/src/test/restore-config-expected/202.conf new file mode 100644 index 0000000..f46451f --- /dev/null +++ b/src/test/restore-config-expected/202.conf @@ -0,0 +1,12 @@ +# VM state storage missing on the target falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: target:202/vm-202-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 diff --git a/src/test/restore-config-expected/203.conf b/src/test/restore-config-expected/203.conf new file mode 100644 index 0000000..6f93f8c --- /dev/null +++ b/src/test/restore-config-expected/203.conf @@ -0,0 +1,12 @@ +# VM state storage not available on this node falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: target:203/vm-203-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 diff --git a/src/test/restore-config-expected/204.conf b/src/test/restore-config-expected/204.conf new file mode 100644 index 0000000..926dab2 --- /dev/null +++ b/src/test/restore-config-expected/204.conf @@ -0,0 +1,12 @@ +# disabled VM state storage falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: target:204/vm-204-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 diff --git a/src/test/restore-config-input/201.conf b/src/test/restore-config-input/201.conf new file mode 100644 index 0000000..4459b73 --- /dev/null +++ b/src/test/restore-config-input/201.conf @@ -0,0 +1,14 @@ +# VM state storage available on the target is kept +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: mydir:201/vm-201-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 +vmstatestorage: mydir +#qmdump#map:scsi0:drive-scsi0:mydir:qcow2: diff --git a/src/test/restore-config-input/202.conf b/src/test/restore-config-input/202.conf new file mode 100644 index 0000000..15c340d --- /dev/null +++ b/src/test/restore-config-input/202.conf @@ -0,0 +1,14 @@ +# VM state storage missing on the target falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: mydir:202/vm-202-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 +vmstatestorage: local-zfs-pve1 +#qmdump#map:scsi0:drive-scsi0:mydir:qcow2: diff --git a/src/test/restore-config-input/203.conf b/src/test/restore-config-input/203.conf new file mode 100644 index 0000000..8f15277 --- /dev/null +++ b/src/test/restore-config-input/203.conf @@ -0,0 +1,14 @@ +# VM state storage not available on this node falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: mydir:203/vm-203-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 +vmstatestorage: othernode +#qmdump#map:scsi0:drive-scsi0:mydir:qcow2: diff --git a/src/test/restore-config-input/204.conf b/src/test/restore-config-input/204.conf new file mode 100644 index 0000000..bb8f578 --- /dev/null +++ b/src/test/restore-config-input/204.conf @@ -0,0 +1,14 @@ +# disabled VM state storage falls back to automatic selection +boot: order=scsi0 +cores: 1 +memory: 2048 +name: hibernated +net0: virtio=BC:24:11:6B:2A:5F,bridge=vmbr0 +ostype: win10 +scsi0: mydir:204/vm-204-disk-0.qcow2,size=32G +scsihw: virtio-scsi-single +smbios1: uuid=0c5a8b3e-9d43-4bd1-8a0e-3f6f2b1f3c11 +sockets: 1 +vmgenid: 0 +vmstatestorage: disabledstore +#qmdump#map:scsi0:drive-scsi0:mydir:qcow2: diff --git a/src/test/run_qemu_restore_config_tests.pl b/src/test/run_qemu_restore_config_tests.pl index 4d5ebb5..8982fe9 100644 --- a/src/test/run_qemu_restore_config_tests.pl +++ b/src/test/run_qemu_restore_config_tests.pl @@ -17,7 +17,7 @@ my $INPUT_DIR = './restore-config-input'; my $EXPECTED_DIR = './restore-config-expected'; # NOTE update when you add/remove tests -plan tests => 4; +plan tests => 8; my $pve_cluster_module = Test::MockModule->new("PVE::Cluster"); $pve_cluster_module->mock( @@ -32,6 +32,29 @@ $pve_cluster_module->mock( }, ); +my $pve_storage_module = Test::MockModule->new("PVE::Storage"); +$pve_storage_module->mock( + config => sub { + return { + ids => { + mydir => { type => 'dir', path => '/mnt/mydir', content => { images => 1 } }, + othernode => { + type => 'dir', + path => '/mnt/othernode', + content => { images => 1 }, + nodes => { 'some-other-node' => 1 }, + }, + disabledstore => { + type => 'dir', + path => '/mnt/disabled', + content => { images => 1 }, + disable => 1, + }, + }, + }; + }, +); + dir_glob_foreach( './restore-config-input', '[0-9]+.conf',