--- 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($) {