From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 895FA1FF15C for ; Wed, 18 Sep 2024 16:50:17 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id A28473723B; Wed, 18 Sep 2024 16:50:09 +0200 (CEST) From: Filip Schauer To: pve-devel@lists.proxmox.com Date: Wed, 18 Sep 2024 16:49:52 +0200 Message-Id: <20240918144953.130780-6-f.schauer@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240918144953.130780-1-f.schauer@proxmox.com> References: <20240918144953.130780-1-f.schauer@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.052 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy 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 v4 storage 5/6] support moving VMA backups to PBS 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: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" Extend the move API to support moving VMA backups to a Proxmox Backup Server. Signed-off-by: Filip Schauer --- debian/control | 1 + src/PVE/API2/Storage/Content.pm | 86 ++++++++++++++++++--------------- src/PVE/Storage/PBSPlugin.pm | 65 +++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 40 deletions(-) diff --git a/debian/control b/debian/control index d7afa98..6b43557 100644 --- a/debian/control +++ b/debian/control @@ -42,6 +42,7 @@ Depends: ceph-common (>= 12.2~), nfs-common, proxmox-backup-client (>= 2.1.10~), proxmox-backup-file-restore, + proxmox-vma-to-pbs (>= 0.1.0), pve-cluster (>= 5.0-32), smartmontools, smbclient, diff --git a/src/PVE/API2/Storage/Content.pm b/src/PVE/API2/Storage/Content.pm index 4a8fe33..6e38a2e 100644 --- a/src/PVE/API2/Storage/Content.pm +++ b/src/PVE/API2/Storage/Content.pm @@ -10,6 +10,7 @@ use File::Copy qw(copy move); use PVE::SafeSyslog; use PVE::Cluster; use PVE::Storage; +use PVE::Storage::PBSPlugin; use PVE::INotify; use PVE::Exception qw(raise_param_exc); use PVE::RPCEnvironment; @@ -495,7 +496,7 @@ sub volume_move { my $source_scfg = PVE::Storage::storage_config($cfg, $source_storeid); my $source_plugin = PVE::Storage::Plugin->lookup($source_scfg->{type}); - my ($vtype) = $source_plugin->parse_volname($source_volname); + my ($vtype, undef, $vmid) = $source_plugin->parse_volname($source_volname); die "source storage '$source_storeid' does not support volumes of type '$vtype'\n" if !$source_scfg->{content}->{$vtype}; @@ -522,53 +523,58 @@ sub volume_move { die "$source_path does not exist" if (!-e $source_path); my $source_dirname = dirname($source_path); - die "expected storage '$target_storeid' to be path based\n" if !$target_scfg->{path}; my $target_plugin = PVE::Storage::Plugin->lookup($target_scfg->{type}); - my $target_path = $target_plugin->path($target_scfg, $source_volname, $target_storeid); - PVE::Storage::activate_storage($cfg, $target_storeid); - die "$target_path already exists" if (-e $target_path); - - my @created_files = (); - - eval { - if ($vtype eq 'backup') { - my $target_dirname = dirname($target_path); - my $info = PVE::Storage::archive_info($source_path); - - for my $type (qw(log notes)) { - my $filename = $info->{"${type}filename"} or next; - my $auxiliary_source_path = "$source_dirname/$filename"; - my $auxiliary_target_path = "$target_dirname/$filename"; - if (-e $auxiliary_source_path) { - copy($auxiliary_source_path, $auxiliary_target_path) - or die "copying backup $type file failed: $!"; - push(@created_files, $auxiliary_target_path); + + if ($vtype eq 'backup' && $target_scfg->{type} eq 'pbs') { + PVE::Storage::PBSPlugin::vma_to_pbs( + $source_scfg, $source_volid, $target_scfg, $target_storeid); + } else { + die "expected storage '$target_storeid' to be path based\n" if !$target_scfg->{path}; + my $target_path = $target_plugin->path($target_scfg, $source_volname, $target_storeid); + die "$target_path already exists" if (-e $target_path); + + my @created_files = (); + + eval { + if ($vtype eq 'backup') { + my $target_dirname = dirname($target_path); + my $info = PVE::Storage::archive_info($source_path); + + for my $type (qw(log notes)) { + my $filename = $info->{"${type}filename"} or next; + my $auxiliary_source_path = "$source_dirname/$filename"; + my $auxiliary_target_path = "$target_dirname/$filename"; + if (-e $auxiliary_source_path) { + copy($auxiliary_source_path, $auxiliary_target_path) + or die "copying backup $type file failed: $!"; + push(@created_files, $auxiliary_target_path); + } } - } - my $protected = $source_plugin->get_volume_attribute( - $source_scfg, $source_storeid, $source_volname, 'protected'); + my $protected = $source_plugin->get_volume_attribute( + $source_scfg, $source_storeid, $source_volname, 'protected'); - if ($protected) { - my $protection_target_path = PVE::Storage::protection_file_path($target_path); - $target_plugin->update_volume_attribute( - $target_scfg, $target_storeid, $source_volname, 'protected', 1); - push(@created_files, $protection_target_path); + if ($protected) { + my $protection_target_path = PVE::Storage::protection_file_path($target_path); + $target_plugin->update_volume_attribute( + $target_scfg, $target_storeid, $source_volname, 'protected', 1); + push(@created_files, $protection_target_path); + } } - } - if ($delete) { - move($source_path, $target_path) or die "failed to move $vtype: $!"; - } else { - copy($source_path, $target_path) or die "failed to copy $vtype: $!"; - } - }; - if (my $err = $@) { - for my $created_file (@created_files) { - unlink $created_file or $!{ENOENT} or warn $!; + if ($delete) { + move($source_path, $target_path) or die "failed to move $vtype: $!"; + } else { + copy($source_path, $target_path) or die "failed to copy $vtype: $!"; + } + }; + if (my $err = $@) { + for my $created_file (@created_files) { + unlink $created_file or $!{ENOENT} or warn $!; + } + die $err; } - die $err; } PVE::Storage::archive_remove($source_path) if $delete; diff --git a/src/PVE/Storage/PBSPlugin.pm b/src/PVE/Storage/PBSPlugin.pm index 0808bcc..4f8a05d 100644 --- a/src/PVE/Storage/PBSPlugin.pm +++ b/src/PVE/Storage/PBSPlugin.pm @@ -6,6 +6,7 @@ use strict; use warnings; use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC); +use File::Basename; use IO::File; use JSON; use MIME::Base64 qw(decode_base64); @@ -971,4 +972,68 @@ sub volume_has_feature { return undef; } +sub vma_to_pbs { + my ($source_scfg, $source_volid, $target_scfg, $target_storeid) = @_; + + my $source_plugin = PVE::Storage::Plugin->lookup($source_scfg->{type}); + my $target_plugin = PVE::Storage::Plugin->lookup($target_scfg->{type}); + my ($source_storeid, $source_volname) = PVE::Storage::parse_volume_id($source_volid, 0); + my $source_path = $source_plugin->path($source_scfg, $source_volname, $source_storeid); + my $info = PVE::Storage::archive_info($source_path); + die "moving non-VMA backups to a Proxmox Backup Server is not supported\n" + if $info->{format} ne 'vma'; + + my $repo = PVE::PBSClient::get_repository($target_scfg); + my $vmid = ($source_plugin->parse_volname($source_volname))[2]; + my $fingerprint = $target_scfg->{fingerprint}; + my $password = PVE::Storage::PBSPlugin::pbs_password_file_name($target_scfg, $target_storeid); + my $namespace = $target_scfg->{namespace}; + my $keyfile = PVE::Storage::PBSPlugin::pbs_encryption_key_file_name( + $target_scfg, $target_storeid); + my $master_keyfile = PVE::Storage::PBSPlugin::pbs_master_pubkey_file_name( + $target_scfg, $target_storeid); + + my $comp = $info->{compression}; + my $backup_time = $info->{ctime}; + my $source_dirname = dirname($source_path); + my $log_file_path = "$source_dirname/$info->{logfilename}"; + my $notes_file_path = "$source_dirname/$info->{notesfilename}"; + + my $vma_to_pbs_cmd = [ + "vma-to-pbs", + "--repository", $repo, + "--vmid", $vmid, + "--fingerprint", $fingerprint, + "--password-file", $password, + "--backup-time", $backup_time, + "--compress", + ]; + + push @$vma_to_pbs_cmd, "--ns", $namespace if $namespace; + push @$vma_to_pbs_cmd, "--log-file", $log_file_path if -e $log_file_path; + push @$vma_to_pbs_cmd, "--notes-file", $notes_file_path if -e $notes_file_path; + push @$vma_to_pbs_cmd, "--encrypt", "--keyfile", $keyfile if -e $keyfile; + push @$vma_to_pbs_cmd, "--master-keyfile", $master_keyfile if -e $master_keyfile; + + if ($comp) { + PVE::Storage::decompress_archive_into_pipe($source_path, $vma_to_pbs_cmd); + } else { + push @$vma_to_pbs_cmd, $source_path; + run_command($vma_to_pbs_cmd); + } + + my $protected = $source_plugin->get_volume_attribute( + $source_scfg, $source_storeid, $source_volname, 'protected'); + + if ($protected) { + my $target_volid = PVE::Storage::PBSPlugin::print_volid( + $target_storeid, 'vm', $vmid, $backup_time); + my (undef, $target_volname) = PVE::Storage::parse_volume_id($target_volid, 0); + $target_plugin->update_volume_attribute( + $target_scfg, $target_storeid, $target_volname, 'protected', 1); + } + + return; +} + 1; -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel