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