]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/VZDump.pm
api: vzdump: extract config: add content type check
[pve-manager.git] / PVE / API2 / VZDump.pm
CommitLineData
bf58f8dd
DM
1package PVE::API2::VZDump;
2
3use strict;
4use warnings;
31aef761 5use PVE::Exception qw(raise_param_exc);
bf58f8dd
DM
6use PVE::Tools qw(extract_param);
7use PVE::Cluster qw(cfs_register_file cfs_read_file);
8use PVE::INotify;
9use PVE::RPCEnvironment;
10use PVE::AccessControl;
11use PVE::JSONSchema qw(get_standard_option);
12use PVE::Storage;
13use PVE::VZDump;
2424074e 14use PVE::VZDump::Common;
f3376261 15use PVE::API2Tools;
bf58f8dd
DM
16
17use Data::Dumper; # fixme: remove
18
19
20use base qw(PVE::RESTHandler);
21
22__PACKAGE__->register_method ({
60e049c2 23 name => 'vzdump',
bf58f8dd
DM
24 path => '',
25 method => 'POST',
26 description => "Create backup.",
98e84b16 27 permissions => {
ff119724
TL
28 description => "The user needs 'VM.Backup' permissions on any VM, and 'Datastore.AllocateSpace'"
29 ." on the backup storage. The 'maxfiles', 'prune-backups', 'tmpdir', 'dumpdir', 'script',"
30 ." 'bwlimit' and 'ionice' parameters are restricted to the 'root\@pam' user.",
98e84b16
DM
31 user => 'all',
32 },
30edfad9 33 protected => 1,
49046e53 34 proxyto => 'node',
bf58f8dd 35 parameters => {
ff119724 36 additionalProperties => 0,
2424074e 37 properties => PVE::VZDump::Common::json_config_properties({
bf58f8dd
DM
38 stdout => {
39 type => 'boolean',
40 description => "Write tar to stdout, not to a file.",
41 optional => 1,
42 },
ac27b58d 43 }),
bf58f8dd
DM
44 },
45 returns => { type => 'string' },
46 code => sub {
47 my ($param) = @_;
48
49 my $rpcenv = PVE::RPCEnvironment::get();
50
51 my $user = $rpcenv->get_user();
52
53 my $nodename = PVE::INotify::nodename();
54
55 if ($rpcenv->{type} ne 'cli') {
56 raise_param_exc({ node => "option is only allowed on the command line interface."})
57 if $param->{node} && $param->{node} ne $nodename;
58
59 raise_param_exc({ stdout => "option is only allowed on the command line interface."})
60 if $param->{stdout};
61 }
62
e6946086 63 foreach my $key (qw(maxfiles prune-backups tmpdir dumpdir script bwlimit ionice)) {
6d0507a8
FG
64 raise_param_exc({ $key => "Only root may set this option."})
65 if defined($param->{$key}) && ($user ne 'root@pam');
66 }
67
31aef761 68 PVE::VZDump::verify_vzdump_parameters($param, 1);
bf58f8dd
DM
69
70 # silent exit if we run on wrong node
eab837c4 71 return 'OK' if $param->{node} && $param->{node} ne $nodename;
60e049c2 72
2424074e 73 my $cmdline = PVE::VZDump::Common::command_line($param);
df5875b4
AL
74
75 my $vmids_per_node = PVE::VZDump::get_included_guests($param);
76
77 my $local_vmids = delete $vmids_per_node->{$nodename} // [];
78
7f874148
FE
79 # include IDs for deleted guests, and visibly fail later
80 my $orphaned_vmids = delete $vmids_per_node->{''} // [];
81 push @{$local_vmids}, @{$orphaned_vmids};
82
df5875b4 83 my $skiplist = [ map { @$_ } values $vmids_per_node->%* ];
bf58f8dd 84
eab837c4
DM
85 if($param->{stop}){
86 PVE::VZDump::stop_running_backups();
df5875b4 87 return 'OK' if !scalar(@{$local_vmids});
eab837c4
DM
88 }
89
5c4da4c3 90 # silent exit if specified VMs run on other nodes
df5875b4 91 return "OK" if !scalar(@{$local_vmids}) && !$param->{all};
336ec53a 92
f8ed6af8 93 PVE::VZDump::parse_mailto_exclude_path($param);
bf58f8dd
DM
94
95 die "you can only backup a single VM with option --stdout\n"
df5875b4 96 if $param->{stdout} && scalar(@{$local_vmids}) != 1;
bf58f8dd 97
4412265f
DM
98 $rpcenv->check($user, "/storage/$param->{storage}", [ 'Datastore.AllocateSpace' ])
99 if $param->{storage};
100
bf58f8dd 101 my $worker = sub {
eab837c4
DM
102 my $upid = shift;
103
bf58f8dd
DM
104 $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
105 die "interrupted by signal\n";
106 };
107
df5875b4 108 $param->{vmids} = $local_vmids;
403761c4
WB
109 my $vzdump = PVE::VZDump->new($cmdline, $param, $skiplist);
110
875c2e5a 111 my $LOCK_FH = eval {
eab837c4 112 $vzdump->getlock($upid); # only one process allowed
6ec9de44 113 };
eab837c4
DM
114 if (my $err = $@) {
115 $vzdump->sendmail([], 0, $err);
6ec9de44
SP
116 exit(-1);
117 }
bf58f8dd
DM
118
119 if (defined($param->{ionice})) {
120 if ($param->{ionice} > 7) {
121 PVE::VZDump::run_command(undef, "ionice -c3 -p $$");
122 } else {
123 PVE::VZDump::run_command(undef, "ionice -c2 -n$param->{ionice} -p $$");
124 }
125 }
60e049c2 126 $vzdump->exec_backup($rpcenv, $user);
875c2e5a
FE
127
128 close($LOCK_FH);
60e049c2 129 };
bf58f8dd
DM
130
131 open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout};
132 open STDERR, '>/dev/null' if $param->{quiet};
133
134 if ($rpcenv->{type} eq 'cli') {
135 if ($param->{stdout}) {
136
137 open my $saved_stdout, ">&STDOUT"
138 || die "can't dup STDOUT: $!\n";
139
140 open STDOUT, '>&STDERR' ||
141 die "unable to redirect STDOUT: $!\n";
142
143 $param->{stdout} = $saved_stdout;
144 }
145 }
146
742d2ad2 147 my $taskid;
df5875b4 148 $taskid = $local_vmids->[0] if scalar(@{$local_vmids}) == 1;
742d2ad2
FG
149
150 return $rpcenv->fork_worker('vzdump', $taskid, $user, $worker);
bf58f8dd 151 }});
7619e4dd 152
5b9a4030
FE
153__PACKAGE__->register_method ({
154 name => 'defaults',
155 path => 'defaults',
156 method => 'GET',
157 description => "Get the currently configured vzdump defaults.",
158 permissions => {
159 description => "The user needs 'Datastore.Audit' or 'Datastore.AllocateSpace' " .
160 "permissions for the specified storage (or default storage if none specified). Some " .
161 "properties are only returned when the user has 'Sys.Audit' permissions for the node.",
162 user => 'all',
163 },
164 proxyto => 'node',
165 parameters => {
166 additionalProperties => 0,
167 properties => {
168 node => get_standard_option('pve-node'),
169 storage => get_standard_option('pve-storage-id', { optional => 1 }),
170 },
171 },
172 returns => {
173 type => 'object',
174 additionalProperties => 0,
175 properties => PVE::VZDump::Common::json_config_properties(),
176 },
177 code => sub {
178 my ($param) = @_;
179
180 my $node = extract_param($param, 'node');
181 my $storage = extract_param($param, 'storage');
182
183 my $rpcenv = PVE::RPCEnvironment::get();
184 my $authuser = $rpcenv->get_user();
185
186 my $res = PVE::VZDump::read_vzdump_defaults();
187
188 $res->{storage} = $storage if defined($storage);
189
190 if (!defined($res->{dumpdir}) && !defined($res->{storage})) {
191 $res->{storage} = 'local';
192 }
193
194 if (defined($res->{storage})) {
195 $rpcenv->check_any(
196 $authuser,
197 "/storage/$res->{storage}",
198 ['Datastore.Audit', 'Datastore.AllocateSpace'],
199 );
200
201 my $info = PVE::VZDump::storage_info($res->{storage});
202 for my $key (qw(dumpdir prune-backups)) {
203 $res->{$key} = $info->{$key} if defined($info->{$key});
204 }
205 }
206
207 if (defined($res->{'prune-backups'})) {
208 $res->{'prune-backups'} = PVE::JSONSchema::print_property_string(
209 $res->{'prune-backups'},
210 'prune-backups',
211 );
212 }
213
214 $res->{mailto} = join(",", @{$res->{mailto}})
215 if defined($res->{mailto});
216
217 $res->{'exclude-path'} = join(",", @{$res->{'exclude-path'}})
218 if defined($res->{'exclude-path'});
219
220 # normal backup users don't need to know these
221 if (!$rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1)) {
222 delete $res->{mailto};
223 delete $res->{tmpdir};
224 delete $res->{dumpdir};
225 delete $res->{script};
226 delete $res->{ionice};
227 }
228
229 my $pool = $res->{pool};
230 if (defined($pool) &&
91db3ece 231 !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1)) {
5b9a4030
FE
232 delete $res->{pool};
233 }
234
5b9a4030
FE
235 return $res;
236 }});
237
7619e4dd
FG
238__PACKAGE__->register_method ({
239 name => 'extractconfig',
240 path => 'extractconfig',
241 method => 'GET',
242 description => "Extract configuration from vzdump backup archive.",
243 permissions => {
244 description => "The user needs 'VM.Backup' permissions on the backed up guest ID, and 'Datastore.AllocateSpace' on the backup storage.",
245 user => 'all',
246 },
247 protected => 1,
248 proxyto => 'node',
249 parameters => {
250 additionalProperties => 0,
251 properties => {
252 node => get_standard_option('pve-node'),
253 volume => {
254 description => "Volume identifier",
255 type => 'string',
256 completion => \&PVE::Storage::complete_volume,
257 },
258 },
259 },
260 returns => { type => 'string' },
261 code => sub {
262 my ($param) = @_;
263
264 my $volume = extract_param($param, 'volume');
265
266 my $rpcenv = PVE::RPCEnvironment::get();
267 my $authuser = $rpcenv->get_user();
268
269 my $storage_cfg = PVE::Storage::config();
c53d5c5e
FE
270 PVE::Storage::check_volume_access(
271 $rpcenv,
272 $authuser,
273 $storage_cfg,
274 undef,
275 $volume,
276 'backup',
277 );
7619e4dd 278
0bd224e5
FE
279 if (PVE::Storage::parse_volume_id($volume, 1)) {
280 my (undef, undef, $ownervm) = PVE::Storage::parse_volname($storage_cfg, $volume);
281 $rpcenv->check($authuser, "/vms/$ownervm", ['VM.Backup']);
282 }
283
7619e4dd
FG
284 return PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
285 }});
286
2871;