merged stable
authorHeiko Schlittermann <hs@schlittermann.de>
Mon, 01 Feb 2010 00:07:30 +0100
branchstable
changeset 38 38af4add892a
parent 34 28090de7d005 (current diff)
parent 37 a61b92c60367 (diff)
child 39 06bffc9d8977
merged
lib/SI/dumper.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/is	Mon Feb 01 00:07:30 2010 +0100
@@ -0,0 +1,99 @@
+#! /usr/bin/perl
+# system imager - proof of concept
+# (c) 2010 Heiko Schlittermann <hs@schlittermann.de>
+# see http://keller.schlittermann.de/hg/ius/si
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use File::Basename;
+use Data::Dumper;
+
+use lib "lib";
+use SI::system;
+use SI::ptable;
+use SI::lvm;
+use SI::dumper;
+use SI::grub;
+
+my $ME = basename $0;
+my $opt_base = "..";
+my $opt_src = undef;
+my $opt_verbose = undef;
+
+
+MAIN: {
+
+    GetOptions(
+	"base=s" => \$opt_base,
+	"src=s"	=> \$opt_src,
+	"help" => sub { pod2usage(-verbose => 1, exit => 0) },
+	"man" => sub { pod2usage(-verbose => 2, exit => 0) },
+	"verbose" => \$opt_verbose,
+    ) or pod2usage;
+		
+    my $id = SI::system::id();
+
+    # now check if we find a suitable image
+    my $src = defined $opt_src ? $opt_src : "image-$id";
+    $src = "$opt_base/$src" if $src !~ /\//;
+
+    -d $src or die "$ME: $src: $!\n";
+
+    our $VAR1;
+    do "$src/info/devices";
+    my %devices = %$VAR1;
+
+    #SI::ptable::restore(%devices);
+    #SI::ptable::mkfs(%devices);
+    #SI::lvm::pvcreate(%devices);
+    #SI::lvm::vgcfgrestore("$src/lvm/vg.*", %devices);
+    #SI::lvm::mkfs(%devices);
+    #SI::dumper::restore("$src/dump/*", %devices);
+    SI::grub::restore(%devices);
+
+
+    exit;
+    die Dumper \%devices;
+
+}
+
+__END__
+
+=head1 NAME
+
+is - revert si (recover from system image)
+
+=head1 SYNOPSIS
+
+  is [--base=BASE] [--source=SOURCE]
+  is --help
+  is --man
+
+=head1 DESCRIPTION
+
+This B<is> tool installs the image you saved with B<si>.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-b>|B<--base> I<base>
+
+The directory where to look for the images. Each image should have 
+a unique subdirectory there. (default: I<..>)
+
+=item B<-s>|B<--source> I<source>
+
+The source of the image. If the source does not contain a slash ("/"), the
+it is expected to be a subdirectory of the base (see option B<--base>).
+
+Normally the source directories are named by the MAC address of the system. 
+For convenience a symbolic link with the hostname of the saved systems may be
+in place. (default: mac address)
+
+=back
+
+=cut
+# vim:sts=4 sw=4 aw ai si:
--- a/lib/SI/dumper.pm	Fri Jan 22 23:28:45 2010 +0100
+++ b/lib/SI/dumper.pm	Mon Feb 01 00:07:30 2010 +0100
@@ -5,6 +5,8 @@
 use warnings;
 use SI::tools;
 use File::Basename;
+use File::Temp qw(tempdir);
+use File::Find;
 
 sub dump($\%) {
     my ($file, $devs) = @_;
@@ -25,6 +27,39 @@
     }
 }
 
+sub restore($\%) {
+    my @dumps = glob(shift);
+    my $devs = shift;
+
+    my $tmpdir = tempdir (CLEANUP => 1);
+
+    foreach my $dump (@dumps) {
+	# suppose it's gzipped
+	# find the device
+	my $dev;
+	my $base = basename($dump);
+	find(sub { 
+	    return if defined $dev;
+	    if ($_ eq $base) {
+		$dev = $File::Find::name;
+		return;
+	    } }, "/dev");
+	my $fs = $devs->{volume}{$dev}{type};
+
+	verbose("restore $dump -> $dev : $fs\n");
+	run("mount -t $fs $dev $tmpdir");
+	my $pid = fork or do {
+	    open(STDIN, $dump) or die "Can't open $dump as STDIN: $!\n";
+	    chdir $tmpdir  or die "Can't chdir to $tmpdir: $!\n";
+	    exec restore => "-orf-";
+	    die "Can't exec restore: $!\n";
+	};
+	waitpid($pid, 0);
+	run("umount $tmpdir");
+    }
+}
+
+
 1;
 
 # vim:sts=4 sw=4 aw ai sm:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/SI/grub.pm	Mon Feb 01 00:07:30 2010 +0100
@@ -0,0 +1,72 @@
+package SI::grub;
+
+use if $ENV{DEBUG} ~~ /grub|all/ => "Smart::Comments";
+use strict;
+use warnings;
+use File::Temp qw(tempdir);
+use IO::File;
+
+use SI::tools;
+
+sub restore(\%) {
+    my $devs = shift;
+
+    # assume the first ext is the boot device (should have
+    # been labeled
+
+    my ($boot, undef) = grep { $devs->{volume}{$_}{type} =~ /^ext/ }
+    grep { $devs->{volume}{$_}{origin} eq "ptable" }
+    grep { defined $devs->{volume}{$_}{type} } 
+    sort keys %{$devs->{volume}};
+
+    # now find the partition containing the fstab, it should
+    # be the root file system
+    my $tmpdir = tempdir(CLEANUP => 0);
+
+    my @fs = 
+	grep { $devs->{volume}{$_}{type} =~ /^ext/ }
+	grep { defined $devs->{volume}{$_}{type} }
+	keys %{$devs->{volume}};
+
+
+    my @mounted;
+    eval {
+    
+	foreach (@fs) {
+	    run("mount -oro -t $devs->{volume}{$_}{type} $_ $tmpdir");
+	    last if -f "$tmpdir/etc/fstab";
+	    run("umount $tmpdir");
+	    push @mounted, $tmpdir;
+	}
+
+	# and now try to mount the rest
+
+
+	my $fstab = new IO::File "$tmpdir/etc/fstab";
+	foreach (grep { /^\// } <$fstab>) {
+	    my ($dev, $mp, $type, $options, undef) = split;
+	    next if $options =~ /noauto/
+		or $mp eq "/"
+		or $mp !~ /^\//;
+	    run("mount -t $type $dev $tmpdir/$mp");
+	    push @mounted, "$tmpdir/$mp";
+	}
+	close($fstab);
+
+	run("mount -o bind /dev $tmpdir/dev");
+	push @mounted, "$tmpdir/dev";
+
+	run("chroot $tmpdir grub-mkdevicemap");
+	run("chroot $tmpdir /usr/sbin/grub-install '(hd0)'");
+	#system("/bin/bash --login");
+    };
+    if ($@) {
+	warn "** EVAL: $@\n";
+    }
+    map { system("umount $_") } reverse @mounted;
+    rmdir($tmpdir);
+
+}
+
+1;
+# vim:sts=4 sw=4 aw ai sm:
--- a/lib/SI/lvm.pm	Fri Jan 22 23:28:45 2010 +0100
+++ b/lib/SI/lvm.pm	Mon Feb 01 00:07:30 2010 +0100
@@ -7,6 +7,7 @@
 use IO::File;
 use Cwd qw(abs_path);
 use File::Basename;
+use feature "switch";
 
 use SI::tools;
 
@@ -67,6 +68,51 @@
     run("vgcfgbackup -f '$file' >/dev/null");
 }
 
+sub pvcreate(\%) {
+    my $devs = shift;
+    foreach my $volume ( keys %{$devs->{volume}} ) {
+	my $v = $devs->{volume}{$volume};
+	next if $v->{origin} ne "ptable"
+	    or not defined $v->{type}
+	    or $v->{type} !~ /^lvm/i;
+	run("pvcreate -y -ff -u $v->{uuid} $volume");
+    }
+}
+
+sub vgcfgrestore($\%) {
+    my @cfgs = glob($_[0]);
+    my $devs = shift;
+
+    foreach (@cfgs) {
+	my $vg = (split /\./, basename($_))[1];
+	next if $vg eq "*";
+	run("vgcfgrestore -f $_ $vg");
+	run("vgchange -ay $vg");
+    }
+    run("udevadm settle");
+}
+
+sub mkfs(\%) {
+    my $devs = shift;
+    foreach my $volume ( keys %{$devs->{volume}} ) {
+	my $v = $devs->{volume}{$volume};
+	next if $v->{origin} ne "lvm"
+	    or not defined $v->{type};
+	
+	my $label = defined $v->{label} ? "-L '$v->{label}'" : "";
+	my $uuid = defined $v->{uuid} ? "-U '$v->{uuid}'" : "";
+
+	given($v->{type}) {
+	    when(/ext/) {
+		run("mkfs -t $v->{type} $label $uuid $volume");
+	    };
+	    when(/swap/) {
+		run("mkswap $label $uuid $volume");
+	    };
+	}
+    }
+}
+
 1;
 
 # vim:sts=4 sw=4 aw ai si:
--- a/lib/SI/ptable.pm	Fri Jan 22 23:28:45 2010 +0100
+++ b/lib/SI/ptable.pm	Mon Feb 01 00:07:30 2010 +0100
@@ -7,6 +7,7 @@
 use File::Find;
 use File::Basename;
 use IO::File;
+use feature "switch";
 
 use SI::tools;
 $ENV{LC_ALL} = "C";
@@ -65,6 +66,47 @@
 
     return;
 }
+
+sub restore(\%) {
+    my $devs = shift;
+
+    foreach my $disk (keys %{$devs->{disk}}) {
+	verbose("restoring partition table of $disk\n");
+	die "I won't use the disk ($disk) I'm running on!" 
+	    if (stat $0)[0] ~~ [ map { (stat)[6] } map { "/dev/" . basename dirname $_ } glob ("/sys/block/" . basename($disk) . "/*/partition")];
+
+	    my $sfdisk = new IO::File "|sfdisk --force $disk >/dev/null"
+		or die "Can't open |sfdisk $disk: $!\n";
+	    print {$sfdisk} @{$devs->{disk}{$disk}{pt}};
+	    $sfdisk->close or die $? >> 8;
+	    run("partprobe $disk");
+	    run("udevadm settle");
+    }
+}
+
+sub mkfs(\%) {
+    my $devs = shift;
+    foreach my $volume ( keys %{$devs->{volume}} ) {
+	my $v = $devs->{volume}{$volume};
+	next if $v->{origin} ne "ptable";
+	warn $volume, "\n";
+	next if not defined $v->{type};
+	given ($v->{type}) {
+	    when(/ext\d/) {
+		my $label = defined $v->{label} ? $v->{label} : "";
+		my $uuid = defined $v->{uuid} ? $v->{uuid} : "";
+		run("mkfs -t $v->{type} -L '$label' -U '$uuid' $volume");
+	    };
+	    when("swap") {
+		my $label = defined $v->{label} ? $v->{label} : "";
+		my $uuid = defined $v->{uuid} ? $v->{uuid} : "";
+		run("mkswap -L '$label' -U '$uuid' $volume");
+	    };
+	};
+
+    }
+
+}
 1;
 
 # vim:sts=4 sw=4 aw ai si:
--- a/lib/SI/tools.pm	Fri Jan 22 23:28:45 2010 +0100
+++ b/lib/SI/tools.pm	Mon Feb 01 00:07:30 2010 +0100
@@ -8,7 +8,7 @@
 use Data::Dumper;
 use base "Exporter";
 
-our @EXPORT = qw(&run &verbose &find_by_devid &cat &barf);
+our @EXPORT = qw(&run &verbose &find_by_devid &cat &barf &def);
 
 sub run(@) {
     system(@_);
@@ -16,6 +16,10 @@
       if $?;
 }
 
+sub def($$) {
+    return defined $_[1] ? $_[1] : $_[0];
+}
+
 sub barf(@) { die Dumper @_ }
 
 sub cat($) {