]>
Commit | Line | Data |
---|---|---|
aaeeeebe | 1 | #!/usr/bin/perl -w |
aaeeeebe DM |
2 | |
3 | use strict; | |
4a4051d8 DM |
4 | |
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::SafeSyslog; | |
9 | use PVE::INotify; | |
10 | use PVE::RPCEnvironment; | |
11 | use PVE::CLIHandler; | |
12 | use PVE::JSONSchema qw(get_standard_option); | |
13 | use PVE::Storage; | |
aaeeeebe DM |
14 | use PVE::VZDump; |
15 | ||
4a4051d8 DM |
16 | use Data::Dumper; # fixme: remove |
17 | ||
18 | use base qw(PVE::CLIHandler); | |
19 | ||
20 | $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; | |
21 | ||
22 | initlog('vzdump'); | |
23 | ||
24 | die "please run as root\n" if $> != 0; | |
25 | ||
26 | PVE::INotify::inotify_init(); | |
27 | my $nodename = PVE::INotify::nodename(); | |
28 | ||
29 | my $rpcenv = PVE::RPCEnvironment->init('cli'); | |
30 | ||
31 | $rpcenv->init_request(); | |
32 | $rpcenv->set_language($ENV{LANG}); | |
33 | $rpcenv->set_user('root@pam'); | |
34 | ||
35 | __PACKAGE__->register_method ({ | |
36 | name => 'vzdump', | |
37 | path => 'vzdump', | |
38 | method => 'PUT', | |
39 | description => "Create backup.", | |
40 | parameters => { | |
41 | additionalProperties => 0, | |
42 | properties => { | |
43 | vmid => { | |
44 | type => 'string', format => 'pve-vmid-list', | |
45 | description => "The ID of the VM you want to backup.", | |
46 | optional => 1, | |
47 | }, | |
48 | node => get_standard_option('pve-node', { | |
49 | description => "Only run if executed on this node.", | |
50 | optional => 1, | |
51 | }), | |
52 | all => { | |
53 | type => 'boolean', | |
54 | description => "Backup all known VMs on this host.", | |
55 | optional => 1, | |
56 | default => 0, | |
57 | }, | |
58 | stdexcludes => { | |
59 | type => 'boolean', | |
60 | description => "Exclude temorary files and logs.", | |
61 | optional => 1, | |
62 | default => 1, | |
63 | }, | |
64 | compress => { | |
65 | type => 'boolean', | |
66 | description => "Compress dump file (gzip).", | |
67 | optional => 1, | |
68 | default => 0, | |
69 | }, | |
70 | quiet => { | |
71 | type => 'boolean', | |
72 | description => "Be quiet.", | |
73 | optional => 1, | |
74 | default => 0, | |
75 | }, | |
76 | stop => { | |
77 | type => 'boolean', | |
78 | description => "Stop/Restart VM when running.", | |
79 | optional => 1, | |
80 | }, | |
81 | snapshot => { | |
82 | type => 'boolean', | |
83 | description => "Try to use (LVM) snapshots when running.", | |
84 | optional => 1, | |
85 | }, | |
86 | suspend => { | |
87 | type => 'boolean', | |
88 | description => "Suspend/resume VM when running", | |
89 | optional => 1, | |
90 | }, | |
91 | stdout => { | |
92 | type => 'boolean', | |
93 | description => "Write tar to stdout, not to a file.", | |
94 | optional => 1, | |
95 | }, | |
96 | exclude => { | |
97 | type => 'string', format => 'pve-vmid-list', | |
98 | description => "exclude specified VMs (assumes --all)", | |
99 | optional => 1, | |
100 | }, | |
101 | 'exclude-path' => { | |
102 | type => 'string', format => 'string-list', | |
103 | description => "exclude certain files/directories (regex).", | |
104 | optional => 1, | |
105 | }, | |
106 | mailto => { | |
107 | type => 'string', format => 'string-list', | |
108 | description => "", | |
109 | optional => 1, | |
110 | }, | |
111 | tmpdir => { | |
112 | type => 'string', | |
113 | description => "Store temporary files to specified directory.", | |
114 | optional => 1, | |
115 | }, | |
116 | dumpdir => { | |
117 | type => 'string', | |
118 | description => "Store resulting files to specified directory.", | |
119 | optional => 1, | |
120 | }, | |
121 | script => { | |
122 | type => 'string', | |
123 | description => "Use specified hook script.", | |
124 | optional => 1, | |
125 | }, | |
126 | storage => get_standard_option('pve-storage-id', { | |
127 | description => "Store resulting file to this storage.", | |
128 | optional => 1, | |
129 | }), | |
130 | size => { | |
131 | type => 'integer', | |
132 | description => "LVM snapshot size im MB.", | |
133 | optional => 1, | |
134 | minimum => 500, | |
135 | }, | |
136 | bwlimit => { | |
137 | type => 'integer', | |
138 | description => "Limit I/O bandwidth (KBytes per second).", | |
139 | optional => 1, | |
140 | minimum => 0, | |
141 | }, | |
142 | ionice => { | |
143 | type => 'integer', | |
144 | description => "Set CFQ ionice priority.", | |
145 | optional => 1, | |
146 | minimum => 0, | |
147 | maximum => 8, | |
148 | }, | |
149 | lockwait => { | |
150 | type => 'integer', | |
151 | description => "Maximal time to wait for the global lock (minutes).", | |
152 | optional => 1, | |
153 | minimum => 0, | |
154 | }, | |
155 | stopwait => { | |
156 | type => 'integer', | |
157 | description => "Maximal time to wait until a VM is stopped (minutes).", | |
158 | optional => 1, | |
159 | minimum => 0, | |
160 | }, | |
161 | maxfiles => { | |
162 | type => 'integer', | |
163 | description => "Maximal number of backup files per VM.", | |
164 | optional => 1, | |
165 | minimum => 1, | |
166 | }, | |
167 | }, | |
168 | }, | |
169 | returns => { type => 'string' }, | |
170 | code => sub { | |
171 | my ($param) = @_; | |
172 | ||
4a4051d8 | 173 | my $rpcenv = PVE::RPCEnvironment::get(); |
aaeeeebe | 174 | |
4a4051d8 | 175 | my $user = $rpcenv->get_user(); |
aaeeeebe | 176 | |
4a4051d8 DM |
177 | if ($rpcenv->{type} ne 'cli') { |
178 | raise_param_exc({ node => "option is only allowed on the command line interface."}) | |
179 | if $param->{node}; | |
aaeeeebe | 180 | |
4a4051d8 DM |
181 | raise_param_exc({ stdout => "option is only allowed on the command line interface."}) |
182 | if $param->{stdout}; | |
183 | } | |
aaeeeebe | 184 | |
4a4051d8 DM |
185 | # by default we set --rsyncable for gzip |
186 | local $ENV{GZIP} = "--rsyncable" if !$ENV{GZIP}; | |
aaeeeebe | 187 | |
4a4051d8 | 188 | $param->{all} = 1 if defined($param->{exclude}); |
aaeeeebe | 189 | |
4a4051d8 | 190 | raise_param_exc({ all => "option conflicts with option 'vmid'"}) |
b2ae28cc | 191 | if $param->{all} && $param->{vmid}; |
aaeeeebe | 192 | |
4a4051d8 | 193 | raise_param_exc({ vmid => "property is missing"}) |
b2ae28cc | 194 | if !$param->{all} && !$param->{vmid}; |
aaeeeebe | 195 | |
4a4051d8 DM |
196 | # silent exit if we run on wrong node |
197 | my $nodename = PVE::INotify::nodename(); | |
198 | exit(0) if $param->{node} && $param->{node} ne $nodename; | |
aaeeeebe | 199 | |
4a4051d8 DM |
200 | # convert string lists to arrays |
201 | my @vmids = PVE::Tools::split_list(extract_param($param, 'vmid')); | |
b2ae28cc DM |
202 | |
203 | my $cmdline = 'vzdump'; | |
204 | $cmdline .= ' ' . join(' ', @vmids) if scalar(@vmids); | |
205 | foreach my $p (keys %$param) { | |
206 | $cmdline .= " --$p $param->{$p}"; | |
207 | } | |
208 | ||
4a4051d8 DM |
209 | $param->{vmids} = PVE::VZDump::check_vmids(@vmids) if !$param->{all}; |
210 | my @exclude = PVE::Tools::split_list(extract_param($param, 'exclude')); | |
211 | $param->{exclude} = PVE::VZDump::check_vmids(@exclude); | |
212 | ||
213 | # exclude-path list need to be 0 separated | |
214 | my @expaths = split(/\0/, $param->{'exclude-path'} || ''); | |
215 | $param->{'exclude-path'} = @expaths; | |
aaeeeebe | 216 | |
4a4051d8 DM |
217 | my @mailto = PVE::Tools::split_list(extract_param($param, 'mailto')); |
218 | $param->{mailto} = [ @mailto ]; | |
aaeeeebe | 219 | |
4a4051d8 DM |
220 | die "you can only backup a single VM with option --stdout\n" |
221 | if $param->{stdout} && scalar(@vmids) != 1; | |
aaeeeebe | 222 | |
4a4051d8 | 223 | my $vzdump = PVE::VZDump->new($cmdline, $param); |
aaeeeebe | 224 | |
4a4051d8 DM |
225 | my $worker = sub { |
226 | $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { | |
227 | die "interrupted by signal\n"; | |
228 | }; | |
aaeeeebe | 229 | |
4a4051d8 | 230 | $vzdump->getlock (); # only one process allowed |
aaeeeebe | 231 | |
4a4051d8 DM |
232 | if (defined($param->{ionice})) { |
233 | if ($param->{ionice} > 7) { | |
234 | PVE::VZDump::run_command(undef, "ionice -c3 -p $$"); | |
235 | } else { | |
236 | PVE::VZDump::run_command(undef, "ionice -c2 -n$param->{ionice} -p $$"); | |
237 | } | |
238 | } | |
239 | $vzdump->exec_backup(); | |
240 | }; | |
aaeeeebe | 241 | |
4a4051d8 DM |
242 | open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout}; |
243 | open STDERR, '>/dev/null' if $param->{quiet}; | |
aaeeeebe | 244 | |
4a4051d8 DM |
245 | if ($rpcenv->{type} eq 'cli') { |
246 | if ($param->{stdout}) { | |
aaeeeebe | 247 | |
4a4051d8 DM |
248 | open my $saved_stdout, ">&STDOUT" |
249 | || die "can't dup STDOUT: $!\n"; | |
aaeeeebe | 250 | |
4a4051d8 DM |
251 | open STDOUT, '>&STDERR' || |
252 | die "unable to redirect STDOUT: $!\n"; | |
aaeeeebe | 253 | |
4a4051d8 DM |
254 | $param->{stdout} = $saved_stdout; |
255 | } | |
256 | } | |
aaeeeebe | 257 | |
4a4051d8 DM |
258 | return $rpcenv->fork_worker('vzdump', undef, $user, $worker); |
259 | }}); | |
aaeeeebe | 260 | |
b2ae28cc | 261 | my $cmddef = [ __PACKAGE__, 'vzdump', 'vmid', undef, |
4a4051d8 DM |
262 | sub { |
263 | my $upid = shift; | |
264 | my $status = PVE::Tools::upid_read_status($upid); | |
265 | exit($status eq 'OK' ? 0 : -1); | |
266 | }]; | |
aaeeeebe | 267 | |
4a4051d8 DM |
268 | push @ARGV, 'help' if !scalar(@ARGV); |
269 | ||
270 | PVE::CLIHandler::handle_simple_cmd($cmddef, \@ARGV, undef, $0); | |
271 | ||
272 | exit 0; | |
273 | ||
274 | __END__ | |
275 | ||
276 | =head1 NAME | |
277 | ||
278 | vzdump - backup utility for virtual machine | |
279 | ||
280 | =head1 SYNOPSIS | |
281 | ||
282 | =include synopsis | |
aaeeeebe DM |
283 | |
284 | =head1 DESCRIPTION | |
285 | ||
286 | vzdump is an utility to make consistent snapshots of running virtual | |
287 | machines (VMs). It basically creates a tar archive of the VM private area, | |
288 | which also includes the VM configuration files. vzdump currently | |
289 | supports OpenVZ and QemuServer VMs. | |
290 | ||
291 | There are several ways to provide consistency: | |
292 | ||
293 | =over 2 | |
294 | ||
295 | =item C<stop> mode | |
296 | ||
297 | Stop the VM during backup. This results in a very long downtime. | |
298 | ||
299 | =item C<suspend> mode | |
300 | ||
301 | For OpenVZ, this mode uses rsync to copy the VM to a temporary | |
302 | location (see option --tmpdir). Then the VM is suspended and a second | |
303 | rsync copies changed files. After that, the VM is started (resume) | |
304 | again. This results in a minimal downtime, but needs additional space | |
305 | to hold the VM copy. | |
306 | ||
307 | For QemuServer, this mode work like C<stop> mode, but uses | |
308 | suspend/resume instead of stop/start. | |
309 | ||
310 | =item C<snapshot> mode | |
311 | ||
312 | This mode uses LVM2 snapshots. There is no downtime, but snapshot mode | |
313 | needs LVM2 and some free space on the corresponding volume group to | |
314 | create the LVM snapshot. | |
315 | ||
316 | =back | |
317 | ||
318 | =head1 BACKUP FILE NAMES | |
319 | ||
320 | Newer version of vzdump encodes the virtual machine type and the | |
321 | backup time into the filename, for example | |
322 | ||
323 | vzdump-openvz-105-2009_10_09-11_04_43.tar | |
324 | ||
325 | That way it is possible to store several backup into the same | |
326 | directory. The parameter C<maxfiles> can be used to specify the maximal | |
327 | number of backups to keep. | |
328 | ||
329 | =head1 RESTORE | |
330 | ||
331 | The resulting tar files can be restored with the following programs. | |
332 | ||
333 | =over 1 | |
334 | ||
335 | =item vzrestore: OpenVZ restore utility | |
336 | ||
337 | =item qmrestore: QemuServer restore utility | |
338 | ||
339 | =back | |
340 | ||
341 | For details see the corresponding manual pages. | |
342 | ||
343 | =head1 CONFIGURATION | |
344 | ||
345 | Global configuration is stored in /etc/vzdump.conf. | |
346 | ||
347 | tmpdir: DIR | |
348 | dumpdir: DIR | |
349 | storage: STORAGE_ID | |
350 | mode: snapshot|suspend|stop | |
351 | bwlimit: KBPS | |
352 | ionize: PRI | |
353 | lockwait: MINUTES | |
354 | stopwait: MINUTES | |
355 | size: MB | |
356 | maxfiles: N | |
357 | script: FILENAME | |
358 | ||
359 | =head1 HOOK SCRIPT | |
360 | ||
361 | You can specify a hook script with option C<--script>. This script is called at various phases of the backup process, with parameters accordingly set. You can find an example in the documentation directory (C<hook-script.pl>). | |
362 | ||
363 | =head1 EXCLUSIONS (OpenVZ only) | |
364 | ||
365 | vzdump skips the following files wit option --stdexcludes | |
366 | ||
367 | /var/log/.+ | |
368 | /tmp/.+ | |
369 | /var/tmp/.+ | |
370 | /var/run/.+pid | |
371 | ||
372 | You can manually specify exclude paths, for example: | |
373 | ||
b2ae28cc | 374 | # vzdump 777 --exclude-path C</tmp/.+> --exclude-path C</var/tmp/.+> |
aaeeeebe DM |
375 | |
376 | (only excludes tmp directories) | |
377 | ||
378 | Configuration files are also stored inside the backup archive (/etc/vzdump), and will be correctly restored. | |
379 | ||
380 | =head1 LIMITATIONS | |
381 | ||
382 | VZDump does not save ACLs. | |
383 | ||
384 | =head1 EXAMPLES | |
385 | ||
386 | Simply dump VM 777 - no snapshot, just archive the VM private area and configuration files to the default dump directory (usually /vz/dump/). | |
387 | ||
b2ae28cc | 388 | # vzdump 777 |
aaeeeebe DM |
389 | |
390 | Use rsync and suspend/resume to create an snapshot (minimal downtime). | |
391 | ||
b2ae28cc | 392 | # vzdump 777 --suspend |
aaeeeebe | 393 | |
4a4051d8 | 394 | Backup all VMs and send notification mails to root and admin. |
aaeeeebe | 395 | |
b2ae28cc | 396 | # vzdump --all --suspend --mailto root --mailto admin |
aaeeeebe DM |
397 | |
398 | Use LVM2 to create snapshots (no downtime). | |
399 | ||
b2ae28cc DM |
400 | # vzdump 777 --dumpdir /mnt/backup --snapshot |
401 | ||
402 | Backup more than one VM (selectively) | |
403 | ||
404 | # vzdump 101 102 103 --mailto root | |
aaeeeebe DM |
405 | |
406 | Backup all VMs excluding VM 101 and 102 | |
407 | ||
b2ae28cc | 408 | # vzdump --suspend --exclude 101,102 |
aaeeeebe DM |
409 | |
410 | Restore an OpenVZ machine to VM 600 | |
411 | ||
4a4051d8 | 412 | # vzrestore /mnt/backup/vzdump-openvz-777.tar 600 |
aaeeeebe DM |
413 | |
414 | Restore an Qemu/KVM machine to VM 601 | |
415 | ||
4a4051d8 | 416 | # qmrestore /mnt/backup/vzdump-qemu-888.tar 601 |
aaeeeebe DM |
417 | |
418 | =head1 SEE ALSO | |
419 | ||
4a4051d8 | 420 | vzrestore(1) qmrestore(1) |
aaeeeebe | 421 | |
4a4051d8 | 422 | =include pve_copyright |