From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id A70A0E14C for ; Thu, 21 Apr 2022 13:27:37 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 670F51E337 for ; Thu, 21 Apr 2022 13:27:13 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 6E4A81E25A for ; Thu, 21 Apr 2022 13:27:07 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 47F3B4265F for ; Thu, 21 Apr 2022 13:27:07 +0200 (CEST) From: Fabian Ebner To: pve-devel@lists.proxmox.com Date: Thu, 21 Apr 2022 13:26:55 +0200 Message-Id: <20220421112659.74011-10-f.ebner@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220421112659.74011-1-f.ebner@proxmox.com> References: <20220421112659.74011-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.088 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH v2 qemu-server 7/7] restore: allow preserving drives during restore X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 21 Apr 2022 11:27:37 -0000 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 --- 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= 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