[pve-devel] [PATCH v2 qemu-server 7/7] restore: allow preserving drives during restore

Fabian Ebner f.ebner at proxmox.com
Thu Apr 21 13:26:55 CEST 2022


Preserving a drive means:
  * The disk it references will not be touched by the restore
    operation.
  * The drive will be left as is in the VM configuration.
  * If the drive is present in the backup, that disk will not be
    restored.

A drive that is not present in the backup was/is re-added as unused by
default, but when preserving, it's kept configured instead.

If a drive is not currently configured and passed as part of preserve,
restore is skipped and its entry in the restored config will be
removed.

Signed-off-by: Fabian Ebner <f.ebner at proxmox.com>
---

Dependency bump for pve-qemu is needed.

Changes from v1:
  * Allow skipping restore of drive that's not currently configured
    (see below).
  * Adapt to skip=<devname> syntax when passing drive map to VMA.
  * When merging configs, remove key from the final config if value
    is explicitly undef.
  * Add 'preserve-drives' parameter instead of treating passing
    existing disk in a special way. While this makes it less
    flexible, the advantages are:
      * Can be used to skip restoring a disk that's not currently
        configured.
      * Less automagic behavior, it might not be intuitive what
        passing existing disk will do.
      * No conflict with currently existing syntax for containers.
      * No need to specify the disk's options again. The disk will
        be preserved like it's currently in the config.

 PVE/API2/Qemu.pm  | 25 ++++++++++++++++++++++++-
 PVE/QemuServer.pm | 34 +++++++++++++++++++++++++++++++---
 2 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 8df2cc8d..86359cef 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -749,6 +749,14 @@ __PACKAGE__->register_method({
 		    description => "Start the VM immediately from the backup and restore in background. PBS only.",
 		    requires => 'archive',
 		},
+		'preserve-drives' => {
+		    optional => 1,
+		    type => 'string',
+		    format => 'pve-configid-list',
+		    description => "List of drives (e.g. scsi0) for which to preserve the current ".
+			"configuration and disk contents.",
+		    requires => 'archive',
+		},
 		pool => {
 		    optional => 1,
 		    type => 'string', format => 'pve-poolid',
@@ -793,6 +801,7 @@ __PACKAGE__->register_method({
 	my $storage = extract_param($param, 'storage');
 	my $unique = extract_param($param, 'unique');
 	my $live_restore = extract_param($param, 'live-restore');
+	my $preserve_drives = extract_param($param, 'preserve-drives');
 
 	if (defined(my $ssh_keys = $param->{sshkeys})) {
 		$ssh_keys = URI::Escape::uri_unescape($ssh_keys);
@@ -825,10 +834,18 @@ __PACKAGE__->register_method({
 	if ($archive) {
 	    for my $opt (sort keys $param->%*) {
 		if (PVE::QemuServer::Drive::is_valid_drivename($opt)) {
-		    raise_param_exc({ $opt => "option conflicts with option 'archive'" });
+		    raise_param_exc({
+			$opt => "option conflicts with option 'archive' (do you mean to use ".
+			    "'preserve-drives'?)",
+		    });
 		}
 	    }
 
+	    for my $opt (PVE::Tools::split_list($preserve_drives)) {
+		raise_param_exc({ 'preserve-drives' => "$opt - not a drive key" })
+		    if !PVE::QemuServer::Drive::is_valid_drivename($opt);
+	    }
+
 	    if ($archive eq '-') {
 		die "pipe requires cli environment\n" if $rpcenv->{type} ne 'cli';
 		$archive = { type => 'pipe' };
@@ -876,6 +893,12 @@ __PACKAGE__->register_method({
 
 	    die "$emsg vm is running\n" if PVE::QemuServer::check_running($vmid);
 
+	    for my $opt (PVE::Tools::split_list($preserve_drives)) {
+		die "internal error - expected drive key\n"
+		    if !PVE::QemuServer::Drive::is_valid_drivename($opt);
+		$param->{$opt} = $conf->{$opt};
+	    }
+
 	    my $realcmd = sub {
 		my $restore_options = {
 		    storage => $storage,
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index e64a9c7a..affdd0bb 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -6206,6 +6206,15 @@ sub tar_restore_cleanup {
     }
 }
 
+# Possilbe options are
+# $format: archive format
+# $storage: target storage
+# $pool: add VM to this pool
+# $unique: make VM unique (mac addressses, vmgenid)
+# $bwlimit: limit restore speed
+# $live: live restore (PBS only)
+# $override_conf: Settings that will be overwritten. If a key is explicitly set to undef, it will be
+#     deleted from the final config. Drives whose keys appear in this config are not restored.
 sub restore_file_archive {
     my ($archive, $vmid, $user, $opts) = @_;
 
@@ -6309,6 +6318,8 @@ my $parse_backup_hints = sub {
 	    $devinfo->{$devname}->{format} = $format;
 	    $devinfo->{$devname}->{storeid} = $storeid;
 
+	    next if exists($options->{override_conf}->{$virtdev}); # not being restored
+
 	    my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
 	    $check_storage->($storeid, $scfg); # permission and content type check
 
@@ -6481,7 +6492,11 @@ my $restore_merge_config = sub {
 
     my $backup_conf = parse_vm_config($filename, $backup_conf_raw);
     for my $key (keys $override_conf->%*) {
-	$backup_conf->{$key} = $override_conf->{$key};
+	if (defined($override_conf->{$key})) {
+	    $backup_conf->{$key} = $override_conf->{$key};
+	} else {
+	    delete $backup_conf->{$key};
+	}
     }
 
     return $backup_conf;
@@ -6818,6 +6833,12 @@ sub restore_proxmox_backup_archive {
 	# these special drives are already restored before start
 	delete $devinfo->{'drive-efidisk0'};
 	delete $devinfo->{'drive-tpmstate0-backup'};
+
+	for my $key (keys $options->{override_conf}->%*) {
+	    next if !is_valid_drivename($key);
+	    delete $devinfo->{"drive-$key"};
+	}
+
 	pbs_live_restore($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
 
 	PVE::QemuConfig->remove_lock($vmid, "create");
@@ -7010,8 +7031,15 @@ sub restore_vma_archive {
 	my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
 
 	# print restore information to $fifofh
-	foreach my $virtdev (sort keys %$virtdev_hash) {
-	    my $d = $virtdev_hash->{$virtdev};
+	for my $devname (sort keys $devinfo->%*) {
+	    my $d = $devinfo->{$devname};
+
+	    if (!$virtdev_hash->{$d->{virtdev}}) { # skipped
+		print $fifofh "skip=$d->{devname}\n";
+		print "not restoring '$d->{devname}', but keeping current disk\n";
+		next;
+	    }
+
 	    next if $d->{is_cloudinit}; # no need to restore cloudinit
 
 	    my $storeid = $d->{storeid};
-- 
2.30.2






More information about the pve-devel mailing list