]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/VZDump.pm
4093d82ffe9855ec3a162684cb7d6cffe0721cc1
[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 'Datastore.AllocateSpace' on the backup storage. The 'maxfiles', 'prune-backups', 'tmpdir', 'dumpdir', 'script', 'bwlimit' and 'ionice' parameters are restricted to the 'root\@pam' user.",
29 user => 'all',
30 },
31 protected => 1,
32 proxyto => 'node',
33 parameters => {
34 additionalProperties => 0,
35 properties => PVE::VZDump::Common::json_config_properties({
36 stdout => {
37 type => 'boolean',
38 description => "Write tar to stdout, not to a file.",
39 optional => 1,
40 },
41 }),
42 },
43 returns => { type => 'string' },
44 code => sub {
45 my ($param) = @_;
46
47 my $rpcenv = PVE::RPCEnvironment::get();
48
49 my $user = $rpcenv->get_user();
50
51 my $nodename = PVE::INotify::nodename();
52
53 if ($rpcenv->{type} ne 'cli') {
54 raise_param_exc({ node => "option is only allowed on the command line interface."})
55 if $param->{node} && $param->{node} ne $nodename;
56
57 raise_param_exc({ stdout => "option is only allowed on the command line interface."})
58 if $param->{stdout};
59 }
60
61 foreach my $key (qw(maxfiles prune-backups tmpdir dumpdir script bwlimit ionice)) {
62 raise_param_exc({ $key => "Only root may set this option."})
63 if defined($param->{$key}) && ($user ne 'root@pam');
64 }
65
66 PVE::VZDump::verify_vzdump_parameters($param, 1);
67
68 # silent exit if we run on wrong node
69 return 'OK' if $param->{node} && $param->{node} ne $nodename;
70
71 my $cmdline = PVE::VZDump::Common::command_line($param);
72
73 my $vmids_per_node = PVE::VZDump::get_included_guests($param);
74
75 my $local_vmids = delete $vmids_per_node->{$nodename} // [];
76
77 # include IDs for deleted guests, and visibly fail later
78 my $orphaned_vmids = delete $vmids_per_node->{''} // [];
79 push @{$local_vmids}, @{$orphaned_vmids};
80
81 my $skiplist = [ map { @$_ } values $vmids_per_node->%* ];
82
83 if($param->{stop}){
84 PVE::VZDump::stop_running_backups();
85 return 'OK' if !scalar(@{$local_vmids});
86 }
87
88 # silent exit if specified VMs run on other nodes
89 return "OK" if !scalar(@{$local_vmids}) && !$param->{all};
90
91 PVE::VZDump::parse_mailto_exclude_path($param);
92
93 die "you can only backup a single VM with option --stdout\n"
94 if $param->{stdout} && scalar(@{$local_vmids}) != 1;
95
96 $rpcenv->check($user, "/storage/$param->{storage}", [ 'Datastore.AllocateSpace' ])
97 if $param->{storage};
98
99 my $worker = sub {
100 my $upid = shift;
101
102 $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
103 die "interrupted by signal\n";
104 };
105
106 $param->{vmids} = $local_vmids;
107 my $vzdump = PVE::VZDump->new($cmdline, $param, $skiplist);
108
109 my $LOCK_FH = eval {
110 $vzdump->getlock($upid); # only one process allowed
111 };
112 if (my $err = $@) {
113 $vzdump->sendmail([], 0, $err);
114 exit(-1);
115 }
116
117 if (defined($param->{ionice})) {
118 if ($param->{ionice} > 7) {
119 PVE::VZDump::run_command(undef, "ionice -c3 -p $$");
120 } else {
121 PVE::VZDump::run_command(undef, "ionice -c2 -n$param->{ionice} -p $$");
122 }
123 }
124 $vzdump->exec_backup($rpcenv, $user);
125
126 close($LOCK_FH);
127 };
128
129 open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout};
130 open STDERR, '>/dev/null' if $param->{quiet};
131
132 if ($rpcenv->{type} eq 'cli') {
133 if ($param->{stdout}) {
134
135 open my $saved_stdout, ">&STDOUT"
136 || die "can't dup STDOUT: $!\n";
137
138 open STDOUT, '>&STDERR' ||
139 die "unable to redirect STDOUT: $!\n";
140
141 $param->{stdout} = $saved_stdout;
142 }
143 }
144
145 my $taskid;
146 $taskid = $local_vmids->[0] if scalar(@{$local_vmids}) == 1;
147
148 return $rpcenv->fork_worker('vzdump', $taskid, $user, $worker);
149 }});
150
151 __PACKAGE__->register_method ({
152 name => 'defaults',
153 path => 'defaults',
154 method => 'GET',
155 description => "Get the currently configured vzdump defaults.",
156 permissions => {
157 description => "The user needs 'Datastore.Audit' or 'Datastore.AllocateSpace' " .
158 "permissions for the specified storage (or default storage if none specified). Some " .
159 "properties are only returned when the user has 'Sys.Audit' permissions for the node.",
160 user => 'all',
161 },
162 proxyto => 'node',
163 parameters => {
164 additionalProperties => 0,
165 properties => {
166 node => get_standard_option('pve-node'),
167 storage => get_standard_option('pve-storage-id', { optional => 1 }),
168 },
169 },
170 returns => {
171 type => 'object',
172 additionalProperties => 0,
173 properties => PVE::VZDump::Common::json_config_properties(),
174 },
175 code => sub {
176 my ($param) = @_;
177
178 my $node = extract_param($param, 'node');
179 my $storage = extract_param($param, 'storage');
180
181 my $rpcenv = PVE::RPCEnvironment::get();
182 my $authuser = $rpcenv->get_user();
183
184 my $res = PVE::VZDump::read_vzdump_defaults();
185
186 $res->{storage} = $storage if defined($storage);
187
188 if (!defined($res->{dumpdir}) && !defined($res->{storage})) {
189 $res->{storage} = 'local';
190 }
191
192 if (defined($res->{storage})) {
193 $rpcenv->check_any(
194 $authuser,
195 "/storage/$res->{storage}",
196 ['Datastore.Audit', 'Datastore.AllocateSpace'],
197 );
198
199 my $info = PVE::VZDump::storage_info($res->{storage});
200 for my $key (qw(dumpdir prune-backups)) {
201 $res->{$key} = $info->{$key} if defined($info->{$key});
202 }
203 }
204
205 if (defined($res->{'prune-backups'})) {
206 $res->{'prune-backups'} = PVE::JSONSchema::print_property_string(
207 $res->{'prune-backups'},
208 'prune-backups',
209 );
210 }
211
212 $res->{mailto} = join(",", @{$res->{mailto}})
213 if defined($res->{mailto});
214
215 $res->{'exclude-path'} = join(",", @{$res->{'exclude-path'}})
216 if defined($res->{'exclude-path'});
217
218 # normal backup users don't need to know these
219 if (!$rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1)) {
220 delete $res->{mailto};
221 delete $res->{tmpdir};
222 delete $res->{dumpdir};
223 delete $res->{script};
224 delete $res->{ionice};
225 }
226
227 my $pool = $res->{pool};
228 if (defined($pool) &&
229 !$rpcenv->check($authuser, "/pool/$pool", ['Pool.Audit'], 1)) {
230 delete $res->{pool};
231 }
232
233 delete $res->{size}; # deprecated, to be dropped with PVE 7.0
234
235 return $res;
236 }});
237
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();
270 PVE::Storage::check_volume_access($rpcenv, $authuser, $storage_cfg, undef, $volume);
271
272 return PVE::Storage::extract_vzdump_config($storage_cfg, $volume);
273 }});
274
275 1;