# HG changeset patch # User Heiko Schlittermann # Date 1256511227 -3600 # Node ID 5f03a7843dc2c93832a6c7835a2a3cc9dcac2106 # Parent 505c5bd2327934f07cb60b3dbede8c7738fb1627 works for level 0 diff -r 505c5bd23279 -r 5f03a7843dc2 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($) {