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/

Reply via email to