]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Storage/PBSPlugin.pm
pbs: integrate support for protected
[pve-storage.git] / PVE / Storage / PBSPlugin.pm
index 68c702aa1f466b2eb3b2253494a3f11ec2ceacc1..06ebfa76f6b6adec1dfe45731368a3d5fab86254 100644 (file)
@@ -9,7 +9,8 @@ use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC);
 use IO::File;
 use JSON;
 use MIME::Base64 qw(decode_base64);
-use POSIX qw(strftime ENOENT);
+use POSIX qw(mktime strftime ENOENT);
+use POSIX::strptime;
 
 use PVE::APIClient::LWP;
 use PVE::JSONSchema qw(get_standard_option);
@@ -45,7 +46,7 @@ sub properties {
            type => 'string',
        },
        'master-pubkey' => {
-           description => "Base64-encoded, PEM-formatted public RSA key. Used tp encrypt a copy of the encryption-key which will be added to each encrypted backup.",
+           description => "Base64-encoded, PEM-formatted public RSA key. Used to encrypt a copy of the encryption-key which will be added to each encrypted backup.",
            type => 'string',
        },
        port => {
@@ -218,6 +219,36 @@ sub print_volid {
     return "${storeid}:${volname}";
 }
 
+# essentially the inverse of print_volid
+sub api_param_from_volname {
+    my ($class, $volname) = @_;
+
+    my $name = ($class->parse_volname($volname))[1];
+
+    my ($btype, $bid, $timestr) = split('/', $name);
+
+    my @tm = (POSIX::strptime($timestr, "%FT%TZ"));
+    # expect sec, min, hour, mday, mon, year
+    die "error parsing time from '$volname'" if grep { !defined($_) } @tm[0..5];
+
+    my $btime;
+    {
+       local $ENV{TZ} = 'UTC'; # $timestr is UTC
+
+       # Fill in isdst to avoid undef warning. No daylight saving time for UTC.
+       $tm[8] //= 0;
+
+       my $since_epoch = mktime(@tm) or die "error converting time from '$volname'\n";
+       $btime = int($since_epoch);
+    }
+
+    return {
+       'backup-type' => $btype,
+       'backup-id' => $bid,
+       'backup-time' => $btime,
+    };
+}
+
 my $USE_CRYPT_PARAMS = {
     backup => 1,
     restore => 1,
@@ -403,9 +434,12 @@ sub prune_backups {
                my $vmid = $backup->{'backup-id'};
                my $volid = print_volid($storeid, $type, $vmid, $ctime);
 
+               my $mark = $backup->{keep} ? 'keep' : 'remove';
+               $mark = 'protected' if $backup->{protected};
+
                push @{$prune_list}, {
                    ctime => $ctime,
-                   mark => $backup->{keep} ? 'keep' : 'remove',
+                   mark => $mark,
                    type => $type eq 'vm' ? 'qemu' : 'lxc',
                    vmid => $vmid,
                    volid => $volid,
@@ -565,7 +599,7 @@ sub path {
 
     my $repo = PVE::PBSClient::get_repository($scfg);
 
-    # artifical url - we currently do not use that anywhere
+    # artificial url - we currently do not use that anywhere
     my $path = "pbs://$repo/$name";
 
     return ($path, $vmid, $vtype);
@@ -658,6 +692,7 @@ sub list_volumes {
 
        $info->{verification} = $item->{verification} if defined($item->{verification});
        $info->{notes} = $item->{comment} if defined($item->{comment});
+       $info->{protected} = 1 if $item->{protected};
        if (defined($item->{fingerprint})) {
            $info->{encrypted} = $item->{fingerprint};
        } elsif (snapshot_files_encrypted($item->{files})) {
@@ -758,7 +793,7 @@ sub activate_storage {
        }
     }
 
-    die "$storeid: Cannot find datastore '$datastore', check permissions and existance!\n";
+    die "$storeid: Cannot find datastore '$datastore', check permissions and existence!\n";
 }
 
 sub deactivate_storage {
@@ -782,6 +817,8 @@ sub deactivate_volume {
     return 1;
 }
 
+# FIXME remove on the next APIAGE reset.
+# Deprecated, use get_volume_attribute instead.
 sub get_volume_notes {
     my ($class, $scfg, $storeid, $volname, $timeout) = @_;
 
@@ -792,6 +829,8 @@ sub get_volume_notes {
     return $data->{notes};
 }
 
+# FIXME remove on the next APIAGE reset.
+# Deprecated, use update_volume_attribute instead.
 sub update_volume_notes {
     my ($class, $scfg, $storeid, $volname, $notes, $timeout) = @_;
 
@@ -802,6 +841,53 @@ sub update_volume_notes {
     return undef;
 }
 
+sub get_volume_attribute {
+    my ($class, $scfg, $storeid, $volname, $attribute) = @_;
+
+    if ($attribute eq 'notes') {
+       return $class->get_volume_notes($scfg, $storeid, $volname);
+    }
+
+    if ($attribute eq 'protected') {
+       my $param = $class->api_param_from_volname($volname);
+
+       my $password = pbs_get_password($scfg, $storeid);
+       my $conn = pbs_api_connect($scfg, $password);
+       my $datastore = $scfg->{datastore};
+
+       my $res = eval { $conn->get("/api2/json/admin/datastore/$datastore/$attribute", $param); };
+       if (my $err = $@) {
+           return if $err->{code} == 404; # not supported
+           die $err;
+       }
+       return $res;
+    }
+
+    return;
+}
+
+sub update_volume_attribute {
+    my ($class, $scfg, $storeid, $volname, $attribute, $value) = @_;
+
+    if ($attribute eq 'notes') {
+       return $class->update_volume_notes($scfg, $storeid, $volname, $value);
+    }
+
+    if ($attribute eq 'protected') {
+       my $param = $class->api_param_from_volname($volname);
+       $param->{$attribute} = $value;
+
+       my $password = pbs_get_password($scfg, $storeid);
+       my $conn = pbs_api_connect($scfg, $password);
+       my $datastore = $scfg->{datastore};
+
+       $conn->put("/api2/json/admin/datastore/$datastore/$attribute", $param);
+       return;
+    }
+
+    die "attribute '$attribute' is not supported for storage type '$scfg->{type}'\n";
+}
+
 sub volume_size_info {
     my ($class, $scfg, $storeid, $volname, $timeout) = @_;
 
@@ -811,7 +897,9 @@ sub volume_size_info {
 
     my $size = 0;
     foreach my $info (@$data) {
-       $size += $info->{size} if $info->{size};
+       if ($info->{size} && $info->{size} =~ /^(\d+)$/) { # untaints
+           $size += $1;
+       }
     }
 
     my $used = $size;