works for level 0
authorHeiko Schlittermann <hs@schlittermann.de>
Sun, 25 Oct 2009 23:53:47 +0100
changeset 2 5f03a7843dc2
parent 1 505c5bd23279
child 3 3f1318ea6bcb
child 4 79ab63474be7
works for level 0
py2b
--- a/py2b	Fri Oct 23 23:51:12 2009 +0200
+++ b/py2b	Sun Oct 25 23:53:47 2009 +0100
@@ -18,6 +18,7 @@
 my $opt_today = strftime("%F", localtime);
 my @opt_debug = ();
 my $opt_verbose = 0;
+my $opt_dry = 0;
 #my $opt_node = hostname;
 #my $opt_dir = "backups/$opt_node/daily";
 
@@ -29,6 +30,10 @@
 sub get_candidates();
 sub verbose(@);
 
+our @AT_EXIT;
+END { $_->() foreach @AT_EXIT };
+$SIG{INT} = sub { warn "Got signal INT\n"; exit 1 };
+
 MAIN: {
     GetOptions(
 	"l|level=i" => \$opt_level,
@@ -36,19 +41,21 @@
 	"h|help" => sub { pod2usage(-exit => 0, -verbose => 1) },
 	"m|man" => sub { pod2usage(-exit => 0, -verbose => 3) },
 	"v|verbose" => \$opt_verbose,
+	"dry" => \$opt_dry,
     ) or pod2usage;
 
     my %cf = get_configs($CONFIG_DIR);
     my %default = %{$cf{DEFAULT}};
     ### config: %cf
 
-    my %dev = get_candidates();
-    ### current devices: %dev
+    my @dev = get_candidates();
+    ### current candiates: @dev
 
     my $ftp = new FTP($default{FTP_HOST}, 
 	Passive => $default{FTP_PASSIVE}, 
 	Debug => @opt_debug ~~ /^ftp$/) or die $@;
     $ftp->login or die $ftp->message;
+    $ftp->try(binary => ());
     $ftp->try(mkpath => $default{FTP_DIR});    
     $ftp->try(cwd => $default{FTP_DIR});
 
@@ -68,27 +75,61 @@
     verbose "Now in @{[$ftp->pwd]}.\n";
 
     # and now we can start doing something with our filesystems
-    foreach my $dev (keys %dev) {
-	my $file = basename($dev) . ".$opt_level.gz.ssl";
-	my $label = "$NODE:" . basename($dev{$dev});
-	verbose "Working on $dev as $dev{$dev}, stored as $file\n";
+    foreach my $dev (@dev) {
+
+	my $file = basename($dev->{dev}) . ".$opt_level.gz.ssl";
+	my $label = "$NODE:" . basename($dev->{rdev});
+	verbose "Working on $dev->{dev} as $dev->{rdev}, stored as $file\n";
+
+	# For LVM do a snapshot, for regular partitions
+	# do nothing. But anyway the device to dump is named in $dev->{dump}
+	if ($dev->{lvm}) {
+	    # we can do a snapshot
+	    # FIXME: calculate the size
+	    my $snap = "$dev->{lvm}{path}-0";
+
+	    verbose "Creating snapshot $snap\n";
+	    system($_ = "lvcreate -s -L 1G -n $snap $dev->{lvm}{path} >/dev/null");
+	    die "failed system command: $_\n" if $?;
+
+	    $dev->{cleanup} = sub { system "lvdisplay $snap &>/dev/null"
+				      . " && lvremove -f $snap >/dev/null" };
+	    push @AT_EXIT, $dev->{cleanup};
+
+	    (my $device) = (grep /lv name/i, `lvdisplay $snap`)[0] =~ /(\S+)\s*$/;
+
+	    system($_ = "fsck -f -C0 -y $device");
+	    warn "fsck on $device (using: $_) failed\n" if $?;
+
+	    ($dev->{dump}) = $device;
+
+	}
+	else {
+	    $dev->{dump} = $dev->{rdev}
+	}
+
+	### $dev
+
 	$ENV{key} = $default{KEY};
 	my $dumper = open(my $dump, "-|") or do {
 	    my $head = <<__;
 #! /bin/bash
-echo "LEVEL $opt_level: $dev $dev{$dev}"
-read -p "sure to restore? (yes/no): "
-test "\$REPLY" = "yes" || exit
-exec dd if=\$0 bs=10k skip=1 | openssl enc -d -blowfish "\$@" | gzip -d | restore -rf-
+echo "LEVEL $opt_level: $dev->{dev} $dev->{rdev} ($dev->{dump})" >&2
+tail -c XXXX \$0 | openssl enc -d -blowfish "\$@" | gzip -d
 exit
+
 __
-	    print $head, " " x (10240 - length($head) - 1), "\n";
-	    exec "dump -$opt_level -L $label -f- -u $dev{$dev}"
+	    # adjust the placeholder
+	    $head =~ s/XXXX/sprintf "% 4s", "+" . (length($head) +1)/e;
+	    print $head;
+	    exec "dump -$opt_level -L $label -f- -u $dev->{dump}"
 	    . "| gzip"
 	    . "| openssl enc -pass env:key -salt -blowfish";
 	    die "Can't exec dumper\n";
 	};
+
 	$ftp->try(put => $dump, $file);
+	$dev->{cleanup}->() if $dev->{cleanup};
 	verbose "Done.\n";
     }
 
@@ -102,7 +143,12 @@
 sub get_candidates() {
 # return the list of backup candidates
 
-    my %dev;
+    my @dev;
+
+    # later we need the major of the device mapper
+    my $dev_mapper = 0;
+    $_ = (grep /device.mapper/, slurp("/proc/devices"))[0]
+	and $dev_mapper = (split)[0];
 
     foreach (slurp("/etc/fstab")) {
 	my ($dev, $mp, $fstype, $options, $dump, $check)
@@ -114,10 +160,25 @@
 	if ($dev ~~ /^(LABEL|UUID)=/) {
 	    chomp($rdev = `blkid -c /dev/null -o device -t '$dev'`);
 	}
-	$dev{$dev} = $rdev;
+	$rdev = readlink $rdev while -l $rdev;
+
+	# if it's LVM we gather more information (to support snapshots)
+	my $lvm;
+	if ((stat $rdev)[6] >> 8 == $dev_mapper) {
+	    @{$lvm}{qw/vg lv/} = map { s/--/-/g; $_ } basename($rdev) =~ /(.+[^-])-([^-].+)/;
+	    $lvm->{path} = "$lvm->{vg}/$lvm->{lv}";
+	}
+
+	push @dev, {
+	    dev => $dev,
+	    rdev => $rdev,
+	    mount_point => $mp,
+	    fstype => $fstype,
+	    lvm => $lvm,
+	};
     }
 
-    return %dev;
+    return @dev;
 }
 
 sub get_configs($) {