mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-05-17 20:09:06 +00:00
moved vzdump sources to this packages
We want to share code and use new PVE 2 framework features. So it is no longer possible to distribute this a separate packages.
This commit is contained in:
parent
ee9fe4959f
commit
aaeeeebe40
@ -1,6 +1,6 @@
|
||||
include ../defines.mk
|
||||
|
||||
SUBDIRS=API2
|
||||
SUBDIRS=API2 VZDump
|
||||
|
||||
PERLSOURCE = \
|
||||
API2.pm \
|
||||
@ -8,6 +8,7 @@ PERLSOURCE = \
|
||||
APIDaemon.pm \
|
||||
REST.pm \
|
||||
OpenVZ.pm \
|
||||
VZDump.pm \
|
||||
APLInfo.pm
|
||||
|
||||
all: pvecfg.pm ${SUBDIRS}
|
||||
|
1141
PVE/VZDump.pm
Normal file
1141
PVE/VZDump.pm
Normal file
File diff suppressed because it is too large
Load Diff
19
PVE/VZDump/Makefile
Normal file
19
PVE/VZDump/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
include ../../defines.mk
|
||||
|
||||
PERLSOURCE = \
|
||||
OpenVZ.pm \
|
||||
Plugin.pm
|
||||
|
||||
all:
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf *~
|
||||
|
||||
.PHONY: install
|
||||
install: ${PERLSOURCE}
|
||||
install -d ${PERLLIBDIR}/PVE/VZDump
|
||||
install -m 0644 ${PERLSOURCE} ${PERLLIBDIR}/PVE/VZDump
|
400
PVE/VZDump/OpenVZ.pm
Normal file
400
PVE/VZDump/OpenVZ.pm
Normal file
@ -0,0 +1,400 @@
|
||||
package 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 <dietmar@proxmox.com>
|
||||
|
||||
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";
|
||||
|
||||
$rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $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");
|
||||
}
|
||||
|
||||
# we use --skiplock for vzctl because we have already locked the VM
|
||||
# by calling lock_vm()
|
||||
|
||||
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 $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 $cmd = "(";
|
||||
$cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|";
|
||||
$cmd .= "tar c${zflag}pf - $taropts --null -T -";
|
||||
|
||||
if ($opts->{bwlimit}) {
|
||||
my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
|
||||
$cmd .= "|cstream -t $bwl";
|
||||
}
|
||||
|
||||
$cmd .= ")";
|
||||
|
||||
if ($opts->{stdout}) {
|
||||
$self->cmd ($cmd, output => ">&=" . fileno($opts->{stdout}));
|
||||
} else {
|
||||
$self->cmd ("$cmd >$filename");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
149
PVE/VZDump/Plugin.pm
Normal file
149
PVE/VZDump/Plugin.pm
Normal file
@ -0,0 +1,149 @@
|
||||
package 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 <dietmar@proxmox.com>
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub set_logfd {
|
||||
my ($self, $logfd) = @_;
|
||||
|
||||
$self->{logfd} = $logfd;
|
||||
}
|
||||
|
||||
sub cmd {
|
||||
my ($self, $cmdstr, %param) = @_;
|
||||
|
||||
return PVE::VZDump::run_command($self->{logfd}, $cmdstr, %param);
|
||||
}
|
||||
|
||||
sub cmd_noerr {
|
||||
my ($self, $cmdstr, %param) = @_;
|
||||
|
||||
my $res;
|
||||
eval { $res = $self->cmd($cmdstr, %param); };
|
||||
$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;
|
@ -3,6 +3,8 @@ include ../defines.mk
|
||||
SUBDIRS = init.d cron test
|
||||
|
||||
SCRIPTS = \
|
||||
vzdump \
|
||||
vzrestore \
|
||||
pvestatd \
|
||||
pvesh \
|
||||
pveam \
|
||||
|
399
bin/vzdump
Executable file
399
bin/vzdump
Executable file
@ -0,0 +1,399 @@
|
||||
#!/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 <dietmar@proxmox.com>
|
||||
#
|
||||
|
||||
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',
|
||||
'ionice=i',
|
||||
'lockwait=i',
|
||||
'stopwait=i',
|
||||
'tmpdir=s',
|
||||
'dumpdir=s',
|
||||
'maxfiles=i',
|
||||
'script=s',
|
||||
'storage=s',
|
||||
'stdout',
|
||||
);
|
||||
|
||||
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--stdout write to stdout, not to a file\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";
|
||||
print STDERR "\t--ionice PRI\t\tset ionice priority (0-8)\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);
|
||||
}
|
||||
|
||||
open STDOUT, '>/dev/null' if $opts->{quiet} && !$opts->{stdout};
|
||||
open STDERR, '>/dev/null' if $opts->{quiet};
|
||||
|
||||
if ($opts->{stdout}) {
|
||||
|
||||
open my $saved_stdout, ">&STDOUT"
|
||||
|| die "can't dup STDOUT: $!\n";
|
||||
|
||||
open STDOUT, '>&STDERR' ||
|
||||
die "unable to redirect STDOUT: $!\n";
|
||||
|
||||
$opts->{stdout} = $saved_stdout;
|
||||
|
||||
die "you can only backup a single VM with option --stdout\n"
|
||||
if scalar(@ARGV) != 1;
|
||||
}
|
||||
|
||||
$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 {
|
||||
if (defined($opts->{ionice})) {
|
||||
if ($opts->{ionice} > 7) {
|
||||
PVE::VZDump::run_command (undef, "ionice -c3 -p $$");
|
||||
} else {
|
||||
PVE::VZDump::run_command (undef, "ionice -c2 -n$opts->{ionice} -p $$");
|
||||
}
|
||||
}
|
||||
$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 | <VMID>]
|
||||
|
||||
--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
|
||||
|
||||
--stdout write to stdout, not to a file. You can only
|
||||
backup a single VM when using this mode.
|
||||
|
||||
--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
|
||||
|
||||
--ionice PRI set ionice priority (0-8). default is 7 (lowest 'best
|
||||
effort' priority). Value 8 uses the ionice
|
||||
'idle' scheduling class.
|
||||
|
||||
--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<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 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 <dietmar@proxmox.com>
|
||||
|
||||
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.
|
||||
|
182
bin/vzrestore
Executable file
182
bin/vzrestore
Executable file
@ -0,0 +1,182 @@
|
||||
#!/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 <dietmar@proxmox.com>
|
||||
#
|
||||
|
||||
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] <ARCHIVE> <VMID>\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";
|
||||
|
||||
if ($archive eq '-') {
|
||||
debugmsg ('info', "extracting archive from STDIN");
|
||||
run_command ($cmd, input => "<&STDIN");
|
||||
} else {
|
||||
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 -r -e 's/VE_ROOT=.*/VE_ROOT=\\\"$qroot\\\"/' -e 's/VE_PRIVATE=.*/VE_PRIVATE=\\\"$qprivate\\\"/' -e 's/host_ifname=veth[0-9]+\./host_ifname=veth${vmid}./' <'$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();
|
||||
|
||||
if ($archive ne '-') {
|
||||
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 <archive> <VMID>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Restore the OpenVZ vzdump backup <archive> to virtual machine <VMID>.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
vzdump(1) qmrestore(1)
|
4
debian/control.in
vendored
4
debian/control.in
vendored
@ -4,7 +4,9 @@ Section: admin
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: perl5, libtimedate-perl, apache2-mpm-prefork, libauthen-pam-perl, libintl-perl, rsync, libapache2-request-perl, libjson-perl, libdigest-sha1-perl, liblockfile-simple-perl, vncterm, qemu-server (>= 1.1-1), libwww-perl, wget, libnet-dns-perl, vlan, ifenslave-2.6 (>= 1.1.0-10), liblinux-inotify2-perl, debconf (>= 0.5) | debconf-2.0, netcat-traditional, pve-cluster, libpve-common-perl, libpve-storage-perl, libterm-readline-gnu-perl, libpve-access-control, libio-socket-ssl-perl, libfilesys-df-perl, libfile-readbackwards-perl, libfile-sync-perl, redhat-cluster-pve
|
||||
Conflicts: netcat-openbsd
|
||||
Conflicts: netcat-openbsd, vzdump
|
||||
Replaces: vzdump
|
||||
Provides: vzdump
|
||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||
Description: The Proxmox Virtual Environment
|
||||
This package contains the Proxmox Virtual Environment management tools.
|
||||
|
Loading…
Reference in New Issue
Block a user