# with ovz awareness to improve backup and restore efficiency and features.
# FIXME: signal handling to clean up mount point and snapshot on termination
-# FIXME: saveConfigs and restoreConfigs aren't being used yet
use strict;
use Socket;
use Proc::PID::File;
# Various constants
-my @HNS = ('pe18001.titaniummirror.com', 'pe18002.titaniummirror.com');
my @script_ext = qw(start stop mount umount);
my @velist = ();
my $pidfile = "/tmp/".basename($0).".pid";
my $vzsnap = 'vzsnap'; # Mount point and lv names. Mount is relative to /.
my $snapsize = '1g';
+my $hnlistFile = "/etc/backuppc/".basename($0).".hnlist";
+my $velistFile = $ENV{HOME}."/log/".basename($0).".velist";
sub cmdExecOrEval
{
- my($cmd, @args) = @_;
-
- if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
- $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
- eval($cmd);
- print(STDERR "Perl code fragment for exec shouldn't return!!\n");
- exit(1);
- } else {
- $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
- alarm(0);
- $cmd = [map { m/(.*)/ } @$cmd]; # untaint
- #
- # force list-form of exec(), ie: no shell even for 1 arg
- #
- exec { $cmd->[0] } @$cmd;
- print(STDERR "Exec failed for @$cmd\n");
- exit(1);
- }
+ my($cmd, @args) = @_;
+
+ if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
+ $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
+ eval($cmd);
+ print(STDERR "Perl code fragment for exec shouldn't return!!\n");
+ exit(1);
+ } else {
+ $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
+ alarm(0);
+ $cmd = [map { m/(.*)/ } @$cmd]; # untaint
+ #
+ # force list-form of exec(), ie: no shell even for 1 arg
+ #
+ exec { $cmd->[0] } @$cmd;
+ print(STDERR "Exec failed for @$cmd\n");
+ exit(1);
+ }
}
# FIXME: this guy sometimes needs to use /dev/null as stdin, stdout and stderr
# so that it doesn't pollute the stream back to the backup server.
sub cmdSystemOrEval
{
- my($cmd, @args) = @_;
-
- $? = 0;
- $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
- if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
- eval($cmd);
- } else {
- system($cmd);
- }
+ my($cmd, @args) = @_;
+
+ $? = 0;
+ $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
+ if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
+ eval($cmd);
+ } else {
+ system($cmd);
+ }
}
# To be called by the BackupPC server in a preuser script. These are the
# client invocation -- will cause the backup to hang indefinitely.
sub refreshConfig()
{
- # Write the VEs on all HNs to a config file on the BackupPC server for
- # later use.
- open my $out, ">/etc/backuppc/vzlist_bpc" ||
- die "Cannot write to /etc/backuppc/vzlist_bpc";
- foreach my $hn (@HNS) {
- open my $fh, "ssh -l root $hn vzlist -a |" ||
- die "Can run remote vzlist command";
- while (<$fh>) {
- chomp;
- my ($veid, $junk, $running, $junk, $hostname) = split(' ');
- if ($veid =~ /[0-9]+/ && $hostname ne '-') {
- if ($running eq "running") {
- $running = 1;
- } else {
- $running = 0;
- }
- print $out "$hostname,$veid,$hn,$running\n";
- }
+ # Write the VEs on all HNs to a config file on the BackupPC server for
+ # later use.
+ my @HNS = ();
+ open my $cfg, "<$hnlistFile" || die "Cannot read $hnlistFile";
+ while (<$cfg>) {
+ chomp;
+ push(@HNS, split(' ')) if (! /^#/);
+ }
+ close($cfg);
+ die "No HNs defined in $hnlistFile" if ($#HNS < 0);
+
+ open my $out, ">$velistFile" || die "Cannot write to $velistFile";
+ foreach my $hn (@HNS) {
+ open my $fh, "ssh -l root $hn vzlist -a |" ||
+ die "Can run remote vzlist command";
+ while (<$fh>) {
+ chomp;
+ my ($veid, $junk, $running, $junk, $hostname) = split(' ');
+ if ($veid =~ /[0-9]+/ && $hostname ne '-') {
+ if ($running eq "running") {
+ $running = 1;
+ } else {
+ $running = 0;
}
- close($fh);
- }
- close($out);
-
- # Copy the script to the HNs
- my $remoteCmd = "/usr/bin/".basename($0);
- foreach my $hn (@HNS) {
- my $cmd = "rsync -aq -e ssh $0 root\@$hn:$remoteCmd";
- #print "Executing '$cmd'\n";
- cmdSystemOrEval($cmd);
+ print $out "$hostname,$veid,$hn,$running\n";
+ }
}
+ close($fh);
+ }
+ close($out);
+
+ # Copy the script to the HNs
+ my $remoteCmd = "/usr/bin/".basename($0);
+ foreach my $hn (@HNS) {
+ my $cmd = "rsync -aq -e ssh $0 root\@$hn:$remoteCmd";
+ #print "Executing '$cmd'\n";
+ cmdSystemOrEval($cmd);
+ }
}
# For use on the BackupPC server.
sub loadVeList()
{
- open my $fh, "</etc/backuppc/vzlist_bpc" ||
- die "Cannot read from /etc/backuppc/vzlist_bpc. Set a preuser script.";
-
- while (<$fh>) {
- chomp;
- my ($hostname, $veid, $host, $running) = split(',');
- push(@velist, {
- "hostname" => $hostname,
- "VEID" => $veid,
- "HN" => $host,
- "running" => $running
- });
- }
- close($fh);
+ open my $fh, "<$velistFile" ||
+ die "Cannot read from $velistFile. Set a preuser script.";
+
+ while (<$fh>) {
+ chomp;
+ my ($hostname, $veid, $host, $running) = split(',');
+ push(@velist, {
+ "hostname" => $hostname,
+ "VEID" => $veid,
+ "HN" => $host,
+ "running" => $running
+ });
+ }
+ close($fh);
}
-# For use on the HN
+# For use on the HN. Derive the VE record from its VEID.
sub localVe($)
{
- my ($veid) = @_;
- die "HN: no veid" if (!defined($veid));
-
- my $vzdir = '/etc/vz';
- die "HN is not running OpenVZ" if (!defined($vzdir));
-
- my $lockdir = undef;
- my $dumpdir = undef;
- my $private = undef;
- my $root = undef;
- open my $fh, "<$vzdir/vz.conf" || die "Cannot open $vzdir/vz.conf";
- while (<$fh>) {
- chomp;
- my ($name, $value) = split('=');
- if ($name eq 'LOCKDIR') {
- $lockdir = $value;
- die "OpenVZ LOCKDIR ($lockdir) is invalid" if (! -d $lockdir);
- } elsif ($name eq 'DUMPDIR') {
- $dumpdir = $value;
- die "OpenVZ DUMPDIR ($dumpdir) is invalid" if (! -d $dumpdir);
- } elsif ($name eq 'VE_PRIVATE') {
- $private = $value;
- } elsif ($name eq 'VE_ROOT') {
- $root = $value;
- }
+ my ($veid) = @_;
+ die "HN: no veid" if (!defined($veid));
+
+ my $vzdir = '/etc/vz';
+ die "HN is not running OpenVZ" if (!defined($vzdir));
+
+ my $lockdir = undef;
+ my $dumpdir = undef;
+ my $private = undef;
+ my $root = undef;
+ open my $fh, "<$vzdir/vz.conf" || die "Cannot open $vzdir/vz.conf";
+ while (<$fh>) {
+ chomp;
+ my ($name, $value) = split('=');
+ if ($name eq 'LOCKDIR') {
+ $lockdir = $value;
+ die "OpenVZ LOCKDIR ($lockdir) is invalid" if (! -d $lockdir);
+ } elsif ($name eq 'DUMPDIR') {
+ $dumpdir = $value;
+ die "OpenVZ DUMPDIR ($dumpdir) is invalid" if (! -d $dumpdir);
+ } elsif ($name eq 'VE_PRIVATE') {
+ $private = $value;
+ } elsif ($name eq 'VE_ROOT') {
+ $root = $value;
}
- close($fh);
-
- my $confdir = "$vzdir/conf";
- die "OpenVZ conf dir ($confdir) not found" if (! -d $confdir);
-
- my $hostname = undef;
- my $conffile = "$confdir/$veid.conf";
- open $fh, "<$conffile" || die "Cannot open $conffile";
- while (<$fh>) {
- chomp;
- my ($name, $value) = split('=');
- $private = $value if ($name eq 'VE_PRIVATE');
- $root = $value if ($name eq 'VE_ROOT');
- if ($name eq 'HOSTNAME') {
- $value =~ s/"//g;
- $hostname = gethostbyaddr(gethostbyname($value), AF_INET);
- }
+ }
+ close($fh);
+
+ my $confdir = "$vzdir/conf";
+ die "OpenVZ conf dir ($confdir) not found" if (! -d $confdir);
+
+ my $hostname = undef;
+ my $conffile = "$confdir/$veid.conf";
+ open $fh, "<$conffile" || die "Cannot open $conffile";
+ while (<$fh>) {
+ chomp;
+ my ($name, $value) = split('=');
+ $private = $value if ($name eq 'VE_PRIVATE');
+ $root = $value if ($name eq 'VE_ROOT');
+ if ($name eq 'HOSTNAME') {
+ $value =~ s/"//g;
+ $hostname = gethostbyaddr(gethostbyname($value), AF_INET);
}
- close($fh);
-
- $private =~ s/"//g;
- $private =~ s|/\$VEID|/$veid|g;
- die "VE_PRIVATE is not defined" if (!defined($private));
- die "VE $veid private dir ($private) not found" if (! -d $private);
-
- $root =~ s|/\$VEID|/$veid|g;
- $root =~ s/"//g;
- die "VE_ROOT is not defined" if (!defined($root));
- die "VE $veid root dir ($root) not found" if (! -d $root);
-
- die "VE $veid has no HOSTNAME" if (!defined($hostname));
-
- my $status = `vzctl status $veid`;
- my $running = 0;
- $running = 1 if ($status =~ /running/);
-
- return {
- "vzdir" => $vzdir,
- "lockdir" => $lockdir,
- "dumpdir" => $dumpdir,
- "confdir" => $confdir,
- "conffile" => $conffile,
- "private" => $private,
- "root" => $root,
- "hostname" => $hostname,
- "VEID" => $veid,
- "running" => $running
- };
+ }
+ close($fh);
+
+ $private =~ s/"//g;
+ $private =~ s|/\$VEID|/$veid|g;
+ die "VE_PRIVATE is not defined" if (!defined($private));
+ die "VE $veid private dir ($private) not found" if (! -d $private);
+
+ $root =~ s|/\$VEID|/$veid|g;
+ $root =~ s/"//g;
+ die "VE_ROOT is not defined" if (!defined($root));
+ die "VE $veid root dir ($root) not found" if (! -d $root);
+
+ die "VE $veid has no HOSTNAME" if (!defined($hostname));
+
+ my $status = `vzctl status $veid`;
+ my $running = 0;
+ $running = 1 if ($status =~ /running/);
+
+ return {
+ "vzdir" => $vzdir,
+ "lockdir" => $lockdir,
+ "dumpdir" => $dumpdir,
+ "confdir" => $confdir,
+ "conffile" => $conffile,
+ "private" => $private,
+ "root" => $root,
+ "hostname" => $hostname,
+ "VEID" => $veid,
+ "running" => $running
+ };
}
sub getVeEntry($$)
{
- my ($field, $arg) = @_;
- for (my $i = 0; $i <= $#velist; $i++) {
- my $ve = ${\$velist[$i]};
- return $ve if ($ve->{$field} eq $arg);
- }
- return undef;
+ my ($field, $arg) = @_;
+ for (my $i = 0; $i <= $#velist; $i++) {
+ my $ve = ${\$velist[$i]};
+ return $ve if ($ve->{$field} eq $arg);
+ }
+ return undef;
}
sub getVeByHostname($)
{
- my ($host) = @_;
+ my ($host) = @_;
- my $hostname = gethostbyaddr(gethostbyname($host), AF_INET);
- die "Host $host not found" if (!defined($hostname));
+ my $hostname = gethostbyaddr(gethostbyname($host), AF_INET);
+ die "Host $host not found" if (!defined($hostname));
- my $ve = getVeEntry('hostname', $hostname);
- die "Host $hostname is not a VE" if (!defined($ve));
- return $ve;
+ my $ve = getVeEntry('hostname', $hostname);
+ die "Host $hostname is not a VE" if (!defined($ve));
+ return $ve;
}
sub getVeEntries($$)
{
- my ($field, $host) = @_;
- my @entries;
-
- for (my $i = 0; $i <= $#velist; $i++) {
- my $ve = ${\$velist[$i]};
- push(@entries, $ve) if ($ve->{$field} eq $host);
- }
- return @entries;
+ my ($field, $host) = @_;
+ my @entries;
+
+ for (my $i = 0; $i <= $#velist; $i++) {
+ my $ve = ${\$velist[$i]};
+ push(@entries, $ve) if ($ve->{$field} eq $host);
+ }
+ return @entries;
}
sub printVeEntry($)
{
- my ($ve) = @_;
-
- die "No VE to print" if (!defined($ve));
- print STDERR '{ ';
- #foreach my $k (keys %{$velist[0]}) {
- foreach my $k (keys %{$ve}) {
- print STDERR $k.' => '.$ve->{$k}."\n ";
- }
- print STDERR "}\n";
+ my ($ve) = @_;
+
+ die "No VE to print" if (!defined($ve));
+ print STDERR '{ ';
+ #foreach my $k (keys %{$velist[0]}) {
+ foreach my $k (keys %{$ve}) {
+ print STDERR $k.' => '.$ve->{$k}."\n ";
+ }
+ print STDERR "}\n";
}
sub delSnapshot($)
{
- my ($ve) = @_;
- die "No VE record for delSnapshot" if (!defined($ve));
-
- #print "delSnapshot: doing nothing for now\n";
- #printVeEntry($ve);
- #return;
-
- my $dir = $ve->{'snaproot'};
- if (defined($dir) && -d $dir) {
- #cmdSystemOrEval("rm -rf $dir/etc/vzdump");
- cmdSystemOrEval("umount $dir");
- cmdSystemOrEval("rmdir $dir");
- }
-
- my $dev = $ve->{'snapdev'};
- cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+ my ($ve) = @_;
+ die "No VE record for delSnapshot" if (!defined($ve));
+
+ #print "delSnapshot: doing nothing for now\n";
+ #printVeEntry($ve);
+ #return;
+
+ my $dir = $ve->{'snaproot'};
+ if (defined($dir) && -d $dir) {
+ #cmdSystemOrEval("rm -rf $dir/etc/vzdump");
+ cmdSystemOrEval("umount $dir");
+ cmdSystemOrEval("rmdir $dir");
+ }
+
+ my $dev = $ve->{'snapdev'};
+ cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
}
sub getDevice($)
{
- my ($dir) = @_;
-
- open my $fh, "df -P '$dir'|" || die "Unable to exec df";
- <$fh>; # skip header
- my $df = <$fh>;
- close($fh);
- chomp($df);
- my ($dev, $junk, $junk, $junk, $junk, $mpoint) = split (/\s+/, $df);
-
- my $vg = undef;
- my $lv = undef;
- open $fh, "lvscan|" || die "Unable to exec lvscan";
- while (my $line = <$fh>) {
- if ($line =~ m|^\s+ACTIVE\s+\'/dev/([^/]+)/([^\']+)\'\s|) {
- # vg is $1, lv is $2
- if ($dev eq "/dev/$1/$2" || $dev eq "/dev/mapper/$1-$2") {
- $vg = $1;
- $lv = $2;
- }
- }
+ my ($dir) = @_;
+
+ open my $fh, "df -P '$dir'|" || die "Unable to exec df";
+ <$fh>; # skip header
+ my $df = <$fh>;
+ close($fh);
+ chomp($df);
+ my ($dev, $junk, $junk, $junk, $junk, $mpoint) = split (/\s+/, $df);
+
+ my $vg = undef;
+ my $lv = undef;
+ open $fh, "lvscan|" || die "Unable to exec lvscan";
+ while (my $line = <$fh>) {
+ if ($line =~ m|^\s+ACTIVE\s+\'/dev/([^/]+)/([^\']+)\'\s|) {
+ # vg is $1, lv is $2
+ if ($dev eq "/dev/$1/$2" || $dev eq "/dev/mapper/$1-$2") {
+ $vg = $1;
+ $lv = $2;
+ }
}
- close($fh);
+ }
+ close($fh);
- die "Device $dev has no LVM entry for volume group" if (!defined($vg));
- die "Device $dev has no LVM entry for logical volume" if (!defined($lv));
- return ($dev, $mpoint, $vg, $lv);
+ die "Device $dev has no LVM entry for volume group" if (!defined($vg));
+ die "Device $dev has no LVM entry for logical volume" if (!defined($lv));
+ return ($dev, $mpoint, $vg, $lv);
}
sub makeSnapshot($)
{
- my ($ve) = @_;
- die "No VE record for snapshot" if (!defined($ve));
-
- my ($dev, $lvmpath, $vg, $lv) = getDevice($ve->{'private'});
- #print "snapshot: dev=$dev, lvmpath=$lvmpath, vg=$vg, lm=$lv\n";
- die "Can't find device for VE filesystem" if (!defined($dev));
-
- my $snaproot = "/$vzsnap";
-
- mkpath "$snaproot" ||
- die "Can't create snapshot directory (backup in progress?)";
- $ve->{'snaproot'} = $snaproot;
-
- my $snapdev = "/dev/$vg/$vzsnap";
- die "Snapshot dev $snapdev exists (backup in progress?)" if (-b $snapdev);
- # FIXME: xfs_freeze hangs; without it we are likely to fail at some point.
- # FIXME: finding the mount point instead of hard coding.
- #cmdSystemOrEval("xfs_freeze -f /var/lib/vz/private");
- cmdSystemOrEval("lvcreate --size $snapsize --snapshot --name $vzsnap /dev/$vg/$lv >/dev/null 2>&1");
- #cmdSystemOrEval("xfs_freeze -u /var/lib/vz/private");
- if (! -b $snapdev) {
- delSnapshot($ve);
- die "Failed to create snapshot device"
- }
- $ve->{'snapdev'} = $snapdev;
- cmdSystemOrEval("mount -o nouuid $snapdev $snaproot");
-
- my $snapprivate = $ve->{'private'};
- $snapprivate =~ s|/?$lvmpath/?|/$vzsnap/|;
- #print "snapshot: snapprivate = $snapprivate\n";
- if ($snapprivate !~ /$vzsnap/ || ! -d $snapprivate) {
- delSnapshot($ve);
- die "Wrong lvm mount point $lvmpath";
- }
- $ve->{'snapprivate'} = $snapprivate;
-
- if (! -d "/$snapprivate/etc") {
- delSnapshot($ve);
- die "Mount failure or filesystem doesn't belong to a VE";
- }
+ my ($ve) = @_;
+ die "No VE record for snapshot" if (!defined($ve));
+
+ my ($dev, $lvmpath, $vg, $lv) = getDevice($ve->{'private'});
+ #print "snapshot: dev=$dev, lvmpath=$lvmpath, vg=$vg, lm=$lv\n";
+ die "Can't find device for VE filesystem" if (!defined($dev));
+
+ my $snaproot = "/$vzsnap";
+
+ mkpath "$snaproot" ||
+ die "Can't create snapshot directory (backup in progress?)";
+ $ve->{'snaproot'} = $snaproot;
+
+ my $snapdev = "/dev/$vg/$vzsnap";
+ die "Snapshot dev $snapdev exists (backup in progress?)" if (-b $snapdev);
+ # FIXME: xfs_freeze hangs; without it we are likely to fail at some point.
+ # FIXME: finding the mount point instead of hard coding.
+ #cmdSystemOrEval("xfs_freeze -f /var/lib/vz/private");
+ cmdSystemOrEval("lvcreate --size $snapsize --snapshot --name $vzsnap /dev/$vg/$lv >/dev/null 2>&1");
+ #cmdSystemOrEval("xfs_freeze -u /var/lib/vz/private");
+ if (! -b $snapdev) {
+ delSnapshot($ve);
+ die "Failed to create snapshot device"
+ }
+ $ve->{'snapdev'} = $snapdev;
+ cmdSystemOrEval("mount -o nouuid $snapdev $snaproot");
+
+ my $snapprivate = $ve->{'private'};
+ $snapprivate =~ s|/?$lvmpath/?|/$vzsnap/|;
+ #print "snapshot: snapprivate = $snapprivate\n";
+ if ($snapprivate !~ /$vzsnap/ || ! -d $snapprivate) {
+ delSnapshot($ve);
+ die "Wrong lvm mount point $lvmpath";
+ }
+ $ve->{'snapprivate'} = $snapprivate;
+
+ if (! -d "/$snapprivate/etc") {
+ delSnapshot($ve);
+ die "Mount failure or filesystem doesn't belong to a VE";
+ }
}
sub saveConfigs($)
{
- my ($ve) = @_;
- die "No VE record for saveConfigs" if (!defined($ve));
-
- # Copy configuration and other scripts belonging to VE into VE's snapshot
- my $snapprivate = $ve->{'snapprivate'};
- mkpath "$snapprivate/etc/vzdump";
- my $conffile = $ve->{'conffile'};
- cmdSystemOrEval("cp $conffile $snapprivate/etc/vzdump/vps.conf");
- foreach my $ext (@script_ext) {
- my $fn = $ve->{'confdir'}."/".$ve->{'VEID'}.".$ext";
- cmdSystemOrEval("cp $fn $snapprivate/etc/vzdump/vps.$ext") if (-f $fn);
- }
+ my ($ve) = @_;
+ die "No VE record for saveConfigs" if (!defined($ve));
+
+ # Copy configuration and other scripts belonging to VE into VE's snapshot.
+ # Do this in a manner similar to that supported by vzdump for consistency.
+ my $snapprivate = $ve->{'snapprivate'};
+ mkpath "$snapprivate/etc/vzdump";
+ my $conffile = $ve->{'conffile'};
+ cmdSystemOrEval("cp $conffile $snapprivate/etc/vzdump/vps.conf");
+ foreach my $ext (@script_ext) {
+ my $fn = $ve->{'confdir'}."/".$ve->{'VEID'}.".$ext";
+ cmdSystemOrEval("cp $fn $snapprivate/etc/vzdump/vps.$ext") if (-f $fn);
+ }
}
+# Because of the complexities involved with restoring VE configuartions and
+# knowing when and how to do so, this feature is not currently implemented.
sub restoreConfigs($)
{
- my ($ve) = @_;
- die "No VE record for restoreConfigs" if (!defined($ve));
-
- my $private = $ve->{'private'};
- die "Can't restore invalid private dir $private" if (! -d $private);
- my $qprivate = $private;
- $qprivate =~ s|/|\\\/|g;
- $qprivate =~ s|/$ve->{'VEID'}$|/\$VEID|;
-
- my $root = $ve->{'root'};
- die "Can't restore invalid root dir $root" if (! -d $root);
- my $qroot = $root;
- $qroot =~ s|/|\\\/|g;
- $qroot =~ s|/$ve->{'VEID'}$|/\$VEID|;
-
- my $conffile = $ve->{'conffile'};
- my $cmd = "sed -e 's/VE_ROOT=.*/VE_ROOT=\\\"$qroot\\\"/' -e 's/VE_PRIVATE=.*/VE_PRIVATE=\\\"$qprivate\\\"/' <'$private/etc/vzdump/vps.conf' >'$conffile'";
- cmdSystemOrEval($cmd);
-
- foreach my $s (@script_ext) {
- my $cfgdir = $ve->{'confdir'};
- my $src = $ve->{'private'}."/etc/vzdump/vps.$s";
- my $dest = "$cfgdir/".$ve->{'VEID'}.".$s";
- cmdSystemOrEval("mv '$src' '$dest'") if (-f $src);
- }
-
- rmtree "$private/etc/vzdump";
-
- # FIXME: on error, if there was no private dir to start, we should
- # remove everything we added.?
+ my ($ve) = @_;
+ die "No VE record for restoreConfigs" if (!defined($ve));
+
+ my $private = $ve->{'private'};
+ die "Can't restore invalid private dir $private" if (! -d $private);
+ my $qprivate = $private;
+ $qprivate =~ s|/|\\\/|g;
+ $qprivate =~ s|/$ve->{'VEID'}$|/\$VEID|;
+
+ my $root = $ve->{'root'};
+ die "Can't restore invalid root dir $root" if (! -d $root);
+ my $qroot = $root;
+ $qroot =~ s|/|\\\/|g;
+ $qroot =~ s|/$ve->{'VEID'}$|/\$VEID|;
+
+ my $conffile = $ve->{'conffile'};
+ my $cmd = "sed -e 's/VE_ROOT=.*/VE_ROOT=\\\"$qroot\\\"/' -e 's/VE_PRIVATE=.*/VE_PRIVATE=\\\"$qprivate\\\"/' <'$private/etc/vzdump/vps.conf' >'$conffile'";
+ cmdSystemOrEval($cmd);
+
+ foreach my $s (@script_ext) {
+ my $cfgdir = $ve->{'confdir'};
+ my $src = $ve->{'private'}."/etc/vzdump/vps.$s";
+ my $dest = "$cfgdir/".$ve->{'VEID'}.".$s";
+ cmdSystemOrEval("mv '$src' '$dest'") if (-f $src);
+ }
+
+ rmtree "$private/etc/vzdump";
+
+ # FIXME: on error, if there was no private dir to start, we should
+ # remove everything we added.?
}
sub checkRunningClient()
{
- die "A backup or restore operation are already in progress"
- if (Proc::PID::File->running({ dir => '/tmp', verify => 1 }));
-
- # Clean up any prior backup's mount point and snapshot, if it exists.
- # Note that the snapshot is small, so we don't really want it lying around!
- my $vg = undef;
- open my $fh, "lvscan|" || die "Unable to exec lvscan";
- while (my $line = <$fh>) {
- if ($line =~ m|^\s+ACTIVE\s+Snapshot\s+\'/dev/([^/]+)/$vzsnap\'\s|) {
- $vg = $1;
- }
- }
- close($fh);
- if (defined($vg)) {
- my $dev = "/dev/mapper/$vg-$vzsnap";
- #print "Found vzsnap lv $dev\n";
- cmdSystemOrEval("umount /$vzsnap");
- cmdSystemOrEval("rmdir /$vzsnap") if (-d "/$vzsnap");
- cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+ die "A backup or restore operation are already in progress"
+ if (Proc::PID::File->running({ dir => '/tmp', verify => 1 }));
+
+ # Clean up any prior backup's mount point and snapshot, if it exists.
+ # Note that the snapshot is small, so we don't really want it lying around!
+ my $vg = undef;
+ open my $fh, "lvscan|" || die "Unable to exec lvscan";
+ while (my $line = <$fh>) {
+ if ($line =~ m|^\s+ACTIVE\s+Snapshot\s+\'/dev/([^/]+)/$vzsnap\'\s|) {
+ $vg = $1;
}
+ }
+ close($fh);
+ if (defined($vg)) {
+ my $dev = "/dev/mapper/$vg-$vzsnap";
+ #print "Found vzsnap lv $dev\n";
+ cmdSystemOrEval("umount /$vzsnap");
+ cmdSystemOrEval("rmdir /$vzsnap") if (-d "/$vzsnap");
+ cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+ }
}
sub runClient($)
{
- my ($restore) = @_;
-
- checkRunningClient();
-
- my $veid = shift(@ARGV);
- die "HN needs a VEID argument" if (!defined($veid));
- die "HN: no command to execute after VEID" if ($#ARGV < 0);
-
- # Find $host in the list of VEs
- my $ve = localVe($veid);
- die "VE $veid not found on this HN" if (!defined($ve));
- #printVeEntry($ve);
-
- if (! $restore) {
- cmdSystemOrEval("vzctl stop $veid >/dev/null 2>&1")
- if ($ve->{'running'});
- makeSnapshot($ve);
- cmdSystemOrEval("vzctl start $veid >/dev/null 2>&1")
- if ($ve->{'running'});
- die "Failed to make snapshot of filesystem for VE $veid"
- if (!defined($ve->{'snaproot'}));
-
- # Make and exec the backup command. Do it in a chroot to the snapshot
- # of the VE's root dir so that any relative path information in the
- # backup command is accurate. This does mean that each VE needs rsync,
- # etc.
- my $cmd = "chroot ".$ve->{'snapprivate'}." ".join(' ', @ARGV);
- #print "HN: cmd |$cmd|\n";
- $? = 0;
- cmdSystemOrEval($cmd);
- my $ret = $?; # FIXME
-
- # Remove snapshot, we're done
- delSnapshot($ve);
-
- # Pass the return code back
- #exit $ret; FIXME: currently, cmdSystemOrEval doesn't return a retcode.
- exit 0;
- } else {
- # Restores work off the VE's live root filesystem. A full restore
- # should be done to the HN host, redirecting the restore to the VE's
- # private directory on the HN when the VE is stopped.
- my $cmd = "chroot ".$ve->{'private'}." ".join(' ', @ARGV);
- #print "HN: cmd |$cmd|\n";
- # A restore can exec, because we have no cleanup to do.
- cmdExecOrEval($cmd);
- }
-}
-
-sub runServer($)
-{
- my ($restore) = @_;
-
- # Build the beginning remote command
- my $remoteCmd = "/usr/bin/".basename($0);
- #print "Remote command is $remoteCmd\n";
-
- my $host = shift(@ARGV);
- die "Hostname argument required" if (!defined($host));
- die "No command to execute after hostname" if ($#ARGV < 0);
-
- # Find $host in the list of VEs
- loadVeList();
- my $ve = getVeByHostname($host);
- die "VE $host not found" if (!defined($ve));
- #printVeEntry($ve);
-
- # The command line is bisected by the next occurrence of $host. Everything
- # before is the ssh command (sans what to run on the VE) and everything
- # after is the xfer command to run on the VE.
- my @sshCmd;
- my @xferCmd;
- my $foundHost = 0;
- foreach my $arg (@ARGV) {
- if ($arg eq $host) {
- $foundHost = 1;
- } else {
- if ($foundHost) {
- push(@xferCmd, $arg);
- } else {
- push(@sshCmd, $arg);
- }
- }
- }
- die "No ssh command found" if ($#sshCmd < 0);
- die "No xfer command found" if ($#xferCmd < 0);
- #print "ssh command: |".join(' ', @sshCmd)."|\n";
- #print "xfer command: |".join(' ', @xferCmd)."|\n";
-
- # Create command line to initiate the remote side of the backup. The
- # remote side runs on the VE's HN and is given the VE's VEID.
- my $cmd = join(' ', @sshCmd)." ".$ve->{'HN'}." $remoteCmd ".
- ($restore ? "restore " : "").$ve->{'VEID'}." ".join(' ', @xferCmd);
- #print "remote command: |$cmd|\n";
-
- ## Search and replace
- #foreach my $key (keys %{$velist[0]}) {
- # my $val = $ve->{$key};
- # $cmd =~ s/\@$key\@/$val/g if (defined($val));
- #}
-
+ my ($restore) = @_;
+
+ checkRunningClient();
+
+ my $veid = shift(@ARGV);
+ die "HN needs a VEID argument" if (!defined($veid));
+ die "HN: no command to execute after VEID" if ($#ARGV < 0);
+
+ # We (the HN where this code is running) must be hosting the requested VE.
+ my $ve = localVe($veid);
+ die "VE $veid not found on this HN" if (!defined($ve));
+ #printVeEntry($ve);
+
+ if (!$restore) {
+ cmdSystemOrEval("vzctl stop $veid >/dev/null 2>&1") if ($ve->{'running'});
+ makeSnapshot($ve);
+ cmdSystemOrEval("vzctl start $veid >/dev/null 2>&1") if ($ve->{'running'});
+ die "Failed to make snapshot of filesystem for VE $veid"
+ if (!defined($ve->{'snaproot'}));
+
+ # Save the VE configuration from its hosted HN into the filesystem. If
+ # /etc is backed up, so will the VE configuration.
+ saveConfigs($ve);
+
+ # Make and exec the backup command. Do it in a chroot to the snapshot
+ # of the VEs root dir so that any relative path information in the
+ # backup command is accurate. This does mean that each VE needs rsync,
+ # etc.
+ my $cmd = "chroot ".$ve->{'snapprivate'}." ".join(' ', @ARGV);
+ #print "HN: cmd |$cmd|\n";
+ $? = 0;
+ cmdSystemOrEval($cmd);
+ my $ret = $?; # FIXME - modify cmdSystemOrEval to get a return value.
+
+ # Remove snapshot, we're done
+ delSnapshot($ve);
+
+ # Pass the return code back
+ #exit $ret; FIXME: currently, cmdSystemOrEval doesn't return a retcode.
+ exit 0;
+ } else {
+ # Restores work off the VE's live root filesystem. A full restore
+ # should be done to the HN host, redirecting the restore to the VE's
+ # private directory on the HN when the VE is stopped.
+ my $cmd = "chroot ".$ve->{'private'}." ".join(' ', @ARGV);
+ #print "HN: cmd |$cmd|\n";
+ # A restore can exec, because we have no cleanup to do.
cmdExecOrEval($cmd);
+ }
}
-# A hard-coded test; didn't seem to help
-sub runServer_test($)
+sub runServer($)
{
- my ($restore) = @_;
-
- # Build the beginning remote command
- my $remoteCmd = "/usr/bin/".basename($0);
- #print "Remote command is $remoteCmd\n";
-
- my $host = shift(@ARGV);
- die "Hostname argument required" if (!defined($host));
- die "No command to execute after hostname" if ($#ARGV < 0);
-
- # Find $host in the list of VEs
- loadVeList();
- my $ve = getVeByHostname($host);
- die "VE $host not found" if (!defined($ve));
- #printVeEntry($ve);
-
- # The command line is bisected by the next occurrence of $host. Everything
- # before is the ssh command (sans what to run on the VE) and everything
- # after is the xfer command to run on the VE.
- my @sshCmd;
- my @xferCmd;
- my $foundHost = 0;
- foreach my $arg (@ARGV) {
- if ($arg eq $host) {
- $foundHost = 1;
- } else {
- if ($foundHost) {
- push(@xferCmd, $arg);
- } else {
- push(@sshCmd, $arg);
- }
- }
+ my ($restore) = @_;
+
+ my $host = shift(@ARGV);
+ die "Hostname argument required" if (!defined($host));
+ die "No command to execute after hostname" if ($#ARGV < 0);
+
+ # Find $host in the list of VEs
+ loadVeList();
+ my $ve = getVeByHostname($host);
+ die "$host is not a VE or list of HNs in $0 is wrong" if (!defined($ve));
+ #printVeEntry($ve);
+
+ # The client command is bisected by the next occurrence of $host. Everything
+ # before is the ssh command to reach the client and everything after is the
+ # xfer command sent to the client via ssh.
+ my @sshCmd;
+ my @xferCmd;
+ my $foundHost = 0;
+ foreach my $arg (@ARGV) {
+ if ($arg eq $host) {
+ $foundHost = 1;
+ } else {
+ if ($foundHost) {
+ push(@xferCmd, $arg);
+ } else {
+ push(@sshCmd, $arg);
+ }
}
- die "No ssh command found" if ($#sshCmd < 0);
- die "No xfer command found" if ($#xferCmd < 0);
- #print "ssh command: |".join(' ', @sshCmd)."|\n";
- #print "xfer command: |".join(' ', @xferCmd)."|\n";
-
- # Create command line to initiate the remote side of the backup. The
- # remote side runs on the VE's HN and is given the VE's VEID.
- my $cmd = join(' ', @sshCmd)." pe18002.titaniummirror.com $remoteCmd ".
- ($restore ? "restore " : "")."151 ".join(' ', @xferCmd);
- #print "remote command: |$cmd|\n";
-
- ## Search and replace
- #foreach my $key (keys %{$velist[0]}) {
- # my $val = $ve->{$key};
- # $cmd =~ s/\@$key\@/$val/g if (defined($val));
- #}
-
- cmdExecOrEval($cmd);
+ }
+ die "No ssh command found" if ($#sshCmd < 0);
+ die "No xfer command found" if ($#xferCmd < 0);
+ #print "ssh command: |".join(' ', @sshCmd)."|\n";
+ #print "xfer command: |".join(' ', @xferCmd)."|\n";
+
+ # Create the command line to initiate the client side of the backup. We
+ # contact the HN hosting the VE and run an invocation of this script there
+ # to perform the required operations for VE backup or restore.
+ my $cmd;
+ $cmd = join(' ', @sshCmd)." ".$ve->{'HN'}." /usr/bin/".basename($0)." ".
+ ($restore ? "restore " : "").$ve->{'VEID'}." ".join(' ', @xferCmd);
+ #print "remote command: |$cmd|\n";
+ cmdExecOrEval($cmd);
}
## MAIN
# options server and restore may be seen together.
my $server = 0;
if ($ARGV[0] eq "server") {
- shift(@ARGV);
- $server = 1;
+ shift(@ARGV);
+ $server = 1;
}
my $refresh = 0;
if ($ARGV[0] eq "refresh") {
- shift(@ARGV);
- $refresh = 1;
+ shift(@ARGV);
+ $refresh = 1;
}
my $restore = 0;
if ($ARGV[0] eq "restore") {
- shift(@ARGV);
- $restore = 1;
- #print "Restore mode\n";
+ shift(@ARGV);
+ $restore = 1;
+ #print "Restore mode\n";
}
if ($server) {
- runServer($restore);
+ runServer($restore);
} elsif ($refresh) {
- refreshConfig();
+ refreshConfig();
} else {
- runClient($restore);
+ runClient($restore);
}