# HG changeset patch # User Heiko Schlittermann # Date 1264979250 -3600 # Node ID 38af4add892ae5c4dafcfffbfda43c900efe2473 # Parent 28090de7d005682c82ff2c9afe99d239e4cb7e9a# Parent a61b92c603672c8b432afe0bf01b7e0a13bbf9b5 merged diff -r 28090de7d005 -r 38af4add892a is --- /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 +# 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 tool installs the image you saved with B. + +=head1 OPTIONS + +=over + +=item B<-b>|B<--base> I + +The directory where to look for the images. Each image should have +a unique subdirectory there. (default: I<..>) + +=item B<-s>|B<--source> I + +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: diff -r 28090de7d005 -r 38af4add892a lib/SI/dumper.pm --- 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: diff -r 28090de7d005 -r 38af4add892a lib/SI/grub.pm --- /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: diff -r 28090de7d005 -r 38af4add892a lib/SI/lvm.pm --- 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: diff -r 28090de7d005 -r 38af4add892a lib/SI/ptable.pm --- 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: diff -r 28090de7d005 -r 38af4add892a lib/SI/tools.pm --- 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($) {