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::Tools qw(extract_param);
+use PVE::VZDump::Common;
use PVE::VZDump;
-use Data::Dumper; # fixme: remove
+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',
+ 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', '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,
- properties => PVE::VZDump::json_config_properties({
+ additionalProperties => 0,
+ properties => PVE::VZDump::Common::json_config_properties({
stdout => {
type => 'boolean',
description => "Write tar to stdout, not to a file.",
if $param->{stdout};
}
- # by default we set --rsyncable for gzip
- local $ENV{GZIP} = "--rsyncable" if !$ENV{GZIP};
+ assert_param_permission_vzdump($rpcenv, $user, $param);
PVE::VZDump::verify_vzdump_parameters($param, 1);
# silent exit if we run on wrong node
return 'OK' if $param->{node} && $param->{node} ne $nodename;
-
- my $cmdline = PVE::VZDump::command_line($param);
- # convert string lists to arrays
- my @vmids = PVE::Tools::split_list(extract_param($param, 'vmid'));
+ my $cmdline = PVE::VZDump::Common::command_line($param);
- if($param->{stop}){
- PVE::VZDump::stop_running_backups();
- return 'OK' if !scalar(@vmids);
- }
+ my $vmids_per_node = PVE::VZDump::get_included_guests($param);
- my $skiplist = [];
- if (!$param->{all}) {
- if (!$param->{node}) {
- my $vmlist = PVE::Cluster::get_vmlist();
- my @localvmids = ();
- foreach my $vmid (@vmids) {
- my $d = $vmlist->{ids}->{$vmid};
- if ($d && ($d->{node} ne $nodename)) {
- push @$skiplist, $vmid;
- } else {
- push @localvmids, $vmid;
- }
- }
- @vmids = @localvmids;
- # silent exit if specified VMs run on other nodes
- return "OK" if !scalar(@vmids);
- }
+ my $local_vmids = delete $vmids_per_node->{$nodename} // [];
- $param->{vmids} = PVE::VZDump::check_vmids(@vmids)
- }
+ # include IDs for deleted guests, and visibly fail later
+ my $orphaned_vmids = delete $vmids_per_node->{''} // [];
+ push @{$local_vmids}, @{$orphaned_vmids};
- my @exclude = PVE::Tools::split_list(extract_param($param, 'exclude'));
- $param->{exclude} = PVE::VZDump::check_vmids(@exclude);
+ my $skiplist = [ map { @$_ } values $vmids_per_node->%* ];
- # 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($param->{stop}){
+ PVE::VZDump::stop_running_backups();
+ return 'OK' if !scalar(@{$local_vmids});
}
- if (defined($param->{mailto})) {
- my @mailto = PVE::Tools::split_list(extract_param($param, 'mailto'));
- $param->{mailto} = [ @mailto ];
- }
+ # silent exit if specified VMs run on other nodes
+ return "OK" if !scalar(@{$local_vmids}) && !$param->{all};
+
+ PVE::VZDump::parse_mailto_exclude_path($param);
die "you can only backup a single VM with option --stdout\n"
- if $param->{stdout} && scalar(@vmids) != 1;
+ if $param->{stdout} && scalar(@{$local_vmids}) != 1;
- foreach my $key (qw(maxfiles tmpdir dumpdir script bwlimit ionice)) {
- raise_param_exc({ $key => "Only root may set this option."})
- if defined($param->{$key}) && ($user ne 'root@pam');
+ if (my $storeid = PVE::VZDump::get_storage_param($param)) {
+ $rpcenv->check($user, "/storage/$storeid", [ 'Datastore.AllocateSpace' ]);
}
- $rpcenv->check($user, "/storage/$param->{storage}", [ 'Datastore.AllocateSpace' ])
- if $param->{storage};
-
my $worker = sub {
my $upid = shift;
die "interrupted by signal\n";
};
+ $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 = $@) {
PVE::VZDump::run_command(undef, "ionice -c2 -n$param->{ionice} -p $$");
}
}
- $vzdump->exec_backup($rpcenv, $user);
- };
+ $vzdump->exec_backup($rpcenv, $user);
+
+ close($LOCK_FH);
+ };
open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout};
open STDERR, '>/dev/null' if $param->{quiet};
}
}
- return $rpcenv->fork_worker('vzdump', undef, $user, $worker);
+ my $taskid;
+ $taskid = $local_vmids->[0] if scalar(@{$local_vmids}) == 1;
+
+ 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',
my $authuser = $rpcenv->get_user();
my $storage_cfg = PVE::Storage::config();
- $rpcenv->check_volume_access($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);
}});