As I mentioned before on the list, I've been working on some simple
scripts to cause BackupPC to use LVM file system snapshots rather
than the original directory during an rsync backup. I've had these
working for about a week and so far there have been no problems. I
was asked to post these on the list, so I thought I did. The
"snap_share" and "unsnap_share" run as DumpPreShareCmd and
DumpPostShareCmd like this:
/usr/bin/sudo /sw/BackupPC/utkcs/snap_share $share
The snap_rsync runs as RsyncClientCmd like this:
/usr/bin/sudo /sw/BackupPC/utkcs/snap_rsync $rsyncPath $argList+
Right now snap_rsync will just rsync from the original share if it
cannot detect that there is a snapshot. This allows me to use this
command for file systems where I don't want to take a snapshot.
I cannot guarantee that this will work for anyone else but me. I also
provide no warranty of any kind on these scripts. There is probably a
lot more error checking that could and should be done. Also, I have
only tested this under Red Hat Enterprise Linux 5. The paths, etc.
will be different on other systems. I'd appreciate any feedback. I'm
going to write a snap_tar wrapper also for tar-based backups. I do
realize there is a lot of redundancy between the scripts and I could
create a small Perl library to contain the common functions, but I'm
lazy :) ...
I assume this would also work over ssh, Haven't' tested that yet.
# cat snap_share
#!/usr/bin/perl -w
# Written 2007 by Markus Iturriaga [EMAIL PROTECTED]
use POSIX;
# Configurables:
# Paths
$mountPoint = "/bin/mountpoint";
$mount = "/bin/mount";
$mountdir = "/tmp";
$lvs = "/usr/sbin/lvs";
$lvcreate = "/usr/sbin/lvcreate";
$lvdisplay = "/usr/sbin/lvdisplay";
$lvremove = "/usr/sbin/lvremove";
$ratio = 0.15; # Percent of original volume size to be used for snapshot
# Subroutines
# Globals
$tmpdir="";
$snapDevice="";
sub usage {
print "usage: $0 share_name\n";
}
sub get_mountpoint {
my $dir = shift;
system($mountPoint, "-q", $dir);
# Check for breakout condition
if ($? == 0) { return $dir; }
if ($dir eq "/") { return $dir; }
$dir =~ /(.*)\//; # greedily match up to the last slash
return get_mountpoint($1);
}
sub get_snapdevice {
my $device = shift;
my $name = shift;
@output = `$lvdisplay -v $device 2>/dev/null`;
if ($?) {
# lvdisplay failed
print "Cannot determine if a snapshot by this name
($snapName) already exists!\n";
die "Command failed: $lvdisplay -v $mounts
{$mountPoint}";
}
@gout = grep (/\Q$name\E/, @output);
if ( scalar @gout > 1 ) {
die "lvdisplay returned more than snapshot matching
$name - there is some sort of problem. Aborting.\n";
} elsif ( scalar @gout == 0 ) { return ""; }
else {
$gout[0] =~ /\s*(\S*)/;
return $1;
}
}
sub lv_remove {
my $device = shift;
system("$lvremove -f $device 2>&1 >/dev/null");
if ($?) {
die "Tried to remove snapshot device $device and
failed. Snapshot might still be active.";
}
}
# Main
# Process Command Line Arguments
# We need 1 argument
if ($#ARGV) { usage(); exit(-1); }
$shareName = $ARGV[0];
$shareName =~ s/\/$//;
# Check to see if shareName is a directory
if ( ! -d $shareName ) {
print "$shareName is not a directory.\n";
usage();
exit(-1);
}
# Get mointpoint for share
open (MOUNTS, "</proc/mounts") || die "No /proc/mounts ???";
while (<MOUNTS>) {
($device, $mntpnt) = split (' ', $_, 3);
$mounts{$mntpnt} = $device;
}
close MOUNTS;
$mountPoint = get_mountpoint($shareName);
print "Sharename: $shareName\nMountPoint: $mountPoint\nDevice: $mounts
{$mountPoint}\n";
if (! $mounts{$mountPoint}) {
print "Very Confused. Could not retrieve a device for
$shareName. I calculated a mountpoint of $mountPoint\n";
usage();
exit(-1);
}
# Figure out if mountpoint is an lvm volume
$output = `$lvs --noheadings --unbuffered --units k --options lv_size
$mounts{$mountPoint} 2>/dev/null`;
if ($?) {
# Mount point is not an LVM volume
print "Finished $0: $shareName is not located on an LVM
volume.\n";
exit (0);
} else {
# Mount point *is* an LVM volume
$output =~ /\s*(\d*)/;
$size = ceil($1 * $ratio); # Snapshot size will be $ratio of
volume size.
# Take the snapshot
$snapName = $shareName;;
$snapName =~ s/\//_/g;
$snapName = "backuppc" . $snapName;;
# Check to see if the snapshot already exists
$snapDevice = get_snapdevice($mounts{$mountPoint}, $snapName);
if ($snapDevice ne "") {
die "There was already a snapshot by the name of
$snapName - Aborting.";
}
# Actually take the snapshot;
$output = `$lvcreate -L${size}K -s -p r -n $snapName $mounts
{$mountPoint} 2>/dev/null`;
if ($?) {
# lvcreate failed
die "Command failed: $lvcreate -L${size}K -s -p r -n
$snapName $mounts{$mountPoint}";
}
$snapDevice = get_snapdevice($mounts{$mountPoint}, $snapName);
print "Snapshot Device: $snapDevice\n" ;
# Create a suitable mountpoint for the snapshot
$tmpdir = "$mountdir/$snapName";
if ( -e $tmpdir ) {
print "Mount point $tmpdir already exists. Could be
a sign of problems.\n";
} else {
mkdir $tmpdir || die "Cannot mkdir $tmpdir - Aborting";
print "Tmpdir: $tmpdir\n";
}
system($mount, "-o", "ro", $snapDevice, $tmpdir);
if ($?) {
print "Command failed: $mount -o ro $snapDevice
$tmpdir failed. Trying to remove snapshot";
lv_remove($snapDevice);
exit(-1);
}
print "Snapshot mounted. Exiting\n";
exit(0);
}
# cat unsnap_share
#!/usr/bin/perl -w
# Written 2007 by Markus Iturriaga [EMAIL PROTECTED]
use POSIX;
# Configurables:
# Paths
$mountpoint = "/bin/mountpoint";
$umount = "/bin/umount";
$mountDir = "/tmp";
$lvdisplay = "/usr/sbin/lvdisplay";
$lvremove = "/usr/sbin/lvremove";
# Globals
sub usage {
print "usage: $0 share_name\n";
}
sub get_mountpoint {
my $dir = shift;
system($mountpoint, "-q", $dir);
# Check for breakout condition
if ($? == 0) { return $dir; }
if ($dir eq "/") { return $dir; }
$dir =~ /(.*)\//; # greedily match up to the last slash
return get_mountpoint($1);
}
sub lv_remove {
my $device = shift;
system("$lvremove -f $device 2>&1 >/dev/null");
if ($?) {
die "Tried to remove snapshot device $device and
failed. Snapshot might still be active.";
}
}
# Main
# Process Command Line Arguments
# We need 1 argument
if ($#ARGV) { usage(); exit(-1); }
$shareName = $ARGV[0];
# Remove final slash if there
$shareName =~ s/\/$//;
# See if a snapshot for the share name is mounted.
$snapName = $shareName;;
$snapName =~ s/\//_/g;
$snapName = "backuppc" . $snapName;;
$tmpDir = "$mountDir/$snapName";
open (MOUNTS, "</proc/mounts") || die "No /proc/mounts ???";
$found = 0;
while (<MOUNTS>) {
@mntpnt = split (' ', $_, 3);
if ($mntpnt[1] eq $tmpDir) {
# Foun[1]d a filesystem mounted at the tmpdir
$found = 1;
system($umount, $tmpDir);
if ($?) {
die "Command failed: $umount $tmpDir failed.";
} else {
print "Unmounted $tmpDir\n";
}
}
}
close MOUNTS;
if (!$found) {
print "Did not find anything mounted on \"$tmpDir\"\n";
print "Continuing, but might not find a snapshot device.\n";
}
# Remove the tmpdir
if (-d $tmpDir) {
print "Removing temporary directory $tmpDir\n";
rmdir $tmpDir || die "Could not remove $tmpDir - not empty?";
}
# Find the snapshot device
# We can't use the mapper device directly to remove the volume.
@output = `$lvdisplay -c 2>/dev/null`;
if ($?) {
# lvdisplay failed
die "Command failed: $lvdisplay -c";
}
@gout = grep (/\Q$snapName\E/, @output);
if (scalar @gout >1) {
print "Found more than one device that matched \"$snapName\":
\n @gout\n";
print "Cannot remove snapshot device.\n";
exit (1);
} elsif (scalar @gout < 1) {
print "Cannot find a snapshot device that matches \"$snapName
\"\n";
exit (1);
}
$snapDevice = $gout[0];
$snapDevice =~ s/^\s+//; # Remove leading whitespace
($snapDevice) = split (':', $snapDevice);
print "Removing Snapshot Device: $snapDevice\n";
lv_remove($snapDevice);
# cat snap_rsync
#!/usr/bin/perl -w
# Written 2007 by Markus Iturriaga [EMAIL PROTECTED]
use POSIX;
# Configurables:
# Paths
$mountDir = "/tmp";
$mountPoint = "/bin/mountpoint";
# Subroutines
sub get_mountpoint {
my $dir = shift;
system($mountPoint, "-q", $dir);
# Check for breakout condition
if ($? == 0) { return $dir; }
if ($dir eq "/") { return $dir; }
$dir =~ /(.*)\//; # greedily match up to the last slash
return get_mountpoint($1);
}
sub usage {
print STDERR "usage: $0 rsync_path [rsync_arguments]
share_name\n";
}
# We need at least 2 arguments
if ($#ARGV < 1) { usage(); exit(-1); }
$rsyncPath = $ARGV[0];
$shareName = $ARGV[$#ARGV];
$shareName =~ s/\/$//;
$newShare = $shareName;
$i = 0;
foreach $argnum (1 .. $#ARGV-1) {
$argList[$i] = $ARGV[$argnum];;
$i++;
}
# Check to see if rsync exists
if ( ! -x $rsyncPath ) {
print STDERR "$rsyncPath is not an executable.\n";
usage();
exit(-1);
}
$shareMount = $shareName;
$shareMount =~ s/\//_/g;
$shareMount = "$mountDir/backuppc$shareMount";
# Check to see if anything is mounted on $shareMount
open (MOUNTS, "</proc/mounts") || die "No /proc/mounts ???";
while (<MOUNTS>) {
@mntpnt = split (' ', $_, 3);
if ($mntpnt[1] eq $shareMount) {
# Something is mounted on $shareMount - run the rsync.
# First - make sure the new directory reflects the
new mount point
$mountPoint = get_mountpoint($shareName);
$newShare =~ s/\Q$mountPoint\E/$shareMount/;
exec ("$rsyncPath @argList $newShare/") || die
"Could not execute $rsyncPath @argList $newShare";
}
}
# Didn't find anything mounted in the appropriate directory, so just
run the rsync from the real share.
exec ("$rsyncPath @argList $shareName") || die "Could not execute
$rsyncPath @argList $shareName";
---
Markus A. Iturriaga Woelfel, IT Administrator
Electrical Engineering and Computer Science
University of Tennessee
203 Claxton Complex / 1122 Volunteer Blvd.
Knoxville, TN 37996-3450
[EMAIL PROTECTED] / (865) 974-3837
-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems? Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
BackupPC-users mailing list
[email protected]
List: https://lists.sourceforge.net/lists/listinfo/backuppc-users
Wiki: http://backuppc.wiki.sourceforge.net
Project: http://backuppc.sourceforge.net/