vzdump_2010-05-25/0000755000000000000000000000000011376661437012227 5ustar rootrootvzdump_2010-05-25/vzdump.spec.in0000644000000000000000000000250011376661437015032 0ustar rootrootName: vzdump Vendor: Proxmox Packager: Proxmox Server Solutions GmbH Version: @@VERSION@@ Release: @@PKGRELEASE@@ BuildArch: noarch BuildPreReq: perl Requires: perl, vzctl, cstream, rsync, smtpdaemon, perl(LockFile::Simple) Summary: OpenVZ backup scripts BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root URL: http://www.proxmox.com/ Source: %{name}-%{version}.tar.gz License: GPL Group: Utilities %description This package contains the vzdump script to backup and restore openvz images. %prep %setup %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %attr(755,root,root) %_sbindir/vzdump %attr(755,root,root) %_sbindir/vzrestore %attr(644,root,root) %_mandir/man1/vzdump.1.* %attr(644,root,root) %_mandir/man1/vzrestore.1.* %attr(644,root,root) /usr/share/perl5/PVE/VZDump.pm %attr(644,root,root) /usr/share/perl5/PVE/VZDump/Plugin.pm %attr(644,root,root) /usr/share/perl5/PVE/VZDump/OpenVZ.pm %doc hook-script.pl ChangeLog changelog.Debian copyright %changelog * Thu Sep 3 2007 Dietmar Maurer 1.2-1 - added Vendor and Packager tags * Tue Mar 22 2007 Dietmar Maurer 0.3-1 - added Vendor and Packager tags * Tue Mar 20 2007 Kir Kolyshkin 0.3-1 - created spec file vzdump_2010-05-25/TODO0000644000000000000000000000064711376661437012726 0ustar rootroot restore to configurable directory --- mail from Jim Archer > Hi All... > > Is it possible to use vzdump --restore in such a way that the > files are placed back in a particular location? I tried > --dumpdir but that made no difference. VZDump keeps putting > the files into a location different than where they came from. > > I realize I could restore and then move the files, but I am > trying to automate this. vzdump_2010-05-25/copyright0000644000000000000000000000157611376661437014173 0ustar rootroot Copyright (C) 2007 Proxmox Server Solutions GmbH Copyright: vzdump is under GNU GPL, the GNU General Public License. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. The complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. vzdump_2010-05-25/vzrestore0000755000000000000000000001072311376661437014223 0ustar rootroot#!/usr/bin/perl -w # # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH # # Copyright: vzdump is under GNU GPL, the GNU General Public License. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Dietmar Maurer # use strict; use Getopt::Long; use Sys::Syslog; use File::Path; use PVE::VZDump; use PVE::VZDump::OpenVZ; $ENV{LANG} = "C"; # avoid locale related issues/warnings openlog ('vzdump', 'cons,pid', 'daemon'); my $force = 0; sub print_usage { my $msg = shift; print STDERR "ERROR: $msg\n\n" if $msg; print STDERR "usage: $0 [OPTIONS] \n"; print STDERR "\n"; print STDERR "\t--force overwrite existing conf file, private and root directory\n\n"; } if (!GetOptions ('force' => \$force)) { print_usage (); exit (-1); } if ($#ARGV != 1) { print_usage (); exit (-1); } my $archive = shift; my $vmid = PVE::VZDump::check_vmids ((shift))->[0]; $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { die "interrupted by signal\n"; }; sub debugmsg { PVE::VZDump::debugmsg (@_); } # just a shortcut sub run_command { PVE::VZDump::run_command (undef, @_); } # just a shortcut sub restore_openvz { my ($archive, $vmid) = @_; my $vzconf = PVE::VZDump::OpenVZ::read_global_vz_config (); my $cfgdir = PVE::VZDump::OpenVZ::VZDIR . "/conf"; my $conffile = "$cfgdir/${vmid}.conf"; my $private = $vzconf->{privatedir}; $private =~ s/\$VEID/$vmid/; my $root = $vzconf->{rootdir}; $root =~ s/\$VEID/$vmid/; print "you choose to force overwriting VPS config file, private and root directories.\n" if $force; die "unable to restore VM '$vmid' - VM already exists\n" if !$force && -f $conffile; ; die "unable to restore VPS '${vmid}' - " . "directory '$private' already exists\n" if !$force && -d $private; die "unable to restore VPS '${vmid}' - " . "directory '$root' already exists\n" if !$force && -d $root; eval { mkpath $private || die "unable to create private dir '$private'"; mkpath $root || die "unable to create private dir '$private'"; my $cmd = "tar xpf $archive --totals --sparse -C $private"; debugmsg ('info', "extracting archive '$archive'"); run_command ($cmd); debugmsg ('info', "extracting configuration to '$conffile'"); my $qroot = $vzconf->{rootdir}; $qroot =~ s|/|\\\/|g; my $qprivate = $vzconf->{privatedir}; $qprivate =~ s|/|\\\/|g; my $scmd = "sed -e 's/VE_ROOT=.*/VE_ROOT=\\\"$qroot\\\"/' -e 's/VE_PRIVATE=.*/VE_PRIVATE=\\\"$qprivate\\\"/' <'$private/etc/vzdump/vps.conf' >'$conffile'"; run_command ($scmd); foreach my $s (PVE::VZDump::OpenVZ::SCRIPT_EXT) { my $tfn = "$cfgdir/${vmid}.$s"; my $sfn = "$private/etc/vzdump/vps.$s"; if (-f $sfn) { run_command ("cp '$sfn' '$tfn'"); } } rmtree "$private/etc/vzdump"; }; my $err = $@; if ($err) { rmtree $private; rmtree $root; unlink $conffile; die $err; } } my $plugin = PVE::VZDump::OpenVZ->new(); my $firstfile = PVE::VZDump::read_firstfile ($archive); if ($firstfile eq 'qemu-server.conf') { die "ERROR: please use 'qmrestore' to restore QemuServer VMs\n"; } my $lock = $plugin->lock_vm ($vmid); eval { debugmsg ('info', "restore openvz backup '$archive' using ID $vmid", undef, 1); restore_openvz ($archive, $vmid); debugmsg ('info', "restore openvz backup '$archive' successful", undef, 1); }; my $err = $@; $plugin->unlock_vm ($vmid); if ($err) { debugmsg ('err', "restore openvz backup '$archive' failed - $err", undef, 1); exit (-1); } exit (0); __END__ =head1 NAME vzrestore - restore OpenVZ vzdump backups =head1 SYNOPSIS vzrestore =head1 DESCRIPTION Restore the OpenVZ vzdump backup to virtual machine . =head1 SEE ALSO vzdump(1) qmrestore(1) vzdump_2010-05-25/ChangeLog0000644000000000000000000001455011376661437014006 0ustar rootroot2010-05-20 Proxmox Support Team * vzrestore (restore_openvz): new --force option to overwrite existing data (patch from gilles) 2009-10-22 Proxmox Support Team * VZDump.pm (run_command): correctly detect rsync command (avoid backup to fail when files vanished before they could be transferred ) 2009-10-19 Proxmox Support Team * vzrestore (restore_openvz): correctly handle '$VEID' macro in config files (VE_ROOT, VE_PRIVATE). * OpenVZ.pm (read_global_vz_config): do not substitute $VEID 2009-10-13 Proxmox Support Team * OpenVZ.pm (read_vz_list): use -x (don't cross filesystem boundaries) for rsync (archive): also use --one-file-system for tar * VZDump.pm (run_hook_script): support for new --script option * hook-script.pl: example hook script 2009-10-12 Proxmox Support Team * VZDump.pm (exec_backup_task): also remove old log files 2009-10-09 Proxmox Support Team * VZDump.pm: encode backup time in backup filenames 2009-09-21 Proxmox Support Team * VZDump.pm (storage_info): new --storage option for PVE 2009-09-08 Proxmox Support Team * Makefile (vmtar, sparsecp, qmrestore): moved the QemuServer related code to the qemu-server package. 2009-09-07 Proxmox Support Team * Makefile (PACKAGE): increase version to 1.2-1 * sparsecp.c: small utility to copy stdin to a file preserving holes (sparse copy). 2009-09-03 Proxmox Support Team * vzrestore: split out restore code into new binary * qmrestore: split out restore code into new binary 2009-09-02 Proxmox Support Team * OpenVZ.pm (prepare): delay loading config file to this stage. * OpenVZ.pm (lock_vm): set lock during backup * vzdump.spec.in (Requires): add perl-LockFile-Simple * control (Depends): add liblockfile-simple-perl * vmtar.c: tar like utility to backup devices. * OpenVZ.pm (prepare): use same snapshot name as with QemuServer.pm ("vzsnap-$hostname-0"). * VZDump.pm: we now try to avoid using /var/tmp as temporary storage whenever possible. Only use /var/tmp when dumpdir is no posix filesystem (or when user explicit specifies --tmpdir). 2009-08-31 Proxmox Support Team * vzdump.spec.in (Requires): replace MTA with smtpdaemon 2009-08-25 Proxmox Support Team * rewrite: new plugin architecture. This way we can support plain OpenVZ system, but also use QemuServer and other libraries on PVE. * Plugin.pm: the base class for plugins. * VZDump.pm: contains common code and control logic. * OpenVZ: The OpenVZ plugin. * QemuServer: The PVE QemuServer plugin. * VZDump.pm (read_vzdump_defaults): use snapshot mode by default (it is no longer possible to make online backups without stop/suspend/snapshot) 2009-08-03 Proxmox Support Team * vzdump: set LANG=C to avoid locale warnings 2009-05-29 Proxmox Support Team * vzdump (get_lvm_device): also return filesystem type (snapshot_vm): always mount with '-t $fstype', pass "-o nouuid" for xfs filesystems. (get_lvm_mapping): use ':' as separator 2008-09-17 Proxmox Support Team * vzdump (snapshot_vm): use --no-whole-file --inplace for rsync (works better for large files), also use --stats --numeric-id and apply bandwidth limits with --bwlimit (archive_vm): use --sparse option for tar * vzdump: support to backup/restore qemu-server images * vzdump: new --stdexcludes option (we no longer exclude files by default, because most usres expect to have a full backup including all files and logs) * vzdump: add --tmpdir option as suggested by Ugo Bellavance * vzdump: add --wait option, using locbal lock file to make sure only one instance is running * vzdump: set default snapshot size to 1GB, new --size option to specify snapshot size * copyright: fixed fsf address * Makefile (SBINDIR): set to /usr/sbin (command is only useful for user root) * vzdump.spec.in, control.in: we now depend on rsync, cstream and MTA 2008-09-16 Proxmox Support Team * vzdump (read_vzdump_defaults): added global configuration file. 2008-08-18 Proxmox Support Team * vzdump: added --node option (proxmox ve cluster support), use --rsyncable for compressed files removed --xdelta option 2007-06-28 Proxmox Support Team * vzdump: use values of vz.conf if no private/root dir specified in XXX.conf 2007-05-25 Proxmox Support Team * vzdump (run_command): better parser (read_global_config): improved parser (send_mail): allow multiple receivers (multiple --mailto options) (send_mail): add additional status infos 2007-05-24 Proxmox Support Team * vzdump (send_mail): generate nicer mails 2007-05-21 Proxmox Support Team * vzdump: new --exclude-path option, use 'find' to avoid tar sockets warnings, try to skip all log files by default, use tar option --ignore-failed-read 2007-05-10 Proxmox Support Team * vzdump: additionally log time, new --exclude option (suggested by Ugo Bellavance) 2007-04-04 Proxmox Support Team * vzdump (get_device): use -P with df (fixes bug reported by Jernej Proenta) 2007-03-26 Proxmox Support Team * vzdump (read_global_config): read config of VEs >= 1000 - issue warning when doing backup without suspend/snapshot 2007-03-22 Proxmox Support Team * vzdump.spec.in: new file from Kir Kolyshkin * Makefile: merged in patch from Kir Kolyshkin - use rpmbuild instead of alien - removed install-deb and install-rpm target - added standard install target - renamed source package to vzdump-${VERSION}.tar.gz 2007-03-21 Proxmox Support Team * vzdump (read_global_config): remove /vz/private/$VEID/etc/vpsdump after backup (bug reported by Thorsten Schifferdecker) (check_bin): find correct path (bug reported by Thorsten Schifferdecker) (debugmsg): support IDs > 999 2007-03-20 Proxmox Support Team * Makefile: fix TGZ package 2007-03-07 Proxmox Support Team * Makefile: first try vzdump_2010-05-25/VZDump.pm0000644000000000000000000006373211376661437013765 0ustar rootrootpackage PVE::VZDump; # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH # # Copyright: vzdump is under GNU GPL, the GNU General Public License. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Dietmar Maurer use strict; use warnings; use Fcntl ':flock'; use Sys::Hostname; use Sys::Syslog; use IO::File; use IO::Select; use IPC::Open3; use POSIX qw(strftime); use File::Path; use PVE::VZDump::OpenVZ; use Time::localtime; use Time::Local; my @posix_filesystems = qw(ext3 ext4 nfs nfs4 reiserfs xfs); my $lockfile = '/var/run/vzdump.lock'; my $logdir = '/var/log/vzdump'; my @plugins = qw (PVE::VZDump::OpenVZ); # Load available plugins my $pveplug = "/usr/share/perl5/PVE/VZDump/QemuServer.pm"; if (-f $pveplug) { eval { require $pveplug; }; if (!$@) { PVE::VZDump::QemuServer->import (); push @plugins, "PVE::VZDump::QemuServer"; } else { warn $@; } } # helper functions my $debugstattxt = { err => 'ERROR:', info => 'INFO:', warn => 'WARN:', }; sub debugmsg { my ($mtype, $msg, $logfd, $syslog) = @_; chomp $msg; return if !$msg; my $pre = $debugstattxt->{$mtype} || $debugstattxt->{'err'}; my $timestr = strftime ("%b %d %H:%M:%S", CORE::localtime); syslog ($mtype eq 'info' ? 'info' : 'err', "$pre $msg") if $syslog; foreach my $line (split (/\n/, $msg)) { print "$pre $line\n"; print $logfd "$timestr $pre $line\n" if $logfd; } } sub run_command { my ($logfd, $cmdstr, $input, $timeout) = @_; my $reader = IO::File->new(); my $writer = IO::File->new(); my $error = IO::File->new(); my $orig_pid = $$; my $pid; eval { # suppress LVM warnings like: "File descriptor 3 left open"; local $ENV{LVM_SUPPRESS_FD_WARNINGS} = "1"; $pid = open3 ($writer, $reader, $error, ($cmdstr)) || die $!; }; my $err = $@; # catch exec errors if ($orig_pid != $$) { debugmsg ('err', "command '$cmdstr' failed - fork failed", $logfd); POSIX::_exit (1); kill ('KILL', $$); } die $err if $err; print $writer $input if defined $input; close $writer; my $select = new IO::Select; $select->add ($reader); $select->add ($error); my ($ostream, $estream, $logout, $logerr) = ('', '', '', ''); while ($select->count) { my @handles = $select->can_read ($timeout); if (defined ($timeout) && (scalar (@handles) == 0)) { die "command '$cmdstr' failed: timeout\n"; } foreach my $h (@handles) { my $buf = ''; my $count = sysread ($h, $buf, 4096); if (!defined ($count)) { waitpid ($pid, 0); die "command '$cmdstr' failed: $!\n"; } $select->remove ($h) if !$count; if ($h eq $reader) { $ostream .= $buf; $logout .= $buf; while ($logout =~ s/^([^\n]*\n)//s) { my $line = $1; debugmsg ('info', $line, $logfd); } } elsif ($h eq $error) { $estream .= $buf; $logerr .= $buf; while ($logerr =~ s/^([^\n]*\n)//s) { my $line = $1; debugmsg ('info', $line, $logfd); } } } } debugmsg ('info', $logout, $logfd); debugmsg ('info', $logerr, $logfd); waitpid ($pid, 0); my $ec = ($? >> 8); return $ostream if $ec == 24 && ($cmdstr =~ m|^(\S+/)?rsync\s|); die "command '$cmdstr' failed with exit code $ec\n" if $ec; return $ostream; } sub storage_info { my $storage = shift; eval { require PVE::Storage; }; die "unable to query storage info for '$storage' - $@\n" if $@; my $cfg = PVE::Storage::load_config(); my $scfg = PVE::Storage::storage_config ($cfg, $storage); my $type = $scfg->{type}; die "can't use storage type '$type' for backup\n" if (!($type eq 'dir' || $type eq 'nfs')); die "can't use storage for backups - wrong content type\n" if (!$scfg->{content}->{backup}); PVE::Storage::activate_storage ($cfg, $storage); return { dumpdir => $scfg->{path}, }; } sub format_size { my $size = shift; my $kb = $size / 1024; if ($kb < 1024) { return int ($kb) . "KB"; } my $mb = $size / (1024*1024); if ($mb < 1024) { return int ($mb) . "MB"; } else { my $gb = $mb / 1024; return sprintf ("%.2fGB", $gb); } } sub format_time { my $seconds = shift; my $hours = int ($seconds/3600); $seconds = $seconds - $hours*3600; my $min = int ($seconds/60); $seconds = $seconds - $min*60; return sprintf ("%02d:%02d:%02d", $hours, $min, $seconds); } sub encode8bit { my ($str) = @_; $str =~ s/^(.{990})/$1\n/mg; # reduce line length return $str; } sub escape_html { my ($str) = @_; $str =~ s/&/&/g; $str =~ s//>/g; return $str; } sub check_bin { my ($bin) = @_; foreach my $p (split (/:/, $ENV{PATH})) { my $fn = "$p/$bin"; if (-x $fn) { return $fn; } } die "unable to find command '$bin'\n"; } sub check_vmids { my (@vmids) = @_; my $res = []; foreach my $vmid (@vmids) { die "ERROR: strange VM ID '${vmid}'\n" if $vmid !~ m/^\d+$/; $vmid = int ($vmid); # remove leading zeros die "ERROR: got reserved VM ID '${vmid}'\n" if $vmid < 100; push @$res, $vmid; } return $res; } sub read_vzdump_defaults { my $fn = "/etc/vzdump.conf"; my $res = { bwlimit => 10240, size => 1024, lockwait => 3*60, # 3 hours stopwait => 10, # 10 minutes mode => 'snapshot', maxfiles => 1, }; my $fh = IO::File->new ("<$fn"); return $res if !$fh; my $line; while (defined ($line = <$fh>)) { next if $line =~ m/^\s*$/; next if $line =~ m/^\#/; if ($line =~ m/tmpdir:\s*(.*\S)\s*$/) { $res->{tmpdir} = $1; } elsif ($line =~ m/dumpdir:\s*(.*\S)\s*$/) { $res->{dumpdir} = $1; } elsif ($line =~ m/storage:\s*(\S+)\s*$/) { $res->{storage} = $1; } elsif ($line =~ m/script:\s*(.*\S)\s*$/) { $res->{script} = $1; } elsif ($line =~ m/bwlimit:\s*(\d+)\s*$/) { $res->{bwlimit} = int($1); } elsif ($line =~ m/lockwait:\s*(\d+)\s*$/) { $res->{lockwait} = int($1); } elsif ($line =~ m/stopwait:\s*(\d+)\s*$/) { $res->{stopwait} = int($1); } elsif ($line =~ m/size:\s*(\d+)\s*$/) { $res->{size} = int($1); } elsif ($line =~ m/maxfiles:\s*(\d+)\s*$/) { $res->{maxfiles} = int($1); } elsif ($line =~ m/mode:\s*(stop|snapshot|suspend)\s*$/) { $res->{mode} = $1; } else { debugmsg ('warn', "unable to parse configuration file '$fn' - error at line " . $., undef, 1); } } close ($fh); return $res; } sub find_add_exclude { my ($self, $excltype, $value) = @_; if (($excltype eq '-regex') || ($excltype eq '-files')) { $value = "\.$value"; } if ($excltype eq '-files') { push @{$self->{findexcl}}, "'('", '-not', '-type', 'd', '-regex' , "'$value'", "')'", '-o'; } else { push @{$self->{findexcl}}, "'('", $excltype , "'$value'", '-prune', "')'", '-o'; } } sub read_firstfile { my $archive = shift; die "ERROR: file '$archive' does not exist\n" if ! -f $archive; # try to detect archive type first my $pid = open (TMP, "tar tf '$archive'|") || die "unable to open file '$archive'\n"; my $firstfile = ; kill 15, $pid; close TMP; die "ERROR: archive contaions no data\n" if !$firstfile; chomp $firstfile; return $firstfile; } my $sendmail = sub { my ($self, $tasklist, $totaltime) = @_; my $opts = $self->{opts}; my $mailto = $opts->{mailto}; return if !$mailto; my $cmdline = $self->{cmdline}; my $ecount = 0; foreach my $task (@$tasklist) { $ecount++ if $task->{state} ne 'ok'; chomp $task->{msg} if $task->{msg}; $task->{backuptime} = 0 if !$task->{backuptime}; $task->{size} = 0 if !$task->{size}; $task->{tarfile} = 'unknown' if !$task->{tarfile}; $task->{hostname} = "VM $task->{vmid}" if !$task->{hostname}; if ($task->{state} eq 'todo') { $task->{msg} = 'aborted'; } } my $stat = $ecount ? 'backup failed' : 'backup successful'; my $hostname = `hostname -f` || hostname(); chomp $hostname; my $boundary = "----_=_NextPart_001_".int(time).$$; my $rcvrarg = ''; foreach my $r (@$mailto) { $rcvrarg .= " '$r'"; } open (MAIL,"|sendmail -B 8BITMIME $rcvrarg") || die "unable to open 'sendmail' - $!"; my $rcvrtxt = join (', ', @$mailto); print MAIL "Content-Type: multipart/alternative;\n"; print MAIL "\tboundary=\"$boundary\"\n"; print MAIL "FROM: vzdump backup tool \n"; print MAIL "TO: $rcvrtxt\n"; print MAIL "SUBJECT: vzdump backup status ($hostname) : $stat\n"; print MAIL "\n"; print MAIL "This is a multi-part message in MIME format.\n\n"; print MAIL "--$boundary\n"; print MAIL "Content-Type: text/plain;\n"; print MAIL "\tcharset=\"UTF8\"\n"; print MAIL "Content-Transfer-Encoding: 8bit\n"; print MAIL "\n"; # text part my $fill = ' '; # Avoid The Remove Extra Line Breaks Issue (MS Outlook) print MAIL sprintf ("${fill}%-10s %-6s %10s %10s %s\n", qw(VMID STATUS TIME SIZE FILENAME)); foreach my $task (@$tasklist) { my $vmid = $task->{vmid}; if ($task->{state} eq 'ok') { print MAIL sprintf ("${fill}%-10s %-6s %10s %10s %s\n", $vmid, $task->{state}, format_time($task->{backuptime}), format_size ($task->{size}), $task->{tarfile}); } else { print MAIL sprintf ("${fill}%-10s %-6s %10s %8.2fMB %s\n", $vmid, $task->{state}, format_time($task->{backuptime}), 0, '-'); } } print MAIL "${fill}\n"; print MAIL "${fill}Detailed backup logs:\n"; print MAIL "${fill}\n"; print MAIL "$fill$cmdline\n"; print MAIL "${fill}\n"; foreach my $task (@$tasklist) { my $vmid = $task->{vmid}; my $log = $task->{tmplog}; if (!$log) { print MAIL "${fill}$vmid: no log available\n\n"; next; } open (TMP, "$log"); while (my $line = ) { print MAIL encode8bit ("${fill}$vmid: $line"); } close (TMP); print MAIL "${fill}\n"; } # end text part print MAIL "\n--$boundary\n"; print MAIL "Content-Type: text/html;\n"; print MAIL "\tcharset=\"UTF8\"\n"; print MAIL "Content-Transfer-Encoding: 8bit\n"; print MAIL "\n"; # html part print MAIL "\n"; print MAIL "\n"; print MAIL "\n"; my $ssize = 0; foreach my $task (@$tasklist) { my $vmid = $task->{vmid}; my $name = $task->{hostname}; if ($task->{state} eq 'ok') { $ssize += $task->{size}; print MAIL sprintf ("\n", $vmid, $name, format_time($task->{backuptime}), format_size ($task->{size}), escape_html ($task->{tarfile})); } else { print MAIL sprintf ("\n", $vmid, $name, format_time($task->{backuptime}), escape_html ($task->{msg})); } } print MAIL sprintf ("", format_time ($totaltime), format_size ($ssize)); print MAIL "
VMIDNAMESTATUSTIMESIZEFILENAME
%s%sOK%s%s%s
%s%sFAILED%s%s
TOTAL%s%s


\n"; print MAIL "Detailed backup logs:
\n"; print MAIL "
\n"; print MAIL "
\n";
    print MAIL escape_html($cmdline) . "\n";
    print MAIL "\n";

    foreach my $task (@$tasklist) {
	my $vmid = $task->{vmid};
	my $log = $task->{tmplog};
	if (!$log) {
	    print MAIL "$vmid: no log available\n\n";
	    next;
	}
	open (TMP, "$log");
	while (my $line = ) {
	    if ($line =~ m/^\S+\s\d+\s+\d+:\d+:\d+\s+(ERROR|WARN):/) {
		print MAIL encode8bit ("$vmid: ". 
				       escape_html ($line) . ""); 
	    } else {
		print MAIL encode8bit ("$vmid: " . escape_html ($line)); 
	    }
	}
	close (TMP);
	print MAIL "\n";
    }
    print MAIL "
\n"; print MAIL "\n"; # end html part print MAIL "\n--$boundary--\n"; }; sub new { my ($class, $cmdline, $opts) = @_; mkpath $logdir; check_bin ('cp'); check_bin ('df'); check_bin ('sendmail'); check_bin ('rsync'); check_bin ('tar'); check_bin ('mount'); check_bin ('umount'); check_bin ('cstream'); if ($opts->{snapshot}) { check_bin ('lvcreate'); check_bin ('lvs'); check_bin ('lvremove'); } my $defaults = read_vzdump_defaults(); foreach my $k (keys %$defaults) { if ($k eq 'dumpdir' || $k eq 'storage') { $opts->{$k} = $defaults->{$k} if !defined ($opts->{dumpdir}) && !defined ($opts->{storage}); } else { $opts->{$k} = $defaults->{$k} if !defined ($opts->{$k}); } } $opts->{mode} = 'stop' if $opts->{stop}; $opts->{mode} = 'suspend' if $opts->{suspend}; $opts->{mode} = 'snapshot' if $opts->{snapshot}; $opts->{dumpdir} =~ s|/+$|| if ($opts->{dumpdir}); $opts->{tmpdir} =~ s|/+$|| if ($opts->{tmpdir}); my $self = bless { cmdline => $cmdline, opts => $opts }; #always skip '.' push @{$self->{findexcl}}, "'('", '-regex' , "'^\\.\$'", "')'", '-o'; $self->find_add_exclude ('-type', 's'); # skip sockets if ($opts->{'exclude-path'}) { foreach my $path (@{$opts->{'exclude-path'}}) { $self->find_add_exclude ('-regex', $path); } } if ($opts->{stdexcludes}) { $self->find_add_exclude ('-files', '/var/log/.+'); $self->find_add_exclude ('-regex', '/tmp/.+'); $self->find_add_exclude ('-regex', '/var/tmp/.+'); $self->find_add_exclude ('-regex', '/var/run/.+pid'); } foreach my $p (@plugins) { my $pd = $p->new ($self); push @{$self->{plugins}}, $pd; if (!$opts->{dumpdir} && !$opts->{storage} && ($p eq 'PVE::VZDump::OpenVZ')) { $opts->{dumpdir} = $pd->{dumpdir}; } } if (!$opts->{dumpdir} && !$opts->{storage}) { die "no dumpdir/storage specified - use option '--dumpdir' or option '--storage'\n"; } if ($opts->{storage}) { my $info = storage_info ($opts->{storage}); $opts->{dumpdir} = $info->{dumpdir}; } elsif ($opts->{dumpdir}) { die "dumpdir '$opts->{dumpdir}' does not exist\n" if ! -d $opts->{dumpdir}; } else { die "internal error"; } if ($opts->{tmpdir} && ! -d $opts->{tmpdir}) { die "tmpdir '$opts->{tmpdir}' does not exist\n"; } return $self; } sub get_lvm_mapping { my $devmapper; my $cmd = "lvs --units m --separator ':' --noheadings -o vg_name,lv_name,lv_size"; if (my $fd = IO::File->new ("$cmd 2>/dev/null|")) { while (my $line = <$fd>) { if ($line =~ m|^\s*(\S+):(\S+):(\d+(\.\d+))M$|) { my $vg = $1; my $lv = $2; $devmapper->{"/dev/$vg/$lv"} = [$vg, $lv]; my $qlv = $lv; $qlv =~ s/-/--/g; my $qvg = $vg; $qvg =~ s/-/--/g; $devmapper->{"/dev/mapper/$qvg-$qlv"} = [$vg, $lv]; } } close ($fd); } return $devmapper; } sub get_mount_info { my ($dir) = @_; my $out; if (my $fd = IO::File->new ("df -P -T '$dir' 2>/dev/null|")) { <$fd>; #skip first line $out = <$fd>; close ($fd); } return undef if !$out; my @res = split (/\s+/, $out); return undef if scalar (@res) != 7; return { device => $res[0], fstype => $res[1], mountpoint => $res[6] }; } sub get_lvm_device { my ($dir, $mapping) = @_; my $info = get_mount_info ($dir); return undef if !$info; my $dev = $info->{device}; my ($vg, $lv); ($vg, $lv) = @{$mapping->{$dev}} if defined $mapping->{$dev}; return wantarray ? ($dev, $info->{mountpoint}, $vg, $lv, $info->{fstype}) : $dev; } sub getlock { my ($self) = @_; my $maxwait = $self->{opts}->{lockwait} || $self->{lockwait}; if (!open (SERVER_FLCK, ">>$lockfile")) { debugmsg ('err', "can't open lock on file '$lockfile' - $!", undef, 1); exit (-1); } if (flock (SERVER_FLCK, LOCK_EX|LOCK_NB)) { return; } if (!$maxwait) { debugmsg ('err', "can't aquire lock '$lockfile' (wait = 0)", undef, 1); exit (-1); } debugmsg('info', "trying to get global lock - waiting...", undef, 1); eval { alarm ($maxwait * 60); local $SIG{ALRM} = sub { alarm (0); die "got timeout\n"; }; if (!flock (SERVER_FLCK, LOCK_EX)) { my $err = $!; close (SERVER_FLCK); alarm (0); die "$err\n"; } alarm (0); }; alarm (0); my $err = $@; if ($err) { debugmsg ('err', "can't aquire lock '$lockfile' - $err", undef, 1); exit (-1); } debugmsg('info', "got global lock", undef, 1); } sub run_hook_script { my ($self, $phase, $task, $logfd) = @_; my $opts = $self->{opts}; my $script = $opts->{script}; return if !$script; my $cmd = "$script $phase"; $cmd .= " $task->{mode} $task->{vmid}" if ($task); local %ENV; foreach my $ek (qw(vmtype dumpdir hostname tarfile logfile)) { $ENV{uc($ek)} = $task->{$ek} if $task->{$ek}; } run_command ($logfd, $cmd); } sub exec_backup_task { my ($self, $task) = @_; my $opts = $self->{opts}; my $vmid = $task->{vmid}; my $plugin = $task->{plugin}; my $vmstarttime = time (); my $logfd; my $cleanup = {}; my $vmstoptime = 0; eval { die "unable to find VM '$vmid'\n" if !$plugin; my $vmtype = $plugin->type(); my $tmplog = "$logdir/$vmtype-$vmid.log"; my $lt = localtime(); my $bkname = "vzdump-$vmtype-$vmid"; my $basename = sprintf "${bkname}-%04d_%02d_%02d-%02d_%02d_%02d", $lt->year + 1900, $lt->mon + 1, $lt->mday, $lt->hour, $lt->min, $lt->sec; my $logfile = $task->{logfile} = "$opts->{dumpdir}/$basename.log"; my $ext = $opts->{compress} ? '.tgz' : '.tar'; my $tarfile = $task->{tarfile} = "$opts->{dumpdir}/$basename$ext"; $task->{tmptar} = $task->{tarfile}; $task->{tmptar} =~ s/\.[^\.]+$/\.dat/; $task->{vmtype} = $vmtype; unlink $task->{tmptar}; if ($opts->{tmpdir}) { $task->{tmpdir} = "$opts->{tmpdir}/vzdumptmp$$"; } else { # dumpdir is posix? then use it as temporary dir my $info = get_mount_info ($opts->{dumpdir}); if ($vmtype eq 'qemu' || grep ($_ eq $info->{fstype}, @posix_filesystems)) { $task->{tmpdir} = "$opts->{dumpdir}/$basename.tmp"; } else { $task->{tmpdir} = "/var/tmp/vzdumptmp$$"; debugmsg ('info', "filesystem type on dumpdir is '$info->{fstype}' -" . "using $task->{tmpdir} for temporary files", $logfd); } } rmtree $task->{tmpdir}; mkdir $task->{tmpdir}; -d $task->{tmpdir} || die "unable to create temporary directory '$task->{tmpdir}'"; $logfd = IO::File->new (">$tmplog") || die "unable to create log file '$tmplog'"; $task->{dumpdir} = $opts->{dumpdir}; $task->{tmplog} = $tmplog; unlink $logfile; debugmsg ('info', "Starting Backup of VM $vmid ($vmtype)", $logfd, 1); $plugin->set_logfd ($logfd); # test is VM is running my ($running, $status_text) = $plugin->vm_status ($vmid); debugmsg ('info', "status = ${status_text}", $logfd); # lock VM (prevent config changes) $plugin->lock_vm ($vmid); $cleanup->{unlock} = 1; # prepare my $mode = $running ? $opts->{mode} : 'stop'; if ($mode eq 'snapshot') { my %saved_task = %$task; eval { $plugin->prepare ($task, $vmid, $mode); }; if (my $err = $@) { die $err if $err !~ m/^mode failure/; debugmsg ('info', $err, $logfd); debugmsg ('info', "trying 'suspend' mode instead", $logfd); $mode = 'suspend'; # so prepare is called again below %$task = %saved_task; } } $task->{mode} = $mode; debugmsg ('info', "backup mode: $mode", $logfd); debugmsg ('info', "bandwidth limit: $opts->{bwlimit} KB/s", $logfd) if $opts->{bwlimit}; if ($mode eq 'stop') { $plugin->prepare ($task, $vmid, $mode); $self->run_hook_script ('backup-start', $task, $logfd); if ($running) { debugmsg ('info', "stopping vm", $logfd); $vmstoptime = time (); $self->run_hook_script ('pre-stop', $task, $logfd); $plugin->stop_vm ($task, $vmid); $cleanup->{restart} = 1; } } elsif ($mode eq 'suspend') { $plugin->prepare ($task, $vmid, $mode); $self->run_hook_script ('backup-start', $task, $logfd); if ($vmtype eq 'openvz') { # pre-suspend rsync $plugin->copy_data_phase1 ($task, $vmid); } debugmsg ('info', "suspend vm", $logfd); $vmstoptime = time (); $self->run_hook_script ('pre-stop', $task, $logfd); $plugin->suspend_vm ($task, $vmid); $cleanup->{resume} = 1; if ($vmtype eq 'openvz') { # post-suspend rsync $plugin->copy_data_phase2 ($task, $vmid); debugmsg ('info', "resume vm", $logfd); $cleanup->{resume} = 0; $self->run_hook_script ('pre-restart', $task, $logfd); $plugin->resume_vm ($task, $vmid); my $delay = time () - $vmstoptime; debugmsg ('info', "vm is online again after $delay seconds", $logfd); } } elsif ($mode eq 'snapshot') { my $snapshot_count = $task->{snapshot_count} || 0; $self->run_hook_script ('pre-stop', $task, $logfd); if ($snapshot_count > 1) { debugmsg ('info', "suspend vm to make snapshot", $logfd); $vmstoptime = time (); $plugin->suspend_vm ($task, $vmid); $cleanup->{resume} = 1; } $self->run_hook_script ('pre-restart', $task, $logfd); $plugin->snapshot ($task, $vmid); if ($snapshot_count > 1) { debugmsg ('info', "resume vm", $logfd); $cleanup->{resume} = 0; $plugin->resume_vm ($task, $vmid); my $delay = time () - $vmstoptime; debugmsg ('info', "vm is online again after $delay seconds", $logfd); } } else { die "internal error - unknown mode '$mode'\n"; } # assemble archive image $plugin->assemble ($task, $vmid); # produce archive debugmsg ('info', "creating archive '$task->{tarfile}'", $logfd); $plugin->archive ($task, $vmid, $task->{tmptar}); rename ($task->{tmptar}, $task->{tarfile}) || die "unable to rename '$task->{tmptar}' to '$task->{tarfile}'\n"; # determine size $task->{size} = (-s $task->{tarfile}) || 0; my $cs = format_size ($task->{size}); debugmsg ('info', "archive file size: $cs", $logfd); # purge older backup my $maxfiles = $opts->{maxfiles}; if ($maxfiles) { my @bklist = (); my $dir = $opts->{dumpdir}; foreach my $fn (<$dir/${bkname}-*>) { next if $fn eq $task->{tarfile}; if ($fn =~ m!/${bkname}-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.(tgz|tar)$!) { my $t = timelocal ($6, $5, $4, $3, $2 - 1, $1 - 1900); push @bklist, [$fn, $t]; } } @bklist = sort { $b->[1] <=> $a->[1] } @bklist; my $ind = scalar (@bklist); while (scalar (@bklist) >= $maxfiles) { my $d = pop @bklist; debugmsg ('info', "delete old backup '$d->[0]'", $logfd); unlink $d->[0]; my $logfn = $d->[0]; $logfn =~ s/\.(tgz|tar)$/\.log/; unlink $logfn; } } $self->run_hook_script ('backup-end', $task, $logfd); }; my $err = $@; if ($plugin) { # clean-up if ($cleanup->{unlock}) { eval { $plugin->unlock_vm ($vmid); }; warn $@ if $@; } eval { $plugin->cleanup ($task, $vmid) }; warn $@ if $@; eval { $plugin->set_logfd (undef); }; warn $@ if $@; if ($cleanup->{resume} || $cleanup->{restart}) { eval { $self->run_hook_script ('pre-restart', $task, $logfd); if ($cleanup->{resume}) { debugmsg ('info', "resume vm", $logfd); $plugin->resume_vm ($task, $vmid); } else { debugmsg ('info', "restarting vm", $logfd); $plugin->start_vm ($task, $vmid); } }; my $err = $@; if ($err) { warn $err; } else { my $delay = time () - $vmstoptime; debugmsg ('info', "vm is online again after $delay seconds", $logfd); } } } eval { unlink $task->{tmptar} if $task->{tmptar} && -f $task->{tmptar}; }; warn $@ if $@; eval { rmtree $task->{tmpdir} if $task->{tmpdir} && -d $task->{tmpdir}; }; warn $@ if $@; my $delay = $task->{backuptime} = time () - $vmstarttime; if ($err) { $task->{state} = 'err'; $task->{msg} = $err; debugmsg ('err', "Backup of VM $vmid failed - $err", $logfd, 1); eval { $self->run_hook_script ('backup-abort', $task, $logfd); }; } else { $task->{state} = 'ok'; my $tstr = format_time ($delay); debugmsg ('info', "Finished Backup of VM $vmid ($tstr)", $logfd, 1); } close ($logfd) if $logfd; if ($task->{tmplog} && $task->{logfile}) { system ("cp '$task->{tmplog}' '$task->{logfile}'"); } die $err if $err && $err =~ m/^interrupted by signal$/; } sub exec_backup { my ($self) = @_; my $opts = $self->{opts}; debugmsg ('info', "starting new backup job: $self->{cmdline}", undef, 1); my $tasklist = []; if ($opts->{all}) { foreach my $plugin (@{$self->{plugins}}) { my $vmlist = $plugin->vmlist(); foreach my $vmid (sort @$vmlist) { next if grep { $_ eq $vmid } @{$opts->{exclude}}; push @$tasklist, { vmid => $vmid, state => 'todo', plugin => $plugin }; } } } else { foreach my $vmid (sort @{$opts->{vmids}}) { my $plugin; foreach my $pg (@{$self->{plugins}}) { my $vmlist = $pg->vmlist(); if (grep { $_ eq $vmid } @$vmlist) { $plugin = $pg; last; } } push @$tasklist, { vmid => $vmid, state => 'todo', plugin => $plugin }; } } my $starttime = time(); my $errcount = 0; eval { $self->run_hook_script ('job-start'); foreach my $task (@$tasklist) { $self->exec_backup_task ($task); $errcount += 1 if $task->{state} ne 'ok'; } $self->run_hook_script ('job-end'); }; my $err = $@; $self->run_hook_script ('job-abort') if $err; if ($err) { debugmsg ('err', "Backup job failed - $err", undef, 1); } else { if ($errcount) { debugmsg ('info', "Backup job finished with errors", undef, 1); } else { debugmsg ('info', "Backup job finished successfuly", undef, 1); } } my $totaltime = time() - $starttime; eval { $self->$sendmail ($tasklist, $totaltime); }; debugmsg ('err', $@) if $@; } 1; vzdump_2010-05-25/hook-script.pl0000755000000000000000000000221211376661437015026 0ustar rootroot#!/usr/bin/perl -w # example hook script for vzdump (--script option) use strict; print "HOOK: " . join (' ', @ARGV) . "\n"; my $phase = shift; if ($phase eq 'job-start' || $phase eq 'job-end' || $phase eq 'job-abort') { # do what you want } elsif ($phase eq 'backup-start' || $phase eq 'backup-end' || $phase eq 'backup-abort' || $phase eq 'pre-stop' || $phase eq 'pre-restart') { print "HOOK: " . join (' ', @ARGV) . "\n"; my $mode = shift; # stop/suspend/snapshot my $vmid = shift; my $vmtype = $ENV{VMTYPE}; # openvz/qemu my $dumpdir = $ENV{DUMPDIR}; my $hostname = $ENV{HOSTNAME}; my $tarfile = $ENV{TARFILE}; my $logfile = $ENV{LOGFILE}; print "HOOK-ENV: vmtype=$vmtype;dumpdir=$dumpdir;hostname=$hostname;tarfile=$tarfile;logfile=$logfile\n"; # example: copy resulting files to another host using scp #if ($phase eq 'backup-end') { # system ("scp $tarfile $logfile backup-host:/backup-dir") == 0 || # die "copy to backup-host failed"; # unlink $tarfile; # unlink $logfile; #} } else { die "got unknown phase '$phase'"; } exit (0); vzdump_2010-05-25/changelog.Debian0000644000000000000000000000467511376661437015276 0ustar rootrootvzdump (1.2-6) unstable; urgency=low * vzrestore: new --force option to overwrite existing data -- Proxmox Support Team Thu, 20 May 2010 11:13:48 +0200 vzdump (1.2-5) unstable; urgency=low * re-added missing liblockfile-simple-perl -- Proxmox Support Team Fri, 23 Oct 2009 08:55:00 +0200 vzdump (1.2-4) unstable; urgency=low * correctly detect rsync command (avoid backup to fail when files vanished before they could be transferred) -- Proxmox Support Team Thu, 22 Oct 2009 10:01:32 +0200 vzdump (1.2-3) unstable; urgency=low * correctly handle '$VEID' macro in config files (VE_ROOT, VE_PRIVATE). * don't cross filesystem boundaries with OpenVZ backups (bind mount are no longer included). * support hook scripts (new --script option) * new naming scheme - include date/time into file names. -- Proxmox Support Team Mon, 12 Oct 2009 12:50:18 +0200 vzdump (1.2-2) unstable; urgency=low * new storage option for PVE -- Proxmox Support Team Mon, 21 Sep 2009 11:36:01 +0200 vzdump (1.2-1) unstable; urgency=low * complete rewrite -- Proxmox Support Team Thu, 1 Sep 2009 11:00:06 +0200 vzdump (1.0-2) unstable; urgency=low * bug fix in config parser -- Proxmox Support Team Thu, 28 Jun 2007 11:38:06 +0200 vzdump (1.0-1) stable; urgency=low * set version number to 1.0, because we are quite stable * added copyright notice to man page -- Proxmox Support Team Thu, 10 May 2007 09:50:01 +0200 vzdump (0.4-1) unstable; urgency=low * also read config of VEs with ID > 999 -- Proxmox Support Team Mon, 26 Mar 2007 09:05:28 +0200 vzdump (0.3-1) unstable; urgency=low * merged in patch from Kir Kolyshkin (rpm related changes) -- Proxmox Support Team Thu, 22 Mar 2007 08:35:55 +0100 vzdump (0.2-2) unstable; urgency=low * changed package dependency (perl > 5.6.0-16, vzdump) -- Proxmox Support Team Wed, 21 Mar 2007 10:42:10 +0100 vzdump (0.2-1) stable; urgency=low * dm2 snapshot support * many bug fixes * new manual page -- Proxmox Support Team Mon, 19 Mar 2007 11:10:25 +0100 vzdump (0.1-1) stable; urgency=low * first release -- Proxmox Support Team Wed, 7 Mar 2007 16:20:48 +0100 vzdump_2010-05-25/vzdump0000755000000000000000000002423711376661437013512 0ustar rootroot#!/usr/bin/perl -w # # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH # # Copyright: vzdump is under GNU GPL, the GNU General Public License. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Dietmar Maurer # use strict; use Getopt::Long; use Sys::Syslog; use PVE::VZDump; $ENV{LANG} = "C"; # avoid locale related issues/warnings # by default we set --rsyncable for gzip $ENV{GZIP} = "--rsyncable" if !$ENV{GZIP}; # just to be sure that we have a resonable path $ENV{PATH} = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"; my $cmdline = join (' ', 'vzdump', @ARGV); openlog ('vzdump', 'cons,pid', 'daemon'); $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { die "interrupted by signal\n"; }; my @std_opts = ( 'all', 'exclude=s@', 'exclude-path=s@', 'stdexcludes', 'compress', 'mailto=s@', 'quiet', 'stop', 'suspend', 'snapshot', 'size=i', 'node=i', 'bwlimit=i', 'lockwait=i', 'stopwait=i', 'tmpdir=s', 'dumpdir=s', 'maxfiles=i', 'script=s', 'storage=s', ); sub print_usage { my $msg = shift; print STDERR "ERROR: $msg\n\n" if $msg; print STDERR "usage: $0 OPTIONS [--all | VMID]\n\n"; print STDERR "\t--exclude VMID\t\texclude VMID (assumes --all)\n"; print STDERR "\t--exclude-path REGEX\texclude certain files/directories\n"; print STDERR "\t--stdexcludes\t\texclude temorary files and logs\n\n"; print STDERR "\t--compress\t\tcompress dump file (gzip)\n"; print STDERR "\t--dumpdir DIR\t\tstore resulting files in DIR\n"; print STDERR "\t--maxfiles N\t\tmaximal number of backup files per VM\n"; print STDERR "\t--script FILENAME\texecute hook script\n"; print STDERR "\t--storage STORAGE_ID\tstore resulting files to STORAGE_ID (PVE only)\n"; print STDERR "\t--tmpdir DIR\t\tstore temporary files in DIR\n\n"; print STDERR "\t--mailto EMAIL\t\tsend notification mail to EMAIL.\n"; print STDERR "\t--quiet\t\t\tbe quiet.\n"; print STDERR "\t--stop\t\t\tstop/start VM if running\n"; print STDERR "\t--suspend\t\tsuspend/resume VM when running\n"; print STDERR "\t--snapshot\t\tuse LVM snapshot when running\n"; print STDERR "\t--size MB\t\tLVM snapshot size\n\n"; print STDERR "\t--node CID\t\tonly run on pve cluster node CID\n"; print STDERR "\t--lockwait MINUTES\tmaximal time to wait for the global lock\n"; print STDERR "\t--stopwait MINUTES\tmaximal time to wait until a VM is stopped\n"; print STDERR "\t--bwlimit KBPS\t\tlimit I/O bandwidth; KBytes per second\n\n"; print STDERR "\n"; } my $opts = {}; if (!GetOptions ($opts, @std_opts)) { print_usage (); exit (-1); } if ($opts->{node}) { PVE::VZDump::check_bin ('pveca'); my $info = `pveca -i`; chomp $info; die "unable to parse pveca info" if $info !~ m/^(\d+)\s+\S+\s+\S+\s+\S+$/; my $cid = $1; # silent exit if we run on wrong node exit (0) if $cid != $opts->{node}; } $opts->{all} = 1 if $opts->{exclude}; if ($opts->{all} && $#ARGV >= 0) { print_usage (); exit (-1); } if (!$opts->{all} && $#ARGV == -1) { print_usage (); exit (-1); } if ($opts->{quiet}) { open STDOUT, '>/dev/null'; open STDERR, '>/dev/null'; } $opts->{vmids} = PVE::VZDump::check_vmids (@ARGV) if !$opts->{all}; $opts->{exclude} = PVE::VZDump::check_vmids (@{$opts->{exclude}}) if $opts->{exclude}; my $vzdump = PVE::VZDump->new ($cmdline, $opts); $vzdump->getlock (); # only one process allowed # parameters are OK - now start real work and log everything eval { $vzdump->exec_backup(); }; my $err = $@; if ($err) { PVE::VZDump::debugmsg ('err', $err, undef, 1); exit (-1); } exit 0; __END__ =head1 NAME vzdump - backup utility for virtual machine =head1 SYNOPSIS vzdump OPTIONS [--all | ] --exclude VMID exclude VMID (assumes --all) --exclude-path REGEX exclude certain files/directories. You can use this option more than once to specify multiple exclude paths --stdexcludes exclude temporary files and logs --compress compress dump file (gzip) --storage STORAGE_ID store resulting files to STORAGE_ID (PVE only) --script execute hook script --dumpdir DIR store resulting files in DIR --maxfiles N maximal number of backup files per VM. --tmpdir DIR store temporary files in DIR. --suspend and --stop are using this directory to store a copy of the VM. --mailto EMAIL send notification mail to EMAIL. You can use this option more than once to specify multiple receivers --stop stop/start VM if running --suspend suspend/resume VM when running --snapshot use LVM snapshot when running --size MB LVM snapshot size (default 1024) --bwlimit KBPS limit I/O bandwidth; KBytes per second --lockwait MINUTES maximal time to wait for the global lock. vzdump uses a global lock file to make sure that only one instance is running (running several instance puts too much load on a server). Default is 180 (3 hours). --stopwait MINUTES maximal time to wait until a VM is stopped. =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 mode Stop the VM during backup. This results in a very long downtime. =item C 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 mode, but uses suspend/resume instead of stop/start. =item C 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 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 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). =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
--exclude-path C 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 777 Use rsync and suspend/resume to create an snapshot (minimal downtime). > vzdump --suspend 777 Backup all VMs and send notification mails to root. > vzdump --suspend --all --mailto root Use LVM2 to create snapshots (no downtime). > vzdump --dumpdir /mnt/backup --snapshot 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) =head1 AUTHOR Dietmar Maurer Many thanks to Proxmox Server Solutions (www.proxmox.com) for sponsoring this work. =head1 COPYRIGHT AND DISCLAIMER Copyright (C) 2007-2009 Proxmox Server Solutions GmbH Copyright: vzdump is under GNU GPL, the GNU General Public License. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. vzdump_2010-05-25/Makefile0000644000000000000000000000664311376661437013700 0ustar rootroot# # Makefile to generate DEB, RPM and TGZ for vzdump # # possible targets: # # all: create DEB, RPM and TGZ packages # clean: cleanup # deb: create debian package # rpm: create rpm package # srpm: create src.rpm package # dist: create tgz package # install: install files VERSION=1.2 PACKAGE=vzdump PKGREL=6 #ARCH:=$(shell dpkg-architecture -qDEB_BUILD_ARCH) #RPMARCH:=$(shell rpm --eval %_build_arch) ARCH=all RPMARCH=noarch DESTDIR= PREFIX=/usr SBINDIR=${PREFIX}/sbin MANDIR=${PREFIX}/share/man PERLLIBDIR=${PREFIX}/share/perl5/PVE DOCDIR=${PREFIX}/share/doc MAN1DIR=${MANDIR}/man1/ DEB=${PACKAGE}_${VERSION}-${PKGREL}_${ARCH}.deb RPM=${PACKAGE}-${VERSION}-${PKGREL}.${RPMARCH}.rpm SRPM=${PACKAGE}-${VERSION}-${PKGREL}.src.rpm DISTDIR=$(PACKAGE)-$(VERSION) TGZ=${DISTDIR}.tar.gz RPMSRCDIR=$(shell rpm --eval %_sourcedir) RPMDIR=$(shell rpm --eval %_rpmdir) SRPMDIR=$(shell rpm --eval %_srcrpmdir) DISTFILES= \ ChangeLog \ TODO \ Makefile \ changelog.Debian \ control.in \ vzdump.spec.in \ hook-script.pl \ copyright \ VZDump.pm \ Plugin.pm \ OpenVZ.pm \ vzrestore \ vzdump PKGSOURCE= \ vzdump \ vzdump.1.gz \ vzrestore \ vzrestore.1.gz \ VZDump.pm \ Plugin.pm \ OpenVZ.pm \ control all: ${TGZ} ${DEB} ${RPM} control: control.in sed -e s/@@ARCH@@/${ARCH}/ -e s/@@VERSION@@/${VERSION}/ -e s/@@PKGRELEASE@@/${PKGREL}/ <$< >$@ vzdump.spec: vzdump.spec.in sed -e s/@@ARCH@@/${ARCH}/ -e s/@@VERSION@@/${VERSION}/ -e s/@@PKGRELEASE@@/${PKGREL}/ <$< >$@ .PHONY: install install: ${PKGSOURCE} install -d ${DESTDIR}${SBINDIR} install -m 0755 vzdump ${DESTDIR}${SBINDIR} install -m 0755 vzrestore ${DESTDIR}${SBINDIR} install -d ${DESTDIR}${MAN1DIR} install -m 0644 vzdump.1.gz ${DESTDIR}${MAN1DIR} install -m 0644 vzrestore.1.gz ${DESTDIR}${MAN1DIR} install -d ${DESTDIR}${PERLLIBDIR} install -m 0644 VZDump.pm ${DESTDIR}${PERLLIBDIR} install -d ${DESTDIR}${PERLLIBDIR}/VZDump install -m 0644 Plugin.pm ${DESTDIR}${PERLLIBDIR}/VZDump install -m 0644 OpenVZ.pm ${DESTDIR}${PERLLIBDIR}/VZDump .PHONY: deb deb ${DEB}: ${PKGSOURCE} ${DISTFILES} rm -rf debian mkdir debian make DESTDIR=debian install install -d -m 0755 debian/DEBIAN install -m 0644 control debian/DEBIAN install -D -m 0644 copyright debian/${DOCDIR}/${PACKAGE}/copyright install -D -m 0644 hook-script.pl debian/${DOCDIR}/${PACKAGE}/examples/hook-script.pl install -m 0644 changelog.Debian debian/${DOCDIR}/${PACKAGE}/ install -m 0644 ChangeLog debian/${DOCDIR}/${PACKAGE}/changelog gzip -9 debian/${DOCDIR}/${PACKAGE}/changelog.Debian gzip -9 debian/${DOCDIR}/${PACKAGE}/changelog dpkg-deb --build debian mv debian.deb ${DEB} rm -rf debian lintian ${DEB} %.1.gz: % rm -f $*.1.gz pod2man -n $* -s 1 -r ${VERSION} -c "Proxmox Documentation" <$* |gzip -c9 >$*.1.gz .PHONY: rpm rpm ${RPM}: ${TGZ} ${PACKAGE}.spec cp ${TGZ} ${RPMSRCDIR} rpmbuild -bb --nodeps --clean --rmsource ${PACKAGE}.spec mv ${RPMDIR}/${RPMARCH}/${RPM} ${RPM} .PHONY: srpm srpm ${SRPM}: ${TGZ} ${PACKAGE}.spec cp ${TGZ} ${RPMSRCDIR} rpmbuild -bs --nodeps --rmsource ${PACKAGE}.spec mv ${SRPMDIR}/${SRPM} ${SRPM} .PHONY: dist dist: ${TGZ} ${TGZ}: ${DISTFILES} make clean rm -rf ${TGZ} ${DISTDIR} mkdir ${DISTDIR} cp ${DISTFILES} ${DISTDIR} tar czvf ${TGZ} ${DISTDIR} rm -rf ${DISTDIR} .PHONY: clean clean: rm -rf debian *~ *.deb *.tar.gz *.rpm *.1.gz vzdump.spec control ${DISTDIR}vzdump_2010-05-25/OpenVZ.pm0000644000000000000000000002221311376661437013746 0ustar rootrootpackage PVE::VZDump::OpenVZ; # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH # # Copyright: vzdump is under GNU GPL, the GNU General Public License. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Dietmar Maurer use strict; use warnings; use File::Path; use File::Basename; use PVE::VZDump; use Sys::Hostname; use LockFile::Simple; use base qw (PVE::VZDump::Plugin); use constant SCRIPT_EXT => qw (start stop mount umount); use constant VZDIR => '/etc/vz'; my $remove_quotes = sub { my $str = shift; $str =~ s/^\s*\"?//; $str =~ s/\"?\s*$//; return $str; }; # read global vz.conf sub read_global_vz_config { local $/; my $res = { rootdir => '/vz/root/$VEID', # note '$VEID' is a place holder privatedir => '/vz/private/$VEID', # note '$VEID' is a place holder dumpdir => '/vz/dump', lockdir => '/var/lib/vz/lock', }; my $filename = VZDIR . "/vz.conf"; my $fh = IO::File->new ($filename, "r"); return $res if !$fh; my $data = <$fh> || ''; $fh->close(); if ($data =~ m/^\s*VE_PRIVATE=(.*)$/m) { my $dir = &$remove_quotes ($1); if ($dir !~ m/\$VEID/) { warn "VE_PRIVATE does not contain '\$VEID' ('$dir')\n"; } else { $res->{privatedir} = $dir; } } if ($data =~ m/^\s*VE_ROOT=(.*)$/m) { my $dir = &$remove_quotes ($1); if ($dir !~ m/\$VEID/) { warn "VE_ROOT does not contain '\$VEID' ('$dir')\n"; } else { $res->{rootdir} = $dir; } } if ($data =~ m/^\s*DUMPDIR=(.*)$/m) { my $dir = &$remove_quotes ($1); $dir =~ s|/\$VEID$||; $res->{dumpdir} = $dir; } if ($data =~ m/^\s*LOCKDIR=(.*)$/m) { my $dir = &$remove_quotes ($1); $res->{lockdir} = $dir; } return $res; } my $load_vz_conf = sub { my ($self, $vmid) = @_; local $/; my $conf = $self->{vmlist}->{$vmid}->{conffile}; my $fh = IO::File->new ($conf, "r") || die "unable to open config file '$conf'\n"; my $data = <$fh>; $fh->close(); my $dir; if ($data =~ m/^\s*VE_PRIVATE=(.*)$/m) { $dir = &$remove_quotes ($1); } else { $dir = $self->{privatedir}; } $dir =~ s/\$VEID/$vmid/; $self->{vmlist}->{$vmid}->{dir} = $dir; if ($data =~ m/^\s*HOSTNAME=(.*)/m) { $self->{vmlist}->{$vmid}->{hostname} = &$remove_quotes ($1); } else { $self->{vmlist}->{$vmid}->{hostname} = "VM $vmid"; } }; sub read_vz_list { my $vmlist = {}; my $dir = VZDIR . "/conf"; foreach my $conf (<$dir/*.conf>) { next if $conf !~ m|/(\d\d\d+)\.conf$|; my $vmid = $1; $vmlist->{$vmid}->{conffile} = $conf; } return $vmlist; } my $rsync_vm = sub { my ($self, $task, $from, $to, $text) = @_; $self->loginfo ("starting $text sync $from to $to"); my $starttime = time(); my $opts = $self->{vzdump}->{opts}; my $rsyncopts = "--stats -x --numeric-ids --bwlimit=$opts->{bwlimit}"; $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'"); my $delay = time () - $starttime; $self->loginfo ("$text sync finished ($delay seconds)"); }; sub new { my ($class, $vzdump) = @_; PVE::VZDump::check_bin ('vzctl'); my $self = bless read_global_vz_config (); $self->{vzdump} = $vzdump; $self->{vmlist} = read_vz_list (); return $self; }; sub type { return 'openvz'; } sub vm_status { my ($self, $vmid) = @_; my $status_text = $self->cmd ("vzctl status $vmid"); chomp $status_text; my $running = $status_text =~ m/running/ ? 1 : 0; return wantarray ? ($running, $status_text) : $running; } sub prepare { my ($self, $task, $vmid, $mode) = @_; $self->$load_vz_conf ($vmid); my $dir = $self->{vmlist}->{$vmid}->{dir}; my $diskinfo = { dir => $dir }; $task->{hostname} = $self->{vmlist}->{$vmid}->{hostname}; $task->{diskinfo} = $diskinfo; my $hostname = hostname(); if ($mode eq 'snapshot') { my $lvmmap = PVE::VZDump::get_lvm_mapping(); my ($srcdev, $lvmpath, $lvmvg, $lvmlv, $fstype) = PVE::VZDump::get_lvm_device ($dir, $lvmmap); my $targetdev = PVE::VZDump::get_lvm_device ($task->{dumpdir}, $lvmmap); die ("mode failure - unable to detect lvm volume group\n") if !$lvmvg; die ("mode failure - wrong lvm mount point '$lvmpath'\n") if $dir !~ m|/?$lvmpath/?|; die ("mode failure - unable to dump into snapshot (use option --dumpdir)\n") if $targetdev eq $srcdev; $diskinfo->{snapname} = "vzsnap-$hostname-0"; $diskinfo->{snapdev} = "/dev/$lvmvg/$diskinfo->{snapname}"; $diskinfo->{srcdev} = $srcdev; $diskinfo->{lvmvg} = $lvmvg; $diskinfo->{lvmlv} = $lvmlv; $diskinfo->{fstype} = $fstype; $diskinfo->{lvmpath} = $lvmpath; $diskinfo->{mountpoint} = "/mnt/vzsnap0"; $task->{snapdir} = $dir; $task->{snapdir} =~ s|/?$lvmpath/?|$diskinfo->{mountpoint}/|; } elsif ($mode eq 'suspend') { $task->{snapdir} = $task->{tmpdir}; } else { $task->{snapdir} = $dir; } } sub lock_vm { my ($self, $vmid) = @_; my $filename = "$self->{lockdir}/103.lck"; my $lockmgr = LockFile::Simple->make(-format => '%f', -autoclean => 1, -max => 30, -delay => 2, -stale => 1, -nfs => 0); $self->{lock} = $lockmgr->lock($filename) || die "can't lock VM $vmid\n"; } sub unlock_vm { my ($self, $vmid) = @_; $self->{lock}->release(); } sub copy_data_phase1 { my ($self, $task) = @_; $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first"); } sub stop_vm { my ($self, $task, $vmid) = @_; $self->cmd ("vzctl --skiplock stop $vmid"); } sub start_vm { my ($self, $task, $vmid) = @_; $self->cmd ("vzctl --skiplock start $vmid"); } sub suspend_vm { my ($self, $task, $vmid) = @_; $self->cmd ("vzctl --skiplock chkpnt $vmid --suspend"); } sub snapshot { my ($self, $task) = @_; my $opts = $self->{vzdump}->{opts}; my $di = $task->{diskinfo}; mkpath $di->{mountpoint}; # create mount point for lvm snapshot if (-b $di->{snapdev}) { $self->loginfo ("trying to remove stale snapshot '$di->{snapdev}'"); $self->cmd_noerr ("umount $di->{mountpoint}"); $self->cmd_noerr ("lvremove -f $di->{snapdev}"); } $self->loginfo ("creating lvm snapshot of $di->{srcdev} ('$di->{snapdev}')"); $task->{cleanup}->{lvm_snapshot} = 1; $self->cmd ("lvcreate --size $opts->{size}M --snapshot" . " --name $di->{snapname} /dev/$di->{lvmvg}/$di->{lvmlv}"); my $mopts = $di->{fstype} eq 'xfs' ? "-o nouuid" : ''; $task->{cleanup}->{snapshot_mount} = 1; $self->cmd ("mount -t $di->{fstype} $mopts $di->{snapdev} $di->{mountpoint}"); } sub copy_data_phase2 { my ($self, $task) = @_; $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final"); } sub resume_vm { my ($self, $task, $vmid) = @_; $self->cmd ("vzctl --skiplock chkpnt $vmid --resume"); } sub assemble { my ($self, $task, $vmid) = @_; my $conffile = $self->{vmlist}->{$vmid}->{conffile}; my $dir = $task->{snapdir}; $task->{cleanup}->{etc_vzdump} = 1; mkpath "$dir/etc/vzdump/"; $self->cmd ("cp '$conffile' '$dir/etc/vzdump/vps.conf'"); my $cfgdir = dirname ($conffile); foreach my $s (SCRIPT_EXT) { my $fn = "$cfgdir/$vmid.$s"; $self->cmd ("cp '$fn' '$dir/etc/vzdump/vps.$s'") if -f $fn; } } sub archive { my ($self, $task, $vmid, $filename) = @_; my $findexcl = $self->{vzdump}->{findexcl}; my $findargs = join (' ', @$findexcl) . ' -print0'; my $opts = $self->{vzdump}->{opts}; my $srcdir = $self->{vmlist}->{$vmid}->{dir}; my $snapdir = $task->{snapdir}; my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream my $zflag = $opts->{compress} ? 'z' : ''; my $taropts = "--totals --sparse --numeric-owner --no-recursion --ignore-failed-read --one-file-system"; if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) { $taropts .= " --remove-files"; # try to save space } my $out = ">$filename"; $out = "|cstream -t $bwl $out" if $opts->{bwlimit}; $self->cmd ("(cd $snapdir;find . $findargs|" . "tar c${zflag}pf - $taropts --null -T - $out )"); } sub cleanup { my ($self, $task, $vmid) = @_; my $di = $task->{diskinfo}; if ($task->{cleanup}->{snapshot_mount}) { $self->cmd_noerr ("umount $di->{mountpoint}"); } if ($task->{cleanup}->{lvm_snapshot}) { $self->cmd_noerr ("lvremove -f $di->{snapdev}") if -b $di->{snapdev}; } if ($task->{cleanup}->{etc_vzdump}) { my $dir = "$task->{snapdir}/etc/vzdump"; eval { rmtree $dir if -d $dir; }; $self->logerr ($@) if $@; } } 1; vzdump_2010-05-25/control.in0000644000000000000000000000057711376661437014250 0ustar rootrootPackage: vzdump Version: @@VERSION@@-@@PKGRELEASE@@ Section: admin Priority: optional Architecture: @@ARCH@@ Depends: perl (>= 5.6.0-16), vzctl, cstream, rsync, liblockfile-simple-perl, exim4 | mail-transport-agent Maintainer: Proxmox Support Team Description: OpenVZ backup scripts This package contains the vzdump script to backup and restore openvz images. vzdump_2010-05-25/Plugin.pm0000644000000000000000000000572511376661437014034 0ustar rootrootpackage PVE::VZDump::Plugin; # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH # # Copyright: vzdump is under GNU GPL, the GNU General Public License. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Dietmar Maurer use strict; use warnings; sub set_logfd { my ($self, $logfd) = @_; $self->{logfd} = $logfd; } sub cmd { my ($self, $cmdstr, $input) = @_; return PVE::VZDump::run_command ($self->{logfd}, $cmdstr, $input); } sub cmd_noerr { my ($self, $cmdstr, $input) = @_; my $res; eval { $res = $self->cmd ($cmdstr, $input); }; $self->logerr ($@) if $@; return $res; } sub loginfo { my ($self, $msg) = @_; PVE::VZDump::debugmsg ('info', $msg, $self->{logfd}, 0); } sub logerr { my ($self, $msg) = @_; PVE::VZDump::debugmsg ('err', $msg, $self->{logfd}, 0); } sub type { return 'unknown'; }; sub vmlist { my ($self) = @_; return [ keys %{$self->{vmlist}} ] if $self->{vmlist}; return []; } sub vm_status { my ($self, $vmid) = @_; die "internal error"; # implement in subclass } sub prepare { my ($self, $task, $vmid, $mode) = @_; die "internal error"; # implement in subclass } sub lock_vm { my ($self, $vmid) = @_; die "internal error"; # implement in subclass } sub unlock_vm { my ($self, $vmid) = @_; die "internal error"; # implement in subclass } sub stop_vm { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub start_vm { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub suspend_vm { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub resume_vm { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub snapshot { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub copy_data_phase2 { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub assemble { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } sub archive { my ($self, $task, $vmid, $filename) = @_; die "internal error"; # implement in subclass } sub cleanup { my ($self, $task, $vmid) = @_; die "internal error"; # implement in subclass } 1;