return $plugin->storage_can_replicate($scfg, $storeid, $format);
}
+sub get_max_protected_backups {
+ my ($scfg, $storeid) = @_;
+
+ return $scfg->{'max-protected-backups'} if defined($scfg->{'max-protected-backups'});
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+
+ return $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.Allocate'], 1) ? -1 : 5;
+}
+
sub storage_ids {
my ($cfg) = @_;
my $scfg = storage_config($cfg, $storeid);
my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+ my ($vtype, undef, $vmid) = $plugin->parse_volname($volname);
+ my $max_protected_backups = get_max_protected_backups($scfg, $storeid);
+
+ if (
+ $vtype eq 'backup'
+ && $vmid
+ && $attribute eq 'protected'
+ && $value
+ && !$plugin->get_volume_attribute($scfg, $storeid, $volname, 'protected')
+ && $max_protected_backups > -1 # -1 is unlimited
+ ) {
+ my $backups = $plugin->list_volumes($storeid, $scfg, $vmid, ['backup']);
+ my ($backup_type) = map { $_->{subtype} } grep { $_->{volid} eq $volid } $backups->@*;
+
+ my $protected_count = grep {
+ $_->{protected} && (!$backup_type || ($_->{subtype} && $_->{subtype} eq $backup_type))
+ } $backups->@*;
+
+ if ($max_protected_backups <= $protected_count) {
+ die "The number of protected backups per guest is limited to $max_protected_backups ".
+ "on storage '$storeid'\n";
+ }
+ }
+
return $plugin->update_volume_attribute($scfg, $storeid, $volname, $attribute, $value);
}
shared => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
- 'prune-backups'=> { optional => 1 },
+ 'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
is_mountpoint => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
username => { optional => 1 },
maxfiles => { optional => 1 },
keyring => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
'fs-name' => { optional => 1 },
};
}
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
mkdir => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
mkdir => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
options => { optional => 1 },
content => { optional => 1 },
format => { optional => 1 },
'master-pubkey' => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
+ 'max-protected-backups' => { optional => 1 },
fingerprint => { optional => 1 },
};
}
optional => 1,
},
'prune-backups' => get_standard_option('prune-backups'),
+ 'max-protected-backups' => {
+ description => "Maximal number of protected backups per guest. Use '-1' for unlimited.",
+ type => 'integer',
+ minimum => -1,
+ optional => 1,
+ default => "Unlimited for users with Datastore.Allocate privilege, 5 for other users",
+ },
shared => {
description => "Mark storage as shared.",
type => 'boolean',