From: Fabian Ebner <f.ebner@proxmox.com>
To: Dominik Csapak <d.csapak@proxmox.com>,
Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Subject: Re: [pve-devel] [RFC storage 4/6] fix #3307: make it possible to set protection for backups
Date: Fri, 24 Sep 2021 13:17:18 +0200 [thread overview]
Message-ID: <d64d403c-df21-5c49-242d-cbd4cec605ce@proxmox.com> (raw)
In-Reply-To: <001f7e45-8693-6364-94bc-2261bfb9f3a0@proxmox.com>
Am 24.09.21 um 10:54 schrieb Dominik Csapak:
> On 9/17/21 15:02, Fabian Ebner wrote:
>> A protected backup is not removed by free_image and ignored when
>> pruning.
>>
>> The protection_file_path function is introduced in Storage.pm, so that
>> it can also be used by vzdump itself and in archive_remove.
>>
>> Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
>> ---
>>
>> Needs an APIAGE+APIVER bump (can be covered by the one for patch #2),
>> because of the new attribute.
>>
>> I decided to not just re-use the "renamed is protected" behavior when
>> pruning, because:
>> 1. it wouldn't protect against removal
>> 2. it would make it necessary to rename notes and log files too
>>
>> Should the protection files rather be hidden files?
>
> imho no, because if users clean the dir up manually, they're likely
> to miss those
>
>>
>> Should we also bother to set the immutable flag on filesystems that
>> support it?
>
> if a user want to remove that file manually, why should we make
> it harder for a user? my guess is that most would then either
> remove the protection via the api, or come to the forum, where
> there will probably be someone that explains how to remove
> the immutable flag
>
> i think it would just add complexity for little gain
>
> if we would want it, setting the immutable flag on the
> volume to protect it could work though, but since
> there can be an arbitrary filesystem underneath,
> we would have to test each time
>
>>
>> PVE/API2/Storage/Content.pm | 37 ++++++++++++++++++++++++++++---------
>> PVE/Storage.pm | 9 +++++++++
>> PVE/Storage/DirPlugin.pm | 26 ++++++++++++++++++++++++--
>> PVE/Storage/Plugin.pm | 7 +++++++
>> test/prune_backups_test.pm | 13 +++++++++++++
>> 5 files changed, 81 insertions(+), 11 deletions(-)
>>
>> diff --git a/PVE/API2/Storage/Content.pm b/PVE/API2/Storage/Content.pm
>> index b3dc593..45b8de8 100644
>> --- a/PVE/API2/Storage/Content.pm
>> +++ b/PVE/API2/Storage/Content.pm
>> @@ -113,6 +113,11 @@ __PACKAGE__->register_method ({
>> },
>> optional => 1,
>> },
>> + protected => {
>> + description => "Protection status. Currently only
>> supported for backups.",
>> + type => 'boolean',
>> + optional => 1,
>> + },
>> },
>> },
>> links => [ { rel => 'child', href => "{volid}" } ],
>> @@ -295,7 +300,12 @@ __PACKAGE__->register_method ({
>> description => "Optional notes.",
>> optional => 1,
>> type => 'string',
>> - }
>> + },
>> + protected => {
>> + description => "Protection status. Currently only supported
>> for backups.",
>> + type => 'boolean',
>> + optional => 1,
>> + },
>> },
>> },
>> code => sub {
>> @@ -321,12 +331,14 @@ __PACKAGE__->register_method ({
>> format => $format,
>> };
>> - # keep going if fetching an optional attribute fails
>> - eval {
>> - my $notes = PVE::Storage::get_volume_attribute($cfg, $volid,
>> 'notes');
>> - $entry->{notes} = $notes if defined($notes);
>> - };
>> - warn $@ if $@;
>> + for my $attribute (qw(notes protected)) {
>> + # keep going if fetching an optional attribute fails
>> + eval {
>> + my $value = PVE::Storage::get_volume_attribute($cfg, $volid,
>> $attribute);
>> + $entry->{$attribute} = $value if defined($value);
>> + };
>> + warn $@ if $@;
>> + }
>> return $entry;
>> }});
>> @@ -356,6 +368,11 @@ __PACKAGE__->register_method ({
>> type => 'string',
>> optional => 1,
>> },
>> + protected => {
>> + description => "Protection status. Currently only supported
>> for backups.",
>> + type => 'boolean',
>> + optional => 1,
>> + },
>> },
>> },
>> returns => { type => 'null' },
>> @@ -371,8 +388,10 @@ __PACKAGE__->register_method ({
>> PVE::Storage::check_volume_access($rpcenv, $authuser, $cfg,
>> undef, $volid);
>> - if (exists $param->{notes}) {
>> - PVE::Storage::update_volume_attribute($cfg, $volid, 'notes',
>> $param->{notes});
>> + for my $attr (qw(notes protected)) {
>> + if (exists $param->{$attr}) {
>> + PVE::Storage::update_volume_attribute($cfg, $volid, $attr,
>> $param->{$attr});
>> + }
>> }
>> return undef;
>> diff --git a/PVE/Storage.pm b/PVE/Storage.pm
>> index e7d8e62..3beb645 100755
>> --- a/PVE/Storage.pm
>> +++ b/PVE/Storage.pm
>> @@ -1463,6 +1463,12 @@ sub decompressor_info {
>> return $info;
>> }
>> +sub protection_file_path {
>> + my ($path) = @_;
>> +
>> + return "${path}.protected";
>> +}
>> +
>> sub archive_info {
>> my ($archive) = shift;
>> my $info;
>> @@ -1494,6 +1500,9 @@ sub archive_info {
>> sub archive_remove {
>> my ($archive_path) = @_;
>> + die "cannot remove protected archive '$archive_path'\n"
>> + if -e protection_file_path($archive_path);
>> +
>> my $dirname = dirname($archive_path);
>> my $archive_info = eval { archive_info($archive_path) } // {};
>> my $logfn = $archive_info->{logfilename};
>> diff --git a/PVE/Storage/DirPlugin.pm b/PVE/Storage/DirPlugin.pm
>> index 6ab9ef2..99a6eaf 100644
>> --- a/PVE/Storage/DirPlugin.pm
>> +++ b/PVE/Storage/DirPlugin.pm
>> @@ -2,8 +2,11 @@ package PVE::Storage::DirPlugin;
>> use strict;
>> use warnings;
>> +
>> use Cwd;
>> use File::Path;
>> +use IO::File;
>> +
>> use PVE::Storage::Plugin;
>> use PVE::JSONSchema qw(get_standard_option);
>> @@ -93,13 +96,16 @@ sub get_volume_attribute {
>> my ($vtype) = $class->parse_volname($volname);
>> return if $vtype ne 'backup';
>> + my $path = $class->filesystem_path($scfg, $volname);
>> +
>> if ($attribute eq 'notes') {
>> - my $path = $class->filesystem_path($scfg, $volname);
>> $path .= $class->SUPER::NOTES_EXT;
>> return PVE::Tools::file_get_contents($path) if -f $path;
>> return '';
>> + } elsif ($attribute eq 'protected') {
>> + return -e PVE::Storage::protection_file_path($path) ? 1 : 0;
>> }
>> return;
>> @@ -111,8 +117,9 @@ sub update_volume_attribute {
>> my ($vtype) = $class->parse_volname($volname);
>> die "only backups support attribute '$attribute'\n" if $vtype ne
>> 'backup';
>> + my $path = $class->filesystem_path($scfg, $volname);
>> +
>> if ($attribute eq 'notes') {
>> - my $path = $class->filesystem_path($scfg, $volname);
>> $path .= $class->SUPER::NOTES_EXT;
>> if (defined($value) && $value ne '') {
>> @@ -120,6 +127,21 @@ sub update_volume_attribute {
>> } elsif (-e $path) {
>> unlink $path or die "could not delete notes - $!\n";
>> }
>> + return;
>> + } elsif ($attribute eq 'protected') {
>> + my $protection_path = PVE::Storage::protection_file_path($path);
>> +
>> + return if !((-e $protection_path) xor $value); # protection
>> status already correct
>
> is that not a '(-e $protection_path) == $value' ?
> if that works, i'd prefer it, since its simpler to read
The operands could be undef, which produces a warning for '=='. Since
xor and ! are for boolean expressions, I used those. But if there is a
cleaner way to compare to booleans in Perl, I'm all ears ;)
>
>> +
>> + if ($value) {
>> + my $fh = IO::File->new($protection_path, O_CREAT, 0644)
>> + or die "unable to create protection file '$protection_path' -
>> $!\n";
>> + close($fh);
>> + } else {
>> + unlink $protection_path
>> + or die "unable to remove protection file '$protection_path' -
>> $!\n";
>
> same thing as in the first patch, only a non ENOENT should probably
> raise an error
>
Will change that in v2.
>> + }
>> +
>> return;
>> }
>> diff --git a/PVE/Storage/Plugin.pm b/PVE/Storage/Plugin.pm
>> index 1182008..1ebd705 100644
>> --- a/PVE/Storage/Plugin.pm
>> +++ b/PVE/Storage/Plugin.pm
>> @@ -782,6 +782,9 @@ sub alloc_image {
>> sub free_image {
>> my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
>> + die "cannot remove protected volume '$volname' on '$storeid'\n"
>> + if $class->get_volume_attribute($scfg, $storeid, $volname,
>> 'protected');
>> +
>> my $path = $class->filesystem_path($scfg, $volname);
>> if ($isBase) {
>> @@ -871,6 +874,7 @@ sub update_volume_notes {
>> # Should die if there is an error fetching the attribute.
>> # Possible attributes:
>> # notes - user-provided comments/notes.
>> +# protected - not to be removed by free_image, and for backups,
>> ignored when pruning.
>> sub get_volume_attribute {
>> my ($class, $scfg, $storeid, $volname, $attribute) = @_;
>> @@ -1115,6 +1119,7 @@ my $get_subdir_files = sub {
>> $info->{notes} = $notes if defined($notes);
>> }
>> + $info->{protected} = 1 if -e
>> PVE::Storage::protection_file_path($original);
>> } elsif ($tt eq 'snippets') {
>> $info = {
>> @@ -1320,6 +1325,8 @@ sub prune_backups {
>> $prune_entry->{mark} = 'protected';
>> }
>> + $prune_entry->{mark} = 'protected' if $backup->{protected};
>> +
>> push @{$prune_list}, $prune_entry;
>> }
>> diff --git a/test/prune_backups_test.pm b/test/prune_backups_test.pm
>> index a87c4b3..8ad6144 100644
>> --- a/test/prune_backups_test.pm
>> +++ b/test/prune_backups_test.pm
>> @@ -29,6 +29,12 @@ foreach my $vmid (@vmids) {
>> 'ctime' => $basetime - 24*60*60 - 60*60,
>> 'vmid' => $vmid,
>> },
>> + {
>> + 'volid' =>
>> "$storeid:backup/vzdump-qemu-$vmid-2019_12_31-11_18_51.tar.zst",
>> + 'ctime' => $basetime - 24*60*60 - 60*60 + 30,
>> + 'vmid' => $vmid,
>> + 'protected' => 1,
>> + },
>> {
>> 'volid' =>
>> "$storeid:backup/vzdump-qemu-$vmid-2019_12_31-11_19_21.tar.zst",
>> 'ctime' => $basetime - 24*60*60 - 60*60 + 60,
>> @@ -140,6 +146,13 @@ sub generate_expected {
>> 'mark' => $marks->[1],
>> 'vmid' => $vmid,
>> },
>> + {
>> + 'volid' =>
>> "$storeid:backup/vzdump-qemu-$vmid-2019_12_31-11_18_51.tar.zst",
>> + 'type' => 'qemu',
>> + 'ctime' => $basetime - 24*60*60 - 60*60 + 30,
>> + 'mark' => 'protected',
>> + 'vmid' => $vmid,
>> + },
>> {
>> 'volid' =>
>> "$storeid:backup/vzdump-qemu-$vmid-2019_12_31-11_19_21.tar.zst",
>> 'type' => 'qemu',
>>
>
>
next prev parent reply other threads:[~2021-09-24 11:17 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-09-17 13:02 [pve-devel] [RFC storage/manager] fix #3307: allow backups to be marked as protected Fabian Ebner
2021-09-17 13:02 ` [pve-devel] [PATCH storage 1/6] dir plugin: update notes: don't attempt to remove non-existent notes Fabian Ebner
2021-09-24 8:54 ` Dominik Csapak
2021-09-24 9:03 ` Thomas Lamprecht
2021-09-24 10:40 ` Fabian Ebner
2021-09-17 13:02 ` [pve-devel] [RFC storage 2/6] add generalized functions to manage volume attributes Fabian Ebner
2021-09-24 8:54 ` Dominik Csapak
2021-09-24 11:05 ` Fabian Ebner
2021-09-24 11:16 ` Dominik Csapak
2021-09-24 11:31 ` Fabian Ebner
2021-09-17 13:02 ` [pve-devel] [PATCH storage 3/6] prune mark: preserve additional information for the keep-all case Fabian Ebner
2021-09-17 13:02 ` [pve-devel] [RFC storage 4/6] fix #3307: make it possible to set protection for backups Fabian Ebner
2021-09-24 8:54 ` Dominik Csapak
2021-09-24 11:17 ` Fabian Ebner [this message]
2021-09-17 13:02 ` [pve-devel] [RFC storage 5/6] prune: mark renamed and protected backups differently Fabian Ebner
2021-09-17 13:02 ` [pve-devel] [RFC storage 6/6] pbs: integrate support for protected Fabian Ebner
2021-09-24 8:55 ` Dominik Csapak
2021-09-24 11:32 ` Fabian Ebner
2021-09-24 11:48 ` Dominik Csapak
2021-09-17 13:02 ` [pve-devel] [RFC manager 1/1] vzdump: skip protected backups for dumpdir pruning Fabian Ebner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d64d403c-df21-5c49-242d-cbd4cec605ce@proxmox.com \
--to=f.ebner@proxmox.com \
--cc=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal