]> git.proxmox.com Git - pve-manager.git/blobdiff - PVE/API2/VZDump.pm
vzdump: adapt to new matcher based notification system
[pve-manager.git] / PVE / API2 / VZDump.pm
index 19fa1e3bb0300c9a6ec81732cfa546668140db80..f66fc740330d14392eb32a48270059eefb4dd208 100644 (file)
@@ -2,36 +2,55 @@ package PVE::API2::VZDump;
 
 use strict;
 use warnings;
+
+use PVE::AccessControl;
+use PVE::Cluster;
 use PVE::Exception qw(raise_param_exc);
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_register_file cfs_read_file);
 use PVE::INotify;
-use PVE::RPCEnvironment;
-use PVE::AccessControl;
 use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
 use PVE::Storage;
-use PVE::VZDump;
+use PVE::Tools qw(extract_param);
 use PVE::VZDump::Common;
+use PVE::VZDump;
+
+use PVE::API2::Backup;
 use PVE::API2Tools;
 
 use Data::Dumper; # fixme: remove
 
-
 use base qw(PVE::RESTHandler);
 
+my sub assert_param_permission_vzdump {
+    my ($rpcenv, $user, $param) = @_;
+    return if $user eq 'root@pam'; # always OK
+
+    PVE::API2::Backup::assert_param_permission_common($rpcenv, $user, $param);
+
+    if (defined($param->{maxfiles}) || defined($param->{'prune-backups'})) {
+       if (my $storeid = PVE::VZDump::get_storage_param($param)) {
+           $rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
+       }
+    }
+}
+
 __PACKAGE__->register_method ({
     name => 'vzdump',
     path => '',
     method => 'POST',
     description => "Create backup.",
     permissions => {
-       description => "The user needs 'VM.Backup' permissions on any VM, and 'Datastore.AllocateSpace' on the backup storage. The 'maxfiles', 'prune-backups', 'tmpdir', 'dumpdir', 'script', 'bwlimit' and 'ionice' parameters are restricted to the 'root\@pam' user.",
+       description => "The user needs 'VM.Backup' permissions on any VM, and "
+           ."'Datastore.AllocateSpace' on the backup storage. The 'tmpdir', 'dumpdir' and "
+           ."'script' parameters are restricted to the 'root\@pam' user. The 'maxfiles' and "
+           ."'prune-backups' settings require 'Datastore.Allocate' on the backup storage. The "
+           ."'bwlimit', 'performance' and 'ionice' parameters require 'Sys.Modify' on '/'. ",
        user => 'all',
     },
     protected => 1,
     proxyto => 'node',
     parameters => {
-       additionalProperties => 0,
+       additionalProperties => 0,
        properties => PVE::VZDump::Common::json_config_properties({
            stdout => {
                type => 'boolean',
@@ -58,10 +77,7 @@ __PACKAGE__->register_method ({
                if $param->{stdout};
        }
 
-       foreach my $key (qw(maxfiles prune-backups tmpdir dumpdir script bwlimit ionice)) {
-           raise_param_exc({ $key => "Only root may set this option."})
-               if defined($param->{$key}) && ($user ne 'root@pam');
-       }
+       assert_param_permission_vzdump($rpcenv, $user, $param);
 
        PVE::VZDump::verify_vzdump_parameters($param, 1);
 
@@ -74,6 +90,10 @@ __PACKAGE__->register_method ({
 
        my $local_vmids = delete $vmids_per_node->{$nodename} // [];
 
+       # include IDs for deleted guests, and visibly fail later
+       my $orphaned_vmids = delete $vmids_per_node->{''} // [];
+       push @{$local_vmids}, @{$orphaned_vmids};
+
        my $skiplist = [ map { @$_ } values $vmids_per_node->%* ];
 
        if($param->{stop}){
@@ -84,22 +104,14 @@ __PACKAGE__->register_method ({
        # silent exit if specified VMs run on other nodes
        return "OK" if !scalar(@{$local_vmids}) && !$param->{all};
 
-       # exclude-path list need to be 0 separated
-       if (defined($param->{'exclude-path'})) {
-           my @expaths = split(/\0/, $param->{'exclude-path'} || '');
-           $param->{'exclude-path'} = [ @expaths ];
-       }
-
-       if (defined($param->{mailto})) {
-           my @mailto = PVE::Tools::split_list(extract_param($param, 'mailto'));
-           $param->{mailto} = [ @mailto ];
-       }
+       PVE::VZDump::parse_mailto_exclude_path($param);
 
        die "you can only backup a single VM with option --stdout\n"
            if $param->{stdout} && scalar(@{$local_vmids}) != 1;
 
-       $rpcenv->check($user, "/storage/$param->{storage}", [ 'Datastore.AllocateSpace' ])
-           if $param->{storage};
+       if (my $storeid = PVE::VZDump::get_storage_param($param)) {
+           $rpcenv->check($user, "/storage/$storeid", [ 'Datastore.AllocateSpace' ]);
+       }
 
        my $worker = sub {
            my $upid = shift;
@@ -111,11 +123,11 @@ __PACKAGE__->register_method ({
            $param->{vmids} = $local_vmids;
            my $vzdump = PVE::VZDump->new($cmdline, $param, $skiplist);
 
-           eval {
+           my $LOCK_FH = eval {
                $vzdump->getlock($upid); # only one process allowed
            };
            if (my $err = $@) {
-               $vzdump->sendmail([], 0, $err);
+               $vzdump->send_notification([], 0, $err);
                exit(-1);
            }
 
@@ -127,6 +139,8 @@ __PACKAGE__->register_method ({
                }
            }
            $vzdump->exec_backup($rpcenv, $user);
+
+           close($LOCK_FH);
        };
 
        open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout};
@@ -151,6 +165,91 @@ __PACKAGE__->register_method ({
        return $rpcenv->fork_worker('vzdump', $taskid, $user, $worker);
    }});
 
+__PACKAGE__->register_method ({
+    name => 'defaults',
+    path => 'defaults',
+    method => 'GET',
+    description => "Get the currently configured vzdump defaults.",
+    permissions => {
+       description => "The user needs 'Datastore.Audit' or 'Datastore.AllocateSpace' " .
+           "permissions for the specified storage (or default storage if none specified). Some " .
+           "properties are only returned when the user has 'Sys.Audit' permissions for the node.",
+       user => 'all',
+    },
+    proxyto => 'node',
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           storage => get_standard_option('pve-storage-id', { optional => 1 }),
+       },
+    },
+    returns => {
+       type => 'object',
+       additionalProperties => 0,
+       properties => PVE::VZDump::Common::json_config_properties(),
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $node = extract_param($param, 'node');
+       my $storage = extract_param($param, 'storage');
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+       my $res = PVE::VZDump::read_vzdump_defaults();
+
+       $res->{storage} = $storage if defined($storage);
+
+       if (!defined($res->{dumpdir}) && !defined($res->{storage})) {
+           $res->{storage} = 'local';
+       }
+
+       if (defined($res->{storage})) {
+           $rpcenv->check_any(
+               $authuser,
+               "/storage/$res->{storage}",
+               ['Datastore.Audit', 'Datastore.AllocateSpace'],
+           );
+
+           my $info = PVE::VZDump::storage_info($res->{storage});
+           for my $key (qw(dumpdir prune-backups)) {
+               $res->{$key} = $info->{$key} if defined($info->{$key});
+           }
+       }
+
+       if (defined($res->{'prune-backups'})) {
+           $res->{'prune-backups'} = PVE::JSONSchema::print_property_string(
+               $res->{'prune-backups'},
+               'prune-backups',
+           );
+       }
+
+       $res->{mailto} = join(",", @{$res->{mailto}})
+           if defined($res->{mailto});
+
+       $res->{'exclude-path'} = join(",", @{$res->{'exclude-path'}})
+           if defined($res->{'exclude-path'});
+
+       # normal backup users don't need to know these
+       if (!$rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1)) {
+           delete $res->{mailto};
+           delete $res->{tmpdir};
+           delete $res->{dumpdir};
+           delete $res->{script};
+           delete $res->{ionice};
+       }
+
+       my $pool = $res->{pool};
+       if (defined($pool) &&
+           !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1)) {
+           delete $res->{pool};
+       }
+
+       return $res;
+    }});
+
 __PACKAGE__->register_method ({
     name => 'extractconfig',
     path => 'extractconfig',
@@ -183,7 +282,19 @@ __PACKAGE__->register_method ({
        my $authuser = $rpcenv->get_user();
 
        my $storage_cfg = PVE::Storage::config();
-       PVE::Storage::check_volume_access($rpcenv, $authuser, $storage_cfg, undef, $volume);
+       PVE::Storage::check_volume_access(
+           $rpcenv,
+           $authuser,
+           $storage_cfg,
+           undef,
+           $volume,
+           'backup',
+       );
+
+       if (PVE::Storage::parse_volume_id($volume, 1)) {
+           my (undef, undef, $ownervm) = PVE::Storage::parse_volname($storage_cfg, $volume);
+           $rpcenv->check($authuser, "/vms/$ownervm", ['VM.Backup']);
+       }
 
        return PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
     }});