pve-manager/bin/vzdump
2011-10-13 14:33:12 +02:00

417 lines
10 KiB
Perl
Executable File

#!/usr/bin/perl -w
use strict;
use PVE::Exception qw(raise_param_exc);;
use PVE::Tools qw(extract_param);
use PVE::Cluster qw(cfs_register_file cfs_read_file);
use PVE::SafeSyslog;
use PVE::INotify;
use PVE::RPCEnvironment;
use PVE::CLIHandler;
use PVE::JSONSchema qw(get_standard_option);
use PVE::Storage;
use PVE::VZDump;
use Data::Dumper; # fixme: remove
use base qw(PVE::CLIHandler);
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
initlog('vzdump');
die "please run as root\n" if $> != 0;
PVE::INotify::inotify_init();
my $nodename = PVE::INotify::nodename();
my $rpcenv = PVE::RPCEnvironment->init('cli');
$rpcenv->init_request();
$rpcenv->set_language($ENV{LANG});
$rpcenv->set_user('root@pam');
__PACKAGE__->register_method ({
name => 'vzdump',
path => 'vzdump',
method => 'PUT',
description => "Create backup.",
parameters => {
additionalProperties => 0,
properties => {
vmid => {
type => 'string', format => 'pve-vmid-list',
description => "The ID of the VM you want to backup.",
optional => 1,
},
node => get_standard_option('pve-node', {
description => "Only run if executed on this node.",
optional => 1,
}),
all => {
type => 'boolean',
description => "Backup all known VMs on this host.",
optional => 1,
default => 0,
},
stdexcludes => {
type => 'boolean',
description => "Exclude temorary files and logs.",
optional => 1,
default => 1,
},
compress => {
type => 'boolean',
description => "Compress dump file (gzip).",
optional => 1,
default => 0,
},
quiet => {
type => 'boolean',
description => "Be quiet.",
optional => 1,
default => 0,
},
stop => {
type => 'boolean',
description => "Stop/Restart VM when running.",
optional => 1,
},
snapshot => {
type => 'boolean',
description => "Try to use (LVM) snapshots when running.",
optional => 1,
},
suspend => {
type => 'boolean',
description => "Suspend/resume VM when running",
optional => 1,
},
stdout => {
type => 'boolean',
description => "Write tar to stdout, not to a file.",
optional => 1,
},
exclude => {
type => 'string', format => 'pve-vmid-list',
description => "exclude specified VMs (assumes --all)",
optional => 1,
},
'exclude-path' => {
type => 'string', format => 'string-list',
description => "exclude certain files/directories (regex).",
optional => 1,
},
mailto => {
type => 'string', format => 'string-list',
description => "",
optional => 1,
},
tmpdir => {
type => 'string',
description => "Store temporary files to specified directory.",
optional => 1,
},
dumpdir => {
type => 'string',
description => "Store resulting files to specified directory.",
optional => 1,
},
script => {
type => 'string',
description => "Use specified hook script.",
optional => 1,
},
storage => get_standard_option('pve-storage-id', {
description => "Store resulting file to this storage.",
optional => 1,
}),
size => {
type => 'integer',
description => "LVM snapshot size im MB.",
optional => 1,
minimum => 500,
},
bwlimit => {
type => 'integer',
description => "Limit I/O bandwidth (KBytes per second).",
optional => 1,
minimum => 0,
},
ionice => {
type => 'integer',
description => "Set CFQ ionice priority.",
optional => 1,
minimum => 0,
maximum => 8,
},
lockwait => {
type => 'integer',
description => "Maximal time to wait for the global lock (minutes).",
optional => 1,
minimum => 0,
},
stopwait => {
type => 'integer',
description => "Maximal time to wait until a VM is stopped (minutes).",
optional => 1,
minimum => 0,
},
maxfiles => {
type => 'integer',
description => "Maximal number of backup files per VM.",
optional => 1,
minimum => 1,
},
},
},
returns => { type => 'string' },
code => sub {
my ($param) = @_;
my $cmdline = 'vzdump';
foreach my $p (keys %$param) {
$cmdline .= " --$p $param->{$p}";
}
my $rpcenv = PVE::RPCEnvironment::get();
my $user = $rpcenv->get_user();
if ($rpcenv->{type} ne 'cli') {
raise_param_exc({ node => "option is only allowed on the command line interface."})
if $param->{node};
raise_param_exc({ stdout => "option is only allowed on the command line interface."})
if $param->{stdout};
}
# by default we set --rsyncable for gzip
local $ENV{GZIP} = "--rsyncable" if !$ENV{GZIP};
$param->{all} = 1 if defined($param->{exclude});
raise_param_exc({ all => "option conflicts with option 'vmid'"})
if $param->{all} && defined($param->{vmid});
raise_param_exc({ vmid => "property is missing"})
if !$param->{all} && !defined($param->{vmid});
# silent exit if we run on wrong node
my $nodename = PVE::INotify::nodename();
exit(0) if $param->{node} && $param->{node} ne $nodename;
# convert string lists to arrays
my @vmids = PVE::Tools::split_list(extract_param($param, 'vmid'));
$param->{vmids} = PVE::VZDump::check_vmids(@vmids) if !$param->{all};
my @exclude = PVE::Tools::split_list(extract_param($param, 'exclude'));
$param->{exclude} = PVE::VZDump::check_vmids(@exclude);
# exclude-path list need to be 0 separated
my @expaths = split(/\0/, $param->{'exclude-path'} || '');
$param->{'exclude-path'} = @expaths;
my @mailto = PVE::Tools::split_list(extract_param($param, 'mailto'));
$param->{mailto} = [ @mailto ];
die "you can only backup a single VM with option --stdout\n"
if $param->{stdout} && scalar(@vmids) != 1;
my $vzdump = PVE::VZDump->new($cmdline, $param);
my $worker = sub {
$SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
die "interrupted by signal\n";
};
$vzdump->getlock (); # only one process allowed
if (defined($param->{ionice})) {
if ($param->{ionice} > 7) {
PVE::VZDump::run_command(undef, "ionice -c3 -p $$");
} else {
PVE::VZDump::run_command(undef, "ionice -c2 -n$param->{ionice} -p $$");
}
}
$vzdump->exec_backup();
};
open STDOUT, '>/dev/null' if $param->{quiet} && !$param->{stdout};
open STDERR, '>/dev/null' if $param->{quiet};
if ($rpcenv->{type} eq 'cli') {
if ($param->{stdout}) {
open my $saved_stdout, ">&STDOUT"
|| die "can't dup STDOUT: $!\n";
open STDOUT, '>&STDERR' ||
die "unable to redirect STDOUT: $!\n";
$param->{stdout} = $saved_stdout;
}
}
return $rpcenv->fork_worker('vzdump', undef, $user, $worker);
}});
my $cmddef = [ __PACKAGE__, 'vzdump', undef, undef,
sub {
my $upid = shift;
my $status = PVE::Tools::upid_read_status($upid);
exit($status eq 'OK' ? 0 : -1);
}];
push @ARGV, 'help' if !scalar(@ARGV);
PVE::CLIHandler::handle_simple_cmd($cmddef, \@ARGV, undef, $0);
exit 0;
__END__
=head1 NAME
vzdump - backup utility for virtual machine
=head1 SYNOPSIS
=include synopsis
=head1 DESCRIPTION
vzdump is an utility to make consistent snapshots of running virtual
machines (VMs). It basically creates a tar archive of the VM private area,
which also includes the VM configuration files. vzdump currently
supports OpenVZ and QemuServer VMs.
There are several ways to provide consistency:
=over 2
=item C<stop> mode
Stop the VM during backup. This results in a very long downtime.
=item C<suspend> mode
For OpenVZ, this mode uses rsync to copy the VM to a temporary
location (see option --tmpdir). Then the VM is suspended and a second
rsync copies changed files. After that, the VM is started (resume)
again. This results in a minimal downtime, but needs additional space
to hold the VM copy.
For QemuServer, this mode work like C<stop> mode, but uses
suspend/resume instead of stop/start.
=item C<snapshot> mode
This mode uses LVM2 snapshots. There is no downtime, but snapshot mode
needs LVM2 and some free space on the corresponding volume group to
create the LVM snapshot.
=back
=head1 BACKUP FILE NAMES
Newer version of vzdump encodes the virtual machine type and the
backup time into the filename, for example
vzdump-openvz-105-2009_10_09-11_04_43.tar
That way it is possible to store several backup into the same
directory. The parameter C<maxfiles> can be used to specify the maximal
number of backups to keep.
=head1 RESTORE
The resulting tar files can be restored with the following programs.
=over 1
=item vzrestore: OpenVZ restore utility
=item qmrestore: QemuServer restore utility
=back
For details see the corresponding manual pages.
=head1 CONFIGURATION
Global configuration is stored in /etc/vzdump.conf.
tmpdir: DIR
dumpdir: DIR
storage: STORAGE_ID
mode: snapshot|suspend|stop
bwlimit: KBPS
ionize: PRI
lockwait: MINUTES
stopwait: MINUTES
size: MB
maxfiles: N
script: FILENAME
=head1 HOOK SCRIPT
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>).
=head1 EXCLUSIONS (OpenVZ only)
vzdump skips the following files wit option --stdexcludes
/var/log/.+
/tmp/.+
/var/tmp/.+
/var/run/.+pid
You can manually specify exclude paths, for example:
# vzdump --exclude-path C</tmp/.+> --exclude-path C</var/tmp/.+> 777
(only excludes tmp directories)
Configuration files are also stored inside the backup archive (/etc/vzdump), and will be correctly restored.
=head1 LIMITATIONS
VZDump does not save ACLs.
=head1 EXAMPLES
Simply dump VM 777 - no snapshot, just archive the VM private area and configuration files to the default dump directory (usually /vz/dump/).
# vzdump --vmid 777
Use rsync and suspend/resume to create an snapshot (minimal downtime).
# vzdump --suspend --vmid 777
Backup all VMs and send notification mails to root and admin.
# vzdump --suspend --all --mailto root --mailto admin
Use LVM2 to create snapshots (no downtime).
# vzdump --dumpdir /mnt/backup --snapshot --vmid 777
Backup all VMs excluding VM 101 and 102
# vzdump --suspend --exclude 101 --exclude 102
Restore an OpenVZ machine to VM 600
# vzrestore /mnt/backup/vzdump-openvz-777.tar 600
Restore an Qemu/KVM machine to VM 601
# qmrestore /mnt/backup/vzdump-qemu-888.tar 601
=head1 SEE ALSO
vzrestore(1) qmrestore(1)
=include pve_copyright