417 lines
10 KiB
Perl
Executable File
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
|