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