mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-05-26 02:23:06 +00:00
313 lines
7.7 KiB
Perl
313 lines
7.7 KiB
Perl
package PVE::VZDump::OpenVZ;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use File::Path;
|
|
use File::Basename;
|
|
use PVE::INotify;
|
|
use PVE::VZDump;
|
|
use PVE::OpenVZ;
|
|
|
|
use base qw (PVE::VZDump::Plugin);
|
|
|
|
my $load_vz_conf = sub {
|
|
my ($self, $vmid) = @_;
|
|
|
|
my $conf = PVE::OpenVZ::load_config($vmid);
|
|
|
|
my $dir = $self->{privatedir};
|
|
if ($conf->{ve_private} && $conf->{ve_private}->{value}) {
|
|
$dir = $conf->{ve_private}->{value};
|
|
}
|
|
$dir =~ s/\$VEID/$vmid/;
|
|
$self->{vmlist}->{$vmid}->{dir} = $dir;
|
|
|
|
my $hostname = "CT $vmid";
|
|
if ($conf->{hostname} && $conf->{hostname}->{value}) {
|
|
$hostname = $conf->{hostname}->{value};
|
|
}
|
|
$self->{vmlist}->{$vmid}->{hostname} = $hostname;
|
|
};
|
|
|
|
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 PVE::OpenVZ::read_global_vz_config ();
|
|
|
|
$self->{vzdump} = $vzdump;
|
|
|
|
$self->{vmlist} = PVE::OpenVZ::config_list();
|
|
|
|
return $self;
|
|
};
|
|
|
|
sub type {
|
|
return 'openvz';
|
|
}
|
|
|
|
sub vm_status {
|
|
my ($self, $vmid) = @_;
|
|
|
|
my $status_text = '';
|
|
$self->cmd ("vzctl status $vmid", outfunc => sub {$status_text .= shift; });
|
|
chomp $status_text;
|
|
|
|
my $running = $status_text =~ m/running/ ? 1 : 0;
|
|
|
|
return wantarray ? ($running, $running ? 'running' : 'stopped') : $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 = PVE::INotify::nodename();
|
|
|
|
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 = PVE::OpenVZ::create_lock_manager();
|
|
|
|
$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 -n -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 = PVE::OpenVZ::config_file($vmid);
|
|
|
|
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 (PVE::OpenVZ::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, $comp) = @_;
|
|
|
|
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 $taropts = "--totals --sparse --numeric-owner --no-recursion --one-file-system";
|
|
|
|
# note: --remove-files does not work because we do not
|
|
# backup all files (filters). tar complains:
|
|
# Cannot rmdir: Directory not empty
|
|
# we we disable this optimization for now
|
|
#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 cpf - $taropts --null -T -";
|
|
my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
|
|
$cmd .= "|cstream -t $bwl" if $opts->{bwlimit};
|
|
$cmd .= "|$comp" if $comp;
|
|
|
|
$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}) {
|
|
# Note: sleep to avoid 'device is busy' message.
|
|
# Seems Kernel need some time to cleanup open file list,
|
|
# fir example when we stop the tar with kill (stop task)
|
|
sleep(1);
|
|
$self->cmd_noerr ("umount $di->{mountpoint}");
|
|
}
|
|
|
|
if ($task->{cleanup}->{lvm_snapshot}) {
|
|
# loop, because we often get 'LV in use: not deactivating'
|
|
# we use run_command() because we do not want to log errors here
|
|
my $wait = 1;
|
|
while(-b $di->{snapdev}) {
|
|
eval {
|
|
my $cmd = ['lvremove', '-f', $di->{snapdev}];
|
|
PVE::Tools::run_command($cmd, outfunc => sub {}, errfunc => sub {});
|
|
};
|
|
last if !$@;
|
|
if ($wait >= 64) {
|
|
$self->logerr($@);
|
|
last;
|
|
}
|
|
$self->loginfo("lvremove failed - trying again in $wait seconds") if $wait >= 8;
|
|
sleep($wait);
|
|
$wait = $wait*2;
|
|
}
|
|
|
|
}
|
|
|
|
if ($task->{cleanup}->{etc_vzdump}) {
|
|
my $dir = "$task->{snapdir}/etc/vzdump";
|
|
eval { rmtree $dir if -d $dir; };
|
|
$self->logerr ($@) if $@;
|
|
}
|
|
|
|
}
|
|
|
|
1;
|