]> oss.titaniummirror.com Git - ovzbpc.git/blobdiff - BackupPC_ovz
BackupPC_ovz: report entry to more functions in debug
[ovzbpc.git] / BackupPC_ovz
index bb4b100dc00322e8bf51b28e616119bcbb65a277..38e888ac0016e6e96afbde72c0bc3ffa9e6562a8 100755 (executable)
@@ -1,12 +1,11 @@
 #!/usr/bin/perl
 #
 # BackupPC_ovz
+# Version: __appVersion__
 #
 # OpenVZ integration for BackupPC allowing the latter to backup OpenVZ VE's
 # with ovz awareness to improve backup and restore efficiency and features.
 #
-# 20080605     0.1     Initial release
-
 # FIXME: signal handling to clean up mount point and snapshot on termination
 
 use strict;
@@ -21,10 +20,26 @@ 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 $snapsize = '25g';
 my $hnlistFile = "/etc/backuppc/".basename($0).".hnlist";
 my $velistFile = $ENV{HOME}."/log/".basename($0).".velist";
 
+# Write to the debug file only if it is already present
+my $dbgfn = "/tmp/BackupPC_ovz.debug";
+my $dbgf;
+if ( ! -f $dbgfn) {
+  $dbgfn = "/dev/null";
+}
+open $dbgf, ">>$dbgfn" || die "Cannot open debug file $dbgfn";
+
+sub mydie($)
+{
+  my ($text) = @_;
+
+  print $dbgf ": die $text\n";
+  die $text;
+}
+
 sub cmdExecOrEval
 {
   my($cmd, @args) = @_;
@@ -35,7 +50,8 @@ sub cmdExecOrEval
     print(STDERR "Perl code fragment for exec shouldn't return!!\n");
     exit(1);
   } else {
-  $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
+    $cmd = [split(/\s+/, $cmd)] if ( ref($cmd) ne "ARRAY" );
+    print $dbgf ": execing command $cmd\n";
     alarm(0);
     $cmd = [map { m/(.*)/ } @$cmd];    # untaint
     #
@@ -56,8 +72,10 @@ sub cmdSystemOrEval
   $? = 0;
   $cmd = join(" ", $cmd) if ( ref($cmd) eq "ARRAY" );
   if ( (ref($cmd) eq "ARRAY" ? $cmd->[0] : $cmd) =~ /^\&/ ) {
+    print $dbgf ": evaluating command $cmd\n";
     eval($cmd);
   } else {
+    print $dbgf ": running command $cmd\n";
     system($cmd);
   }
 }
@@ -70,18 +88,20 @@ sub refreshConfig()
   # 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";
+
+  print $dbgf ": refreshConfig\n";
+  open my $cfg, "<$hnlistFile" || mydie "Cannot read $hnlistFile";
   while (<$cfg>) {
     chomp;
     push(@HNS, split(' ')) if (! /^#/);
   }
   close($cfg);
-  die "No HNs defined in $hnlistFile" if ($#HNS < 0);
+  mydie "No HNs defined in $hnlistFile" if ($#HNS < 0);
 
-  open my $out, ">$velistFile" || die "Cannot write to $velistFile";
+  open my $out, ">$velistFile" || mydie "Cannot write to $velistFile";
   foreach my $hn (@HNS) {
     open my $fh, "ssh -l root $hn vzlist -a |" ||
-       die "Can run remote vzlist command";
+       mydie "Can run remote vzlist command";
     while (<$fh>) {
       chomp;
       my ($veid, $junk, $running, $junk, $hostname) = split(' ');
@@ -111,7 +131,7 @@ sub refreshConfig()
 sub loadVeList()
 {
   open my $fh, "<$velistFile" ||
-      die "Cannot read from $velistFile. Set a preuser script.";
+      mydie "Cannot read from $velistFile. Set a preuser script.";
 
   while (<$fh>) {
     chomp;
@@ -130,25 +150,25 @@ sub loadVeList()
 sub localVe($)
 {
   my ($veid) = @_;
-  die "HN: no veid" if (!defined($veid));
+  mydie "HN: no veid" if (!defined($veid));
 
   my $vzdir = '/etc/vz';
-  die "HN is not running OpenVZ" if (!defined($vzdir));
+  mydie "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";
+  open my $fh, "<$vzdir/vz.conf" || mydie "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);
+      mydie "OpenVZ LOCKDIR ($lockdir) is invalid" if (! -d $lockdir);
     } elsif ($name eq 'DUMPDIR') {
       $dumpdir = $value;
-      die "OpenVZ DUMPDIR ($dumpdir) is invalid" if (! -d $dumpdir);
+      mydie "OpenVZ DUMPDIR ($dumpdir) is invalid" if (! -d $dumpdir);
     } elsif ($name eq 'VE_PRIVATE') {
       $private = $value;
     } elsif ($name eq 'VE_ROOT') {
@@ -158,11 +178,11 @@ sub localVe($)
   close($fh);
 
   my $confdir = "$vzdir/conf";
-  die "OpenVZ conf dir ($confdir) not found" if (! -d $confdir);
+  mydie "OpenVZ conf dir ($confdir) not found" if (! -d $confdir);
 
   my $hostname = undef;
   my $conffile = "$confdir/$veid.conf";
-  open $fh, "<$conffile" || die "Cannot open $conffile";
+  open $fh, "<$conffile" || mydie "Cannot open $conffile";
   while (<$fh>) {
     chomp;
     my ($name, $value) = split('=');
@@ -177,15 +197,15 @@ sub localVe($)
 
   $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);
+  mydie "VE_PRIVATE is not defined" if (!defined($private));
+  mydie "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);
+  mydie "VE_ROOT is not defined" if (!defined($root));
+  mydie "VE $veid root dir ($root) not found" if (! -d $root);
 
-  die "VE $veid has no HOSTNAME" if (!defined($hostname));
+  mydie "VE $veid has no HOSTNAME" if (!defined($hostname));
 
   my $status = `vzctl status $veid`;
   my $running = 0;
@@ -220,10 +240,10 @@ sub getVeByHostname($)
   my ($host) = @_;
 
   my $hostname = gethostbyaddr(gethostbyname($host), AF_INET);
-  die "Host $host not found" if (!defined($hostname));
+  mydie "Host $host not found" if (!defined($hostname));
 
   my $ve = getVeEntry('hostname', $hostname);
-  die "Host $hostname is not a VE" if (!defined($ve));
+  mydie "Host $hostname is not a VE" if (!defined($ve));
   return $ve;
 }
 
@@ -243,7 +263,7 @@ sub printVeEntry($)
 {
   my ($ve) = @_;
 
-  die "No VE to print" if (!defined($ve));
+  mydie "No VE to print" if (!defined($ve));
   print STDERR '{   ';
   #foreach my $k (keys %{$velist[0]}) {
   foreach my $k (keys %{$ve}) {
@@ -255,7 +275,8 @@ sub printVeEntry($)
 sub delSnapshot($)
 {
   my ($ve) = @_;
-  die "No VE record for delSnapshot" if (!defined($ve));
+  print $dbgf ": delSnapshot\n";
+  mydie "No VE record for delSnapshot" if (!defined($ve));
 
   #print "delSnapshot: doing nothing for now\n";
   #printVeEntry($ve);
@@ -264,19 +285,26 @@ sub delSnapshot($)
   my $dir = $ve->{'snaproot'};
   if (defined($dir) && -d $dir) {
     #cmdSystemOrEval("rm -rf $dir/etc/vzdump");
+    print $dbgf ": delSnapshot snapshot mounted at $dir\n";
     cmdSystemOrEval("umount $dir");
     cmdSystemOrEval("rmdir $dir");
   }
 
   my $dev = $ve->{'snapdev'};
-  cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+  #cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+  if (-b $dev) {
+    print $dbgf ": removing LV $dev\n";
+    system("lvremove -f $dev >>$dbgfn 2>&1")
+  } else {
+    print $dbgf ": lvremove: $dev is not block special\n";
+  }
 }
 
 sub getDevice($)
 {
   my ($dir) = @_;
 
-  open my $fh, "df -P '$dir'|" || die "Unable to exec df";
+  open my $fh, "df -P '$dir'|" || mydie "Unable to exec df";
   <$fh>; # skip header
   my $df = <$fh>;
   close($fh);
@@ -285,7 +313,7 @@ sub getDevice($)
 
   my $vg = undef;
   my $lv = undef;
-  open $fh, "lvscan|" || die "Unable to exec lvscan";
+  open $fh, "lvscan|" || mydie "Unable to exec lvscan";
   while (my $line = <$fh>) {
     if ($line =~ m|^\s+ACTIVE\s+\'/dev/([^/]+)/([^\']+)\'\s|) {
       # vg is $1, lv is $2
@@ -297,28 +325,28 @@ sub getDevice($)
   }
   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));
+  mydie "Device $dev has no LVM entry for volume group" if (!defined($vg));
+  mydie "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));
+  mydie "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));
+  mydie "Can't find device for VE filesystem" if (!defined($dev));
 
   my $snaproot = "/$vzsnap";
 
   mkpath "$snaproot" ||
-      die "Can't create snapshot directory (backup in progress?)";
+      mydie "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);
+  mydie "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");
@@ -326,30 +354,30 @@ sub makeSnapshot($)
   #cmdSystemOrEval("xfs_freeze -u /var/lib/vz/private");
   if (! -b $snapdev) {
     delSnapshot($ve);
-    die "Failed to create snapshot device"
+    mydie "Failed to create snapshot device"
   }
   $ve->{'snapdev'} = $snapdev;
-  cmdSystemOrEval("mount -o nouuid $snapdev $snaproot");
+  cmdSystemOrEval("mount -o noatime,nodiratime $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";
+    mydie "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";
+    mydie "Mount failure or filesystem doesn't belong to a VE";
   }
 }
 
 sub saveConfigs($)
 {
   my ($ve) = @_;
-  die "No VE record for saveConfigs" if (!defined($ve));
+  mydie "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.
@@ -368,16 +396,16 @@ sub saveConfigs($)
 sub restoreConfigs($)
 {
   my ($ve) = @_;
-  die "No VE record for restoreConfigs" if (!defined($ve));
+  mydie "No VE record for restoreConfigs" if (!defined($ve));
 
   my $private = $ve->{'private'};
-  die "Can't restore invalid private dir $private" if (! -d $private);
+  mydie "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);
+  mydie "Can't restore invalid root dir $root" if (! -d $root);
   my $qroot = $root;
   $qroot =~ s|/|\\\/|g;
   $qroot =~ s|/$ve->{'VEID'}$|/\$VEID|;
@@ -401,48 +429,81 @@ sub restoreConfigs($)
 
 sub checkRunningClient()
 {
-  die "A backup or restore operation are already in progress"
+  print $dbgf ": checkRunningClient, vzsnap=$vzsnap\n";
+  mydie "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!
+  # There are cases where the snapshot will not show as a snapshot, and in
+  # these cases the LV will not be removed by this function.
   my $vg = undef;
-  open my $fh, "lvscan|" || die "Unable to exec lvscan";
+  open my $fh, "lvscan|" || mydie "Unable to exec lvscan";
   while (my $line = <$fh>) {
+    #print $dbgf ": checkRunningClient lvscan line=$line\n";
     if ($line =~ m|^\s+ACTIVE\s+Snapshot\s+\'/dev/([^/]+)/$vzsnap\'\s|) {
       $vg = $1;
+      #print $dbgf ": checkRunningClient vg=$vg\n";
     }
   }
   close($fh);
   if (defined($vg)) {
     my $dev = "/dev/mapper/$vg-$vzsnap";
-    #print "Found vzsnap lv $dev\n";
+    print $dbgf ": 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);
+    #cmdSystemOrEval("lvremove -f $dev >/dev/null 2>&1") if (-b $dev);
+    system("lvremove -f $dev >>$dbgfn 2>&1") if (-b $dev);
   }
 }
 
+sub runPing()
+{
+  # This command generates ping output by pinging the host specified in the
+  # ping command still within @ARGV.  However, if the host listed therein is
+  # a VE, the HN must be pinged instead.
+
+  # Get the host
+  my $host = shift(@ARGV);
+  my $cmd = join(' ', @ARGV);
+
+  print $dbgf ": runPing host=$host, cmd=$cmd\n";
+  # Find $host in the list of VEs
+  loadVeList();
+  my $hostname = gethostbyaddr(gethostbyname($host), AF_INET);
+  mydie "Host $host not found" if (!defined($hostname));
+  my $ve = getVeEntry('hostname', $hostname);
+  if (defined($ve)) {
+    $hostname = $ve->{'HN'};
+    mydie "HN is undefined for host $host" if (!defined($hostname));
+    $cmd =~ s/$host/$hostname/g;
+    print $dbgf ": ping request for $host remapped to HN $hostname\n";
+  }
+
+  cmdExecOrEval($cmd);
+}
+
 sub runClient($)
 {
   my ($restore) = @_;
 
+  print $dbgf ": runClient restore=$restore\n";
   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);
+  mydie "HN needs a VEID argument" if (!defined($veid));
+  mydie "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));
+  mydie "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"
+    mydie "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
@@ -462,6 +523,8 @@ sub runClient($)
     # Remove snapshot, we're done
     delSnapshot($ve);
 
+    print $dbgf ": runClient complete\n";
+
     # Pass the return code back
     #exit $ret; FIXME: currently, cmdSystemOrEval doesn't return a retcode.
     exit 0;
@@ -479,15 +542,16 @@ sub runClient($)
 sub runServer($)
 {
   my ($restore) = @_;
-
   my $host = shift(@ARGV);
-  die "Hostname argument required" if (!defined($host));
-  die "No command to execute after hostname" if ($#ARGV < 0);
+
+  print $dbgf ": runServer restore=$restore, host=$host\n";
+  mydie "Hostname argument required" if (!defined($host));
+  mydie "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));
+  mydie "$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
@@ -507,8 +571,8 @@ sub runServer($)
       }
     }
   }
-  die "No ssh command found" if ($#sshCmd < 0);
-  die "No xfer command found" if ($#xferCmd < 0);
+  mydie "No ssh command found" if ($#sshCmd < 0);
+  mydie "No xfer command found" if ($#xferCmd < 0);
   #print "ssh command: |".join(' ', @sshCmd)."|\n";
   #print "xfer command: |".join(' ', @xferCmd)."|\n";
 
@@ -545,10 +609,21 @@ if ($ARGV[0] eq "restore") {
   #print "Restore mode\n";
 }
 
+my $ping  = 0;
+if ($ARGV[0] eq "ping") {
+  shift(@ARGV);
+  $ping = 1;
+  #print "Ping mode\n";
+}
+
+print $dbgf ": server=$server, refresh=$refresh, restore=$restore ping=$ping";
+print $dbgf " on ".`date`;
 if ($server) {
   runServer($restore);
 } elsif ($refresh) {
   refreshConfig();
+} elsif ($ping) {
+  runPing();
 } else {
   runClient($restore);
 }