use strict;
use warnings;
-use File::Path;
+
use File::Basename;
-use PVE::INotify;
+use File::Path;
+
use PVE::Cluster qw(cfs_read_file);
-use PVE::Storage;
-use PVE::VZDump;
+use PVE::INotify;
+use PVE::LXC::Config;
use PVE::LXC;
+use PVE::Storage;
use PVE::Tools;
+use PVE::VZDump;
use base qw (PVE::VZDump::Plugin);
my $default_mount_point = "/mnt/vzsnap0";
my $rsync_vm = sub {
- my ($self, $task, $to, $text) = @_;
+ my ($self, $task, $to, $text, $first) = @_;
my $disks = $task->{disks};
my $from = $disks->[0]->{dir} . '/';
my @xattr = $task->{no_xattrs} ? () : ('-X', '-A');
my $rsync = ['rsync', '--stats', @xattr, '--numeric-ids',
- '-aH', '--delete', '--no-whole-file', '--inplace',
+ '-aH', '--delete', '--no-whole-file',
+ ($first ? '--sparse' : '--inplace'),
'--one-file-system', '--relative'];
push @$rsync, "--bwlimit=$opts->{bwlimit}" if $opts->{bwlimit};
push @$rsync, map { "--exclude=$_" } @{$self->{vzdump}->{findexcl}};
sub new {
my ($class, $vzdump) = @_;
-
+
PVE::VZDump::check_bin('lxc-stop');
PVE::VZDump::check_bin('lxc-start');
PVE::VZDump::check_bin('lxc-freeze');
$self->{vzdump} = $vzdump;
$self->{storecfg} = PVE::Storage::config();
-
+
$self->{vmlist} = PVE::LXC::config_list();
return $self;
my ($self, $vmid) = @_;
my $running = PVE::LXC::check_running($vmid) ? 1 : 0;
-
- return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
+
+ return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
}
my $check_mountpoint_empty = sub {
my ($mountpoint) = @_;
- die "mountpoint '$mountpoint' is not a directory\n" if ! -d $mountpoint;
+ die "mount point '$mountpoint' is not a directory\n" if ! -d $mountpoint;
PVE::Tools::dir_glob_foreach($mountpoint, qr/.*/, sub {
my $entry = shift;
return if $entry eq '.' || $entry eq '..';
- die "mountpoint '$mountpoint' not empty\n";
+ die "mount point '$mountpoint' not empty\n";
});
};
my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::Config->load_config($vmid);
my $storage_cfg = $self->{storecfg};
+ $self->loginfo("CT Name: $conf->{hostname}")
+ if defined($conf->{hostname});
+
my $running = PVE::LXC::check_running($vmid);
my $disks = $task->{disks} = [];
my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
$task->{userns_cmd} = PVE::LXC::userns_command($id_map);
+ $task->{rootuid} = $rootuid;
+ $task->{rootgid} = $rootgid;
my $volids = $task->{volids} = [];
- PVE::LXC::Config->foreach_mountpoint($conf, sub {
+ PVE::LXC::Config->foreach_volume($conf, sub {
my ($name, $data) = @_;
my $volid = $data->{volume};
my $mount = $data->{mp};
return if !$volid || !$mount;
- if ($name ne 'rootfs' && !$data->{backup}) {
+ if (!PVE::LXC::Config->mountpoint_backup_enabled($name, $data)) {
push @$exclude_dirs, $mount;
+ $self->loginfo("excluding $type mount point $name ('$mount') from backup");
return;
}
+ $data->{name} = $name;
+
+ # immutable raw base images need RO mount
+ if ($conf->{template} && !defined($data->{ro})) {
+ $data->{ro} = 1;
+ }
push @$disks, $data;
push @$volids, $volid
if $type eq 'volume';
my $rootdir = $default_mount_point;
mkpath $rootdir;
&$check_mountpoint_empty($rootdir);
- PVE::Storage::activate_volumes($storage_cfg, $volids);
} elsif ($mode eq 'suspend') {
my $pid = PVE::LXC::find_lxc_pid($vmid);
foreach my $disk (@$disks) {
# If we perform mount operations, let's unshare the mount namespace
# to not influence the running host.
PVE::Tools::unshare(PVE::Tools::CLONE_NEWNS);
- PVE::Tools::run_command(['mount', '--make-rprivate', '/']);
+ PVE::Tools::run_command(['mount', '--make-rslave', '/']);
}
}
$self->lock_vm($vmid);
});
$task->{cleanup}->{remove_snapshot} = 1;
-
+
# reload config
my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::Config->load_config($vmid);
die "unable to read vzdump snapshot config - internal error"
PVE::Storage::activate_volumes($storage_cfg, $volids, 'vzdump');
foreach my $disk (@$disks) {
$disk->{dir} = "${rootdir}$disk->{mp}";
- PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, 'vzdump');
+ PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, 'vzdump', $task->{rootuid}, $task->{rootgid});
}
$task->{snapdir} = $rootdir;
if (my $mntinfo = PVE::VZDump::get_mount_info($task->{snapdir})) {
if ($mntinfo->{fstype} =~ /^nfs4?/) {
- warn "temporary directory is on NFS, disabling xattr and acl support"
- . ", consider configuring a local tmpdir via /etc/vzdump.conf\n";
+ $self->loginfo(
+ "temporary directory is on NFS, disabling xattr and acl"
+ ." support, consider configuring a local tmpdir via"
+ ." /etc/vzdump.conf\n");
$task->{no_xattrs} = 1;
}
}
- $self->$rsync_vm($task, $task->{snapdir}, "first");
+ $self->$rsync_vm($task, $task->{snapdir}, "first", 1);
}
sub copy_data_phase2 {
my ($self, $task) = @_;
- $self->$rsync_vm($task, $task->{snapdir}, "final");
+ $self->$rsync_vm($task, $task->{snapdir}, "final", 0);
}
sub stop_vm {
my ($self, $task, $vmid) = @_;
- $self->cmd("lxc-stop -n $vmid");
+ my $opts = $self->{vzdump}->{opts};
+ my $timeout = $opts->{stopwait} * 60;
- # make sure container is stopped
- $self->cmd("lxc-wait -n $vmid -s STOPPED");
+ PVE::LXC::vm_stop($vmid, 0, $timeout);
}
sub start_vm {
my ($self, $task, $vmid) = @_;
- $self->cmd ("lxc-start -n $vmid");
+ $self->cmd(['systemctl', 'start', "pve-container\@$vmid"]);
}
sub suspend_vm {
sub assemble {
my ($self, $task, $vmid) = @_;
- my $tmpdir = $task->{tmpdir};
-
- mkpath "$tmpdir/etc/vzdump/";
+ my $opts = $self->{vzdump}->{opts};
my $conf = PVE::LXC::Config->load_config($vmid);
delete $conf->{lock};
delete $conf->{snapshots};
- delete $conf->{'pve.parent'};
+ delete $conf->{parent};
+ delete $conf->{pending};
+
+ my $tmpdir = $task->{tmpdir};
+
+ mkpath "$tmpdir/etc/vzdump/";
PVE::Tools::file_set_contents("$tmpdir/etc/vzdump/pct.conf", PVE::LXC::Config::write_pct_config("/lxc/$vmid.conf", $conf));
my $firewall ="/etc/pve/firewall/$vmid.fw";
- if (-e $firewall) {
- PVE::Tools::file_copy($firewall, "$tmpdir/etc/vzdump/pct.fw");
+ my $fwconftmp = "$tmpdir/etc/vzdump/pct.fw";
+
+ if ($opts->{scfg}->{type} eq 'pbs') {
+ # fixme: do not store pct.conf and fw.conf into $tmpdir
+ if (-e $firewall) {
+ PVE::Tools::file_copy($firewall, $fwconftmp);
+ }
+ } else {
+ if (-e $firewall) {
+ PVE::Tools::file_copy($firewall, $fwconftmp);
+ } else {
+ PVE::Tools::file_set_contents($fwconftmp, '');
+ }
$task->{fw} = 1;
}
}
my @sources;
if ($task->{mode} eq 'stop') {
- my $rootdir = $default_mount_point;
my $storage_cfg = $self->{storecfg};
+
+ PVE::Storage::activate_volumes($storage_cfg, $task->{volids});
+
+ my $rootdir = $default_mount_point;
foreach my $disk (@$disks) {
$disk->{dir} = "${rootdir}$disk->{mp}";
- PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg);
+ PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, undef, $task->{rootuid}, $task->{rootgid});
# add every enabled mountpoint (since we use --one-file-system)
# mp already starts with a / so we only need to add the dot
push @sources, ".$disk->{mp}";
my $tmpdir = $task->{tmpdir};
my $userns_cmd = $task->{userns_cmd};
- my $tar = [@$userns_cmd, 'tar', 'cpf', '-', '--totals',
- @$PVE::LXC::COMMON_TAR_FLAGS,
- '--one-file-system', '--warning=no-file-ignored'];
-
- # note: --remove-files does not work because we do not
- # backup all files (filters). tar complains:
- # Cannot rmdir: Directory not empty
- # we disable this optimization for now
- #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
- # push @$tar, "--remove-files"; # try to save space
- #}
-
- # The directory parameter can give an alternative directory as source.
- # the second parameter gives the structure in the tar.
- push @$tar, "--directory=$tmpdir", './etc/vzdump/pct.conf';
- push @$tar, "./etc/vzdump/pct.fw" if $task->{fw};
- push @$tar, "--directory=$snapdir";
- push @$tar, '--no-anchored', '--exclude=lost+found' if $userns_cmd;
- push @$tar, '--anchored';
- push @$tar, map { "--exclude=.$_" } @{$self->{vzdump}->{findexcl}};
-
- push @$tar, @sources;
-
- my $cmd = [ $tar ];
-
- my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
- push @$cmd, [ 'cstream', '-t', $bwl ] if $opts->{bwlimit};
- push @$cmd, [ split(/\s+/, $comp) ] if $comp;
-
- if ($opts->{stdout}) {
- push @{$cmd->[-1]}, \(">&" . fileno($opts->{stdout}));
+
+ if ($opts->{scfg}->{type} eq 'pbs') {
+
+ my $rootdir = $default_mount_point;
+ my $param = [];
+
+ push @$param, "pct.conf:$tmpdir/etc/vzdump/pct.conf";
+
+ my $fw_conf = "$tmpdir/etc/vzdump/pct.fw";
+ if (-f $fw_conf) {
+ push @$param, "fw.conf:$fw_conf";
+ }
+
+ push @$param, "root.pxar:$rootdir";
+
+ foreach my $disk (@$disks) {
+ push @$param, '--include-dev', $disk->{dir};
+ }
+
+ push @$param, '--skip-lost-and-found' if $userns_cmd;
+
+ push @$param, '--backup-type', 'ct';
+ push @$param, '--backup-id', $vmid;
+ push @$param, '--backup-time', $task->{backup_time};
+
+ my $logfunc = sub { my $line = shift; $self->loginfo($line); };
+ PVE::Storage::PBSPlugin::run_raw_client_cmd(
+ $opts->{scfg}, $opts->{storage}, 'backup', $param,
+ logfunc => $logfunc, userns_cmd => $userns_cmd);
+
} else {
- push @{$cmd->[-1]}, \(">" . PVE::Tools::shellquote($filename));
+
+ my $tar = [@$userns_cmd, 'tar', 'cpf', '-', '--totals',
+ @PVE::Storage::Plugin::COMMON_TAR_FLAGS,
+ '--one-file-system', '--warning=no-file-ignored'];
+
+ # note: --remove-files does not work because we do not
+ # backup all files (filters). tar complains:
+ # Cannot rmdir: Directory not empty
+ # we disable this optimization for now
+ #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
+ # push @$tar, "--remove-files"; # try to save space
+ #}
+
+ # The directory parameter can give an alternative directory as source.
+ # the second parameter gives the structure in the tar.
+ push @$tar, "--directory=$tmpdir", './etc/vzdump/pct.conf';
+ push @$tar, "./etc/vzdump/pct.fw" if $task->{fw};
+ push @$tar, "--directory=$snapdir";
+ push @$tar, '--no-anchored', '--exclude=lost+found' if $userns_cmd;
+ push @$tar, '--anchored';
+ push @$tar, map { "--exclude=.$_" } @{$self->{vzdump}->{findexcl}};
+
+ push @$tar, @sources;
+
+ my $cmd = [ $tar ];
+
+ my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
+ push @$cmd, [ 'cstream', '-t', $bwl ] if $opts->{bwlimit};
+ push @$cmd, [ split(/\s+/, $comp) ] if $comp;
+
+ if ($opts->{stdout}) {
+ $self->cmd($cmd, output => ">&" . fileno($opts->{stdout}));
+ } else {
+ push @{$cmd->[-1]}, \(">" . PVE::Tools::shellquote($filename));
+ $self->cmd($cmd);
+ }
}
- $self->cmd($cmd);
}
sub cleanup {