Bootstrapping Vinum: A Foundation for Reliable Servers
Prev |  | Next

# A. bootvinum Perl Script

The `bootvinum` Perl script below reads `/etc/fstab` and current drive partitioning. It then writes several files in the current directory and several variants of `/etc/fstab` in `/etc`. These files significantly simplify the installation of Vinum and recovery from spindle failures.

    #!/usr/bin/perl -w
    use strict;
    use FileHandle;

    my $config_tag1 = '$Id: VinumBootstrap.sgml,v 1.28 2001/10/14 14:08:39 bob Exp bob $';
    # Copyright (C) 2001 Robert A. Van Valzah
    #
    # Bootstrap Vinum
    #
    # Read /etc/fstab and current partitioning for all spindles mentioned there.
    # Generate files needed to mirror all filesystems on root spindle.
    #  A new partition table for each spindle
    #  Input for the vinum create command to create Vinum objects on each spindle
    #  A copy of fstab mounting Vinum volumes instead of BSD partitions
    #  Copies of fstab altered for server's degraded modes of operation
    # See handbook for instructions on how to use the files generated.
    # N.B. This bootstrapping method shrinks size of swap partition by the size
    # of Vinum's on-disk configuration (265 sectors).  It embeds existing file
    # systems on the root spindle in Vinum objects without having to copy them.
    # Thanks to Greg Lehey for suggesting this bootstrapping method.
    # Expectations:
    #  The root spindle must contain at least root, swap, and /usr partitions
    #  The rootback spindle must have matching /rootback and swap partitions
    #  Other spindles should only have a /NOFUTURE* filesystem and maybe swap
    #  File systems named /NOFUTURE* will be replaced with Vinum drives

    # Change configuration variables below to suit your taste
    my $vip = 'h';					# VInum Partition
    my @drv = ('YouCrazy', 'UpWindow', 'ThruBank',	# Vinum DRiVe names
      'OutSnakes', 'MeWild', 'InMovie', 'HomeJames', 'DownPrices', 'WhileBlind');
    # No configuration variables beyond this point

    my %vols;		# One entry per Vinum volume to be created
    my @spndl;		# One entry per SPiNDLe
    my $rsp;		# Root SPindle (as in /dev/$rsp)
    my $rbsp;		# RootBack SPindle (as in /dev/$rbsp)
    my $cfgsiz = 265;	# Size of Vinum on-disk configuration info in sectors
    my $nxtpas = 2;		# Next fsck pass number for non-root filesystems

    # Parse fstab, generating the version we'll need for Vinum and noting
    # spindles in use.
    my $fsin = "/etc/fstab";
    #my $fsin = "simu/fstab";
    open(FSIN, "$fsin") || die("Couldn't open $fsin: $!\n");

    my $fsout = "/etc/fstab.vinum";
    open(FSOUT, ">$fsout") || die("Couldn't open $fsout for writing: $!\n");

    while (<FSIN>) {
      my ($dev, $mnt, $fstyp, $opt, $dump, $pass) = split;
      next if $dev =~ /^#/;
      if ($mnt eq '/' || $mnt eq '/rootback' || $mnt =~ /^\/NOFUTURE/) {
        my $dn = substr($dev, 5, length($dev)-6);	# Device Name without /dev/
        push(@spndl, $dn) unless grep($_ eq $dn, @spndl);
        $rsp = $dn if $mnt eq '/';
        next if $mnt =~ /^\/NOFUTURE/;
      }
      # Move /rootback from partition e to a
      if ($mnt =~ /^\/rootback/) {
        $dev =~ s/e$/a/;
        $pass = 1;
        $rbsp = substr($dev, 5, length($dev)-6);
        print FSOUT "$dev\t\t$mnt\t$fstyp\t$opt\t\t$dump\t$pass\n";
        next;
      }
      # Move non-root filesystems on smallest spindle into Vinum
      if (defined($rsp) && $dev =~ /^\/dev\/$rsp/ && $dev =~ /[d-h]$/) {
        $pass = $nxtpas++;
        print FSOUT "/dev/vinum$mnt\t\t$mnt\t\t$fstyp\t$opt\t\t$dump\t$pass\n";
        $vols{$dev}->{mnt} = substr($mnt, 1);
        next;
      }
      print FSOUT $_;
    }
    close(FSOUT);
    die("Found more spindles than we have abstract names\n") if $#spndl > $#drv;
    die("Didn't find a root partition!\n") if !defined($rsp);
    die("Didn't find a /rootback partition!\n") if !defined($rbsp);

    # Table of server's Degraded Modes
    # One row per mode with hash keys
    #   fn	FileName
    #   xpr	eXPRession needed to convert fstab lines for this mode
    #   cm1	CoMment 1 describing this mode
    #   cm2	CoMment 2 describing this mode
    #   FH  FileHandle (dynamically initialized below)
    my @DM = (
      { cm1 => "When we only have $rsp, comment out lines using $rbsp",
        fn  => "/etc/fstab_only_have_$rsp",
        xpr => "s:^/dev/$rbsp:#\$&:",
      },
      { cm1 => "When we only have $rbsp, comment out lines using $rsp and",
        cm2 => "rootback becomes root",
        fn  => "/etc/fstab_only_have_$rbsp",
        xpr => "s:^/dev/$rsp:#\$&: || s:/rootback:/\t:",
      },
      { cm1 => "When only $rsp root is bad, /rootback becomes root and",
        cm2 => "root becomes /rootbad",
        fn  => "/etc/fstab_${rsp}_root_bad",
        xpr => "s:\t/\t:\t/rootbad: || s:/rootback:/\t:",
      },
    );

    # Initialize output FileHandles and write comments
    foreach my $dm (@DM) {
      my $fh = new FileHandle;
      $fh->open(">$dm->{fn}") || die("Can't write $dm->{fn}: $!\n");
      print $fh "# $dm->{cm1}\n" if $dm->{cm1};
      print $fh "# $dm->{cm2}\n" if $dm->{cm2};
      $dm->{FH} = $fh;
    }

    # Parse the Vinum version of fstab written above and write versions needed
    # for server's degraded modes.
    open(FSOUT, "$fsout") || die("Couldn't open $fsout: $!\n");
    while (<FSOUT>) {
      my $line = $_;
      foreach my $dm (@DM) {
        $_ = $line;
        eval $dm->{xpr};
        print {$dm->{FH}} $_;
      }
    }

    # Parse partition table for each spindle and write versions needed for Vinum
    my $rootsiz;	# ROOT partition SIZe
    my $swapsiz;	# SWAP partition SIZe
    my $rspminoff;	# Root SPindle MINimum OFFset of non-root, non-swap, non-c parts
    my $rspsiz;	# Root SPindle SIZe
    my $rbspsiz;	# RootBack SPindle SIZe
    foreach my $i (0..$#spndl) {
      my $dlin = "disklabel $spndl[$i] |";
    #  my $dlin = "simu/disklabel.$spndl[$i]";
      open(DLIN, "$dlin") || die("Couldn't open $dlin: $!\n");

      my $dlout = "disklabel.$spndl[$i]";
      open(DLOUT, ">$dlout") || die("Couldn't open $dlout for writing: $!\n");

      my $dlb4 = "$dlout.b4vinum";
      open(DLB4, ">$dlb4") || die("Couldn't open $dlb4 for writing: $!\n");

      my $minoff;		# MINimum OFFset of non-root, non-swap, non-c partitions
      my $totsiz = 0;	# TOTal SIZe of all non-root, non-swap, non-c partitions
      my $swapspndl = 0;	# True if SWAP partition on this SPiNDLe
      while (<DLIN>) {
        print DLB4 $_;
        my ($part, $siz, $off, $fstyp, $fsiz, $bsiz, $bps) = split;

        if ($part && $part eq 'a:' && $spndl[$i] eq $rsp) {
    	$rootsiz = $siz;
        }
        if ($part && $part eq 'e:' && $spndl[$i] eq $rbsp) {
          if ($rootsiz != $siz) {
    	die("Rootback size ($siz) != root size ($rootsiz)\n");
          }
        }
        if ($part && $part eq 'c:') {
          $rspsiz  = $siz if $spndl[$i] eq $rsp;
          $rbspsiz = $siz if $spndl[$i] eq $rbsp;
        }
        # Make swap partition $cfgsiz sectors smaller
        if ($part && $part eq 'b:') {
          if ($spndl[$i] eq $rsp) {
    	$swapsiz = $siz;
          } else {
    	if ($swapsiz != $siz) {
    	  die("Swap partition sizes unequal across spindles\n");
    	}
          }
          printf DLOUT "%4s%9d%9d%10s\n", $part, $siz-$cfgsiz, $off, $fstyp;
          $swapspndl = 1;
          next;
        }
        # Move rootback spindle e partitions to a
        if ($part && $part eq 'e:' && $spndl[$i] eq $rbsp) {
          printf DLOUT "%4s%9d%9d%10s%9d%6d%6d\n", 'a:', $siz, $off, $fstyp,
    	$fsiz, $bsiz, $bps;
          next;
        }
        # Delete non-root, non-swap, non-c partitions but note their minimum
        # offset and total size that're needed below.
        if ($part && $part =~ /^[d-h]:$/) {
          $minoff = $off unless $minoff;
          $minoff = $off if $off < $minoff;
          $totsiz += $siz;
          if ($spndl[$i] eq $rsp) {	# If doing spindle containing root
    	my $dev = "/dev/$spndl[$i]" . substr($part, 0, 1);
    	$vols{$dev}->{siz} = $siz;
    	$vols{$dev}->{off} = $off;
    	$rspminoff = $minoff;
          }
          next;
        }
        print DLOUT $_;
      }
      if ($swapspndl) {	# If there was a swap partition on this spindle
        # Make a Vinum partition the size of all non-root, non-swap,
        # non-c partitions + the size of Vinum's on-disk configuration.
        # Set its offset so that the start of the first subdisk it contains
        # coincides with the first filesystem we're embedding in Vinum.
        printf DLOUT "%4s%9d%9d%10s\n", "$vip:", $totsiz+$cfgsiz, $minoff-$cfgsiz,
          'vinum';
      } else {
        # No need to mess with size and offset if there was no swap
        printf DLOUT "%4s%9d%9d%10s\n", "$vip:", $totsiz, $minoff,
          'vinum';
      }
    }
    die("Swap partition not found\n") unless $swapsiz;
    die("Swap partition not larger than $cfgsiz blocks\n") unless $swapsiz>$cfgsiz;
    die("Rootback spindle size not >= root spindle size\n") unless $rbspsiz>=$rspsiz;

    # Generate input to vinum create command needed for each spindle.
    foreach my $i (0..$#spndl) {
      my $cfn = "create.$drv[$i]";	# Create File Name
      open(CF, ">$cfn") || die("Can't open $cfn for writing: $!\n");
      print CF "drive $drv[$i] device /dev/$spndl[$i]$vip\n";
      next unless $spndl[$i] eq $rsp || $spndl[$i] eq $rbsp;
      foreach my $dev (keys(%vols)) {
        my $mnt = $vols{$dev}->{mnt};
        my $siz = $vols{$dev}->{siz};
        my $off = $vols{$dev}->{off}-$rspminoff+$cfgsiz;
        print CF "volume $mnt\n" if $spndl[$i] eq $rsp;
        print CF <<EOF;
      plex name $mnt.p$i org concat volume $mnt
        sd name $mnt.p$i.s0 drive $drv[$i] plex $mnt.p$i len ${siz}s driveoffset ${off}s
    EOF
      }
    }

Prev | Home | Next
Failure Scenarios |  | Manual Vinum Bootstrapping