[Fai-commit] r4713 - in people/michael/features/setup_harddisks_2/implementation: . lib

michael-guest at alioth.debian.org michael-guest at alioth.debian.org
Sun Nov 11 11:21:59 UTC 2007


Author: michael-guest
Date: 2007-11-11 11:21:59 +0000 (Sun, 11 Nov 2007)
New Revision: 4713

Added:
   people/michael/features/setup_harddisks_2/implementation/lib/
   people/michael/features/setup_harddisks_2/implementation/lib/commands.pm
   people/michael/features/setup_harddisks_2/implementation/lib/exec.pm
   people/michael/features/setup_harddisks_2/implementation/lib/fstab.pm
   people/michael/features/setup_harddisks_2/implementation/lib/init.pm
   people/michael/features/setup_harddisks_2/implementation/lib/parser.pm
   people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm
   people/michael/features/setup_harddisks_2/implementation/lib/volumes.pm
   people/michael/features/setup_harddisks_2/implementation/storage-magic
Removed:
   people/michael/features/setup_harddisks_2/implementation/shdd2
   people/michael/features/setup_harddisks_2/implementation/shdd2-commands
   people/michael/features/setup_harddisks_2/implementation/shdd2-exec
   people/michael/features/setup_harddisks_2/implementation/shdd2-fstab
   people/michael/features/setup_harddisks_2/implementation/shdd2-init
   people/michael/features/setup_harddisks_2/implementation/shdd2-parser
   people/michael/features/setup_harddisks_2/implementation/shdd2-sizes
   people/michael/features/setup_harddisks_2/implementation/shdd2-volumes
Log:
moved the files around to later use /usr/share/fai/storage-magic/


Copied: people/michael/features/setup_harddisks_2/implementation/lib/commands.pm (from rev 4712, people/michael/features/setup_harddisks_2/implementation/shdd2-commands)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/commands.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/commands.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,652 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-commands
+#
+# @brief Build the required commands using the config stored in %FAI::configs
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+################################################################################
+#
+# @brief Build the mkfs commands for the partition pointed to by $partition
+#
+# @param $device Device name of the target partition
+# @param $partition Reference to partition in the config hash
+#
+# The command is added @FAI::commands
+#
+################################################################################
+sub build_mkfs_commands {
+  my ( $device, $partition ) = @_;
+
+  defined( $partition->{"filesystem"} )
+    or die "INTERNAL ERROR: filesystem is undefined\n";
+
+  return if ( $partition->{"filesystem"} eq "-" );
+
+  my ($create_options)=$partition->{"fs_options"}=~m/.*createopts="([^"]+)".*/;
+  my ($tune_options)=$partition->{"fs_options"}=~m/.*tuneopts="([^"]+)".*/;
+  $create_options = $partition->{"fs_options"} unless $create_options;
+
+  if ( $partition->{"filesystem"} eq "swap" )
+  {
+    push @FAI::commands, "mkswap " . $create_options . " $device";
+  }
+  else
+  {
+    print STDERR "create_options: $create_options tune_options: $tune_options\n" if $FAI::debug;
+    push @FAI::commands,
+      "mkfs."
+      . $partition->{"filesystem"} . " "
+      . $create_options
+      . " " . $device;
+    push @FAI::commands,
+      "tune2fs "
+      . $tune_options
+      . " " . $device if $tune_options;
+  }
+}
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to create any RAID devices
+#
+# The list is @FAI::commands
+#
+################################################################################
+sub build_raid_commands {
+  # TODO: do we need to stop anything before we continue? Do we need to issue
+  # mdadm --misc --zero-superblock /dev/hdx?
+
+  # loop through all configs
+  foreach my $config ( keys %FAI::configs ) {
+
+    # no LVM here
+    next if ( $config =~ /^VG_(.+)$/ );
+
+    # no physical devices here
+    next if ( $config =~ /^PHY_(.+)$/ );
+
+    # create the RAID devices and the filesystems
+    ( $config eq "RAID" ) or die "INTERNAL ERROR: Invalid config\n";
+
+    # create all raid devices
+    foreach my $id ( sort keys %{ $FAI::configs{$config}{"volumes"} } ) {
+
+      # the desired RAID level
+      my $level = $FAI::configs{$config}{"volumes"}{$id}{"mode"};
+
+      # prepend "raid", if the mode is numeric-only
+      $level = "raid" . $level if ( $level =~ /^\d+$/ );
+
+      # the list of RAID devices
+      my @devs = keys %{ $FAI::configs{$config}{"volumes"}{$id}{"devices"} };
+
+      # set proper partition types for RAID
+      foreach my $d (@devs) {
+        # skip devices marked missing
+        next if( 1 ==
+          $FAI::configs{$config}{"volumes"}{$id}{"devices"}{$d}{"missing"} );
+        # only match physical partitions (this string of matchings is hopefully complete)
+        next unless( $d =~
+          m{^/dev/(cciss/c\dd\dp|ida/c\dd\dp|rd/c\dd\dp|ataraid/d\dp|sd[a-t]|hd[a-t])(\d+)$} );
+        my $disk = "/dev/$1";
+        my $part_no = $2;
+        # in case the name was /dev/cciss/c0d1p or the like, remove the trailing
+        # p to get the disk name
+        $disk =~ s/(\d)p$/$1/;
+        # make sure this device really exists (we can't check for the partition
+        # as that may be created later on
+        ( -b $disk ) or die "Specified disk $disk does not exist in this system!\n";
+        # set the raid flag
+        push @FAI::commands, "parted -s $disk set $part_no raid on";
+      }
+      # wait for udev to set up all devices
+      push @FAI::commands, "udevsettle --timeout=10";
+      
+      # create the command
+      push @FAI::commands,
+        "yes | mdadm --create /dev/md$id --level=$level "
+        . "--raid-devices="
+        . scalar(@devs) . " "
+        . join( " ", @devs );
+
+      # create the filesystem on the volume
+      &FAI::build_mkfs_commands( "/dev/md$id",
+        \%{ $FAI::configs{$config}{"volumes"}{$id} } );
+    }
+  }
+}
+
+################################################################################
+#
+# @brief Erase the LVM signature from a list of devices that should be prestine
+# in order to avoid confusion of the lvm tools
+#
+# The list is @FAI::commands
+#
+################################################################################
+sub erase_lvm_signature {
+    my( $devices_aref ) = @_;
+      # first remove the dm_mod module to prevent ghost lvm volumes 
+      # from existing
+#      push @FAI::commands, "modprobe -r dm_mod";
+      # zero out (broken?) lvm signatures
+#      push @FAI::commands, "dd if=/dev/zero of=$_ bs=1 count=1"
+#        foreach ( @{$devices_aref} );
+    my $device_list = join(" ", (@{$devices_aref}) );
+    ( $FAI::debug > 0 ) and print "list of erased devices: $device_list\n"; 
+    push @FAI::commands, "pvremove -ff -y $device_list";
+
+      # reload module
+#      push @FAI::commands, "modprobe dm_mod";
+
+}
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to setup the LVM
+#
+# The list is @FAI::commands
+#
+################################################################################
+sub build_lvm_commands {
+  # loop through all configs
+  foreach my $config ( keys %FAI::configs ) {
+    # no physical devices here
+    next if ( $config =~ /^PHY_(.+)$/ );
+
+    # no RAID devices here
+    next if ( $config eq "RAID" );
+
+    # create the volume groups, the logical volumes and the filesystems
+    ( $config =~ /^VG_(.+)$/ ) or die "INTERNAL ERROR: Invalid config\n";
+
+    # the volume group
+    my $vg = $1;
+
+    # find volumes that should be preserved or resized and ensure that they
+    # already exist
+    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } ) {
+      next
+        unless ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1
+        || $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} == 1 );
+
+      # preserved or resized volumes must exist already
+      defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
+        or die "/dev/$vg/$lv can't be preserved, it does not exist.\n";
+    }
+
+    # set proper partition types for LVM
+    foreach my $d (keys %{ $FAI::configs{$config}{"devices"} }) {
+      # only match physical partitions (this string of matchings is hopefully complete)
+      next unless( $d =~
+        m{^/dev/(cciss/c\dd\dp|ida/c\dd\dp|rd/c\dd\dp|ataraid/d\dp|sd[a-t]|hd[a-t])(\d+)$} );
+      my $disk = "/dev/$1";
+      my $part_no = $2;
+      # in case the name was /dev/cciss/c0d1p or the like, remove the trailing
+      # p to get the disk name
+      $disk =~ s/(\d)p$/$1/;
+      # make sure this device really exists (we can't check for the partition
+      # as that may be created later on
+      ( -b $disk ) or die "Specified disk $disk does not exist in this system!\n";
+      # set the lvm flag
+      push @FAI::commands, "parted -s $disk set $part_no lvm on";
+    }
+    # wait for udev to set up all devices
+    push @FAI::commands, "udevsettle --timeout=10";
+
+    # create the volume group, if it doesn't exist already
+    if ( !defined( $FAI::current_lvm_config{$vg} ) ) {
+      # create all the devices
+      my @devices = keys %{ $FAI::configs{$config}{"devices"} };
+      &FAI::erase_lvm_signature(\@devices);
+      push @FAI::commands, "pvcreate $_"
+        foreach ( @devices );
+      # create the volume group
+      push @FAI::commands, "vgcreate $vg "
+        . join( " ", keys %{ $FAI::configs{$config}{"devices"} } );
+    }
+
+    # otherwise add or remove the devices for the volume group, run pvcreate
+    # where needed (using pvdisplay <bla> || pvcreate <bla>)
+    else {
+
+      # the list of devices to be created
+      my %new_devs = ();
+
+      # create an undefined entry for each new device
+      @new_devs{ keys %{ $FAI::configs{$config}{"devices"} } } = ();
+      
+      my @new_devices = keys %new_devs;
+      
+      erase_lvm_signature( \@new_devices );
+      
+      # create all the devices
+      push @FAI::commands, "pvcreate $_"
+        foreach ( @new_devices );
+
+      # extend the volume group by the new devices (includes the current ones)
+      push @FAI::commands, "vgextend $vg " . join( " ", keys %new_devs );
+
+      # the devices to be removed
+      my %rm_devs = ();
+      @rm_devs{ @{ $FAI::current_lvm_config{$vg}{"physical_volumes"} } } = ();
+
+      # remove remaining devices from the list
+      delete $rm_devs{$_} foreach ( keys %new_devs );
+
+      # run vgreduce to get them removed
+      push @FAI::commands, "vgreduce $vg " . join( " ", keys %rm_devs )
+        if ( scalar( keys %rm_devs ) );
+    }
+
+    # enable the volume group
+    push @FAI::commands, "vgchange -a y $vg";
+
+    # remove, resize, create the logical volumes
+    # remove all volumes that do not exist anymore or need not be preserved
+    foreach my $lv ( keys %{ $FAI::current_lvm_config{$vg}{"volumes"} } ) {
+      # skip preserved/resized volumes
+      next if ( defined( $FAI::configs{$config}{"volumes"}{$lv} )
+        && ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1
+          || $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} ));
+
+      # remove $lv
+      push @FAI::commands, "lvremove -f $vg/$lv";
+    }
+
+    # now create or resize the configured logical volumes
+    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } ) {
+      # skip preserved partitions, but ensure that they exist
+      if ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1 ) {
+        defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
+          or die "Preserved volume $vg/$lv does not exist\n";
+        next;
+      }
+
+      # resize the volume
+      if ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} == 1 ) {
+        defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
+          or die "Resized volume $vg/$lv does not exist\n";
+
+        # note that resizing a volume destroys the data on it
+        push @FAI::commands,
+          "lvresize -L "
+          . $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"}
+          . " $vg/$lv";
+      }
+
+      # create a new volume
+      else {
+        push @FAI::commands,
+          "lvcreate -n $lv -L "
+          . $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} . " $vg";
+
+        # create the filesystem on the volume
+        &FAI::build_mkfs_commands( "/dev/$vg/$lv",
+          \%{ $FAI::configs{$config}{"volumes"}{$lv} } );
+      }
+    }
+
+  }
+}
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to setup the partitions
+#
+# The list is @FAI::commands
+#
+################################################################################
+sub build_disk_commands {
+  # loop through all configs
+  foreach my $config ( keys %FAI::configs ) {
+    # no RAID devices here
+    next if ( $config eq "RAID" );
+
+    # no LVM here
+    next if ( $config =~ /^VG_(.+)$/ );
+
+    # configure a physical device
+    ( $config =~ /^PHY_(.+)$/ ) or die "INTERNAL ERROR: Invalid config\n";
+
+    # the device to be configured
+    my $disk = $1;
+
+    # create partitions on non-virtual configs
+    if ( $FAI::configs{$config}{"virtual"} == 0 ) {
+      # the list of partitions that must be preserved
+      my @to_preserve = ();
+
+      # find partitions that should be preserved or resized
+      foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
+        next unless (
+          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} == 1
+          || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1 );
+
+        # preserved or resized partitions must exist already
+        defined( $FAI::current_config{$disk}{"partitions"}{$part_id} )
+          or die "$part_id can't be preserved, it does not exist.\n";
+
+        # add a mapping from the configured partition to the existing one
+        # (identical here, may change for extended partitions below)
+        $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"} =
+          $part_id;
+
+        # add $part_id to the list of preserved partitions
+        push @to_preserve, $part_id;
+
+      }
+
+      # sort the list of preserved partitions
+      @to_preserve = sort { $a <=> $b } @to_preserve;
+
+      # add the extended partition as well, if logical partitions must be
+      # preserved; and mark it as resize
+      if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
+        # we assume there are no logical partitions
+        my $has_logical = 0;
+        my $extended    = -1;
+
+        # now check all entries; the array is sorted
+        foreach my $part_id (@to_preserve) {
+          # the extended partition may already be listed; then, the id of the
+          # extended partition must not change
+          if ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 ) {
+            ( defined( $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"extended"})
+                && defined( $FAI::current_config{$disk}{"partitions"}{$extended}{"is_extended"})
+                && $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"extended"} == 1
+                && $FAI::current_config{$disk}{"partitions"}{$extended}{"is_extended"} == 1 ) 
+                or die "ID of extended partition changes\n";
+
+            # make sure resize is set
+            $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} = 1;
+            $extended = $part_id;
+            last;
+          }
+
+          # there is some logical partition
+          if ( $part_id > 4 ) {
+            $has_logical = 1;
+            last;
+          }
+        }
+
+        # if the extended partition is not listed yet, find and add it now; note
+        # that we need to add the existing one
+        if ( 1 == $has_logical && -1 == $extended ) {
+          foreach my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } ) {
+
+            # no extended partition
+            next unless ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 );
+
+            # find the configured extended partition to set the mapping
+            foreach my $p ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
+              next unless (
+                $FAI::configs{$config}{"partitions"}{$p}{"size"}{"extended"} ==
+                1 );
+
+              # make sure resize is set
+              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"resize"} = 1;
+
+              # store the id for further checks
+              $extended = $p;
+
+              # add a mapping entry to the existing extended partition
+              $FAI::configs{$config}{"partitions"}{$p}{"maps_to_existing"} =
+                $part_id;
+
+              # add it to the preserved partitions
+              push @to_preserve, $p;
+
+              last;
+            }
+
+            # sort the list of preserved partitions (again)
+            @to_preserve = sort { $a <=> $b } @to_preserve;
+
+            last;
+          }
+        }
+
+        # a sanity check: if there are logical partitions, they extended must
+        # have been added
+        ( 0 == $has_logical || -1 != $extended ) or die
+          "INTERNAL ERROR: Required extended partition not detected for preserve\n";
+      }
+
+      # A new disk label may only be written if no partitions need to be
+      # preserved
+      ( ( $FAI::configs{$config}{'disklabel'} eq
+            $FAI::current_config{$disk}{'disklabel'})
+          || ( scalar(@to_preserve) == 0 ) ) 
+          or die "Can't change disklabel, partitions are to be preserved\n";
+
+      # write the disklabel to drop the previous partition table
+      push @FAI::commands, "parted -s $disk mklabel "
+        . $FAI::configs{$config}{'disklabel'};
+
+      # once we rebuild partitions, their ids are likely to change; this counter
+      # helps keeping track of this
+      my $part_nr = 0;
+
+      # now rebuild all preserved partitions
+      foreach my $part_id (@to_preserve) {
+        # get the existing id
+        my $mapped_id =
+          $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
+
+        # get the original starts and ends
+        my $start =
+          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"begin_byte"};
+        my $end =
+          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"end_byte"};
+
+        # the type of the partition defaults to primary
+        my $part_type = "primary";
+        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
+
+          # change the partition type to extended or logical as appropriate
+          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} == 1 ) {
+            $part_type = "extended";
+          } elsif ( $part_id > 4 ) {
+            $part_type = "logical";
+            $part_nr = 4 if ( $part_nr < 4 );
+          }
+        }
+
+        # increase the partition counter for the partition created next and
+        # write it to the configuration
+        $part_nr++;
+        $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"} =
+          $part_nr;
+
+        # build a parted command to create the partition
+        push @FAI::commands,
+          "parted -s $disk mkpart $part_type ${start}B ${end}B";
+      }
+
+      # resize partitions; first we shrink partitions, then grow others;
+      # furthermore we start from the end to shrink logical partitions before
+      # the extended one, but grow partitions starting from the beginning
+      my @shrink_list = reverse sort (@to_preserve);
+      my @grow_list   = ();
+
+      # iterate over the worklists
+      foreach my $part_id (@shrink_list) {
+        # anything to be done?
+        next unless (
+          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1 );
+
+        # get the existing id
+        my $mapped_id =
+          $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
+
+        # if partition is to be grown, move it to then grow_list
+        if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} >
+          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"count_byte"} ) {
+          unshift @grow_list, $part_id;
+          next;
+        }
+
+        # get the new partition id
+        my $p = $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"};
+
+        # get the new starts and ends
+        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
+        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
+
+        # build an appropriate command
+        push @FAI::commands, "parted -s $disk resize $p ${start}B ${end}B";
+      }
+
+      # grow the remaining partitions
+      foreach my $part_id (@grow_list) {
+
+        # get the existing id
+        my $mapped_id = $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
+
+        # get the new partition id
+        my $p = $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"};
+
+        # get the new starts and ends
+        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
+        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
+
+        # build an appropriate command
+        push @FAI::commands,
+          "parted -s $disk resize $p ${start}B ${end}B";
+      }
+
+      # write the disklabel again to drop the partition table
+      push @FAI::commands, "parted -s $disk mklabel " . $FAI::configs{$config}{'disklabel'};
+
+      # generate the commands for creating all partitions
+      foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
+
+        # get the new starts and ends
+        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
+        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
+
+        # the type of the partition defaults to primary
+        my $part_type = "primary";
+        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
+
+          # change the partition type to extended or logical as appropriate
+          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} == 1 ) {
+            $part_type = "extended";
+          } elsif ( $part_id > 4 ) {
+            $part_type = "logical";
+          }
+        }
+
+        # build a parted command to create the partition
+        push @FAI::commands,
+          "parted -s $disk mkpart $part_type ${start}B ${end}B";
+      }
+
+      # set the bootable flag, if requested at all
+      push @FAI::commands,
+        "parted -s $disk set "
+        . $FAI::configs{$config}{"bootable"}
+        . " boot on"
+        if ( $FAI::configs{$config}{"bootable"} > -1 );
+
+      # wait for udev to set up all devices
+      push @FAI::commands, "udevsettle --timeout=10";
+    }
+
+    # generate the commands for creating all filesystems
+    foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
+
+      # skip preserved/resized/extended partitions
+      next if (
+        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} == 1
+        || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1
+        || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} ==
+        1 );
+
+      # create the filesystem on $disk$part_id
+      &FAI::build_mkfs_commands( $disk . $part_id,
+        \%{ $FAI::configs{$config}{"partitions"}{$part_id} } );
+    }
+  }
+}
+
+################################################################################
+#
+# @brief Whatever happened, write the previous partition table to the disk again
+#
+################################################################################
+sub restore_partition_table {
+
+  # loop through all existing configs
+  foreach my $disk ( keys %FAI::current_config ) {
+
+    # write the disklabel again to drop the partition table
+    &FAI::execute_command( "parted -s $disk mklabel "
+        . $FAI::current_config{$disk}{'disklabel'} );
+
+    # generate the commands for creating all partitions
+    foreach my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } ) {
+
+      # get the starts and ends
+      my $start = $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"};
+      my $end = $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"};
+
+      # the type of the partition defaults to primary
+      my $part_type = "primary";
+      if ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" ) {
+
+        # change the partition type to extended or logical as appropriate
+        if ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 ) {
+          $part_type = "extended";
+        } elsif ( $part_id > 4 ) {
+          $part_type = "logical";
+        }
+      }
+
+      # build a parted command to create the partition
+      &FAI::execute_command( "parted -s $disk mkpart $part_type ${start}B ${end}B" );
+    }
+    warn "Partition table of disk $disk has been restored\n";
+  }
+
+  die "shdd2 failed, but the partition tables have been restored\n";
+}
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/exec.pm (from rev 4708, people/michael/features/setup_harddisks_2/implementation/shdd2-exec)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/exec.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/exec.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,255 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-exec
+#
+# @brief functions to execute system commands
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+use File::Temp;
+
+package FAI;
+
+################################################################################
+#
+# @brief hash, defined: errors, descriptions, actions on error
+#
+# @scalar error error
+# @scalar message our errormessage
+# @scalar stderr_regex regex to recognize the error message on stderr output of the bash
+# @scalar stdout_regex regex to recognize the error message on stdout output of the bash
+# @scalar program the program this error message can come from
+# @scalar response default action on this error.
+#
+################################################################################
+$FAI::error_codes = [
+  {
+    error   => "parted_1",
+    message => "Parted produced error. Couldn't remove partition\n",
+    stderr_regex =>
+      ".*Error: Could not stat device rm - No such file or directory.*",
+    stdout_regex => "",
+    program      => "parted",
+    response     => "die",
+  },
+  {
+    error        => "parted_2",
+    message      => "Parted produced error. Could not read disk label.\n",
+    stderr_regex => ".*Error: Unable to open .* - unrecognised disk label.*",
+    stdout_regex => "",
+    program      => "parted",
+    response     => "warn",
+  },
+  {
+    error   => "parted_3",
+    message => "Parted produced error. Could not open disk\n",
+    stderr_regex =>
+      ".*Error: Could not stat device .* - No such file or directory.*",
+    stdout_regex => "",
+    program      => "parted",
+    response     => "die"
+  },
+  {
+    error   => "parted_4",
+    message => "parted not found\n",
+    stderr_regex =>
+      ".*(parted: command not found|/sbin/parted: No such file or directory)",
+    stdout_regex => "",
+    program      => "parted",
+    response     => "die"
+  },
+  {
+    error        => "parted_5",
+    message      => "Parted was unable to create the partition\n",
+    stderr_regex => "Warning: You requested a partition from .* to .*\\.\$",
+    stdout_regex => "",
+    program      => "parted",
+    response     => \&FAI::restore_partition_table,
+  },
+  {
+    error => "mkfs.xfs_1",
+    message =>
+"mkfs.xfs refused to create a filesystem. Probably you should add -f to the mkfs options in your disk_config file.\n",
+    stderr_regex =>
+      "mkfs.xfs: /dev/.* appears to contain an existing filesystem",
+    stdout_regex => "",
+    program      => "mkfs.xfs",
+    response     => "die",
+  },
+];
+
+################################################################################
+#
+# @brief returns the error message associated with an error
+#
+# @param error identifier of an error
+#
+# @return our interpretation of the error as string
+#
+################################################################################
+sub get_error_message {
+  my ($error) = @_;
+  my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
+
+  # returns the first found error message.
+  return $treffer[0]->{'message'};
+}
+
+################################################################################
+#
+# @brief gets any part of the error struct associated with an error
+#
+# @param error identifier of an error
+# @param field field of the error struct as string, example: "stderr_regex"
+#
+# @return the associated value
+#
+################################################################################
+sub get_error {
+  my ( $error, $field ) = @_;
+  my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
+
+  # returns the first found error message.
+  return $treffer[0]->{$field};
+}
+################################################################################
+#
+# @brief execute a /bin/bash command, given as string. also catch stderr and
+# stdout, to be passed to the caller function, and also used for error
+# recognition. This execute function does execute the in the error struct
+# defined action, when an error occurs.
+#
+# @param command bash command to be executed as string
+# @reference stdout_ref reference to a list, that should contain the standard
+# output of the bash command
+#
+# @reference stderr_ref reference to a list, that should contain the standard
+# errer output of the bash command
+#
+# @return the identifier of the error
+#
+################################################################################
+sub execute_command_std {
+  my ( $command, $stdout_ref, $stderr_ref ) = @_;
+  my $err = &execute_command( $command, $stdout_ref, $stderr_ref );
+  if ( $err ne "" ) {
+    my $response = &get_error( $err, "response" );
+    my $message  = &get_error( $err, "message" );
+
+    $response->() if ( ref($response) );
+
+    die $message if ( $response eq "die" );
+
+    warn $message if ( $response eq "warn" );
+
+    return $err;
+  }
+  return "";
+}
+
+################################################################################
+#
+# @brief execute a /bin/bash command, given as string. also catch stderr and
+# stdout, to be passed to the caller function, and also used for error
+# recognition. This caller function must handle the error.
+#
+# @param command bash command to be executed as string
+# @reference stdout_ref reference to a list, that should contain the standard
+# output of the bash command
+#
+# @reference stderr_ref reference to a list, that should contain the standard
+# error output of the bash command
+#
+# @return the identifier of the error
+#
+################################################################################
+sub execute_command {
+  my ( $command, $stdout_ref, $stderr_ref ) = @_;
+
+  my @stderr      = ();
+  my @stdout      = ();
+  my $stderr_line = "";
+  my $stdout_line = "";
+
+  #make tempfile, get perl filehandle and filename of the file
+  ( my $stderr_fh, my $stderr_filename ) = File::Temp::tempfile( UNLINK => 1 );
+  ( my $stdout_fh, my $stdout_filename ) = File::Temp::tempfile( UNLINK => 1 );
+
+  # do only execute the given command, when in no_dry_mode
+  if ($FAI::no_dry_run) {
+
+    ($FAI::debug)
+      and print "(CMD) $command 1> $stdout_filename 2> $stderr_filename\n";
+
+    # execute the bash command, write stderr and stdout into the testfiles
+    `$command 1> $stdout_filename 2> $stderr_filename`;
+  } else {
+    print "would run command $command; to have them executed, use -X \n";
+  }
+
+  # read the tempfile into lists, each element of the list one line
+  @stderr = <$stderr_fh>;
+  @stdout = <$stdout_fh>;
+
+  #when closing the files, the tempfiles are removed too
+  close($stderr_fh);
+  close($stdout_fh);
+
+  ($FAI::debug) and print "(STDERR) $_" foreach (@stderr);
+  ($FAI::debug) and print "(STDOUT) $_" foreach (@stdout);
+
+  #if the stderr contains information, get the first line for error recognition
+  $stderr_line = $stderr[0] if ( scalar(@stderr) > 0 );
+
+  #see last comment
+  $stdout_line = $stdout[0] if ( scalar(@stdout) > 0 );
+
+  #if an array is passed to the function, it is filled with the stdout
+  @$stdout_ref = @stdout if ( 'ARRAY' eq ref($stdout_ref) );
+
+  #see above
+  @$stderr_ref = @stderr if ( 'ARRAY' eq ref($stderr_ref) );
+
+  #get the error, if there was any
+  foreach my $err (@$FAI::error_codes) {
+    if ( (
+        $err->{'stdout_regex'} eq "" || $stdout_line =~ /$err->{'stdout_regex'}/
+      ) && ( $err->{'stderr_regex'} eq ""
+        || $stderr_line =~ /$err->{'stderr_regex'}/ )
+      && ( $err->{'program'} eq "" || $command =~ /.*$err->{'program'}.*/ )
+      ) {
+      return $err->{'error'};
+    }
+  }
+
+}
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/fstab.pm (from rev 4708, people/michael/features/setup_harddisks_2/implementation/shdd2-fstab)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/fstab.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/fstab.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,289 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-fstab
+#
+# @brief Generate an fstab file as appropriate for the configuration
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+################################################################################
+#
+# @brief this function generates the fstab file from our representation of the
+# partitions to be created.
+#
+# @reference config Reference to our representation of the partitions to be
+# created
+#
+# @return list of fstab lines
+#
+################################################################################
+sub generate_fstab {
+
+  # config structure is the only input
+  my ($config) = @_;
+
+  # the file to be returned, a list of lines
+  my @fstab = ();
+
+  # walk through all configured parts
+  # the order of entries is most likely wrong, it is fixed at the end
+  foreach my $c ( keys %$config ) {
+
+    # entry is a physical device
+    if ( $c =~ /^PHY_(.+)$/ ) {
+      my $device = $1;
+
+      # make sure the desired fstabkey is defined at all
+      defined( $config->{$c}->{"fstabkey"} )
+        or die "INTERNAL ERROR: fstabkey undefined\n";
+
+      # create a line in the output file for each partition
+      foreach my $p ( sort keys %{ $config->{$c}->{"partitions"} } ) {
+
+        # keep a reference to save some typing
+        my $p_ref = $config->{$c}->{"partitions"}->{$p};
+
+        # skip extended partitions
+        next if ( $p_ref->{"size"}->{"extended"} );
+
+        # skip entries without a mountpoint
+        next if ( $p_ref->{"mountpoint"} eq "-" );
+
+        # each line is a list of values
+        my @fstab_line = ();
+
+        # write the device name as the first entry; if the user prefers uuids
+        # or labels, use these if available
+        my @uuid = ();
+        &execute_command_std(
+          "/lib/udev/vol_id -u $device" . $p_ref->{"number"},
+          \@uuid, 0 );
+
+        # every device must have a uuid, otherwise this is an error (unless we
+        # are testing only)
+        ( $FAI::no_dry_run == 0 || scalar(@uuid) == 1 )
+          or die "Failed to obtain UUID for $device"
+          . $p_ref->{"number"} . "\n";
+
+        # get the label -- this is likely empty
+        my @label = ();
+        &execute_command_std(
+          "/lib/udev/vol_id -l $device" . $p_ref->{"number"},
+          \@label, 0 );
+
+        # using the fstabkey value the desired device entry is defined
+        if ( $config->{$c}->{"fstabkey"} eq "uuid" ) {
+          chomp( $uuid[0] );
+          push @fstab_line, "UUID=" . $uuid[0];
+        } elsif ( $config->{$c}->{"fstabkey"} eq "label" && scalar(@label) == 1 ) {
+          chomp( $label[0] );
+          push @fstab_line, "LABEL=" . $label[0];
+        } else {
+          # otherwise, use the usual device path
+          push @fstab_line, $device . $p_ref->{"number"};
+        }
+
+        # next is the mountpoint
+        push @fstab_line, $p_ref->{"mountpoint"};
+
+        # the filesystem to be used
+        push @fstab_line, $p_ref->{"filesystem"};
+
+        # add the mount options
+        push @fstab_line, $p_ref->{"mount_options"};
+
+        # never dump
+        push @fstab_line, 0;
+
+        # order of filesystem checks; the root filesystem gets a 1, the others 2
+        push @fstab_line, 2;
+        $fstab_line[-1] = 1 if ( $p_ref->{"mountpoint"} eq "/" );
+
+        # join the columns of one line with tabs, and push it to our fstab line array
+        push @fstab, join( "\t", @fstab_line );
+
+        # set the ROOT_PARTITION variable, if this is the mountpoint for /
+        $FAI::disk_var{"ROOT_PARTITION"} = $fstab_line[0]
+          if ( $p_ref->{"mountpoint"} eq "/" );
+
+        # add to the swaplist, if the filesystem is swap
+        $FAI::disk_var{"SWAPLIST"} .= " " . $device . $p_ref->{"number"}
+          if ( $p_ref->{"filesystem"} eq "swap" );
+      }
+    } elsif ( $c =~ /^VG_(.+)$/ ) {
+      my $device = $1;
+
+      # create a line in the output file for each logical volume
+      foreach my $l ( sort keys %{ $config->{$c}->{"volumes"} } ) {
+
+        # keep a reference to save some typing
+        my $l_ref = $config->{$c}->{"volumes"}->{$l};
+
+        # skip entries without a mountpoint
+        next if ( $l_ref->{"mountpoint"} eq "-" );
+
+        # each line is a list of values
+        my @fstab_line = ();
+
+        # resolve the symlink to the real device
+        # and write it as the first entry
+        &execute_command_std(
+          "readlink -f /dev/$device/$l", \@fstab_line, 0 );
+        
+        # remove the newline
+        chomp( $fstab_line[0] );
+
+        # make sure we got back a real device
+        ( $FAI::no_dry_run == 0 || -b $fstab_line[0] ) 
+          or die "Failed to resolve /dev/$device/$l\n";
+
+        # next is the mountpoint
+        push @fstab_line, $l_ref->{"mountpoint"};
+
+        # the filesystem to be used
+        push @fstab_line, $l_ref->{"filesystem"};
+
+        # add the mount options
+        push @fstab_line, $l_ref->{"mount_options"};
+
+        # never dump
+        push @fstab_line, 0;
+
+        # order of filesystem checks; the root filesystem gets a 1, the others 2
+        push @fstab_line, 2;
+        $fstab_line[-1] = 1 if ( $l_ref->{"mountpoint"} eq "/" );
+
+        # join the columns of one line with tabs, and push it to our fstab line array
+        push @fstab, join( "\t", @fstab_line );
+
+        # set the ROOT_PARTITION variable, if this is the mountpoint for /
+        $FAI::disk_var{"ROOT_PARTITION"} = $fstab_line[0]
+          if ( $l_ref->{"mountpoint"} eq "/" );
+
+        # add to the swaplist, if the filesystem is swap
+        $FAI::disk_var{"SWAPLIST"} .= " " . $fstab_line[0]
+          if ( $l_ref->{"filesystem"} eq "swap" );
+      }
+    } elsif ( $c eq "RAID" ) {
+
+      # create a line in the output file for each device
+      foreach my $r ( sort keys %{ $config->{$c}->{"volumes"} } ) {
+
+        # keep a reference to save some typing
+        my $r_ref = $config->{$c}->{"volumes"}->{$r};
+
+        # skip entries without a mountpoint
+        next if ( $r_ref->{"mountpoint"} eq "-" );
+
+        # each line is a list of values
+        my @fstab_line = ();
+
+        # write the device name as the first entry
+        push @fstab_line, "/dev/md" . $r;
+
+        # next is the mountpoint
+        push @fstab_line, $r_ref->{"mountpoint"};
+
+        # the filesystem to be used
+        push @fstab_line, $r_ref->{"filesystem"};
+
+        # add the mount options
+        push @fstab_line, $r_ref->{"mount_options"};
+
+        # never dump
+        push @fstab_line, 0;
+
+        # order of filesystem checks; the root filesystem gets a 1, the others 2
+        push @fstab_line, 2;
+        $fstab_line[-1] = 1 if ( $r_ref->{"mountpoint"} eq "/" );
+
+        # join the columns of one line with tabs, and push it to our fstab line array
+        push @fstab, join( "\t", @fstab_line );
+
+        # set the ROOT_PARTITION variable, if this is the mountpoint for /
+        $FAI::disk_var{"ROOT_PARTITION"} = "/dev/md" . $r
+          if ( $r_ref->{"mountpoint"} eq "/" );
+
+        # add to the swaplist, if the filesystem is swap
+        $FAI::disk_var{"SWAPLIST"} .= " /dev/md$r"
+          if ( $r_ref->{"filesystem"} eq "swap" );
+      }
+    } else {
+      die "INTERNAL ERROR: Unexpected key $c\n";
+    }
+  }
+
+  # cleanup the swaplist (remove leading space)
+  $FAI::disk_var{"SWAPLIST"} =~ s/^\s+//;
+
+  # quote the entries of SWAPLIST
+  $FAI::disk_var{"SWAPLIST"} = '"' . $FAI::disk_var{"SWAPLIST"} . '"';
+
+  # sort the lines in @fstab to enable all sub mounts
+  for ( my $i = 0 ; $i < scalar(@fstab) ; $i++ ) {
+
+    # take out the mountpoint
+    ( $_, my $mp_1 ) = split( "\t", $fstab[$i] );
+
+    # partitions without a mountpoint are fine
+    next if ( $mp_1 eq "none" );
+
+    for ( my $j = $i + 1 ; $j < scalar(@fstab) ; $j++ ) {
+
+      # take out the other mountpoint
+      ( $_, my $mp_2 ) = split( "\t", $fstab[$j] );
+
+      # remove the trailing / (even though this might make it the empty string
+      $mp_2 =~ s/\/$//;
+
+      # $mp_1 depends on $mp_2 being mounted, swap them
+      if ( $mp_1 =~ /^\Q$mp_2\E\// ) {
+        my $line_i = $fstab[$i];
+        $fstab[$i] = $fstab[$j];
+        $fstab[$j] = $line_i;
+        $mp_1      = $mp_2;
+      }
+    }
+  }
+
+  # add a nice header to fstab
+  unshift @fstab,
+    "# <file sys>\t<mount point>\t<type>\t<options>\t<dump>\t<pass>";
+  unshift @fstab, "#";
+  unshift @fstab, "# /etc/fstab: static file system information.";
+
+  # return the list of lines
+  return @fstab;
+}
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/init.pm (from rev 4711, people/michael/features/setup_harddisks_2/implementation/shdd2-init)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/init.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/init.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,110 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-init
+#
+# @brief Initialize all variables and acquire the set of disks of the system.
+#
+# The layout of the data structures is documented in the wiki:
+# http://faiwiki.debian.net/index.php/Setup_harddisks_2
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+################################################################################
+#
+# @brief Enable debugging by setting $debug to a value greater than 0
+#
+################################################################################
+$FAI::debug = 0;
+defined( $ENV{debug} ) and $FAI::debug = $ENV{debug};
+
+################################################################################
+#
+# @brief The lists of disks of the system
+#
+################################################################################
+ at FAI::disks = split( /\n/, $ENV{disklist} );
+( $FAI::debug > 0 ) and print "disklist was:\n" . $ENV{disklist};
+
+################################################################################
+#
+# @brief The variables later written to disk_var.sh
+#
+################################################################################
+%FAI::disk_var = ();
+$FAI::disk_var{"SWAPLIST"} = "";
+
+################################################################################
+#
+# @brief A flag to tell our script that the system is not installed for the
+# first time
+#
+################################################################################
+$FAI::reinstall = 1;
+defined( $ENV{fl_initial} ) and $FAI::reinstall = 0;
+
+################################################################################
+#
+# @brief The hash of all configurations specified in the disk_config file
+#
+################################################################################
+%FAI::configs = ();
+
+################################################################################
+#
+# @brief The current disk configuration
+#
+################################################################################
+%FAI::current_config = ();
+
+################################################################################
+#
+# @brief The current LVM configuration
+#
+################################################################################
+%FAI::current_lvm_config = ();
+
+################################################################################
+#
+# @brief The current RAID configuration
+#
+################################################################################
+%FAI::current_raid_config = ();
+
+################################################################################
+#
+# @brief The list of commands to be executed
+#
+################################################################################
+ at FAI::commands = ();
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/parser.pm (from rev 4711, people/michael/features/setup_harddisks_2/implementation/shdd2-parser)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/parser.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/parser.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,645 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-parser
+#
+# @brief A parser for the disk_config files within FAI, based on the EBNF
+# listed below. The implementation makes use of the RecDescent package.
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig, Sam Vilain, Andreas Schludei
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+use Parse::RecDescent;
+
+package FAI;
+
+################################################################################
+#
+# @brief the name of the device currently being configured, including a prefix
+# such as PHY_ or VG_ to indicate physical devices or LVM volume groups. For
+# RAID, the entry is only "RAID"
+#
+################################################################################
+$FAI::device = "";
+
+################################################################################
+#
+# @brief Test, whether @ref $cmd is available on the system using $PATH
+#
+# @param $cmd Command that is to be found in $PATH
+#
+# @return 1, if the command is found, else 0
+#
+################################################################################
+sub in_path {
+
+  # initialize the parameter
+  my ($cmd) = @_;
+
+  # split $PATH into its components, search all of its components
+  # and test for $cmd being executable
+  ( -x "$_/$cmd" ) and return 1 foreach ( split( ":", $ENV{"PATH"} ) );
+  # return 0 otherwise
+  return 0;
+}
+
+################################################################################
+#
+# @brief Initialise a new entry in @ref $FAI::configs for a physical disk.
+#
+# Besides creating the entry in the hash, the fully path of the device is
+# computed (see @ref $disk) and it is tested, whether this is a block device.
+# The device name is then used to define @ref $FAI::device.
+#
+# @param $disk Either an integer, occurring in the context of, e.g., disk2, or
+# a device name. The latter may be fully qualified, such as /dev/hda, or a short
+# name, such as sdb, in which case /dev/ is prepended.
+#
+################################################################################
+sub init_disk_config {
+
+  # Initialise $disk
+  my ($disk) = @_;
+
+  # test $disk for being numeric
+  if ( $disk =~ /^\d+$/ ) {
+
+    # $disk-1 must be a valid index in the map of all disks in the system
+    ( scalar(@FAI::disks) >= $disk )
+      or die "this system does not have a physical disk $disk\n";
+
+    # fetch the (short) device name
+    $disk = $FAI::disks[ $disk - 1 ];
+  }
+
+  # test, whether the device name starts with a / and prepend /dev/, if
+  # appropriate
+  ( $disk =~ m{^/} ) or $disk = "/dev/$disk";
+
+  # test, whether $disk is a block special device
+  ( -b $disk ) or die "$disk is not a valid device name\n";
+
+  # prepend PHY_
+  $FAI::device = "PHY_$disk";
+
+  # test, whether this is the first disk_config stanza to configure $disk
+  defined( $FAI::configs{$FAI::device} )
+    and die "Duplicate configuration for disk $FAI::disks[ $1-1 ]\n";
+
+  # Initialise the entry in $FAI::configs
+  $FAI::configs{$FAI::device} = {
+    "virtual"    => 0,
+    "disklabel"  => "msdos",
+    "bootable"   => -1,
+    "fstabkey"   => "device",
+    "partitions" => {}
+  };
+}
+
+################################################################################
+#
+# @brief Initialise the entry of a partition in @ref $FAI::configs
+#
+# @param $type The type of the partition. It must be either primary or logical.
+#
+################################################################################
+sub init_part_config {
+
+  # the type of the partition to be created
+  my ($type) = @_;
+
+  # type must either be primary or logical, nothing else may be accepted by the
+  # parser
+  ( $type eq "primary" || $type eq "logical" ) or die "INTERNAL PARSER ERROR\n";
+
+  # check that a physical device is being configured; logical partitions are
+  # only supported on msdos disk labels.
+  (
+    $FAI::device =~ /^PHY_/ && ( $type ne "logical"
+      || $FAI::configs{$FAI::device}{"disklabel"} eq "msdos" )
+  ) or die "Syntax error: invalid partition type";
+
+  # the index of the new partition
+  my $part_number = 0;
+
+  # create a primary partition
+  if ( $type eq "primary" ) {
+
+    # find all previously defined primary partitions
+    foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
+
+      # break, if the partition has not been created by init_part_config
+      defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
+          {"extended"} ) or last;
+
+      # on msdos disklabels we cannot have more than 4 primary partitions
+      last if ( $part_id > 4
+        && $FAI::configs{$FAI::device}{"disklabel"} eq "msdos" );
+
+      # store the latest index found
+      $part_number = $part_id;
+    }
+
+    # the next index available - note that $part_number might have been 0
+    $part_number++;
+
+    # msdos disk labels don't allow for more than 4 primary partitions
+    ( $part_number < 5 || $FAI::configs{$FAI::device}{"disklabel"} ne "msdos" )
+      or die "$part_number are too many primary partitions\n";
+  } else {
+
+    # no further checks for the disk label being msdos have to be performed in
+    # this branch, it has been ensured above
+
+    # find the index of the new partition, initialise it to the highest current index
+    foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
+
+      # skip primary partitions
+      next if ( $part_id < 5 );
+
+      # break, if the partition has not been created by init_part_config
+      defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
+          {"extended"} )
+        or last;
+
+      # store the latest index found
+      $part_number = $part_id;
+    }
+
+    # and use the next one available
+    $part_number++;
+
+    # if this is the first logical partition, the index must be set to 5 and an
+    # extended partition  must be created
+    if ( $part_number <= 5 ) {
+      $part_number = 5;
+
+      # the proposed index of the extended partition
+      my $extended = 0;
+
+      # find all previously defined primary partitions
+      foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
+
+        # break, if the partition has not been created by init_part_config
+        defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
+            {"extended"} ) or last;
+
+        # we cannot have more than 4 primary partitions
+        last if ( $part_id > 4 );
+
+        # store the latest index found
+        $extended = $part_id;
+      }
+
+      # the next index available
+      $extended++;
+
+      # msdos disk labels don't allow for more than 4 primary partitions
+      ( $extended < 5 )
+        or die "Too many primary partitions while creating extended\n";
+
+      # mark the entry as an extended partition
+      $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"extended"} = 1;
+
+      # add the preserve = 0 flag, if it doesn't exist already
+      defined( $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"preserve"} )
+        or $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"preserve"} = 0;
+
+      # add the resize = 0 flag, if it doesn't exist already
+      defined(
+        $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"resize"} )
+        or
+        $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"resize"} =
+        0;
+    }
+  }
+
+  # initialise the hash for the partitions, if it doesn't exist already
+  # note that it might exists due to options, such as preserve:x,y
+  # the initialisation is required for the reference defined next
+  defined( $FAI::configs{$FAI::device}{"partitions"}{$part_number} )
+    or $FAI::configs{$FAI::device}{"partitions"}{$part_number} = {};
+
+  # set the reference to the current partition
+  # the reference is used by all further processing of this config line
+  $FAI::partition_pointer =
+    ( \%FAI::configs )->{$FAI::device}->{"partitions"}->{$part_number};
+
+  # as we can't compute the index from the reference, we need to store the
+  # $part_number explicitly
+  $FAI::partition_pointer->{"number"} = $part_number;
+
+  # the partition is not an extended one
+  $FAI::partition_pointer->{"size"}->{"extended"} = 0;
+
+  # add the preserve = 0 flag, if it doesn't exist already
+  defined( $FAI::partition_pointer->{"size"}->{"preserve"} )
+    or $FAI::partition_pointer->{"size"}->{"preserve"} = 0;
+
+  # add the resize = 0 flag, if it doesn't exist already
+  defined( $FAI::partition_pointer->{"size"}->{"resize"} )
+    or $FAI::partition_pointer->{"size"}->{"resize"} = 0;
+}
+
+################################################################################
+#
+# @brief This function converts different sizes to Mbyte
+#
+# @param $val is the number with its unit
+#
+################################################################################
+sub convert_unit
+{
+  my ($val) = @_;
+  ( $val =~ /^(\d+)([kMGTP%]?)(B)?\s*$/ ) or die "INTERNAL ERROR (convert_unit)\n";
+  $val = $1 * ( 1 / 1024 ) if ( $2 eq "k" );
+  $val = $1 if ( $2 eq "M" );
+  $val = $1 * 1024 if ( $2 eq "G" );
+  $val = $1 * ( 1024 * 1024 ) if ( $2 eq "T" );
+  $val = $1 * ( 1024 * 1024 * 1024 ) if ( $2 eq "P" );
+  # % is returned as is
+  return $val;
+}
+
+# have RecDescent do proper error reporting
+$::RD_HINT = 1;
+
+################################################################################
+#
+# @brief The effective implementation of the parser is instantiated here
+#
+################################################################################
+$FAI::Parser = Parse::RecDescent->new(
+  q{
+    file: line(s?) /\Z/
+        {
+          $return = 1;
+        }
+        | <error>
+
+    line: <skip: qr/[ \t]*/> "\\n"
+        | <skip: qr/[ \t]*/> comment "\\n"
+        | <skip: qr/[ \t]*/> config "\\n"
+
+    comment: /^\s*#.*/
+
+    config: 'disk_config' disk_config_arg
+        | volume
+
+    disk_config_arg: 'raid'
+        {
+          # check, whether raid tools are available
+          ( &FAI::in_path( "mdadm" ) == 1 ) or 
+            die "mdadm not found in PATH\n";
+          $FAI::device = "RAID";
+        }
+        | /^lvm/
+        {
+
+          # check, whether lvm tools are available
+          ( &FAI::in_path( "lvcreate" ) == 1 ) or 
+            die "LVM tools not found in PATH\n";
+          # initialise $FAI::device to inform the following lines about the LVM
+          # being configured
+          $FAI::device = "VG_";
+        }
+        | 'end'
+        {
+          # exit config mode
+          $FAI::device = "";
+        }
+        | /^disk(\d+)/
+        {
+          # check, whether parted is available
+          ( &FAI::in_path( "parted" ) == 1 ) or 
+            die "parted not found in PATH\n";
+          # initialise the entry of the hash corresponding to disk$1
+          &FAI::init_disk_config( $1 );
+        }
+        option(s?)
+        | /^\S+/
+        {
+          # check, whether parted is available
+          ( &FAI::in_path( "parted" ) == 1 ) or 
+            die "parted not found in PATH\n";
+          # initialise the entry of the hash corresponding to $item[1]
+          &FAI::init_disk_config( $item[ 1 ] );
+        }
+        option(s?)
+
+    option: /^preserve_always:(\d+(,\d+)*)/
+        {
+          # set the preserve flag for all ids in all cases
+          $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "preserve" } = 1 foreach ( split( ",", $1 ) );
+        }
+        /^preserve_reinstall:(\d+(,\d+)*)/
+        {
+          # set the preserve flag for all ids if $FAI::reinstall is set
+          if( $FAI::reinstall == 1 ) {
+            $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "preserve" } = 1 foreach ( split( ",", $1 ) );
+          }
+        }
+        | /^resize:(\d+(,\d+)*)/
+        {
+          # set the resize flag for all ids
+          $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "resize" } = 1 foreach ( split( ",", $1 ) );
+        }
+        | /^disklabel:(msdos|gpt)/
+        {
+          # set the disk label - actually not only the above, but all types 
+          # supported by parted could be allowed, but others are not implemented
+          # yet
+          $FAI::configs{ $FAI::device }{ "disklabel" } = $1;
+        }
+        | /^bootable:(\d+)/
+        {
+          # specify a partition that should get the bootable flag set
+          $FAI::configs{ $FAI::device }{ "bootable" } = $1;
+          ( $FAI::device =~ /^PHY_(.+)$/ ) or die 
+            "INTERNAL ERROR: unexpected device name\n";
+          # set the BOOT_DEVICE and BOOT_PARTITION variables
+          $FAI::disk_var{ "BOOT_DEVICE" } = $1; 
+          $FAI::disk_var{ "BOOT_PARTITION" } = $1 . 
+            $FAI::configs{ $FAI::device }{ "bootable" }; 
+        }
+        | 'virtual'
+        {
+          # this is a configuration for a virtual disk
+          $FAI::configs{ $FAI::device }{ "virtual" } = 1;
+        }
+        | /^fstabkey:(device|label|uuid)/
+        {
+          # the information preferred for fstab device identifieres
+          $FAI::configs{ $FAI::device }{ "fstabkey" } = $1;
+        }
+
+    volume: /^vg\s+/ name devices
+        | /^raid([0156])\s+/
+        {
+          # make sure that this is a RAID configuration
+          ( $FAI::device eq "RAID" ) or die "RAID entry invalid in this context\n";
+          # initialise RAID entry, if it doesn't exist already
+          defined( $FAI::configs{"RAID"} ) or $FAI::configs{"RAID"}{"volumes"} = {};
+          # compute the next available index - the size of the entry
+          my $vol_id = scalar( keys %{ $FAI::configs{"RAID"}{"volumes"} } );
+          # set the RAID type of this volume
+          $FAI::configs{"RAID"}{"volumes"}{$vol_id}{"mode"} = $1;
+          # initialise the hash of devices
+          $FAI::configs{"RAID"}{"volumes"}{$vol_id}{"devices"} = {};
+          # set the reference to the current volume
+          # the reference is used by all further processing of this config line
+          $FAI::partition_pointer = ( \%FAI::configs )->{"RAID"}->{"volumes"}->{$vol_id};
+        }
+        mountpoint devices filesystem mount_options fs_options
+        | type mountpoint size filesystem mount_options fs_options
+
+    type: 'primary'
+        {
+          # initialise a primary partition
+          &FAI::init_part_config( $item[ 1 ] );
+        }
+        | 'logical'
+        {
+          # initialise a logical partition
+          &FAI::init_part_config( $item[ 1 ] );
+        }
+        | m{^([^/\s\-]+)-([^/\s\-]+)\s+}
+        {
+          # set $FAI::device to VG_$1
+          $FAI::device = "VG_$1";
+          # make sure, the volume group $1 has been defined before
+          defined( $FAI::configs{$FAI::device} ) or 
+            die "Volume group $1 has not been declared yet.\n";
+          # make sure, $2 has not been defined already
+          defined( $FAI::configs{$FAI::device}{"volumes"}{$2} ) and 
+            die "Logical volume $2 has been defined already.\n";
+          # initialise the new hash
+          $FAI::configs{$FAI::device}{"volumes"}{$2} = {};
+          # initialise the preserve and resize flags
+          $FAI::configs{$FAI::device}{"volumes"}{$2}{"size"}{"preserve"} = 0;
+          $FAI::configs{$FAI::device}{"volumes"}{$2}{"size"}{"resize"} = 0;
+          # set the reference to the current volume
+          # the reference is used by all further processing of this config line
+          $FAI::partition_pointer = ( \%FAI::configs )->{$FAI::device}->{"volumes"}->{$2};
+        }
+
+    mountpoint: '-'
+        {
+          # this partition should not be mounted
+          $FAI::partition_pointer->{ "mountpoint" } = "-";
+        }
+        | 'swap'
+        {
+          # this partition is swap space, not mounted
+          $FAI::partition_pointer->{ "mountpoint" } = "none";
+        }
+        | m{^/\S*}
+        {
+          # set the mount point
+          $FAI::partition_pointer->{ "mountpoint" } = $item[ 1 ];
+          # if the mount point is / or /boot and we are currently doing a
+          # physical device, the variables should be set, unless they are
+          # already
+          if ( $FAI::configs{$FAI::device}{"bootable"} == -1 && 
+            $FAI::device =~ /^PHY_(.+)$/ && 
+            ( $item[ 1 ] eq "/boot" || ( $item[ 1 ] eq "/" && 
+              !defined( $FAI::disk_var{ "BOOT_DEVICE" } ) ) ) ) {
+              # set the BOOT_DEVICE and BOOT_PARTITION variables
+              $FAI::disk_var{ "BOOT_DEVICE" } = $1; 
+              $FAI::disk_var{ "BOOT_PARTITION" } = $1 . 
+                $FAI::partition_pointer->{"number"};
+          }
+        }
+
+    name: m{^([^/\s\-]+)}
+        {
+          # set the device name to VG_ and the name of the volume group
+          $FAI::device = "VG_$1";
+          # make sure, the volume group $1 not has been defined already
+          defined( $FAI::configs{$FAI::device} ) and
+            die "Volume group $1 has been defined already.\n";
+          # make sure this line is part of an LVM configuration
+          ( $FAI::device =~ /^VG_/ ) or
+            die "vg is invalid in a non LVM-context.\n";
+          # initialise the new hash
+          $FAI::configs{$FAI::device}{"volumes"} = {};
+          # initialise the list of physical devices
+          $FAI::configs{$FAI::device}{"devices"} = ();
+          # the rule must not return undef
+          1;
+        }
+
+    size: /^(\d+[kMGTP%]?(-(\d+[kMGTP%]?)?)?)(:resize)?\s+/
+        {
+          # complete the size specification to be a range in all cases
+          my $range = $1;
+          # the size is fixed
+          if( ! defined( $2 ) )
+          {
+            # make it a range of the form x-x
+            $range = "$range-$1";
+          }
+          elsif( ! defined( $3 ) )
+          {
+            # range has no upper limit, assume the whole disk
+            $range = $range . "100%";
+          } 
+          
+          # convert the units, if necessary
+          my ($min, $max) = split(/-/, $range);
+          $min   = &FAI::convert_unit($min);
+          $max   = &FAI::convert_unit($max);
+          $range = "$min-$max";
+          # enter the range into the hash
+          $FAI::partition_pointer->{ "size" }->{ "range" } = $range;
+          # set the resize flag, if required
+          defined( $4 ) and $FAI::partition_pointer->{ "size" }->{ "resize" } = 1;
+        }
+        | /^(-\d+[kMGTP%]?)(:resize)?\s+/
+        {
+          # complete the range by assuming 0 as the lower limit 
+          my $range = "0$1";
+          # convert the units, if necessary
+          my ($min, $max) = split(/-/, $range);
+          $min   = &FAI::convert_unit($min);
+          $max   = &FAI::convert_unit($max);
+          $range = "$min-$max";
+          # enter the range into the hash
+          $FAI::partition_pointer->{ "size" }->{ "range" } = $range;
+          # set the resize flag, if required
+          defined( $2 ) and $FAI::partition_pointer->{ "size" }->{ "resize" } = 1;
+        }
+        | <error: invalid partition size near "$text">
+
+    devices: /^([^\d,:\s\-][^,:\s]*(:(spare|missing))*(,[^,:\s]+(:(spare|missing))*)*)/
+        {
+          # split the device list by ,
+          foreach my $dev ( split( ",", $1 ) )
+          {
+            # match the substrings
+            ( $dev =~ /^([^\d,:\s\-][^,:\s]*)(:(spare|missing))*$/ ) or die "INTERNAL PARSER ERROR\n";
+            # redefine the device string
+            $dev = $1;
+            # make $dev a full path name; can't validate device name yet as it
+            # might be created later on
+            unless ( $dev =~ m{^/} ) {
+		if ( $dev =~ m/^disk(\d+)\.(\d+)/ ) {
+		    my $short_dev = $FAI::disks[ $1 - 1 ];
+		    $dev = "/dev/$short_dev$2";
+		}
+		else {
+		    $dev = "/dev/$dev";
+		}
+	    }
+            # options are only valid for RAID
+            defined( $2 ) and ( $FAI::device ne "RAID" ) and die "Option $2 invalid in a non-RAID context\n";
+            if( $FAI::device eq "RAID" )
+            {
+              # parse all options
+              my $spare = 0;
+              my $missing = 0;
+              if( defined( $2 ) )
+              {
+                ( $2 =~ /spare/ ) and $spare = 1;
+                ( $2 =~ /missing/ ) and $missing = 1;
+              }
+              # each device may only appear once
+              defined( $FAI::partition_pointer->{"devices"}->{$dev} ) and 
+                die "$dev is already part of the RAID volume\n";
+              # set the options
+              $FAI::partition_pointer->{"devices"}->{$dev}->{"options"} = {
+                "spare" => $spare,
+                "missing" => $missing
+              };
+            }
+            else
+            {
+              # create an empty hash for each device
+              $FAI::configs{$FAI::device}{"devices"}{$dev} = {};
+            }
+          }
+          1;
+        }
+        | <error: invalid device spec "$text">
+
+    mount_options: /\S+/
+        {
+          $FAI::partition_pointer->{ "mount_options" } = $item[ 1 ];
+        }
+
+    filesystem: '-'
+        {
+          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
+        }
+        | 'swap'
+        {
+          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
+        }
+        | /^\S+/
+        {
+          ( &FAI::in_path("mkfs.$item[1]") == 1 ) or 
+            die "unknown/invalid filesystem type $item[1] (mkfs.$item[1] not found in PATH)\n";
+          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
+        }
+
+    fs_options: /[^;\n]*/
+        {
+          $FAI::partition_pointer->{ "fs_options" } = $item[ 1 ];
+        }
+}
+);
+
+################################################################################
+#
+# @brief Parse the data from <$IN> using @ref $FAI::Parser
+#
+# @param IN file handle for input file, may be STDIN
+#
+################################################################################
+sub run_parser {
+  my ($IN) = @_;
+
+  # read <$IN> to a single string (not a list), thus $/ has to be unset
+  my $ifs = $/;
+  undef $/;
+  my $input = <$IN>;
+  $/ = $ifs;
+
+  # print the contents of <$IN> for debugging purposes
+  ( $FAI::debug > 0 ) and print "Input was:\n" . $input;
+
+  # check for old-style configuration files
+  ( $input =~ m{(^|\n)[^\n#]+;} )
+    and die "Old style configuration files are not supported\n";
+
+  # attempt to parse $input - any error will lead to termination
+  defined $FAI::Parser->file($input) or die "Syntax error\n";
+}
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm (from rev 4708, people/michael/features/setup_harddisks_2/implementation/shdd2-sizes)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/sizes.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,783 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-sizes
+#
+# @brief Compute the size of the partitions and volumes to be created
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+use POSIX qw(ceil floor);
+
+package FAI;
+
+################################################################################
+#
+# @brief Estimate the size of the device $dev
+#
+# @param $dev Device the size of which should be determined. This may be a
+# a partition, a RAID device or an entire disk.
+#
+# @return the size of the device in megabytes
+#
+################################################################################
+sub estimate_size
+{
+  my ($dev) = @_;
+
+  # try the entire disk first; we then use the data from the current
+  # configuration; this matches in fact for than the allowable strings, but
+  # this should be caught later on
+  if ( $dev =~ /^\/dev\/[sh]d[a-z]$/ )
+  {
+    defined( $FAI::current_config{$dev}{"end_byte"} )
+      or die "$dev is not a valid block device\n";
+
+    # the size is known, return it
+    return ( $FAI::current_config{$dev}{"end_byte"} -
+        $FAI::current_config{$dev}{"begin_byte"} ) / ( 1024 * 1024 );
+  }
+
+  # try a partition
+  elsif ( $dev =~ /^(\/dev\/[sh]d[a-z])(\d+)$/ )
+  {
+
+    # the size is configured, return it
+    defined( $FAI::configs{"PHY_$1"}{"partitions"}{$2}{"size"}{"eff_size"} )
+      and return $FAI::configs{"PHY_$1"}{"partitions"}{$2}{"size"}{"eff_size"} /
+      ( 1024 * 1024 );
+
+    # the size is known from the current configuration on disk, return it
+    defined( $FAI::current_config{$1}{"partitions"}{$2}{"count_byte"} )
+      and return $FAI::current_config{$1}{"partitions"}{$2}{"count_byte"} /
+      ( 1024 * 1024 );
+
+    # the size is not known (yet?)
+    die "Cannot determine size of $dev\n";
+  }
+
+  # try RAID; estimations here are very limited and possible imprecise
+  elsif ( $dev =~ /^\/dev\/md(\d+)$/ )
+  {
+
+    # the list of underlying devices
+    my @devs = ();
+
+    # the raid level, like raid0, raid5, linear, etc.
+    my $level = "";
+
+    # let's see, whether there is a configuration of this volume
+    if ( defined( $FAI::configs{"RAID"}{"volumes"}{$1}{"devices"} ) )
+    {
+      @devs  = keys %{ $FAI::configs{"RAID"}{"volumes"}{$1}{"devices"} };
+      $level = $FAI::configs{"RAID"}{"volumes"}{$1}{"mode"};
+    }
+    elsif ( defined( $FAI::current_raid_config{$1}{"devices"} ) )
+    {
+      @devs  = $FAI::current_raid_config{$1}{"devices"};
+      $level = $FAI::current_raid_config{$1}{"mode"};
+    }
+    else
+    {
+      die "$dev is not a known RAID device\n";
+    }
+
+    # prepend "raid", if the mode is numeric-only
+    $level = "raid" . $level if ( $level =~ /^\d+$/ );
+
+    # the number of devices in the volume
+    my $dev_count = scalar(@devs);
+
+    # now do the mode-specific size estimations
+    if ( $level =~ /^raid[015]$/ )
+    {
+      my $min_size = &estimate_size( shift @devs );
+      foreach (@devs)
+      {
+        my $s = &estimate_size($_);
+        $min_size = $s if ( $s < $min_size );
+      }
+
+      return $min_size * POSIX::floor( $dev_count / 2 )
+        if ( $level eq "raid1" );
+      return $min_size * $dev_count if ( $level eq "raid0" );
+      return $min_size * ( $dev_count - 1 ) if ( $level eq "raid5" );
+    }
+    else
+    {
+
+      # probably some more should be implemented
+      die "Don't know how to estimate the size of a $level device\n";
+    }
+  }
+
+  # otherwise we are clueless
+  else
+  {
+    die "Cannot determine size of $dev\n";
+  }
+}
+
+################################################################################
+#
+# @brief Compute the desired sizes of logical volumes
+#
+################################################################################
+sub compute_lv_sizes
+{
+
+  # loop through all device configurations
+  foreach my $config ( keys %FAI::configs )
+  {
+
+    # for RAID, there is nothing to be done here
+    next if ( $config eq "RAID" );
+
+    # device is an effective disk
+    next if ( $config =~ /^PHY_(.+)$/ );
+
+    # configure a volume group
+    ( $config =~ /^VG_(.+)$/ )
+      or die "INTERNAL ERROR: invalid config entry $config.\n";
+
+    # the volume group name
+    my $vg = $1;
+
+    # compute the size of the volume group; this is not exact, but should at
+    # least give a rough estimation, we assume 1 % of overhead; the value is
+    # stored in megabytes
+    my $vg_size = 0;
+    foreach my $dev ( keys %{ $FAI::configs{$config}{"devices"} } )
+    {
+
+      # $dev may be a partition, an entire disk or a RAID device; otherwise we
+      # cannot deal with it
+      $vg_size += &estimate_size($dev);
+    }
+
+    # now subtract 1% of overhead
+    $vg_size *= 0.99;
+
+    # the volumes that require redistribution of free space
+    my @redist_list = ();
+
+    # the minimum space required in this volume group
+    my $min_space = 0;
+
+    # the maximum space used in this volume group
+    my $max_space = 0;
+
+    # set effective sizes where available
+    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } )
+    {
+
+      # make sure the size specification is a range (even though it might be
+      # something like x-x) and store the dimensions
+      ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} =~
+          /^(\d+%?)-(\d+%?)$/ )
+        or die "INTERNAL ERROR: Invalid range\n";
+      my $start = $1;
+      my $end   = $2;
+
+      # start may be given in percents of the size, rewrite it to megabytes
+      $start = POSIX::floor( $vg_size * $1 / 100 ) if ( $start =~ /^(\d+)%$/ );
+
+      # end may be given in percents of the size, rewrite it to megabytes
+      $end = POSIX::ceil( $vg_size * $1 / 100 ) if ( $end =~ /^(\d+)%$/ );
+
+      # make sure that $end >= $start
+      ( $end >= $start ) or die "INTERNAL ERROR: end < start\n";
+
+      # increase the used space
+      $min_space += $start;
+      $max_space += $end;
+
+      # write back the range in MB
+      $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} = "$start-$end";
+
+      # the size is fixed
+      if ( $start == $end )
+      {
+
+        # write the size back to the configuration
+        $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} = $start;
+      }
+      else
+      {
+
+        # add this volume to the redistribution list
+        push @redist_list, $lv;
+      }
+    }
+
+    # test, whether the configuration fits on the volume group at all
+    ( $min_space < $vg_size )
+      or die "Volume group $vg requires $min_space MB\n";
+
+    # the extension factor
+    my $redist_factor = 0;
+    $redist_factor = ( $vg_size - $min_space ) / ( $max_space - $min_space )
+      if ( $max_space > $min_space );
+
+    # update all sizes that are still ranges
+    foreach my $lv (@redist_list)
+    {
+
+      # get the range again
+      ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} =~
+          /^(\d+%?)-(\d+%?)$/ )
+        or die "INTERNAL ERROR: Invalid range\n";
+      my $start = $1;
+      my $end   = $2;
+
+      # write the final size
+      $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} =
+        $start + ( ( $end - $start ) * $redist_factor );
+    }
+  }
+}
+
+################################################################################
+#
+# @brief Compute the desired sizes of the partitions and test feasibility
+# thereof.
+#
+################################################################################
+sub compute_partition_sizes
+{
+
+  # loop through all device configurations
+  foreach my $config ( keys %FAI::configs )
+  {
+
+    # for RAID, there is nothing to be done here
+    next if ( $config eq "RAID" );
+
+    # don't configure the sizes of logical volumes here
+    next if ( $config =~ /^VG_(.+)$/ );
+
+    # device is an effective disk
+    ( $config =~ /^PHY_(.+)$/ )
+      or die "INTERNAL ERROR: invalid config entry $config.\n";
+
+    # nothing to be done, if this is a configuration for a virtual disk
+    next if ( $FAI::configs{$config}{"virtual"} == 1 );
+
+    # the device name of the disk
+    my $disk = $1;
+
+# at various points the following code highly depends on the desired disk label!
+# initialise variables
+# the id of the extended partition to be created, if required
+    my $extended = -1;
+
+    # the id of the current extended partition, if any; this setup only caters
+    # for a single existing extended partition!
+    my $current_extended = -1;
+
+    # find the first existing extended partition
+    foreach
+      my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } )
+    {
+      if ( 1 ==
+        $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} )
+      {
+        $current_extended = $part_id;
+        last;
+      }
+    }
+
+    # the space required on the disk
+    my $min_req_total_space = 0;
+
+    # the start byte for the next partition
+    my $next_start = 0;
+
+    # on msdos disk labels, the first partitions starts at head #1
+    if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+    {
+      $next_start = $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+        $FAI::current_config{$disk}{"sector_size"};
+
+      # the MBR requires space, too
+      $min_req_total_space +=
+        $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+        $FAI::current_config{$disk}{"sector_size"};
+    }
+
+    # on GPT disk labels the first 34 and last 34 sectors must be left alone
+    if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
+    {
+      $next_start = 34 * $FAI::current_config{$disk}{"sector_size"};
+
+      # modify the disk to claim the space for the second partition table
+      $FAI::current_config{$disk}{"end_byte"} -=
+        34 * $FAI::current_config{$disk}{"sector_size"};
+
+      # the space required by the GPTs
+      $min_req_total_space +=
+        2 * 34 * $FAI::current_config{$disk}{"sector_size"};
+    }
+
+    # the list of partitions that we need to find start and end bytes for
+    my @worklist = ( sort keys %{ $FAI::configs{$config}{"partitions"} } );
+
+    while ( scalar(@worklist) > 0 )
+    {
+
+      # work on the first entry of the list
+      my $part_id = $worklist[0];
+
+      # the partition $part_id must be preserved
+      if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} ==
+        1 )
+      {
+
+        # a partition that should be preserved must exist already
+        defined( $FAI::current_config{$disk}{"partitions"}{$part_id} )
+          or die "$part_id can't be preserved, it does not exist.\n";
+
+        ( $next_start >
+            $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"} )
+          and die
+"Previous partitions overflow begin of preserved partition $part_id\n";
+
+        # set the effective size to the value known already
+        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
+          $FAI::current_config{$disk}{"partitions"}{$part_id}{"count_byte"};
+
+        # copy the start_byte and end_byte information
+        $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
+          $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"};
+        $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} =
+          $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"};
+
+        # and add it to the total disk space required by this config
+        $min_req_total_space +=
+          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"};
+
+        # set the next start
+        $next_start =
+          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} + 1;
+
+        # several msdos specific parts
+        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+        {
+
+          # make sure the partition ends at a cylinder boundary
+          (
+            0 == (
+              $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} +
+                1
+              ) % (
+              $FAI::current_config{$disk}{"sector_size"} *
+                $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+                $FAI::current_config{$disk}{"bios_heads"}
+              )
+            )
+            or die
+"Preserved partition $part_id does not end at a cylinder boundary\n";
+
+          # add one head of disk usage if this is a logical partition
+          $min_req_total_space +=
+            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+            $FAI::current_config{$disk}{"sector_size"}
+            if ( $part_id > 4 );
+
+          # extended partitions consume no space
+          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
+            {"extended"} == 1 )
+          {
+
+            # revert the addition of the size
+            $min_req_total_space -=
+              $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
+              {"eff_size"};
+
+            # set the next start to the start of the extended partition
+            $next_start =
+              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
+          }
+
+        }
+
+        # on gpt, ensure that the partition ends at a sector boundary
+        if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
+        {
+          (
+            0 == (
+              $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} +
+                1
+              ) % $FAI::current_config{$disk}{"sector_size"}
+            )
+            or die
+            "Preserved partition $part_id does not end at a sector boundary\n";
+        }
+
+        # partition done
+        shift @worklist;
+      }
+
+      # msdos specific: deal with extended partitions
+      elsif (
+        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} ==
+        1 )
+      {
+        ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+          or die "found an extended partition on a non-msdos disklabel\n";
+
+        # make sure that there is only one extended partition
+        ( $extended == -1 || 1 == scalar(@worklist) )
+          or die "INTERNAL ERROR: More than 1 extended partition\n";
+
+        # ensure that it is a primary partition
+        ( $part_id <= 4 )
+          or die
+          "INTERNAL ERROR: Extended partition wouldn't be a primary one\n";
+
+        # set the local variable to this id
+        $extended = $part_id;
+
+        # the size cannot be determined now, push it to the end of the
+        # worklist; the check against $extended being == -1 ensures that
+        # there is no indefinite loop
+        if ( scalar(@worklist) > 1 )
+        {
+          push @worklist, shift @worklist;
+        }
+
+        # determine the size of the extended partition
+        else
+        {
+          my $epbr_size =
+            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+            $FAI::current_config{$disk}{"sector_size"};
+
+          # initialise the size and the start byte
+          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
+            0;
+          $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} = -1;
+
+          foreach my $p ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
+          {
+            next if ( $p < 5 );
+
+            if ( -1 ==
+              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} )
+            {
+              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
+                $FAI::configs{$config}{"partitions"}{$p}{"start_byte"} -
+                $epbr_size;
+            }
+
+            $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
+              {"eff_size"} +=
+              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"eff_size"} +
+              $epbr_size;
+
+            $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} =
+              $FAI::configs{$config}{"partitions"}{$p}{"end_byte"};
+          }
+
+          ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} >
+              0 )
+            or die "Extended partition has a size of 0\n";
+
+          # partition done
+          shift @worklist;
+        }
+      }
+      else
+      {
+
+        # make sure the size specification is a range (even though it might be
+        # something like x-x) and store the dimensions
+        ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =~
+            /^(\d+%?)-(\d+%?)$/ )
+          or die "INTERNAL ERROR: Invalid range\n";
+        my $start = $1;
+        my $end   = $2;
+
+        # start may be given in percents of the size
+        if ( $start =~ /^(\d+)%$/ )
+        {
+
+          # rewrite it to bytes
+          $start =
+            POSIX::floor( $FAI::current_config{$disk}{"size"} * $1 / 100 );
+        }
+        else
+        {
+
+          # it is given in megabytes, make it bytes
+          $start = $start * 1024.0 * 1024.0;
+        }
+
+        # end may be given in percents of the size
+        if ( $end =~ /^(\d+)%$/ )
+        {
+
+          # rewrite it to bytes
+          $end = POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
+        }
+        else
+        {
+
+          # it is given in megabytes, make it bytes
+          $end = $end * 1024.0 * 1024.0;
+        }
+
+        # make sure that $end >= $start
+        ( $end >= $start ) or die "INTERNAL ERROR: end < start\n";
+
+        # check, whether the size is fixed
+        if ( $end != $start )
+        {
+
+          # the end of the current range (may be the end of the disk or some
+          # preserved partition
+          my $end_of_range = -1;
+
+         # minimum space required by all partitions, i.e., the lower ends of the
+         # ranges
+         # $min_req_space counts up to the next preserved partition or the
+         # end of the disk
+          my $min_req_space = 0;
+
+          # maximum useful space
+          my $max_space = 0;
+
+          # inspect all remaining entries in the worklist
+          foreach my $p (@worklist)
+          {
+
+            # we have found the delimiter
+            if ( $FAI::configs{$config}{"partitions"}{$p}{"size"}{"preserve"} ==
+              1 )
+            {
+              $end_of_range =
+                $FAI::current_config{$disk}{"partitions"}{$p}{"begin_byte"};
+
+              # logical partitions require the space for the EPBR to be left
+              # out
+              if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+                && ( $p > 4 ) )
+              {
+                $end_of_range -=
+                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+                  $FAI::current_config{$disk}{"sector_size"};
+              }
+              last;
+            }
+            elsif (
+              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"extended"} ==
+              1 )
+            {
+              next;
+            }
+            else
+            {
+
+          # below is a slight duplication of the code
+          # make sure the size specification is a range (even though it might be
+          # something like x-x) and store the dimensions
+              ( $FAI::configs{$config}{"partitions"}{$p}{"size"}{"range"} =~
+                  /^(\d+%?)-(\d+%?)$/ )
+                or die "INTERNAL ERROR: Invalid range\n";
+              my $min_size = $1;
+              my $max_size = $2;
+
+              # start may be given in percents of the size
+              if ( $min_size =~ /^(\d+)%$/ )
+              {
+
+                # rewrite it to bytes
+                $min_size =
+                  POSIX::floor(
+                  $FAI::current_config{$disk}{"size"} * $1 / 100 );
+              }
+              else
+              {
+
+                # it is given in megabytes, make it bytes
+                $min_size *= 1024.0 * 1024.0;
+              }
+
+              # end may be given in percents of the size
+              if ( $max_size =~ /^(\d+)%$/ )
+              {
+
+                # rewrite it to bytes
+                $max_size =
+                  POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
+              }
+              else
+              {
+
+                # it is given in megabytes, make it bytes
+                $max_size *= 1024.0 * 1024.0;
+              }
+
+              # logical partitions require the space for the EPBR to be left
+              # out
+              if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+                && ( $p > 4 ) )
+              {
+                $min_size +=
+                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+                  $FAI::current_config{$disk}{"sector_size"};
+                $max_size +=
+                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+                  $FAI::current_config{$disk}{"sector_size"};
+              }
+
+              $min_req_space += $min_size;
+              $max_space     += $max_size;
+            }
+          }
+
+          # set the end if we have reached the end of the disk
+          $end_of_range = $FAI::current_config{$disk}{"end_byte"}
+            if ( -1 == $end_of_range );
+
+          my $available_space = $end_of_range - $next_start + 1;
+
+          # the next boundary is closer than the minimal space that we need
+          ( $available_space < $min_req_space )
+            and die "Insufficient space available for partition $part_id\n";
+
+          # the new size
+          my $scaled_size = $end;
+          $scaled_size = POSIX::floor(
+            ( $end - $start ) * (
+              ( $available_space - $min_req_space ) /
+                ( $max_space - $min_req_space )
+            )
+            ) + $start
+            if ( $max_space > $available_space );
+
+          ( $scaled_size >= $start )
+            or die
+            "INTERNAL ERROR: scaled size is smaller than the desired minimum\n";
+
+          $start = $scaled_size;
+          $end   = $start;
+        }
+
+        # now we compute the effective locations on the disk
+        # msdos specific offset for logical partitions
+        if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+          && ( $part_id > 4 ) )
+        {
+
+          # add one head of disk usage if this is a logical partition
+          $min_req_total_space +=
+            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+            $FAI::current_config{$disk}{"sector_size"};
+
+          # move the start byte as well
+          $next_start += $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+            $FAI::current_config{$disk}{"sector_size"};
+        }
+
+        # partition starts at where we currently are
+        $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
+          $next_start;
+
+        # the end may need some alignment, depending on the disk label
+        my $end_byte = $next_start + $start - 1;
+
+        # on msdos, ensure that the partition ends at a cylinder boundary
+        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
+        {
+          $end_byte -=
+            ( $end_byte + 1 ) % ( $FAI::current_config{$disk}{"sector_size"} *
+              $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+              $FAI::current_config{$disk}{"bios_heads"} );
+        }
+
+        # on gpt, ensure that the partition ends at a sector boundary
+        if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
+        {
+          $end_byte -=
+            ( $end_byte + 1 ) % $FAI::current_config{$disk}{"sector_size"};
+        }
+
+        # set $start and $end to the effective values
+        $start = $end_byte - $next_start + 1;
+        $end   = $start;
+
+        # write back the size spec in bytes
+        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =
+          $start . "-" . $end;
+
+        # then set eff_size to a proper value
+        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
+          $start;
+
+        # write the end byte to the configuration
+        $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} = $end_byte;
+
+        # and add it to the total disk space required by this config
+        $min_req_total_space +=
+          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"};
+
+        # set the next start
+        $next_start =
+          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} + 1;
+
+        # partition done
+        shift @worklist;
+      }
+    }
+
+    # check, whether there is sufficient space on the disk
+    ( $min_req_total_space > $FAI::current_config{$disk}{"size"} )
+      and die
+"Disk $disk is too small - at least $min_req_total_space bytes are required\n";
+
+    # make sure, extended partitions are only created on msdos disklabels
+    ( $FAI::configs{$config}{"disklabel"} ne "msdos" && $extended > -1 )
+      and die
+"INTERNAL ERROR: extended partitions are not supported by this disklabel\n";
+
+    # ensure that we have done our work
+    foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
+    {
+      ( defined( $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} )
+          && defined(
+          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} ) )
+        or die "INTERNAL ERROR: start or end of partition $part_id not set\n";
+    }
+
+  }
+}
+
+1;
+

Copied: people/michael/features/setup_harddisks_2/implementation/lib/volumes.pm (from rev 4708, people/michael/features/setup_harddisks_2/implementation/shdd2-volumes)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/volumes.pm	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/lib/volumes.pm	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,417 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file shdd2-volumes
+#
+# @brief Parse the current partition table and LVM/RAID configurations
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+################################################################################
+#
+# @brief Collect the current partition information from all disks listed both
+# in $FAI::disks and $FAI::configs{PHY_<disk>}
+#
+################################################################################
+sub get_current_disks {
+
+  # backup value of $FAI::no_dry_run
+  my $no_dry_run = $FAI::no_dry_run;
+
+  # obtain the current state of all disks
+  foreach my $disk (@FAI::disks) {
+
+    # create full paths
+    ( $disk =~ m{^/} ) or $disk = "/dev/$disk";
+
+    # make sure, $disk is a proper block device
+    ( -b $disk ) or die "$disk is not a block special device!\n";
+
+    # initialise the hash
+    $FAI::current_config{$disk}{"partitions"} = {};
+
+    # the list to hold the output of parted commands as parsed below
+    my @parted_print = ();
+
+    # set no_dry_run to perform read-only commands always
+    $FAI::no_dry_run = 1;
+
+    # try to obtain the partition table for $disk
+    # it might fail with parted_2 in case the disk has no partition table
+    my $error =
+      &FAI::execute_command_std( "parted -s $disk unit TiB print", \@parted_print, 0 );
+
+    # reset no_dry_run
+    $FAI::no_dry_run = $no_dry_run;
+
+    # parted_2 happens when the disk has no disk label, because parted then
+    # provides no information about the disk
+    if ( $error eq "parted_2" ) {
+
+      # if there is no disk configuration, write an msdos disklabel
+      if ( !defined( $FAI::configs{"PHY_$disk"}{"disklabel"} ) ) {
+
+        # write the disk label as configured
+        $error = &FAI::execute_command( "parted -s $disk mklabel msdos" );
+      } else {
+
+        # write the disk label as configured
+        $error =
+          "parted -s $disk mklabel " . $FAI::configs{"PHY_$disk"}{"disklabel"} );
+      }
+
+      # set no_dry_run to perform read-only commands always
+      $FAI::no_dry_run = 1;
+
+      # retry partition-table print
+      $error =
+        &FAI::execute_command( "parted -s $disk unit TiB print", \@parted_print, 0 );
+
+      # reset no_dry_run
+      $FAI::no_dry_run = $no_dry_run;
+    }
+
+    # check, whether there is still an error
+    if ( $error ne "" ) {
+      my $response = &FAI::get_error( $error, "response" );
+      ( $response eq "die" ) and die &FAI::get_error( $error, "message" );
+      ( $response eq "warn" ) and warn &FAI::get_error( $error, "message" );
+    }
+
+# the following code parses the output of parted print, using various units
+# (TiB, B, chs)
+# the parser is capable of reading the output of parted version 1.7.1, which
+# looks like
+#
+# $ /sbin/parted -s /dev/hda unit B print
+# WARNING: You are not superuser.  Watch out for permissions.
+#
+# Disk /dev/hda: 80026361855B
+# Sector size (logical/physical): 512B/512B
+# Partition Table: mac
+#
+# Number  Start         End           Size          File system  Name     Flags
+#  1      512B          32767B        32256B                     primary
+#  5      32768B        1033215B      1000448B      hfs          primary  boot
+#  3      134250496B    32212287487B  32078036992B  hfs+         primary
+#  6      32212287488B  46212287487B  14000000000B  ext3         primary
+#  2      46212287488B  47212287999B  1000000512B   linux-swap   primary  swap
+#  4      47212288000B  80026361855B  32814073856B  ext3         primary
+#
+# Note that the output contains an additional column on msdos, indicating,
+# whether the type of a partition is primary, logical or extended.
+#
+# $ parted -s /dev/hda unit B print
+#
+# Disk /dev/hda: 82348277759B
+# Sector size (logical/physical): 512B/512B
+# Partition Table: msdos
+#
+# Number  Start         End           Size          Type      File system  Flags
+#  1      32256B        24675839B     24643584B     primary   ext3
+#  2      24675840B     1077511679B   1052835840B   primary   linux-swap
+#  3      1077511680B   13662190079B  12584678400B  primary   ext3         boot
+#  4      13662190080B  82343278079B  68681088000B  extended
+#  5      13662222336B  14715025919B  1052803584B   logical   ext3
+#         14715058176B  30449986559B  15734928384B
+#  7      30450018816B  32547432959B  2097414144B   logical   ext3
+#  8      32547465216B  82343278079B  49795812864B  logical   ext3
+#
+
+    # As shown above, some entries may be blank. Thus the exact column starts
+    # and lengths must be parsed from the header line. This is stored in the
+    # following hash
+    my %cols = ();
+
+    # Parse the output line by line
+    foreach my $line (@parted_print) {
+
+      # now we test line by line - some of them may be ignored
+      next if ( $line =~ /^Disk /
+        || $line =~ /^\s*$/
+        || $line =~ /^WARNING: You are not superuser/ );
+
+      # determine the logical sector size
+      if ( $line =~ /^Sector size \(logical\/physical\): (\d+)B\/(\d+)B$/ ) {
+        $FAI::current_config{$disk}{"sector_size"} = $1;
+      }
+
+      # read and store the current disk label
+      elsif ( $line =~ /^Partition Table: (.+)$/ ) {
+        $FAI::current_config{$disk}{"disklabel"} = $1;
+      }
+
+      # the line containing the table headers
+      elsif ( $line =~ /^(Number\s+)(\S+\s+)+/ ) {
+        my $col_start = 0;
+
+        # check the length of each heading; note that they might contain spaces
+        while ( $line =~ /^(\S+( [a-z]\S+)?\s*)([A-Z].*)?$/ ) {
+          my $heading = $1;
+
+          # set the line to the remainder
+          $line = "";
+          $line = $3 if defined($3);
+
+          # the width of the column includes any whitespace
+          my $col_width = length($heading);
+          $heading =~ s/(\S+)\s*$/$1/;
+
+          # build the hash entry
+          # this start counter starts at 0, which is useful below
+          $cols{$heading} = {
+            "start"  => $col_start,
+            "length" => $col_width
+          };
+          $col_start += $col_width;
+        }
+      }
+
+      # one of the partitions
+      else {
+
+        # we must have seen the header, otherwise probably the format has
+        # changed
+        defined( $cols{"File system"}{"start"} )
+          or die
+          or die "INTERNAL ERROR: Table header not seen yet\n";
+
+        # the info for the partition number
+        my $num_cols_before = $cols{"Number"}{"start"};
+        my $num_col_width   = $cols{"Number"}{"length"};
+
+        # the info for the file system column
+        my $fs_cols_before = $cols{"File system"}{"start"};
+        my $fs_col_width   = $cols{"File system"}{"length"};
+
+        # get the partition number, if any
+        $line =~ /^.{$num_cols_before}(.{$num_col_width})/;
+        my $id = $1;
+        $id =~ s/\s*//g;
+
+        # if there is no partition number, then it must be free space, so no
+        # file system either
+        next if ( $id eq "" );
+
+        # extract the set of characters
+        $line =~ /^.{$fs_cols_before}(.{$fs_col_width})/;
+        my $fs = $1;
+
+        # remove any trailing space
+        $fs =~ s/\s*$//g;
+
+        # store the information in the hash
+        $FAI::current_config{$disk}{"partitions"}{$id}{"filesystem"} = $fs;
+      }
+    }
+
+    # set no_dry_run to perform read-only commands always
+    $FAI::no_dry_run = 1;
+
+    # reset the output list
+    @parted_print = ();
+
+    # obtain the partition table using bytes as units
+    $error =
+      &FAI::execute_command_std( "parted -s $disk unit B print free", \@parted_print, 0 );
+
+    # reset no_dry_run
+    $FAI::no_dry_run = $no_dry_run;
+
+    # check, whether an error has occured - already handled by
+    # execute_command_std
+    # if ( $error ne "" )
+    # {
+    # my $response = &FAI::get_error( $error, "response" );
+    # ( $response eq "die" ) and die &FAI::get_error( $error, "message" );
+    # ( $response eq "warn" ) and warn &FAI::get_error( $error, "message" );
+    # }
+
+    # Parse the output of the byte-wise partition table
+    foreach my $line (@parted_print) {
+
+      # the disk size line (Disk /dev/hda: 82348277759B)
+      if ( $line =~ /Disk \Q$disk\E: (\d+)B$/ ) {
+        $FAI::current_config{$disk}{"begin_byte"} = 0;
+        $FAI::current_config{$disk}{"end_byte"}   = ( $1 - 1 );
+        $FAI::current_config{$disk}{"size"}       = $1;
+
+        # nothing else to be done
+        next;
+      }
+
+      # One of the partition lines, see above example
+      next
+        unless ( $line =~
+        /^\s*(\d+)\s+(\d+)B\s+(\d+)B\s+(\d+)B(\s+(primary|logical|extended))?/i
+        );
+
+      # mark the bounds of existing partitions
+      $FAI::current_config{$disk}{"partitions"}{$1}{"begin_byte"} = $2;
+      $FAI::current_config{$disk}{"partitions"}{$1}{"end_byte"}   = $3;
+      $FAI::current_config{$disk}{"partitions"}{$1}{"count_byte"} = $4;
+
+      # is_extended defaults to false/0
+      $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 0;
+
+      # but may be true/1 on msdos disk labels
+      ( ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" )
+          && ( $6 eq "extended" ) )
+        and $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 1;
+    }
+
+    # set no_dry_run to perform read-only commands always
+    $FAI::no_dry_run = 1;
+
+    # reset the output list
+    @parted_print = ();
+
+    # obtain the partition table using bytes as units
+    $error =
+      &FAI::execute_command_std(
+      "parted $disk unit chs print free", \@parted_print, 0 );
+
+    # reset no_dry_run
+    $FAI::no_dry_run = $no_dry_run;
+
+    # Parse the output of the CHS partition table
+    foreach my $line (@parted_print) {
+
+   # find the BIOS geometry that looks like this:
+   # BIOS cylinder,head,sector geometry: 10011,255,63.  Each cylinder is 8225kB.
+      if ( $line =~
+        /^BIOS cylinder,head,sector geometry:\s*(\d+),(\d+),(\d+)\.\s*Each cylinder is \d+kB\.$/
+        ) {
+        $FAI::current_config{$disk}{"bios_cylinders"}         = $1;
+        $FAI::current_config{$disk}{"bios_heads"}             = $2;
+        $FAI::current_config{$disk}{"bios_sectors_per_track"} = $3;
+      }
+    }
+
+    # make sure we have determined all the necessary information
+    ( $FAI::current_config{$disk}{"begin_byte"} == 0 )
+      or die "Invalid start byte\n";
+    ( $FAI::current_config{$disk}{"end_byte"} > 0 ) or die "Invalid end byte\n";
+    defined( $FAI::current_config{$disk}{"size"} )
+      or die "Failed to determine disk size\n";
+    defined( $FAI::current_config{$disk}{"sector_size"} )
+      or die "Failed to determine sector size\n";
+    defined( $FAI::current_config{$disk}{"bios_sectors_per_track"} )
+      or die "Failed to determine the number of sectors per track\n";
+
+  }
+}
+
+use Linux::LVM;
+
+################################################################################
+#
+# @brief Collect the current LVM configuration
+#
+################################################################################
+sub get_current_lvm {
+
+  # get the existing volume groups
+  foreach my $vg (get_volume_group_list()) {
+    # initialise the hash entry
+    $FAI::current_lvm_config{$vg}{"physical_volumes"} = ();
+    
+    # store the vg size in MB
+    my %vg_info = get_volume_group_information($vg);
+    $FAI::current_lvm_config{$vg}{"size"} = 
+      &FAI::convert_unit( $vg_info{"alloc_pe_size"} .
+        $vg_info{"alloc_pe_size_unit"} );
+    
+      # store the logical volumes and their sizes
+    my %lv_info = get_logical_volume_information($vg);
+    foreach my $lv_name (sort keys %lv_info) {
+      my $short_name = $lv_name;
+      $short_name =~ "s{/dev/\Q$vg\E/}{}";
+      $FAI::current_lvm_config{$vg}{"volumes"}{$short_name}{"size"} =
+        &FAI::convert_unit( $lv_info{$lv_name}->{"lv_size"} .
+          $lv_info{$lv_name}->{"lv_size_unit"} );
+    }
+    
+    # store the physical volumes
+    $FAI::current_lvm_config{$vg}{"physical_volumes"} = 
+      sort keys get_physical_volume_information($vg);
+  }
+
+}
+
+################################################################################
+#
+# @brief Collect the current RAID device information from all partitions
+# currently active in the system
+#
+################################################################################
+sub get_current_raid {
+
+  # backup value of $FAI::no_dry_run
+  my $no_dry_run = $FAI::no_dry_run;
+
+  # the list to hold the output of mdadm commands as parsed below
+  my @mdadm_print = ();
+
+  # set no_dry_run to perform read-only commands always
+  $FAI::no_dry_run = 1;
+
+  # try to obtain the list of existing RAID arrays
+  my $error =
+    &FAI::execute_command_std( "mdadm --detail --scan --verbose -c partitions",
+    \@mdadm_print, 0 );
+
+# the expected output is as follows
+# $ mdadm --detail --scan --verbose -c partitions
+# ARRAY /dev/md0 level=linear num-devices=2 UUID=7e11efd6:93e977fd:b110d941:ce79a4f6
+#    devices=/dev/hda1,/dev/hda2
+# ARRAY /dev/md1 level=raid0 num-devices=2 UUID=50d7a6ec:4207f0db:b110d941:ce79a4f6
+#    devices=/dev/md0,/dev/hda3
+
+  # the id of the RAID
+  my $id;
+
+  # parse the output line by line
+  foreach my $line (@mdadm_print) {
+    if ( $line =~ /^ARRAY \/dev\/md(\d+) level=(\S+) num-devices=\d+ UUID=/ ) {
+      $id = $1;
+      $FAI::current_raid_config{$id}{"mode"} = $2;
+    } elsif ( $line =~ /^\s*devices=(\S+)$/ ) {
+      @{ $FAI::current_raid_config{$id}{"devices"} } = split( ",", $1 );
+    }
+  }
+
+  # reset no_dry_run
+  $FAI::no_dry_run = $no_dry_run;
+}
+
+1;
+

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,191 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-# treat all warnings about uninitialised values as errors
-use warnings FATAL => qw(uninitialized);
-
-################################################################################
-#
-# @file shdd2
-#
-# @brief The main function of setup harddisks 2 - the tool to configure the
-# partitioning from within FAI.
-#
-# This is an implementation from scratch to properly support LVM and RAID. The
-# input format is documented in @ref shdd2-parser
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-# command line parameter handling
-use Getopt::Std;
-
-# the variables for getopt
-our ( $opt_X, $opt_f );
-
-# parse the command line
-&getopts('Xf:') || die <<EOF;
-USAGE: [-X]                     no test, your harddisks will be formated
-                                default: only test, no real formating
-       [-f<config-filename>]    default: parse classes
-EOF
-
-# $disklist must be provided by the environment
-defined( $ENV{disklist} ) or die "Environment variable disklist is not set";
-
-################################################################################
-#
-# @brief Really write any changes to disk
-#
-################################################################################
-$FAI::no_dry_run = 0;
-($opt_X) and $FAI::no_dry_run = 1;
-($opt_X) or warn "shdd2 is running in test-only mode!\n";
-
-# include all subparts
-require "shdd2-init";
-require "shdd2-lib";
-require "shdd2-volumes";
-require "shdd2-parser";
-require "shdd2-sizes";
-require "shdd2-commands";
-require "shdd2-fstab";
-require "shdd2-exec";
-
-# the config source file
-my $config_file = undef;
-
-# use the config file, if given
-if ($opt_f) {
-  open( $config_file, $opt_f ) or die "Failed to open config file $opt_f\n";
-}
-
-# see which class file to use
-else {
-  foreach my $classfile ( reverse split( /\s+/, $ENV{"classes"} ) ) {
-    next unless ( -r "$ENV{'FAI'}/disk_config/$classfile" );
-    open( $config_file, "$ENV{'FAI'}/disk_config/$classfile" );
-    last;
-  }
-}
-
-# if we could not find any matching class file, bail out
-defined($config_file) or die "No matching disk_config found\n";
-
-# start the parsing - thereby $FAI::configs is filled
-&FAI::run_parser($config_file);
-
-# read the sizes and partition tables of all disks listed in $FAI::disks
-&FAI::get_current_disks;
-
-# see whether there are any existing LVMs
-# load the dm-mod module first, otherwise the LVM tools won't work
-`modprobe dm-mod`;
-&FAI::get_current_lvm;
-
-# see whether there are any existing RAID devices
-# load the md-mod module first, otherwise there is nothing that can be detected
-`modprobe md-mod`;
-&FAI::get_current_raid;
-
-# for debugging purposes to print the hash structures
-use Data::Dumper;
-
-# debugging only: print the current contents of $FAI::current_config
-if ($FAI::debug) {
-  print "Current disk layout\n";
-
-  # make sure perl doesn't warn about it being used only once
-  our %current_config;
-  print Dumper \%current_config;
-
-  print "Current LVM layout\n";
-
-  # make sure perl doesn't warn about it being used only once
-  our %current_lvm_config;
-  print Dumper \%current_lvm_config;
-
-  print "Current RAID layout\n";
-
-  # make sure perl doesn't warn about it being used only once
-  our %current_raid_config;
-  print Dumper \%current_raid_config;
-}
-
-# compute the new LVM and partition sizes; do the partition sizes first to have
-# them available for the the volume group size estimation
-&FAI::compute_partition_sizes;
-&FAI::compute_lv_sizes;
-
-# debugging only: print the current contents of $FAI::configs
-if ($FAI::debug) {
-  print "Desired disk layout\n";
-  print Dumper \%FAI::configs;
-}
-
-# generate the command script
-&FAI::build_disk_commands;
-&FAI::build_raid_commands;
-&FAI::build_lvm_commands;
-
-# run all commands
-# debugging only: print the command script
-($FAI::debug) and print "$_\n" foreach (@FAI::commands);
-
-# run the command (if $FAI::no_dry_run is set)
-&FAI::execute_command_std($_) foreach (@FAI::commands);
-
-# generate the proposed fstab contents
-my @fstab = &FAI::generate_fstab( \%FAI::configs );
-
-# debugging only; print fstab
-($FAI::debug) and print "$_\n" foreach (@fstab);
-
-# write the proposed contents of fstab to $LOGDIR/fstab, if $FAI::no_dry_run is set
-if ($FAI::no_dry_run) {
-  # write fstab to $LOGDIR/fstab
-  open( FSTAB, ">$ENV{LOGDIR}/fstab" )
-    or die "Failed to open $ENV{LOGDIR}/fstab for writing\n";
-  print FSTAB "$_\n" foreach (@fstab);
-  close FSTAB;
-}
-
-# write variables to $LOGDIR/disk_var.sh
-# debugging
-($FAI::debug) and print "$_=$FAI::disk_var{$_}\n"
-  foreach ( keys %FAI::disk_var );
-
-# do it, if $FAI::no_dry_run is set
-if ($FAI::no_dry_run)
-{
-  open( DISK_VAR, ">$ENV{LOGDIR}/disk_var.sh" )
-    or die "Unable to write to file $ENV{LOGDIR}/disk_var.sh\n";
-  print DISK_VAR "$_=$FAI::disk_var{$_}\n" foreach ( keys %FAI::disk_var );
-  close DISK_VAR;
-}
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-commands
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-commands	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-commands	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,652 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-commands
-#
-# @brief Build the required commands using the config stored in %FAI::configs
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-################################################################################
-#
-# @brief Build the mkfs commands for the partition pointed to by $partition
-#
-# @param $device Device name of the target partition
-# @param $partition Reference to partition in the config hash
-#
-# The command is added @FAI::commands
-#
-################################################################################
-sub build_mkfs_commands {
-  my ( $device, $partition ) = @_;
-
-  defined( $partition->{"filesystem"} )
-    or die "INTERNAL ERROR: filesystem is undefined\n";
-
-  return if ( $partition->{"filesystem"} eq "-" );
-
-  my ($create_options)=$partition->{"fs_options"}=~m/.*createopts="([^"]+)".*/;
-  my ($tune_options)=$partition->{"fs_options"}=~m/.*tuneopts="([^"]+)".*/;
-  $create_options = $partition->{"fs_options"} unless $create_options;
-
-  if ( $partition->{"filesystem"} eq "swap" )
-  {
-    push @FAI::commands, "mkswap " . $create_options . " $device";
-  }
-  else
-  {
-    print STDERR "create_options: $create_options tune_options: $tune_options\n" if $FAI::debug;
-    push @FAI::commands,
-      "mkfs."
-      . $partition->{"filesystem"} . " "
-      . $create_options
-      . " " . $device;
-    push @FAI::commands,
-      "tune2fs "
-      . $tune_options
-      . " " . $device if $tune_options;
-  }
-}
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to create any RAID devices
-#
-# The list is @FAI::commands
-#
-################################################################################
-sub build_raid_commands {
-  # TODO: do we need to stop anything before we continue? Do we need to issue
-  # mdadm --misc --zero-superblock /dev/hdx?
-
-  # loop through all configs
-  foreach my $config ( keys %FAI::configs ) {
-
-    # no LVM here
-    next if ( $config =~ /^VG_(.+)$/ );
-
-    # no physical devices here
-    next if ( $config =~ /^PHY_(.+)$/ );
-
-    # create the RAID devices and the filesystems
-    ( $config eq "RAID" ) or die "INTERNAL ERROR: Invalid config\n";
-
-    # create all raid devices
-    foreach my $id ( sort keys %{ $FAI::configs{$config}{"volumes"} } ) {
-
-      # the desired RAID level
-      my $level = $FAI::configs{$config}{"volumes"}{$id}{"mode"};
-
-      # prepend "raid", if the mode is numeric-only
-      $level = "raid" . $level if ( $level =~ /^\d+$/ );
-
-      # the list of RAID devices
-      my @devs = keys %{ $FAI::configs{$config}{"volumes"}{$id}{"devices"} };
-
-      # set proper partition types for RAID
-      foreach my $d (@devs) {
-        # skip devices marked missing
-        next if( 1 ==
-          $FAI::configs{$config}{"volumes"}{$id}{"devices"}{$d}{"missing"} );
-        # only match physical partitions (this string of matchings is hopefully complete)
-        next unless( $d =~
-          m{^/dev/(cciss/c\dd\dp|ida/c\dd\dp|rd/c\dd\dp|ataraid/d\dp|sd[a-t]|hd[a-t])(\d+)$} );
-        my $disk = "/dev/$1";
-        my $part_no = $2;
-        # in case the name was /dev/cciss/c0d1p or the like, remove the trailing
-        # p to get the disk name
-        $disk =~ s/(\d)p$/$1/;
-        # make sure this device really exists (we can't check for the partition
-        # as that may be created later on
-        ( -b $disk ) or die "Specified disk $disk does not exist in this system!\n";
-        # set the raid flag
-        push @FAI::commands, "parted -s $disk set $part_no raid on";
-      }
-      # wait for udev to set up all devices
-      push @FAI::commands, "udevsettle --timeout=10";
-      
-      # create the command
-      push @FAI::commands,
-        "yes | mdadm --create /dev/md$id --level=$level "
-        . "--raid-devices="
-        . scalar(@devs) . " "
-        . join( " ", @devs );
-
-      # create the filesystem on the volume
-      &FAI::build_mkfs_commands( "/dev/md$id",
-        \%{ $FAI::configs{$config}{"volumes"}{$id} } );
-    }
-  }
-}
-
-################################################################################
-#
-# @brief Erase the LVM signature from a list of devices that should be prestine
-# in order to avoid confusion of the lvm tools
-#
-# The list is @FAI::commands
-#
-################################################################################
-sub erase_lvm_signature {
-    my( $devices_aref ) = @_;
-      # first remove the dm_mod module to prevent ghost lvm volumes 
-      # from existing
-#      push @FAI::commands, "modprobe -r dm_mod";
-      # zero out (broken?) lvm signatures
-#      push @FAI::commands, "dd if=/dev/zero of=$_ bs=1 count=1"
-#        foreach ( @{$devices_aref} );
-    my $device_list = join(" ", (@{$devices_aref}) );
-    ( $FAI::debug > 0 ) and print "list of erased devices: $device_list\n"; 
-    push @FAI::commands, "pvremove -ff -y $device_list";
-
-      # reload module
-#      push @FAI::commands, "modprobe dm_mod";
-
-}
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to setup the LVM
-#
-# The list is @FAI::commands
-#
-################################################################################
-sub build_lvm_commands {
-  # loop through all configs
-  foreach my $config ( keys %FAI::configs ) {
-    # no physical devices here
-    next if ( $config =~ /^PHY_(.+)$/ );
-
-    # no RAID devices here
-    next if ( $config eq "RAID" );
-
-    # create the volume groups, the logical volumes and the filesystems
-    ( $config =~ /^VG_(.+)$/ ) or die "INTERNAL ERROR: Invalid config\n";
-
-    # the volume group
-    my $vg = $1;
-
-    # find volumes that should be preserved or resized and ensure that they
-    # already exist
-    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } ) {
-      next
-        unless ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1
-        || $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} == 1 );
-
-      # preserved or resized volumes must exist already
-      defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
-        or die "/dev/$vg/$lv can't be preserved, it does not exist.\n";
-    }
-
-    # set proper partition types for LVM
-    foreach my $d (keys %{ $FAI::configs{$config}{"devices"} }) {
-      # only match physical partitions (this string of matchings is hopefully complete)
-      next unless( $d =~
-        m{^/dev/(cciss/c\dd\dp|ida/c\dd\dp|rd/c\dd\dp|ataraid/d\dp|sd[a-t]|hd[a-t])(\d+)$} );
-      my $disk = "/dev/$1";
-      my $part_no = $2;
-      # in case the name was /dev/cciss/c0d1p or the like, remove the trailing
-      # p to get the disk name
-      $disk =~ s/(\d)p$/$1/;
-      # make sure this device really exists (we can't check for the partition
-      # as that may be created later on
-      ( -b $disk ) or die "Specified disk $disk does not exist in this system!\n";
-      # set the lvm flag
-      push @FAI::commands, "parted -s $disk set $part_no lvm on";
-    }
-    # wait for udev to set up all devices
-    push @FAI::commands, "udevsettle --timeout=10";
-
-    # create the volume group, if it doesn't exist already
-    if ( !defined( $FAI::current_lvm_config{$vg} ) ) {
-      # create all the devices
-      my @devices = keys %{ $FAI::configs{$config}{"devices"} };
-      &FAI::erase_lvm_signature(\@devices);
-      push @FAI::commands, "pvcreate $_"
-        foreach ( @devices );
-      # create the volume group
-      push @FAI::commands, "vgcreate $vg "
-        . join( " ", keys %{ $FAI::configs{$config}{"devices"} } );
-    }
-
-    # otherwise add or remove the devices for the volume group, run pvcreate
-    # where needed (using pvdisplay <bla> || pvcreate <bla>)
-    else {
-
-      # the list of devices to be created
-      my %new_devs = ();
-
-      # create an undefined entry for each new device
-      @new_devs{ keys %{ $FAI::configs{$config}{"devices"} } } = ();
-      
-      my @new_devices = keys %new_devs;
-      
-      erase_lvm_signature( \@new_devices );
-      
-      # create all the devices
-      push @FAI::commands, "pvcreate $_"
-        foreach ( @new_devices );
-
-      # extend the volume group by the new devices (includes the current ones)
-      push @FAI::commands, "vgextend $vg " . join( " ", keys %new_devs );
-
-      # the devices to be removed
-      my %rm_devs = ();
-      @rm_devs{ @{ $FAI::current_lvm_config{$vg}{"physical_volumes"} } } = ();
-
-      # remove remaining devices from the list
-      delete $rm_devs{$_} foreach ( keys %new_devs );
-
-      # run vgreduce to get them removed
-      push @FAI::commands, "vgreduce $vg " . join( " ", keys %rm_devs )
-        if ( scalar( keys %rm_devs ) );
-    }
-
-    # enable the volume group
-    push @FAI::commands, "vgchange -a y $vg";
-
-    # remove, resize, create the logical volumes
-    # remove all volumes that do not exist anymore or need not be preserved
-    foreach my $lv ( keys %{ $FAI::current_lvm_config{$vg}{"volumes"} } ) {
-      # skip preserved/resized volumes
-      next if ( defined( $FAI::configs{$config}{"volumes"}{$lv} )
-        && ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1
-          || $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} ));
-
-      # remove $lv
-      push @FAI::commands, "lvremove -f $vg/$lv";
-    }
-
-    # now create or resize the configured logical volumes
-    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } ) {
-      # skip preserved partitions, but ensure that they exist
-      if ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"preserve"} == 1 ) {
-        defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
-          or die "Preserved volume $vg/$lv does not exist\n";
-        next;
-      }
-
-      # resize the volume
-      if ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"resize"} == 1 ) {
-        defined( $FAI::current_lvm_config{$vg}{"volumes"}{$lv} )
-          or die "Resized volume $vg/$lv does not exist\n";
-
-        # note that resizing a volume destroys the data on it
-        push @FAI::commands,
-          "lvresize -L "
-          . $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"}
-          . " $vg/$lv";
-      }
-
-      # create a new volume
-      else {
-        push @FAI::commands,
-          "lvcreate -n $lv -L "
-          . $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} . " $vg";
-
-        # create the filesystem on the volume
-        &FAI::build_mkfs_commands( "/dev/$vg/$lv",
-          \%{ $FAI::configs{$config}{"volumes"}{$lv} } );
-      }
-    }
-
-  }
-}
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to setup the partitions
-#
-# The list is @FAI::commands
-#
-################################################################################
-sub build_disk_commands {
-  # loop through all configs
-  foreach my $config ( keys %FAI::configs ) {
-    # no RAID devices here
-    next if ( $config eq "RAID" );
-
-    # no LVM here
-    next if ( $config =~ /^VG_(.+)$/ );
-
-    # configure a physical device
-    ( $config =~ /^PHY_(.+)$/ ) or die "INTERNAL ERROR: Invalid config\n";
-
-    # the device to be configured
-    my $disk = $1;
-
-    # create partitions on non-virtual configs
-    if ( $FAI::configs{$config}{"virtual"} == 0 ) {
-      # the list of partitions that must be preserved
-      my @to_preserve = ();
-
-      # find partitions that should be preserved or resized
-      foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
-        next unless (
-          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} == 1
-          || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1 );
-
-        # preserved or resized partitions must exist already
-        defined( $FAI::current_config{$disk}{"partitions"}{$part_id} )
-          or die "$part_id can't be preserved, it does not exist.\n";
-
-        # add a mapping from the configured partition to the existing one
-        # (identical here, may change for extended partitions below)
-        $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"} =
-          $part_id;
-
-        # add $part_id to the list of preserved partitions
-        push @to_preserve, $part_id;
-
-      }
-
-      # sort the list of preserved partitions
-      @to_preserve = sort { $a <=> $b } @to_preserve;
-
-      # add the extended partition as well, if logical partitions must be
-      # preserved; and mark it as resize
-      if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
-        # we assume there are no logical partitions
-        my $has_logical = 0;
-        my $extended    = -1;
-
-        # now check all entries; the array is sorted
-        foreach my $part_id (@to_preserve) {
-          # the extended partition may already be listed; then, the id of the
-          # extended partition must not change
-          if ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 ) {
-            ( defined( $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"extended"})
-                && defined( $FAI::current_config{$disk}{"partitions"}{$extended}{"is_extended"})
-                && $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"extended"} == 1
-                && $FAI::current_config{$disk}{"partitions"}{$extended}{"is_extended"} == 1 ) 
-                or die "ID of extended partition changes\n";
-
-            # make sure resize is set
-            $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} = 1;
-            $extended = $part_id;
-            last;
-          }
-
-          # there is some logical partition
-          if ( $part_id > 4 ) {
-            $has_logical = 1;
-            last;
-          }
-        }
-
-        # if the extended partition is not listed yet, find and add it now; note
-        # that we need to add the existing one
-        if ( 1 == $has_logical && -1 == $extended ) {
-          foreach my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } ) {
-
-            # no extended partition
-            next unless ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 );
-
-            # find the configured extended partition to set the mapping
-            foreach my $p ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
-              next unless (
-                $FAI::configs{$config}{"partitions"}{$p}{"size"}{"extended"} ==
-                1 );
-
-              # make sure resize is set
-              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"resize"} = 1;
-
-              # store the id for further checks
-              $extended = $p;
-
-              # add a mapping entry to the existing extended partition
-              $FAI::configs{$config}{"partitions"}{$p}{"maps_to_existing"} =
-                $part_id;
-
-              # add it to the preserved partitions
-              push @to_preserve, $p;
-
-              last;
-            }
-
-            # sort the list of preserved partitions (again)
-            @to_preserve = sort { $a <=> $b } @to_preserve;
-
-            last;
-          }
-        }
-
-        # a sanity check: if there are logical partitions, they extended must
-        # have been added
-        ( 0 == $has_logical || -1 != $extended ) or die
-          "INTERNAL ERROR: Required extended partition not detected for preserve\n";
-      }
-
-      # A new disk label may only be written if no partitions need to be
-      # preserved
-      ( ( $FAI::configs{$config}{'disklabel'} eq
-            $FAI::current_config{$disk}{'disklabel'})
-          || ( scalar(@to_preserve) == 0 ) ) 
-          or die "Can't change disklabel, partitions are to be preserved\n";
-
-      # write the disklabel to drop the previous partition table
-      push @FAI::commands, "parted -s $disk mklabel "
-        . $FAI::configs{$config}{'disklabel'};
-
-      # once we rebuild partitions, their ids are likely to change; this counter
-      # helps keeping track of this
-      my $part_nr = 0;
-
-      # now rebuild all preserved partitions
-      foreach my $part_id (@to_preserve) {
-        # get the existing id
-        my $mapped_id =
-          $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
-
-        # get the original starts and ends
-        my $start =
-          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"begin_byte"};
-        my $end =
-          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"end_byte"};
-
-        # the type of the partition defaults to primary
-        my $part_type = "primary";
-        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
-
-          # change the partition type to extended or logical as appropriate
-          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} == 1 ) {
-            $part_type = "extended";
-          } elsif ( $part_id > 4 ) {
-            $part_type = "logical";
-            $part_nr = 4 if ( $part_nr < 4 );
-          }
-        }
-
-        # increase the partition counter for the partition created next and
-        # write it to the configuration
-        $part_nr++;
-        $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"} =
-          $part_nr;
-
-        # build a parted command to create the partition
-        push @FAI::commands,
-          "parted -s $disk mkpart $part_type ${start}B ${end}B";
-      }
-
-      # resize partitions; first we shrink partitions, then grow others;
-      # furthermore we start from the end to shrink logical partitions before
-      # the extended one, but grow partitions starting from the beginning
-      my @shrink_list = reverse sort (@to_preserve);
-      my @grow_list   = ();
-
-      # iterate over the worklists
-      foreach my $part_id (@shrink_list) {
-        # anything to be done?
-        next unless (
-          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1 );
-
-        # get the existing id
-        my $mapped_id =
-          $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
-
-        # if partition is to be grown, move it to then grow_list
-        if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} >
-          $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"count_byte"} ) {
-          unshift @grow_list, $part_id;
-          next;
-        }
-
-        # get the new partition id
-        my $p = $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"};
-
-        # get the new starts and ends
-        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
-        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
-
-        # build an appropriate command
-        push @FAI::commands, "parted -s $disk resize $p ${start}B ${end}B";
-      }
-
-      # grow the remaining partitions
-      foreach my $part_id (@grow_list) {
-
-        # get the existing id
-        my $mapped_id = $FAI::configs{$config}{"partitions"}{$part_id}{"maps_to_existing"};
-
-        # get the new partition id
-        my $p = $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"new_id"};
-
-        # get the new starts and ends
-        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
-        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
-
-        # build an appropriate command
-        push @FAI::commands,
-          "parted -s $disk resize $p ${start}B ${end}B";
-      }
-
-      # write the disklabel again to drop the partition table
-      push @FAI::commands, "parted -s $disk mklabel " . $FAI::configs{$config}{'disklabel'};
-
-      # generate the commands for creating all partitions
-      foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
-
-        # get the new starts and ends
-        my $start = $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
-        my $end = $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"};
-
-        # the type of the partition defaults to primary
-        my $part_type = "primary";
-        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" ) {
-
-          # change the partition type to extended or logical as appropriate
-          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} == 1 ) {
-            $part_type = "extended";
-          } elsif ( $part_id > 4 ) {
-            $part_type = "logical";
-          }
-        }
-
-        # build a parted command to create the partition
-        push @FAI::commands,
-          "parted -s $disk mkpart $part_type ${start}B ${end}B";
-      }
-
-      # set the bootable flag, if requested at all
-      push @FAI::commands,
-        "parted -s $disk set "
-        . $FAI::configs{$config}{"bootable"}
-        . " boot on"
-        if ( $FAI::configs{$config}{"bootable"} > -1 );
-
-      # wait for udev to set up all devices
-      push @FAI::commands, "udevsettle --timeout=10";
-    }
-
-    # generate the commands for creating all filesystems
-    foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) {
-
-      # skip preserved/resized/extended partitions
-      next if (
-        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} == 1
-        || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} == 1
-        || $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} ==
-        1 );
-
-      # create the filesystem on $disk$part_id
-      &FAI::build_mkfs_commands( $disk . $part_id,
-        \%{ $FAI::configs{$config}{"partitions"}{$part_id} } );
-    }
-  }
-}
-
-################################################################################
-#
-# @brief Whatever happened, write the previous partition table to the disk again
-#
-################################################################################
-sub restore_partition_table {
-
-  # loop through all existing configs
-  foreach my $disk ( keys %FAI::current_config ) {
-
-    # write the disklabel again to drop the partition table
-    &FAI::execute_command( "parted -s $disk mklabel "
-        . $FAI::current_config{$disk}{'disklabel'} );
-
-    # generate the commands for creating all partitions
-    foreach my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } ) {
-
-      # get the starts and ends
-      my $start = $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"};
-      my $end = $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"};
-
-      # the type of the partition defaults to primary
-      my $part_type = "primary";
-      if ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" ) {
-
-        # change the partition type to extended or logical as appropriate
-        if ( $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} == 1 ) {
-          $part_type = "extended";
-        } elsif ( $part_id > 4 ) {
-          $part_type = "logical";
-        }
-      }
-
-      # build a parted command to create the partition
-      &FAI::execute_command( "parted -s $disk mkpart $part_type ${start}B ${end}B" );
-    }
-    warn "Partition table of disk $disk has been restored\n";
-  }
-
-  die "shdd2 failed, but the partition tables have been restored\n";
-}
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-exec
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-exec	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-exec	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,255 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-exec
-#
-# @brief functions to execute system commands
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-use File::Temp;
-
-package FAI;
-
-################################################################################
-#
-# @brief hash, defined: errors, descriptions, actions on error
-#
-# @scalar error error
-# @scalar message our errormessage
-# @scalar stderr_regex regex to recognize the error message on stderr output of the bash
-# @scalar stdout_regex regex to recognize the error message on stdout output of the bash
-# @scalar program the program this error message can come from
-# @scalar response default action on this error.
-#
-################################################################################
-$FAI::error_codes = [
-  {
-    error   => "parted_1",
-    message => "Parted produced error. Couldn't remove partition\n",
-    stderr_regex =>
-      ".*Error: Could not stat device rm - No such file or directory.*",
-    stdout_regex => "",
-    program      => "parted",
-    response     => "die",
-  },
-  {
-    error        => "parted_2",
-    message      => "Parted produced error. Could not read disk label.\n",
-    stderr_regex => ".*Error: Unable to open .* - unrecognised disk label.*",
-    stdout_regex => "",
-    program      => "parted",
-    response     => "warn",
-  },
-  {
-    error   => "parted_3",
-    message => "Parted produced error. Could not open disk\n",
-    stderr_regex =>
-      ".*Error: Could not stat device .* - No such file or directory.*",
-    stdout_regex => "",
-    program      => "parted",
-    response     => "die"
-  },
-  {
-    error   => "parted_4",
-    message => "parted not found\n",
-    stderr_regex =>
-      ".*(parted: command not found|/sbin/parted: No such file or directory)",
-    stdout_regex => "",
-    program      => "parted",
-    response     => "die"
-  },
-  {
-    error        => "parted_5",
-    message      => "Parted was unable to create the partition\n",
-    stderr_regex => "Warning: You requested a partition from .* to .*\\.\$",
-    stdout_regex => "",
-    program      => "parted",
-    response     => \&FAI::restore_partition_table,
-  },
-  {
-    error => "mkfs.xfs_1",
-    message =>
-"mkfs.xfs refused to create a filesystem. Probably you should add -f to the mkfs options in your disk_config file.\n",
-    stderr_regex =>
-      "mkfs.xfs: /dev/.* appears to contain an existing filesystem",
-    stdout_regex => "",
-    program      => "mkfs.xfs",
-    response     => "die",
-  },
-];
-
-################################################################################
-#
-# @brief returns the error message associated with an error
-#
-# @param error identifier of an error
-#
-# @return our interpretation of the error as string
-#
-################################################################################
-sub get_error_message {
-  my ($error) = @_;
-  my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
-
-  # returns the first found error message.
-  return $treffer[0]->{'message'};
-}
-
-################################################################################
-#
-# @brief gets any part of the error struct associated with an error
-#
-# @param error identifier of an error
-# @param field field of the error struct as string, example: "stderr_regex"
-#
-# @return the associated value
-#
-################################################################################
-sub get_error {
-  my ( $error, $field ) = @_;
-  my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
-
-  # returns the first found error message.
-  return $treffer[0]->{$field};
-}
-################################################################################
-#
-# @brief execute a /bin/bash command, given as string. also catch stderr and
-# stdout, to be passed to the caller function, and also used for error
-# recognition. This execute function does execute the in the error struct
-# defined action, when an error occurs.
-#
-# @param command bash command to be executed as string
-# @reference stdout_ref reference to a list, that should contain the standard
-# output of the bash command
-#
-# @reference stderr_ref reference to a list, that should contain the standard
-# errer output of the bash command
-#
-# @return the identifier of the error
-#
-################################################################################
-sub execute_command_std {
-  my ( $command, $stdout_ref, $stderr_ref ) = @_;
-  my $err = &execute_command( $command, $stdout_ref, $stderr_ref );
-  if ( $err ne "" ) {
-    my $response = &get_error( $err, "response" );
-    my $message  = &get_error( $err, "message" );
-
-    $response->() if ( ref($response) );
-
-    die $message if ( $response eq "die" );
-
-    warn $message if ( $response eq "warn" );
-
-    return $err;
-  }
-  return "";
-}
-
-################################################################################
-#
-# @brief execute a /bin/bash command, given as string. also catch stderr and
-# stdout, to be passed to the caller function, and also used for error
-# recognition. This caller function must handle the error.
-#
-# @param command bash command to be executed as string
-# @reference stdout_ref reference to a list, that should contain the standard
-# output of the bash command
-#
-# @reference stderr_ref reference to a list, that should contain the standard
-# error output of the bash command
-#
-# @return the identifier of the error
-#
-################################################################################
-sub execute_command {
-  my ( $command, $stdout_ref, $stderr_ref ) = @_;
-
-  my @stderr      = ();
-  my @stdout      = ();
-  my $stderr_line = "";
-  my $stdout_line = "";
-
-  #make tempfile, get perl filehandle and filename of the file
-  ( my $stderr_fh, my $stderr_filename ) = File::Temp::tempfile( UNLINK => 1 );
-  ( my $stdout_fh, my $stdout_filename ) = File::Temp::tempfile( UNLINK => 1 );
-
-  # do only execute the given command, when in no_dry_mode
-  if ($FAI::no_dry_run) {
-
-    ($FAI::debug)
-      and print "(CMD) $command 1> $stdout_filename 2> $stderr_filename\n";
-
-    # execute the bash command, write stderr and stdout into the testfiles
-    `$command 1> $stdout_filename 2> $stderr_filename`;
-  } else {
-    print "would run command $command; to have them executed, use -X \n";
-  }
-
-  # read the tempfile into lists, each element of the list one line
-  @stderr = <$stderr_fh>;
-  @stdout = <$stdout_fh>;
-
-  #when closing the files, the tempfiles are removed too
-  close($stderr_fh);
-  close($stdout_fh);
-
-  ($FAI::debug) and print "(STDERR) $_" foreach (@stderr);
-  ($FAI::debug) and print "(STDOUT) $_" foreach (@stdout);
-
-  #if the stderr contains information, get the first line for error recognition
-  $stderr_line = $stderr[0] if ( scalar(@stderr) > 0 );
-
-  #see last comment
-  $stdout_line = $stdout[0] if ( scalar(@stdout) > 0 );
-
-  #if an array is passed to the function, it is filled with the stdout
-  @$stdout_ref = @stdout if ( 'ARRAY' eq ref($stdout_ref) );
-
-  #see above
-  @$stderr_ref = @stderr if ( 'ARRAY' eq ref($stderr_ref) );
-
-  #get the error, if there was any
-  foreach my $err (@$FAI::error_codes) {
-    if ( (
-        $err->{'stdout_regex'} eq "" || $stdout_line =~ /$err->{'stdout_regex'}/
-      ) && ( $err->{'stderr_regex'} eq ""
-        || $stderr_line =~ /$err->{'stderr_regex'}/ )
-      && ( $err->{'program'} eq "" || $command =~ /.*$err->{'program'}.*/ )
-      ) {
-      return $err->{'error'};
-    }
-  }
-
-}
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-fstab
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-fstab	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-fstab	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,289 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-fstab
-#
-# @brief Generate an fstab file as appropriate for the configuration
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-################################################################################
-#
-# @brief this function generates the fstab file from our representation of the
-# partitions to be created.
-#
-# @reference config Reference to our representation of the partitions to be
-# created
-#
-# @return list of fstab lines
-#
-################################################################################
-sub generate_fstab {
-
-  # config structure is the only input
-  my ($config) = @_;
-
-  # the file to be returned, a list of lines
-  my @fstab = ();
-
-  # walk through all configured parts
-  # the order of entries is most likely wrong, it is fixed at the end
-  foreach my $c ( keys %$config ) {
-
-    # entry is a physical device
-    if ( $c =~ /^PHY_(.+)$/ ) {
-      my $device = $1;
-
-      # make sure the desired fstabkey is defined at all
-      defined( $config->{$c}->{"fstabkey"} )
-        or die "INTERNAL ERROR: fstabkey undefined\n";
-
-      # create a line in the output file for each partition
-      foreach my $p ( sort keys %{ $config->{$c}->{"partitions"} } ) {
-
-        # keep a reference to save some typing
-        my $p_ref = $config->{$c}->{"partitions"}->{$p};
-
-        # skip extended partitions
-        next if ( $p_ref->{"size"}->{"extended"} );
-
-        # skip entries without a mountpoint
-        next if ( $p_ref->{"mountpoint"} eq "-" );
-
-        # each line is a list of values
-        my @fstab_line = ();
-
-        # write the device name as the first entry; if the user prefers uuids
-        # or labels, use these if available
-        my @uuid = ();
-        &execute_command_std(
-          "/lib/udev/vol_id -u $device" . $p_ref->{"number"},
-          \@uuid, 0 );
-
-        # every device must have a uuid, otherwise this is an error (unless we
-        # are testing only)
-        ( $FAI::no_dry_run == 0 || scalar(@uuid) == 1 )
-          or die "Failed to obtain UUID for $device"
-          . $p_ref->{"number"} . "\n";
-
-        # get the label -- this is likely empty
-        my @label = ();
-        &execute_command_std(
-          "/lib/udev/vol_id -l $device" . $p_ref->{"number"},
-          \@label, 0 );
-
-        # using the fstabkey value the desired device entry is defined
-        if ( $config->{$c}->{"fstabkey"} eq "uuid" ) {
-          chomp( $uuid[0] );
-          push @fstab_line, "UUID=" . $uuid[0];
-        } elsif ( $config->{$c}->{"fstabkey"} eq "label" && scalar(@label) == 1 ) {
-          chomp( $label[0] );
-          push @fstab_line, "LABEL=" . $label[0];
-        } else {
-          # otherwise, use the usual device path
-          push @fstab_line, $device . $p_ref->{"number"};
-        }
-
-        # next is the mountpoint
-        push @fstab_line, $p_ref->{"mountpoint"};
-
-        # the filesystem to be used
-        push @fstab_line, $p_ref->{"filesystem"};
-
-        # add the mount options
-        push @fstab_line, $p_ref->{"mount_options"};
-
-        # never dump
-        push @fstab_line, 0;
-
-        # order of filesystem checks; the root filesystem gets a 1, the others 2
-        push @fstab_line, 2;
-        $fstab_line[-1] = 1 if ( $p_ref->{"mountpoint"} eq "/" );
-
-        # join the columns of one line with tabs, and push it to our fstab line array
-        push @fstab, join( "\t", @fstab_line );
-
-        # set the ROOT_PARTITION variable, if this is the mountpoint for /
-        $FAI::disk_var{"ROOT_PARTITION"} = $fstab_line[0]
-          if ( $p_ref->{"mountpoint"} eq "/" );
-
-        # add to the swaplist, if the filesystem is swap
-        $FAI::disk_var{"SWAPLIST"} .= " " . $device . $p_ref->{"number"}
-          if ( $p_ref->{"filesystem"} eq "swap" );
-      }
-    } elsif ( $c =~ /^VG_(.+)$/ ) {
-      my $device = $1;
-
-      # create a line in the output file for each logical volume
-      foreach my $l ( sort keys %{ $config->{$c}->{"volumes"} } ) {
-
-        # keep a reference to save some typing
-        my $l_ref = $config->{$c}->{"volumes"}->{$l};
-
-        # skip entries without a mountpoint
-        next if ( $l_ref->{"mountpoint"} eq "-" );
-
-        # each line is a list of values
-        my @fstab_line = ();
-
-        # resolve the symlink to the real device
-        # and write it as the first entry
-        &execute_command_std(
-          "readlink -f /dev/$device/$l", \@fstab_line, 0 );
-        
-        # remove the newline
-        chomp( $fstab_line[0] );
-
-        # make sure we got back a real device
-        ( $FAI::no_dry_run == 0 || -b $fstab_line[0] ) 
-          or die "Failed to resolve /dev/$device/$l\n";
-
-        # next is the mountpoint
-        push @fstab_line, $l_ref->{"mountpoint"};
-
-        # the filesystem to be used
-        push @fstab_line, $l_ref->{"filesystem"};
-
-        # add the mount options
-        push @fstab_line, $l_ref->{"mount_options"};
-
-        # never dump
-        push @fstab_line, 0;
-
-        # order of filesystem checks; the root filesystem gets a 1, the others 2
-        push @fstab_line, 2;
-        $fstab_line[-1] = 1 if ( $l_ref->{"mountpoint"} eq "/" );
-
-        # join the columns of one line with tabs, and push it to our fstab line array
-        push @fstab, join( "\t", @fstab_line );
-
-        # set the ROOT_PARTITION variable, if this is the mountpoint for /
-        $FAI::disk_var{"ROOT_PARTITION"} = $fstab_line[0]
-          if ( $l_ref->{"mountpoint"} eq "/" );
-
-        # add to the swaplist, if the filesystem is swap
-        $FAI::disk_var{"SWAPLIST"} .= " " . $fstab_line[0]
-          if ( $l_ref->{"filesystem"} eq "swap" );
-      }
-    } elsif ( $c eq "RAID" ) {
-
-      # create a line in the output file for each device
-      foreach my $r ( sort keys %{ $config->{$c}->{"volumes"} } ) {
-
-        # keep a reference to save some typing
-        my $r_ref = $config->{$c}->{"volumes"}->{$r};
-
-        # skip entries without a mountpoint
-        next if ( $r_ref->{"mountpoint"} eq "-" );
-
-        # each line is a list of values
-        my @fstab_line = ();
-
-        # write the device name as the first entry
-        push @fstab_line, "/dev/md" . $r;
-
-        # next is the mountpoint
-        push @fstab_line, $r_ref->{"mountpoint"};
-
-        # the filesystem to be used
-        push @fstab_line, $r_ref->{"filesystem"};
-
-        # add the mount options
-        push @fstab_line, $r_ref->{"mount_options"};
-
-        # never dump
-        push @fstab_line, 0;
-
-        # order of filesystem checks; the root filesystem gets a 1, the others 2
-        push @fstab_line, 2;
-        $fstab_line[-1] = 1 if ( $r_ref->{"mountpoint"} eq "/" );
-
-        # join the columns of one line with tabs, and push it to our fstab line array
-        push @fstab, join( "\t", @fstab_line );
-
-        # set the ROOT_PARTITION variable, if this is the mountpoint for /
-        $FAI::disk_var{"ROOT_PARTITION"} = "/dev/md" . $r
-          if ( $r_ref->{"mountpoint"} eq "/" );
-
-        # add to the swaplist, if the filesystem is swap
-        $FAI::disk_var{"SWAPLIST"} .= " /dev/md$r"
-          if ( $r_ref->{"filesystem"} eq "swap" );
-      }
-    } else {
-      die "INTERNAL ERROR: Unexpected key $c\n";
-    }
-  }
-
-  # cleanup the swaplist (remove leading space)
-  $FAI::disk_var{"SWAPLIST"} =~ s/^\s+//;
-
-  # quote the entries of SWAPLIST
-  $FAI::disk_var{"SWAPLIST"} = '"' . $FAI::disk_var{"SWAPLIST"} . '"';
-
-  # sort the lines in @fstab to enable all sub mounts
-  for ( my $i = 0 ; $i < scalar(@fstab) ; $i++ ) {
-
-    # take out the mountpoint
-    ( $_, my $mp_1 ) = split( "\t", $fstab[$i] );
-
-    # partitions without a mountpoint are fine
-    next if ( $mp_1 eq "none" );
-
-    for ( my $j = $i + 1 ; $j < scalar(@fstab) ; $j++ ) {
-
-      # take out the other mountpoint
-      ( $_, my $mp_2 ) = split( "\t", $fstab[$j] );
-
-      # remove the trailing / (even though this might make it the empty string
-      $mp_2 =~ s/\/$//;
-
-      # $mp_1 depends on $mp_2 being mounted, swap them
-      if ( $mp_1 =~ /^\Q$mp_2\E\// ) {
-        my $line_i = $fstab[$i];
-        $fstab[$i] = $fstab[$j];
-        $fstab[$j] = $line_i;
-        $mp_1      = $mp_2;
-      }
-    }
-  }
-
-  # add a nice header to fstab
-  unshift @fstab,
-    "# <file sys>\t<mount point>\t<type>\t<options>\t<dump>\t<pass>";
-  unshift @fstab, "#";
-  unshift @fstab, "# /etc/fstab: static file system information.";
-
-  # return the list of lines
-  return @fstab;
-}
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-init
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-init	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-init	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,110 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-init
-#
-# @brief Initialize all variables and acquire the set of disks of the system.
-#
-# The layout of the data structures is documented in the wiki:
-# http://faiwiki.debian.net/index.php/Setup_harddisks_2
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-################################################################################
-#
-# @brief Enable debugging by setting $debug to a value greater than 0
-#
-################################################################################
-$FAI::debug = 0;
-defined( $ENV{debug} ) and $FAI::debug = $ENV{debug};
-
-################################################################################
-#
-# @brief The lists of disks of the system
-#
-################################################################################
- at FAI::disks = split( /\n/, $ENV{disklist} );
-( $FAI::debug > 0 ) and print "disklist was:\n" . $ENV{disklist};
-
-################################################################################
-#
-# @brief The variables later written to disk_var.sh
-#
-################################################################################
-%FAI::disk_var = ();
-$FAI::disk_var{"SWAPLIST"} = "";
-
-################################################################################
-#
-# @brief A flag to tell our script that the system is not installed for the
-# first time
-#
-################################################################################
-$FAI::reinstall = 1;
-defined( $ENV{fl_initial} ) and $FAI::reinstall = 0;
-
-################################################################################
-#
-# @brief The hash of all configurations specified in the disk_config file
-#
-################################################################################
-%FAI::configs = ();
-
-################################################################################
-#
-# @brief The current disk configuration
-#
-################################################################################
-%FAI::current_config = ();
-
-################################################################################
-#
-# @brief The current LVM configuration
-#
-################################################################################
-%FAI::current_lvm_config = ();
-
-################################################################################
-#
-# @brief The current RAID configuration
-#
-################################################################################
-%FAI::current_raid_config = ();
-
-################################################################################
-#
-# @brief The list of commands to be executed
-#
-################################################################################
- at FAI::commands = ();
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-parser
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-parser	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-parser	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,645 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-parser
-#
-# @brief A parser for the disk_config files within FAI, based on the EBNF
-# listed below. The implementation makes use of the RecDescent package.
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig, Sam Vilain, Andreas Schludei
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-use Parse::RecDescent;
-
-package FAI;
-
-################################################################################
-#
-# @brief the name of the device currently being configured, including a prefix
-# such as PHY_ or VG_ to indicate physical devices or LVM volume groups. For
-# RAID, the entry is only "RAID"
-#
-################################################################################
-$FAI::device = "";
-
-################################################################################
-#
-# @brief Test, whether @ref $cmd is available on the system using $PATH
-#
-# @param $cmd Command that is to be found in $PATH
-#
-# @return 1, if the command is found, else 0
-#
-################################################################################
-sub in_path {
-
-  # initialize the parameter
-  my ($cmd) = @_;
-
-  # split $PATH into its components, search all of its components
-  # and test for $cmd being executable
-  ( -x "$_/$cmd" ) and return 1 foreach ( split( ":", $ENV{"PATH"} ) );
-  # return 0 otherwise
-  return 0;
-}
-
-################################################################################
-#
-# @brief Initialise a new entry in @ref $FAI::configs for a physical disk.
-#
-# Besides creating the entry in the hash, the fully path of the device is
-# computed (see @ref $disk) and it is tested, whether this is a block device.
-# The device name is then used to define @ref $FAI::device.
-#
-# @param $disk Either an integer, occurring in the context of, e.g., disk2, or
-# a device name. The latter may be fully qualified, such as /dev/hda, or a short
-# name, such as sdb, in which case /dev/ is prepended.
-#
-################################################################################
-sub init_disk_config {
-
-  # Initialise $disk
-  my ($disk) = @_;
-
-  # test $disk for being numeric
-  if ( $disk =~ /^\d+$/ ) {
-
-    # $disk-1 must be a valid index in the map of all disks in the system
-    ( scalar(@FAI::disks) >= $disk )
-      or die "this system does not have a physical disk $disk\n";
-
-    # fetch the (short) device name
-    $disk = $FAI::disks[ $disk - 1 ];
-  }
-
-  # test, whether the device name starts with a / and prepend /dev/, if
-  # appropriate
-  ( $disk =~ m{^/} ) or $disk = "/dev/$disk";
-
-  # test, whether $disk is a block special device
-  ( -b $disk ) or die "$disk is not a valid device name\n";
-
-  # prepend PHY_
-  $FAI::device = "PHY_$disk";
-
-  # test, whether this is the first disk_config stanza to configure $disk
-  defined( $FAI::configs{$FAI::device} )
-    and die "Duplicate configuration for disk $FAI::disks[ $1-1 ]\n";
-
-  # Initialise the entry in $FAI::configs
-  $FAI::configs{$FAI::device} = {
-    "virtual"    => 0,
-    "disklabel"  => "msdos",
-    "bootable"   => -1,
-    "fstabkey"   => "device",
-    "partitions" => {}
-  };
-}
-
-################################################################################
-#
-# @brief Initialise the entry of a partition in @ref $FAI::configs
-#
-# @param $type The type of the partition. It must be either primary or logical.
-#
-################################################################################
-sub init_part_config {
-
-  # the type of the partition to be created
-  my ($type) = @_;
-
-  # type must either be primary or logical, nothing else may be accepted by the
-  # parser
-  ( $type eq "primary" || $type eq "logical" ) or die "INTERNAL PARSER ERROR\n";
-
-  # check that a physical device is being configured; logical partitions are
-  # only supported on msdos disk labels.
-  (
-    $FAI::device =~ /^PHY_/ && ( $type ne "logical"
-      || $FAI::configs{$FAI::device}{"disklabel"} eq "msdos" )
-  ) or die "Syntax error: invalid partition type";
-
-  # the index of the new partition
-  my $part_number = 0;
-
-  # create a primary partition
-  if ( $type eq "primary" ) {
-
-    # find all previously defined primary partitions
-    foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
-
-      # break, if the partition has not been created by init_part_config
-      defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
-          {"extended"} ) or last;
-
-      # on msdos disklabels we cannot have more than 4 primary partitions
-      last if ( $part_id > 4
-        && $FAI::configs{$FAI::device}{"disklabel"} eq "msdos" );
-
-      # store the latest index found
-      $part_number = $part_id;
-    }
-
-    # the next index available - note that $part_number might have been 0
-    $part_number++;
-
-    # msdos disk labels don't allow for more than 4 primary partitions
-    ( $part_number < 5 || $FAI::configs{$FAI::device}{"disklabel"} ne "msdos" )
-      or die "$part_number are too many primary partitions\n";
-  } else {
-
-    # no further checks for the disk label being msdos have to be performed in
-    # this branch, it has been ensured above
-
-    # find the index of the new partition, initialise it to the highest current index
-    foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
-
-      # skip primary partitions
-      next if ( $part_id < 5 );
-
-      # break, if the partition has not been created by init_part_config
-      defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
-          {"extended"} )
-        or last;
-
-      # store the latest index found
-      $part_number = $part_id;
-    }
-
-    # and use the next one available
-    $part_number++;
-
-    # if this is the first logical partition, the index must be set to 5 and an
-    # extended partition  must be created
-    if ( $part_number <= 5 ) {
-      $part_number = 5;
-
-      # the proposed index of the extended partition
-      my $extended = 0;
-
-      # find all previously defined primary partitions
-      foreach my $part_id ( sort keys %{ $FAI::configs{$FAI::device}{"partitions"} } ) {
-
-        # break, if the partition has not been created by init_part_config
-        defined( $FAI::configs{$FAI::device}{"partitions"}{$part_id}{"size"}
-            {"extended"} ) or last;
-
-        # we cannot have more than 4 primary partitions
-        last if ( $part_id > 4 );
-
-        # store the latest index found
-        $extended = $part_id;
-      }
-
-      # the next index available
-      $extended++;
-
-      # msdos disk labels don't allow for more than 4 primary partitions
-      ( $extended < 5 )
-        or die "Too many primary partitions while creating extended\n";
-
-      # mark the entry as an extended partition
-      $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"extended"} = 1;
-
-      # add the preserve = 0 flag, if it doesn't exist already
-      defined( $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"preserve"} )
-        or $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"preserve"} = 0;
-
-      # add the resize = 0 flag, if it doesn't exist already
-      defined(
-        $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"resize"} )
-        or
-        $FAI::configs{$FAI::device}{"partitions"}{$extended}{"size"}{"resize"} =
-        0;
-    }
-  }
-
-  # initialise the hash for the partitions, if it doesn't exist already
-  # note that it might exists due to options, such as preserve:x,y
-  # the initialisation is required for the reference defined next
-  defined( $FAI::configs{$FAI::device}{"partitions"}{$part_number} )
-    or $FAI::configs{$FAI::device}{"partitions"}{$part_number} = {};
-
-  # set the reference to the current partition
-  # the reference is used by all further processing of this config line
-  $FAI::partition_pointer =
-    ( \%FAI::configs )->{$FAI::device}->{"partitions"}->{$part_number};
-
-  # as we can't compute the index from the reference, we need to store the
-  # $part_number explicitly
-  $FAI::partition_pointer->{"number"} = $part_number;
-
-  # the partition is not an extended one
-  $FAI::partition_pointer->{"size"}->{"extended"} = 0;
-
-  # add the preserve = 0 flag, if it doesn't exist already
-  defined( $FAI::partition_pointer->{"size"}->{"preserve"} )
-    or $FAI::partition_pointer->{"size"}->{"preserve"} = 0;
-
-  # add the resize = 0 flag, if it doesn't exist already
-  defined( $FAI::partition_pointer->{"size"}->{"resize"} )
-    or $FAI::partition_pointer->{"size"}->{"resize"} = 0;
-}
-
-################################################################################
-#
-# @brief This function converts different sizes to Mbyte
-#
-# @param $val is the number with its unit
-#
-################################################################################
-sub convert_unit
-{
-  my ($val) = @_;
-  ( $val =~ /^(\d+)([kMGTP%]?)(B)?\s*$/ ) or die "INTERNAL ERROR (convert_unit)\n";
-  $val = $1 * ( 1 / 1024 ) if ( $2 eq "k" );
-  $val = $1 if ( $2 eq "M" );
-  $val = $1 * 1024 if ( $2 eq "G" );
-  $val = $1 * ( 1024 * 1024 ) if ( $2 eq "T" );
-  $val = $1 * ( 1024 * 1024 * 1024 ) if ( $2 eq "P" );
-  # % is returned as is
-  return $val;
-}
-
-# have RecDescent do proper error reporting
-$::RD_HINT = 1;
-
-################################################################################
-#
-# @brief The effective implementation of the parser is instantiated here
-#
-################################################################################
-$FAI::Parser = Parse::RecDescent->new(
-  q{
-    file: line(s?) /\Z/
-        {
-          $return = 1;
-        }
-        | <error>
-
-    line: <skip: qr/[ \t]*/> "\\n"
-        | <skip: qr/[ \t]*/> comment "\\n"
-        | <skip: qr/[ \t]*/> config "\\n"
-
-    comment: /^\s*#.*/
-
-    config: 'disk_config' disk_config_arg
-        | volume
-
-    disk_config_arg: 'raid'
-        {
-          # check, whether raid tools are available
-          ( &FAI::in_path( "mdadm" ) == 1 ) or 
-            die "mdadm not found in PATH\n";
-          $FAI::device = "RAID";
-        }
-        | /^lvm/
-        {
-
-          # check, whether lvm tools are available
-          ( &FAI::in_path( "lvcreate" ) == 1 ) or 
-            die "LVM tools not found in PATH\n";
-          # initialise $FAI::device to inform the following lines about the LVM
-          # being configured
-          $FAI::device = "VG_";
-        }
-        | 'end'
-        {
-          # exit config mode
-          $FAI::device = "";
-        }
-        | /^disk(\d+)/
-        {
-          # check, whether parted is available
-          ( &FAI::in_path( "parted" ) == 1 ) or 
-            die "parted not found in PATH\n";
-          # initialise the entry of the hash corresponding to disk$1
-          &FAI::init_disk_config( $1 );
-        }
-        option(s?)
-        | /^\S+/
-        {
-          # check, whether parted is available
-          ( &FAI::in_path( "parted" ) == 1 ) or 
-            die "parted not found in PATH\n";
-          # initialise the entry of the hash corresponding to $item[1]
-          &FAI::init_disk_config( $item[ 1 ] );
-        }
-        option(s?)
-
-    option: /^preserve_always:(\d+(,\d+)*)/
-        {
-          # set the preserve flag for all ids in all cases
-          $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "preserve" } = 1 foreach ( split( ",", $1 ) );
-        }
-        /^preserve_reinstall:(\d+(,\d+)*)/
-        {
-          # set the preserve flag for all ids if $FAI::reinstall is set
-          if( $FAI::reinstall == 1 ) {
-            $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "preserve" } = 1 foreach ( split( ",", $1 ) );
-          }
-        }
-        | /^resize:(\d+(,\d+)*)/
-        {
-          # set the resize flag for all ids
-          $FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "resize" } = 1 foreach ( split( ",", $1 ) );
-        }
-        | /^disklabel:(msdos|gpt)/
-        {
-          # set the disk label - actually not only the above, but all types 
-          # supported by parted could be allowed, but others are not implemented
-          # yet
-          $FAI::configs{ $FAI::device }{ "disklabel" } = $1;
-        }
-        | /^bootable:(\d+)/
-        {
-          # specify a partition that should get the bootable flag set
-          $FAI::configs{ $FAI::device }{ "bootable" } = $1;
-          ( $FAI::device =~ /^PHY_(.+)$/ ) or die 
-            "INTERNAL ERROR: unexpected device name\n";
-          # set the BOOT_DEVICE and BOOT_PARTITION variables
-          $FAI::disk_var{ "BOOT_DEVICE" } = $1; 
-          $FAI::disk_var{ "BOOT_PARTITION" } = $1 . 
-            $FAI::configs{ $FAI::device }{ "bootable" }; 
-        }
-        | 'virtual'
-        {
-          # this is a configuration for a virtual disk
-          $FAI::configs{ $FAI::device }{ "virtual" } = 1;
-        }
-        | /^fstabkey:(device|label|uuid)/
-        {
-          # the information preferred for fstab device identifieres
-          $FAI::configs{ $FAI::device }{ "fstabkey" } = $1;
-        }
-
-    volume: /^vg\s+/ name devices
-        | /^raid([0156])\s+/
-        {
-          # make sure that this is a RAID configuration
-          ( $FAI::device eq "RAID" ) or die "RAID entry invalid in this context\n";
-          # initialise RAID entry, if it doesn't exist already
-          defined( $FAI::configs{"RAID"} ) or $FAI::configs{"RAID"}{"volumes"} = {};
-          # compute the next available index - the size of the entry
-          my $vol_id = scalar( keys %{ $FAI::configs{"RAID"}{"volumes"} } );
-          # set the RAID type of this volume
-          $FAI::configs{"RAID"}{"volumes"}{$vol_id}{"mode"} = $1;
-          # initialise the hash of devices
-          $FAI::configs{"RAID"}{"volumes"}{$vol_id}{"devices"} = {};
-          # set the reference to the current volume
-          # the reference is used by all further processing of this config line
-          $FAI::partition_pointer = ( \%FAI::configs )->{"RAID"}->{"volumes"}->{$vol_id};
-        }
-        mountpoint devices filesystem mount_options fs_options
-        | type mountpoint size filesystem mount_options fs_options
-
-    type: 'primary'
-        {
-          # initialise a primary partition
-          &FAI::init_part_config( $item[ 1 ] );
-        }
-        | 'logical'
-        {
-          # initialise a logical partition
-          &FAI::init_part_config( $item[ 1 ] );
-        }
-        | m{^([^/\s\-]+)-([^/\s\-]+)\s+}
-        {
-          # set $FAI::device to VG_$1
-          $FAI::device = "VG_$1";
-          # make sure, the volume group $1 has been defined before
-          defined( $FAI::configs{$FAI::device} ) or 
-            die "Volume group $1 has not been declared yet.\n";
-          # make sure, $2 has not been defined already
-          defined( $FAI::configs{$FAI::device}{"volumes"}{$2} ) and 
-            die "Logical volume $2 has been defined already.\n";
-          # initialise the new hash
-          $FAI::configs{$FAI::device}{"volumes"}{$2} = {};
-          # initialise the preserve and resize flags
-          $FAI::configs{$FAI::device}{"volumes"}{$2}{"size"}{"preserve"} = 0;
-          $FAI::configs{$FAI::device}{"volumes"}{$2}{"size"}{"resize"} = 0;
-          # set the reference to the current volume
-          # the reference is used by all further processing of this config line
-          $FAI::partition_pointer = ( \%FAI::configs )->{$FAI::device}->{"volumes"}->{$2};
-        }
-
-    mountpoint: '-'
-        {
-          # this partition should not be mounted
-          $FAI::partition_pointer->{ "mountpoint" } = "-";
-        }
-        | 'swap'
-        {
-          # this partition is swap space, not mounted
-          $FAI::partition_pointer->{ "mountpoint" } = "none";
-        }
-        | m{^/\S*}
-        {
-          # set the mount point
-          $FAI::partition_pointer->{ "mountpoint" } = $item[ 1 ];
-          # if the mount point is / or /boot and we are currently doing a
-          # physical device, the variables should be set, unless they are
-          # already
-          if ( $FAI::configs{$FAI::device}{"bootable"} == -1 && 
-            $FAI::device =~ /^PHY_(.+)$/ && 
-            ( $item[ 1 ] eq "/boot" || ( $item[ 1 ] eq "/" && 
-              !defined( $FAI::disk_var{ "BOOT_DEVICE" } ) ) ) ) {
-              # set the BOOT_DEVICE and BOOT_PARTITION variables
-              $FAI::disk_var{ "BOOT_DEVICE" } = $1; 
-              $FAI::disk_var{ "BOOT_PARTITION" } = $1 . 
-                $FAI::partition_pointer->{"number"};
-          }
-        }
-
-    name: m{^([^/\s\-]+)}
-        {
-          # set the device name to VG_ and the name of the volume group
-          $FAI::device = "VG_$1";
-          # make sure, the volume group $1 not has been defined already
-          defined( $FAI::configs{$FAI::device} ) and
-            die "Volume group $1 has been defined already.\n";
-          # make sure this line is part of an LVM configuration
-          ( $FAI::device =~ /^VG_/ ) or
-            die "vg is invalid in a non LVM-context.\n";
-          # initialise the new hash
-          $FAI::configs{$FAI::device}{"volumes"} = {};
-          # initialise the list of physical devices
-          $FAI::configs{$FAI::device}{"devices"} = ();
-          # the rule must not return undef
-          1;
-        }
-
-    size: /^(\d+[kMGTP%]?(-(\d+[kMGTP%]?)?)?)(:resize)?\s+/
-        {
-          # complete the size specification to be a range in all cases
-          my $range = $1;
-          # the size is fixed
-          if( ! defined( $2 ) )
-          {
-            # make it a range of the form x-x
-            $range = "$range-$1";
-          }
-          elsif( ! defined( $3 ) )
-          {
-            # range has no upper limit, assume the whole disk
-            $range = $range . "100%";
-          } 
-          
-          # convert the units, if necessary
-          my ($min, $max) = split(/-/, $range);
-          $min   = &FAI::convert_unit($min);
-          $max   = &FAI::convert_unit($max);
-          $range = "$min-$max";
-          # enter the range into the hash
-          $FAI::partition_pointer->{ "size" }->{ "range" } = $range;
-          # set the resize flag, if required
-          defined( $4 ) and $FAI::partition_pointer->{ "size" }->{ "resize" } = 1;
-        }
-        | /^(-\d+[kMGTP%]?)(:resize)?\s+/
-        {
-          # complete the range by assuming 0 as the lower limit 
-          my $range = "0$1";
-          # convert the units, if necessary
-          my ($min, $max) = split(/-/, $range);
-          $min   = &FAI::convert_unit($min);
-          $max   = &FAI::convert_unit($max);
-          $range = "$min-$max";
-          # enter the range into the hash
-          $FAI::partition_pointer->{ "size" }->{ "range" } = $range;
-          # set the resize flag, if required
-          defined( $2 ) and $FAI::partition_pointer->{ "size" }->{ "resize" } = 1;
-        }
-        | <error: invalid partition size near "$text">
-
-    devices: /^([^\d,:\s\-][^,:\s]*(:(spare|missing))*(,[^,:\s]+(:(spare|missing))*)*)/
-        {
-          # split the device list by ,
-          foreach my $dev ( split( ",", $1 ) )
-          {
-            # match the substrings
-            ( $dev =~ /^([^\d,:\s\-][^,:\s]*)(:(spare|missing))*$/ ) or die "INTERNAL PARSER ERROR\n";
-            # redefine the device string
-            $dev = $1;
-            # make $dev a full path name; can't validate device name yet as it
-            # might be created later on
-            unless ( $dev =~ m{^/} ) {
-		if ( $dev =~ m/^disk(\d+)\.(\d+)/ ) {
-		    my $short_dev = $FAI::disks[ $1 - 1 ];
-		    $dev = "/dev/$short_dev$2";
-		}
-		else {
-		    $dev = "/dev/$dev";
-		}
-	    }
-            # options are only valid for RAID
-            defined( $2 ) and ( $FAI::device ne "RAID" ) and die "Option $2 invalid in a non-RAID context\n";
-            if( $FAI::device eq "RAID" )
-            {
-              # parse all options
-              my $spare = 0;
-              my $missing = 0;
-              if( defined( $2 ) )
-              {
-                ( $2 =~ /spare/ ) and $spare = 1;
-                ( $2 =~ /missing/ ) and $missing = 1;
-              }
-              # each device may only appear once
-              defined( $FAI::partition_pointer->{"devices"}->{$dev} ) and 
-                die "$dev is already part of the RAID volume\n";
-              # set the options
-              $FAI::partition_pointer->{"devices"}->{$dev}->{"options"} = {
-                "spare" => $spare,
-                "missing" => $missing
-              };
-            }
-            else
-            {
-              # create an empty hash for each device
-              $FAI::configs{$FAI::device}{"devices"}{$dev} = {};
-            }
-          }
-          1;
-        }
-        | <error: invalid device spec "$text">
-
-    mount_options: /\S+/
-        {
-          $FAI::partition_pointer->{ "mount_options" } = $item[ 1 ];
-        }
-
-    filesystem: '-'
-        {
-          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
-        }
-        | 'swap'
-        {
-          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
-        }
-        | /^\S+/
-        {
-          ( &FAI::in_path("mkfs.$item[1]") == 1 ) or 
-            die "unknown/invalid filesystem type $item[1] (mkfs.$item[1] not found in PATH)\n";
-          $FAI::partition_pointer->{ "filesystem" } = $item[ 1 ];
-        }
-
-    fs_options: /[^;\n]*/
-        {
-          $FAI::partition_pointer->{ "fs_options" } = $item[ 1 ];
-        }
-}
-);
-
-################################################################################
-#
-# @brief Parse the data from <$IN> using @ref $FAI::Parser
-#
-# @param IN file handle for input file, may be STDIN
-#
-################################################################################
-sub run_parser {
-  my ($IN) = @_;
-
-  # read <$IN> to a single string (not a list), thus $/ has to be unset
-  my $ifs = $/;
-  undef $/;
-  my $input = <$IN>;
-  $/ = $ifs;
-
-  # print the contents of <$IN> for debugging purposes
-  ( $FAI::debug > 0 ) and print "Input was:\n" . $input;
-
-  # check for old-style configuration files
-  ( $input =~ m{(^|\n)[^\n#]+;} )
-    and die "Old style configuration files are not supported\n";
-
-  # attempt to parse $input - any error will lead to termination
-  defined $FAI::Parser->file($input) or die "Syntax error\n";
-}
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-sizes
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-sizes	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-sizes	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,783 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-sizes
-#
-# @brief Compute the size of the partitions and volumes to be created
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-use POSIX qw(ceil floor);
-
-package FAI;
-
-################################################################################
-#
-# @brief Estimate the size of the device $dev
-#
-# @param $dev Device the size of which should be determined. This may be a
-# a partition, a RAID device or an entire disk.
-#
-# @return the size of the device in megabytes
-#
-################################################################################
-sub estimate_size
-{
-  my ($dev) = @_;
-
-  # try the entire disk first; we then use the data from the current
-  # configuration; this matches in fact for than the allowable strings, but
-  # this should be caught later on
-  if ( $dev =~ /^\/dev\/[sh]d[a-z]$/ )
-  {
-    defined( $FAI::current_config{$dev}{"end_byte"} )
-      or die "$dev is not a valid block device\n";
-
-    # the size is known, return it
-    return ( $FAI::current_config{$dev}{"end_byte"} -
-        $FAI::current_config{$dev}{"begin_byte"} ) / ( 1024 * 1024 );
-  }
-
-  # try a partition
-  elsif ( $dev =~ /^(\/dev\/[sh]d[a-z])(\d+)$/ )
-  {
-
-    # the size is configured, return it
-    defined( $FAI::configs{"PHY_$1"}{"partitions"}{$2}{"size"}{"eff_size"} )
-      and return $FAI::configs{"PHY_$1"}{"partitions"}{$2}{"size"}{"eff_size"} /
-      ( 1024 * 1024 );
-
-    # the size is known from the current configuration on disk, return it
-    defined( $FAI::current_config{$1}{"partitions"}{$2}{"count_byte"} )
-      and return $FAI::current_config{$1}{"partitions"}{$2}{"count_byte"} /
-      ( 1024 * 1024 );
-
-    # the size is not known (yet?)
-    die "Cannot determine size of $dev\n";
-  }
-
-  # try RAID; estimations here are very limited and possible imprecise
-  elsif ( $dev =~ /^\/dev\/md(\d+)$/ )
-  {
-
-    # the list of underlying devices
-    my @devs = ();
-
-    # the raid level, like raid0, raid5, linear, etc.
-    my $level = "";
-
-    # let's see, whether there is a configuration of this volume
-    if ( defined( $FAI::configs{"RAID"}{"volumes"}{$1}{"devices"} ) )
-    {
-      @devs  = keys %{ $FAI::configs{"RAID"}{"volumes"}{$1}{"devices"} };
-      $level = $FAI::configs{"RAID"}{"volumes"}{$1}{"mode"};
-    }
-    elsif ( defined( $FAI::current_raid_config{$1}{"devices"} ) )
-    {
-      @devs  = $FAI::current_raid_config{$1}{"devices"};
-      $level = $FAI::current_raid_config{$1}{"mode"};
-    }
-    else
-    {
-      die "$dev is not a known RAID device\n";
-    }
-
-    # prepend "raid", if the mode is numeric-only
-    $level = "raid" . $level if ( $level =~ /^\d+$/ );
-
-    # the number of devices in the volume
-    my $dev_count = scalar(@devs);
-
-    # now do the mode-specific size estimations
-    if ( $level =~ /^raid[015]$/ )
-    {
-      my $min_size = &estimate_size( shift @devs );
-      foreach (@devs)
-      {
-        my $s = &estimate_size($_);
-        $min_size = $s if ( $s < $min_size );
-      }
-
-      return $min_size * POSIX::floor( $dev_count / 2 )
-        if ( $level eq "raid1" );
-      return $min_size * $dev_count if ( $level eq "raid0" );
-      return $min_size * ( $dev_count - 1 ) if ( $level eq "raid5" );
-    }
-    else
-    {
-
-      # probably some more should be implemented
-      die "Don't know how to estimate the size of a $level device\n";
-    }
-  }
-
-  # otherwise we are clueless
-  else
-  {
-    die "Cannot determine size of $dev\n";
-  }
-}
-
-################################################################################
-#
-# @brief Compute the desired sizes of logical volumes
-#
-################################################################################
-sub compute_lv_sizes
-{
-
-  # loop through all device configurations
-  foreach my $config ( keys %FAI::configs )
-  {
-
-    # for RAID, there is nothing to be done here
-    next if ( $config eq "RAID" );
-
-    # device is an effective disk
-    next if ( $config =~ /^PHY_(.+)$/ );
-
-    # configure a volume group
-    ( $config =~ /^VG_(.+)$/ )
-      or die "INTERNAL ERROR: invalid config entry $config.\n";
-
-    # the volume group name
-    my $vg = $1;
-
-    # compute the size of the volume group; this is not exact, but should at
-    # least give a rough estimation, we assume 1 % of overhead; the value is
-    # stored in megabytes
-    my $vg_size = 0;
-    foreach my $dev ( keys %{ $FAI::configs{$config}{"devices"} } )
-    {
-
-      # $dev may be a partition, an entire disk or a RAID device; otherwise we
-      # cannot deal with it
-      $vg_size += &estimate_size($dev);
-    }
-
-    # now subtract 1% of overhead
-    $vg_size *= 0.99;
-
-    # the volumes that require redistribution of free space
-    my @redist_list = ();
-
-    # the minimum space required in this volume group
-    my $min_space = 0;
-
-    # the maximum space used in this volume group
-    my $max_space = 0;
-
-    # set effective sizes where available
-    foreach my $lv ( keys %{ $FAI::configs{$config}{"volumes"} } )
-    {
-
-      # make sure the size specification is a range (even though it might be
-      # something like x-x) and store the dimensions
-      ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} =~
-          /^(\d+%?)-(\d+%?)$/ )
-        or die "INTERNAL ERROR: Invalid range\n";
-      my $start = $1;
-      my $end   = $2;
-
-      # start may be given in percents of the size, rewrite it to megabytes
-      $start = POSIX::floor( $vg_size * $1 / 100 ) if ( $start =~ /^(\d+)%$/ );
-
-      # end may be given in percents of the size, rewrite it to megabytes
-      $end = POSIX::ceil( $vg_size * $1 / 100 ) if ( $end =~ /^(\d+)%$/ );
-
-      # make sure that $end >= $start
-      ( $end >= $start ) or die "INTERNAL ERROR: end < start\n";
-
-      # increase the used space
-      $min_space += $start;
-      $max_space += $end;
-
-      # write back the range in MB
-      $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} = "$start-$end";
-
-      # the size is fixed
-      if ( $start == $end )
-      {
-
-        # write the size back to the configuration
-        $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} = $start;
-      }
-      else
-      {
-
-        # add this volume to the redistribution list
-        push @redist_list, $lv;
-      }
-    }
-
-    # test, whether the configuration fits on the volume group at all
-    ( $min_space < $vg_size )
-      or die "Volume group $vg requires $min_space MB\n";
-
-    # the extension factor
-    my $redist_factor = 0;
-    $redist_factor = ( $vg_size - $min_space ) / ( $max_space - $min_space )
-      if ( $max_space > $min_space );
-
-    # update all sizes that are still ranges
-    foreach my $lv (@redist_list)
-    {
-
-      # get the range again
-      ( $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"range"} =~
-          /^(\d+%?)-(\d+%?)$/ )
-        or die "INTERNAL ERROR: Invalid range\n";
-      my $start = $1;
-      my $end   = $2;
-
-      # write the final size
-      $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} =
-        $start + ( ( $end - $start ) * $redist_factor );
-    }
-  }
-}
-
-################################################################################
-#
-# @brief Compute the desired sizes of the partitions and test feasibility
-# thereof.
-#
-################################################################################
-sub compute_partition_sizes
-{
-
-  # loop through all device configurations
-  foreach my $config ( keys %FAI::configs )
-  {
-
-    # for RAID, there is nothing to be done here
-    next if ( $config eq "RAID" );
-
-    # don't configure the sizes of logical volumes here
-    next if ( $config =~ /^VG_(.+)$/ );
-
-    # device is an effective disk
-    ( $config =~ /^PHY_(.+)$/ )
-      or die "INTERNAL ERROR: invalid config entry $config.\n";
-
-    # nothing to be done, if this is a configuration for a virtual disk
-    next if ( $FAI::configs{$config}{"virtual"} == 1 );
-
-    # the device name of the disk
-    my $disk = $1;
-
-# at various points the following code highly depends on the desired disk label!
-# initialise variables
-# the id of the extended partition to be created, if required
-    my $extended = -1;
-
-    # the id of the current extended partition, if any; this setup only caters
-    # for a single existing extended partition!
-    my $current_extended = -1;
-
-    # find the first existing extended partition
-    foreach
-      my $part_id ( sort keys %{ $FAI::current_config{$disk}{"partitions"} } )
-    {
-      if ( 1 ==
-        $FAI::current_config{$disk}{"partitions"}{$part_id}{"is_extended"} )
-      {
-        $current_extended = $part_id;
-        last;
-      }
-    }
-
-    # the space required on the disk
-    my $min_req_total_space = 0;
-
-    # the start byte for the next partition
-    my $next_start = 0;
-
-    # on msdos disk labels, the first partitions starts at head #1
-    if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-    {
-      $next_start = $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-        $FAI::current_config{$disk}{"sector_size"};
-
-      # the MBR requires space, too
-      $min_req_total_space +=
-        $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-        $FAI::current_config{$disk}{"sector_size"};
-    }
-
-    # on GPT disk labels the first 34 and last 34 sectors must be left alone
-    if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
-    {
-      $next_start = 34 * $FAI::current_config{$disk}{"sector_size"};
-
-      # modify the disk to claim the space for the second partition table
-      $FAI::current_config{$disk}{"end_byte"} -=
-        34 * $FAI::current_config{$disk}{"sector_size"};
-
-      # the space required by the GPTs
-      $min_req_total_space +=
-        2 * 34 * $FAI::current_config{$disk}{"sector_size"};
-    }
-
-    # the list of partitions that we need to find start and end bytes for
-    my @worklist = ( sort keys %{ $FAI::configs{$config}{"partitions"} } );
-
-    while ( scalar(@worklist) > 0 )
-    {
-
-      # work on the first entry of the list
-      my $part_id = $worklist[0];
-
-      # the partition $part_id must be preserved
-      if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} ==
-        1 )
-      {
-
-        # a partition that should be preserved must exist already
-        defined( $FAI::current_config{$disk}{"partitions"}{$part_id} )
-          or die "$part_id can't be preserved, it does not exist.\n";
-
-        ( $next_start >
-            $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"} )
-          and die
-"Previous partitions overflow begin of preserved partition $part_id\n";
-
-        # set the effective size to the value known already
-        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
-          $FAI::current_config{$disk}{"partitions"}{$part_id}{"count_byte"};
-
-        # copy the start_byte and end_byte information
-        $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
-          $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"};
-        $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} =
-          $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"};
-
-        # and add it to the total disk space required by this config
-        $min_req_total_space +=
-          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"};
-
-        # set the next start
-        $next_start =
-          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} + 1;
-
-        # several msdos specific parts
-        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-        {
-
-          # make sure the partition ends at a cylinder boundary
-          (
-            0 == (
-              $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} +
-                1
-              ) % (
-              $FAI::current_config{$disk}{"sector_size"} *
-                $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-                $FAI::current_config{$disk}{"bios_heads"}
-              )
-            )
-            or die
-"Preserved partition $part_id does not end at a cylinder boundary\n";
-
-          # add one head of disk usage if this is a logical partition
-          $min_req_total_space +=
-            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-            $FAI::current_config{$disk}{"sector_size"}
-            if ( $part_id > 4 );
-
-          # extended partitions consume no space
-          if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
-            {"extended"} == 1 )
-          {
-
-            # revert the addition of the size
-            $min_req_total_space -=
-              $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
-              {"eff_size"};
-
-            # set the next start to the start of the extended partition
-            $next_start =
-              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
-          }
-
-        }
-
-        # on gpt, ensure that the partition ends at a sector boundary
-        if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
-        {
-          (
-            0 == (
-              $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} +
-                1
-              ) % $FAI::current_config{$disk}{"sector_size"}
-            )
-            or die
-            "Preserved partition $part_id does not end at a sector boundary\n";
-        }
-
-        # partition done
-        shift @worklist;
-      }
-
-      # msdos specific: deal with extended partitions
-      elsif (
-        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} ==
-        1 )
-      {
-        ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-          or die "found an extended partition on a non-msdos disklabel\n";
-
-        # make sure that there is only one extended partition
-        ( $extended == -1 || 1 == scalar(@worklist) )
-          or die "INTERNAL ERROR: More than 1 extended partition\n";
-
-        # ensure that it is a primary partition
-        ( $part_id <= 4 )
-          or die
-          "INTERNAL ERROR: Extended partition wouldn't be a primary one\n";
-
-        # set the local variable to this id
-        $extended = $part_id;
-
-        # the size cannot be determined now, push it to the end of the
-        # worklist; the check against $extended being == -1 ensures that
-        # there is no indefinite loop
-        if ( scalar(@worklist) > 1 )
-        {
-          push @worklist, shift @worklist;
-        }
-
-        # determine the size of the extended partition
-        else
-        {
-          my $epbr_size =
-            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-            $FAI::current_config{$disk}{"sector_size"};
-
-          # initialise the size and the start byte
-          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
-            0;
-          $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} = -1;
-
-          foreach my $p ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
-          {
-            next if ( $p < 5 );
-
-            if ( -1 ==
-              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} )
-            {
-              $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
-                $FAI::configs{$config}{"partitions"}{$p}{"start_byte"} -
-                $epbr_size;
-            }
-
-            $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
-              {"eff_size"} +=
-              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"eff_size"} +
-              $epbr_size;
-
-            $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} =
-              $FAI::configs{$config}{"partitions"}{$p}{"end_byte"};
-          }
-
-          ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} >
-              0 )
-            or die "Extended partition has a size of 0\n";
-
-          # partition done
-          shift @worklist;
-        }
-      }
-      else
-      {
-
-        # make sure the size specification is a range (even though it might be
-        # something like x-x) and store the dimensions
-        ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =~
-            /^(\d+%?)-(\d+%?)$/ )
-          or die "INTERNAL ERROR: Invalid range\n";
-        my $start = $1;
-        my $end   = $2;
-
-        # start may be given in percents of the size
-        if ( $start =~ /^(\d+)%$/ )
-        {
-
-          # rewrite it to bytes
-          $start =
-            POSIX::floor( $FAI::current_config{$disk}{"size"} * $1 / 100 );
-        }
-        else
-        {
-
-          # it is given in megabytes, make it bytes
-          $start = $start * 1024.0 * 1024.0;
-        }
-
-        # end may be given in percents of the size
-        if ( $end =~ /^(\d+)%$/ )
-        {
-
-          # rewrite it to bytes
-          $end = POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
-        }
-        else
-        {
-
-          # it is given in megabytes, make it bytes
-          $end = $end * 1024.0 * 1024.0;
-        }
-
-        # make sure that $end >= $start
-        ( $end >= $start ) or die "INTERNAL ERROR: end < start\n";
-
-        # check, whether the size is fixed
-        if ( $end != $start )
-        {
-
-          # the end of the current range (may be the end of the disk or some
-          # preserved partition
-          my $end_of_range = -1;
-
-         # minimum space required by all partitions, i.e., the lower ends of the
-         # ranges
-         # $min_req_space counts up to the next preserved partition or the
-         # end of the disk
-          my $min_req_space = 0;
-
-          # maximum useful space
-          my $max_space = 0;
-
-          # inspect all remaining entries in the worklist
-          foreach my $p (@worklist)
-          {
-
-            # we have found the delimiter
-            if ( $FAI::configs{$config}{"partitions"}{$p}{"size"}{"preserve"} ==
-              1 )
-            {
-              $end_of_range =
-                $FAI::current_config{$disk}{"partitions"}{$p}{"begin_byte"};
-
-              # logical partitions require the space for the EPBR to be left
-              # out
-              if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-                && ( $p > 4 ) )
-              {
-                $end_of_range -=
-                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-                  $FAI::current_config{$disk}{"sector_size"};
-              }
-              last;
-            }
-            elsif (
-              $FAI::configs{$config}{"partitions"}{$p}{"size"}{"extended"} ==
-              1 )
-            {
-              next;
-            }
-            else
-            {
-
-          # below is a slight duplication of the code
-          # make sure the size specification is a range (even though it might be
-          # something like x-x) and store the dimensions
-              ( $FAI::configs{$config}{"partitions"}{$p}{"size"}{"range"} =~
-                  /^(\d+%?)-(\d+%?)$/ )
-                or die "INTERNAL ERROR: Invalid range\n";
-              my $min_size = $1;
-              my $max_size = $2;
-
-              # start may be given in percents of the size
-              if ( $min_size =~ /^(\d+)%$/ )
-              {
-
-                # rewrite it to bytes
-                $min_size =
-                  POSIX::floor(
-                  $FAI::current_config{$disk}{"size"} * $1 / 100 );
-              }
-              else
-              {
-
-                # it is given in megabytes, make it bytes
-                $min_size *= 1024.0 * 1024.0;
-              }
-
-              # end may be given in percents of the size
-              if ( $max_size =~ /^(\d+)%$/ )
-              {
-
-                # rewrite it to bytes
-                $max_size =
-                  POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
-              }
-              else
-              {
-
-                # it is given in megabytes, make it bytes
-                $max_size *= 1024.0 * 1024.0;
-              }
-
-              # logical partitions require the space for the EPBR to be left
-              # out
-              if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-                && ( $p > 4 ) )
-              {
-                $min_size +=
-                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-                  $FAI::current_config{$disk}{"sector_size"};
-                $max_size +=
-                  $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-                  $FAI::current_config{$disk}{"sector_size"};
-              }
-
-              $min_req_space += $min_size;
-              $max_space     += $max_size;
-            }
-          }
-
-          # set the end if we have reached the end of the disk
-          $end_of_range = $FAI::current_config{$disk}{"end_byte"}
-            if ( -1 == $end_of_range );
-
-          my $available_space = $end_of_range - $next_start + 1;
-
-          # the next boundary is closer than the minimal space that we need
-          ( $available_space < $min_req_space )
-            and die "Insufficient space available for partition $part_id\n";
-
-          # the new size
-          my $scaled_size = $end;
-          $scaled_size = POSIX::floor(
-            ( $end - $start ) * (
-              ( $available_space - $min_req_space ) /
-                ( $max_space - $min_req_space )
-            )
-            ) + $start
-            if ( $max_space > $available_space );
-
-          ( $scaled_size >= $start )
-            or die
-            "INTERNAL ERROR: scaled size is smaller than the desired minimum\n";
-
-          $start = $scaled_size;
-          $end   = $start;
-        }
-
-        # now we compute the effective locations on the disk
-        # msdos specific offset for logical partitions
-        if ( ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-          && ( $part_id > 4 ) )
-        {
-
-          # add one head of disk usage if this is a logical partition
-          $min_req_total_space +=
-            $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-            $FAI::current_config{$disk}{"sector_size"};
-
-          # move the start byte as well
-          $next_start += $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-            $FAI::current_config{$disk}{"sector_size"};
-        }
-
-        # partition starts at where we currently are
-        $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
-          $next_start;
-
-        # the end may need some alignment, depending on the disk label
-        my $end_byte = $next_start + $start - 1;
-
-        # on msdos, ensure that the partition ends at a cylinder boundary
-        if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
-        {
-          $end_byte -=
-            ( $end_byte + 1 ) % ( $FAI::current_config{$disk}{"sector_size"} *
-              $FAI::current_config{$disk}{"bios_sectors_per_track"} *
-              $FAI::current_config{$disk}{"bios_heads"} );
-        }
-
-        # on gpt, ensure that the partition ends at a sector boundary
-        if ( $FAI::configs{$config}{"disklabel"} eq "gpt" )
-        {
-          $end_byte -=
-            ( $end_byte + 1 ) % $FAI::current_config{$disk}{"sector_size"};
-        }
-
-        # set $start and $end to the effective values
-        $start = $end_byte - $next_start + 1;
-        $end   = $start;
-
-        # write back the size spec in bytes
-        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =
-          $start . "-" . $end;
-
-        # then set eff_size to a proper value
-        $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
-          $start;
-
-        # write the end byte to the configuration
-        $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} = $end_byte;
-
-        # and add it to the total disk space required by this config
-        $min_req_total_space +=
-          $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"};
-
-        # set the next start
-        $next_start =
-          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} + 1;
-
-        # partition done
-        shift @worklist;
-      }
-    }
-
-    # check, whether there is sufficient space on the disk
-    ( $min_req_total_space > $FAI::current_config{$disk}{"size"} )
-      and die
-"Disk $disk is too small - at least $min_req_total_space bytes are required\n";
-
-    # make sure, extended partitions are only created on msdos disklabels
-    ( $FAI::configs{$config}{"disklabel"} ne "msdos" && $extended > -1 )
-      and die
-"INTERNAL ERROR: extended partitions are not supported by this disklabel\n";
-
-    # ensure that we have done our work
-    foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
-    {
-      ( defined( $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} )
-          && defined(
-          $FAI::configs{$config}{"partitions"}{$part_id}{"end_byte"} ) )
-        or die "INTERNAL ERROR: start or end of partition $part_id not set\n";
-    }
-
-  }
-}
-
-1;
-

Deleted: people/michael/features/setup_harddisks_2/implementation/shdd2-volumes
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-volumes	2007-11-11 11:15:43 UTC (rev 4712)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-volumes	2007-11-11 11:21:59 UTC (rev 4713)
@@ -1,417 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file shdd2-volumes
-#
-# @brief Parse the current partition table and LVM/RAID configurations
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-################################################################################
-#
-# @brief Collect the current partition information from all disks listed both
-# in $FAI::disks and $FAI::configs{PHY_<disk>}
-#
-################################################################################
-sub get_current_disks {
-
-  # backup value of $FAI::no_dry_run
-  my $no_dry_run = $FAI::no_dry_run;
-
-  # obtain the current state of all disks
-  foreach my $disk (@FAI::disks) {
-
-    # create full paths
-    ( $disk =~ m{^/} ) or $disk = "/dev/$disk";
-
-    # make sure, $disk is a proper block device
-    ( -b $disk ) or die "$disk is not a block special device!\n";
-
-    # initialise the hash
-    $FAI::current_config{$disk}{"partitions"} = {};
-
-    # the list to hold the output of parted commands as parsed below
-    my @parted_print = ();
-
-    # set no_dry_run to perform read-only commands always
-    $FAI::no_dry_run = 1;
-
-    # try to obtain the partition table for $disk
-    # it might fail with parted_2 in case the disk has no partition table
-    my $error =
-      &FAI::execute_command_std( "parted -s $disk unit TiB print", \@parted_print, 0 );
-
-    # reset no_dry_run
-    $FAI::no_dry_run = $no_dry_run;
-
-    # parted_2 happens when the disk has no disk label, because parted then
-    # provides no information about the disk
-    if ( $error eq "parted_2" ) {
-
-      # if there is no disk configuration, write an msdos disklabel
-      if ( !defined( $FAI::configs{"PHY_$disk"}{"disklabel"} ) ) {
-
-        # write the disk label as configured
-        $error = &FAI::execute_command( "parted -s $disk mklabel msdos" );
-      } else {
-
-        # write the disk label as configured
-        $error =
-          "parted -s $disk mklabel " . $FAI::configs{"PHY_$disk"}{"disklabel"} );
-      }
-
-      # set no_dry_run to perform read-only commands always
-      $FAI::no_dry_run = 1;
-
-      # retry partition-table print
-      $error =
-        &FAI::execute_command( "parted -s $disk unit TiB print", \@parted_print, 0 );
-
-      # reset no_dry_run
-      $FAI::no_dry_run = $no_dry_run;
-    }
-
-    # check, whether there is still an error
-    if ( $error ne "" ) {
-      my $response = &FAI::get_error( $error, "response" );
-      ( $response eq "die" ) and die &FAI::get_error( $error, "message" );
-      ( $response eq "warn" ) and warn &FAI::get_error( $error, "message" );
-    }
-
-# the following code parses the output of parted print, using various units
-# (TiB, B, chs)
-# the parser is capable of reading the output of parted version 1.7.1, which
-# looks like
-#
-# $ /sbin/parted -s /dev/hda unit B print
-# WARNING: You are not superuser.  Watch out for permissions.
-#
-# Disk /dev/hda: 80026361855B
-# Sector size (logical/physical): 512B/512B
-# Partition Table: mac
-#
-# Number  Start         End           Size          File system  Name     Flags
-#  1      512B          32767B        32256B                     primary
-#  5      32768B        1033215B      1000448B      hfs          primary  boot
-#  3      134250496B    32212287487B  32078036992B  hfs+         primary
-#  6      32212287488B  46212287487B  14000000000B  ext3         primary
-#  2      46212287488B  47212287999B  1000000512B   linux-swap   primary  swap
-#  4      47212288000B  80026361855B  32814073856B  ext3         primary
-#
-# Note that the output contains an additional column on msdos, indicating,
-# whether the type of a partition is primary, logical or extended.
-#
-# $ parted -s /dev/hda unit B print
-#
-# Disk /dev/hda: 82348277759B
-# Sector size (logical/physical): 512B/512B
-# Partition Table: msdos
-#
-# Number  Start         End           Size          Type      File system  Flags
-#  1      32256B        24675839B     24643584B     primary   ext3
-#  2      24675840B     1077511679B   1052835840B   primary   linux-swap
-#  3      1077511680B   13662190079B  12584678400B  primary   ext3         boot
-#  4      13662190080B  82343278079B  68681088000B  extended
-#  5      13662222336B  14715025919B  1052803584B   logical   ext3
-#         14715058176B  30449986559B  15734928384B
-#  7      30450018816B  32547432959B  2097414144B   logical   ext3
-#  8      32547465216B  82343278079B  49795812864B  logical   ext3
-#
-
-    # As shown above, some entries may be blank. Thus the exact column starts
-    # and lengths must be parsed from the header line. This is stored in the
-    # following hash
-    my %cols = ();
-
-    # Parse the output line by line
-    foreach my $line (@parted_print) {
-
-      # now we test line by line - some of them may be ignored
-      next if ( $line =~ /^Disk /
-        || $line =~ /^\s*$/
-        || $line =~ /^WARNING: You are not superuser/ );
-
-      # determine the logical sector size
-      if ( $line =~ /^Sector size \(logical\/physical\): (\d+)B\/(\d+)B$/ ) {
-        $FAI::current_config{$disk}{"sector_size"} = $1;
-      }
-
-      # read and store the current disk label
-      elsif ( $line =~ /^Partition Table: (.+)$/ ) {
-        $FAI::current_config{$disk}{"disklabel"} = $1;
-      }
-
-      # the line containing the table headers
-      elsif ( $line =~ /^(Number\s+)(\S+\s+)+/ ) {
-        my $col_start = 0;
-
-        # check the length of each heading; note that they might contain spaces
-        while ( $line =~ /^(\S+( [a-z]\S+)?\s*)([A-Z].*)?$/ ) {
-          my $heading = $1;
-
-          # set the line to the remainder
-          $line = "";
-          $line = $3 if defined($3);
-
-          # the width of the column includes any whitespace
-          my $col_width = length($heading);
-          $heading =~ s/(\S+)\s*$/$1/;
-
-          # build the hash entry
-          # this start counter starts at 0, which is useful below
-          $cols{$heading} = {
-            "start"  => $col_start,
-            "length" => $col_width
-          };
-          $col_start += $col_width;
-        }
-      }
-
-      # one of the partitions
-      else {
-
-        # we must have seen the header, otherwise probably the format has
-        # changed
-        defined( $cols{"File system"}{"start"} )
-          or die
-          or die "INTERNAL ERROR: Table header not seen yet\n";
-
-        # the info for the partition number
-        my $num_cols_before = $cols{"Number"}{"start"};
-        my $num_col_width   = $cols{"Number"}{"length"};
-
-        # the info for the file system column
-        my $fs_cols_before = $cols{"File system"}{"start"};
-        my $fs_col_width   = $cols{"File system"}{"length"};
-
-        # get the partition number, if any
-        $line =~ /^.{$num_cols_before}(.{$num_col_width})/;
-        my $id = $1;
-        $id =~ s/\s*//g;
-
-        # if there is no partition number, then it must be free space, so no
-        # file system either
-        next if ( $id eq "" );
-
-        # extract the set of characters
-        $line =~ /^.{$fs_cols_before}(.{$fs_col_width})/;
-        my $fs = $1;
-
-        # remove any trailing space
-        $fs =~ s/\s*$//g;
-
-        # store the information in the hash
-        $FAI::current_config{$disk}{"partitions"}{$id}{"filesystem"} = $fs;
-      }
-    }
-
-    # set no_dry_run to perform read-only commands always
-    $FAI::no_dry_run = 1;
-
-    # reset the output list
-    @parted_print = ();
-
-    # obtain the partition table using bytes as units
-    $error =
-      &FAI::execute_command_std( "parted -s $disk unit B print free", \@parted_print, 0 );
-
-    # reset no_dry_run
-    $FAI::no_dry_run = $no_dry_run;
-
-    # check, whether an error has occured - already handled by
-    # execute_command_std
-    # if ( $error ne "" )
-    # {
-    # my $response = &FAI::get_error( $error, "response" );
-    # ( $response eq "die" ) and die &FAI::get_error( $error, "message" );
-    # ( $response eq "warn" ) and warn &FAI::get_error( $error, "message" );
-    # }
-
-    # Parse the output of the byte-wise partition table
-    foreach my $line (@parted_print) {
-
-      # the disk size line (Disk /dev/hda: 82348277759B)
-      if ( $line =~ /Disk \Q$disk\E: (\d+)B$/ ) {
-        $FAI::current_config{$disk}{"begin_byte"} = 0;
-        $FAI::current_config{$disk}{"end_byte"}   = ( $1 - 1 );
-        $FAI::current_config{$disk}{"size"}       = $1;
-
-        # nothing else to be done
-        next;
-      }
-
-      # One of the partition lines, see above example
-      next
-        unless ( $line =~
-        /^\s*(\d+)\s+(\d+)B\s+(\d+)B\s+(\d+)B(\s+(primary|logical|extended))?/i
-        );
-
-      # mark the bounds of existing partitions
-      $FAI::current_config{$disk}{"partitions"}{$1}{"begin_byte"} = $2;
-      $FAI::current_config{$disk}{"partitions"}{$1}{"end_byte"}   = $3;
-      $FAI::current_config{$disk}{"partitions"}{$1}{"count_byte"} = $4;
-
-      # is_extended defaults to false/0
-      $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 0;
-
-      # but may be true/1 on msdos disk labels
-      ( ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" )
-          && ( $6 eq "extended" ) )
-        and $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 1;
-    }
-
-    # set no_dry_run to perform read-only commands always
-    $FAI::no_dry_run = 1;
-
-    # reset the output list
-    @parted_print = ();
-
-    # obtain the partition table using bytes as units
-    $error =
-      &FAI::execute_command_std(
-      "parted $disk unit chs print free", \@parted_print, 0 );
-
-    # reset no_dry_run
-    $FAI::no_dry_run = $no_dry_run;
-
-    # Parse the output of the CHS partition table
-    foreach my $line (@parted_print) {
-
-   # find the BIOS geometry that looks like this:
-   # BIOS cylinder,head,sector geometry: 10011,255,63.  Each cylinder is 8225kB.
-      if ( $line =~
-        /^BIOS cylinder,head,sector geometry:\s*(\d+),(\d+),(\d+)\.\s*Each cylinder is \d+kB\.$/
-        ) {
-        $FAI::current_config{$disk}{"bios_cylinders"}         = $1;
-        $FAI::current_config{$disk}{"bios_heads"}             = $2;
-        $FAI::current_config{$disk}{"bios_sectors_per_track"} = $3;
-      }
-    }
-
-    # make sure we have determined all the necessary information
-    ( $FAI::current_config{$disk}{"begin_byte"} == 0 )
-      or die "Invalid start byte\n";
-    ( $FAI::current_config{$disk}{"end_byte"} > 0 ) or die "Invalid end byte\n";
-    defined( $FAI::current_config{$disk}{"size"} )
-      or die "Failed to determine disk size\n";
-    defined( $FAI::current_config{$disk}{"sector_size"} )
-      or die "Failed to determine sector size\n";
-    defined( $FAI::current_config{$disk}{"bios_sectors_per_track"} )
-      or die "Failed to determine the number of sectors per track\n";
-
-  }
-}
-
-use Linux::LVM;
-
-################################################################################
-#
-# @brief Collect the current LVM configuration
-#
-################################################################################
-sub get_current_lvm {
-
-  # get the existing volume groups
-  foreach my $vg (get_volume_group_list()) {
-    # initialise the hash entry
-    $FAI::current_lvm_config{$vg}{"physical_volumes"} = ();
-    
-    # store the vg size in MB
-    my %vg_info = get_volume_group_information($vg);
-    $FAI::current_lvm_config{$vg}{"size"} = 
-      &FAI::convert_unit( $vg_info{"alloc_pe_size"} .
-        $vg_info{"alloc_pe_size_unit"} );
-    
-      # store the logical volumes and their sizes
-    my %lv_info = get_logical_volume_information($vg);
-    foreach my $lv_name (sort keys %lv_info) {
-      my $short_name = $lv_name;
-      $short_name =~ "s{/dev/\Q$vg\E/}{}";
-      $FAI::current_lvm_config{$vg}{"volumes"}{$short_name}{"size"} =
-        &FAI::convert_unit( $lv_info{$lv_name}->{"lv_size"} .
-          $lv_info{$lv_name}->{"lv_size_unit"} );
-    }
-    
-    # store the physical volumes
-    $FAI::current_lvm_config{$vg}{"physical_volumes"} = 
-      sort keys get_physical_volume_information($vg);
-  }
-
-}
-
-################################################################################
-#
-# @brief Collect the current RAID device information from all partitions
-# currently active in the system
-#
-################################################################################
-sub get_current_raid {
-
-  # backup value of $FAI::no_dry_run
-  my $no_dry_run = $FAI::no_dry_run;
-
-  # the list to hold the output of mdadm commands as parsed below
-  my @mdadm_print = ();
-
-  # set no_dry_run to perform read-only commands always
-  $FAI::no_dry_run = 1;
-
-  # try to obtain the list of existing RAID arrays
-  my $error =
-    &FAI::execute_command_std( "mdadm --detail --scan --verbose -c partitions",
-    \@mdadm_print, 0 );
-
-# the expected output is as follows
-# $ mdadm --detail --scan --verbose -c partitions
-# ARRAY /dev/md0 level=linear num-devices=2 UUID=7e11efd6:93e977fd:b110d941:ce79a4f6
-#    devices=/dev/hda1,/dev/hda2
-# ARRAY /dev/md1 level=raid0 num-devices=2 UUID=50d7a6ec:4207f0db:b110d941:ce79a4f6
-#    devices=/dev/md0,/dev/hda3
-
-  # the id of the RAID
-  my $id;
-
-  # parse the output line by line
-  foreach my $line (@mdadm_print) {
-    if ( $line =~ /^ARRAY \/dev\/md(\d+) level=(\S+) num-devices=\d+ UUID=/ ) {
-      $id = $1;
-      $FAI::current_raid_config{$id}{"mode"} = $2;
-    } elsif ( $line =~ /^\s*devices=(\S+)$/ ) {
-      @{ $FAI::current_raid_config{$id}{"devices"} } = split( ",", $1 );
-    }
-  }
-
-  # reset no_dry_run
-  $FAI::no_dry_run = $no_dry_run;
-}
-
-1;
-

Copied: people/michael/features/setup_harddisks_2/implementation/storage-magic (from rev 4711, people/michael/features/setup_harddisks_2/implementation/shdd2)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/storage-magic	                        (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/storage-magic	2007-11-11 11:21:59 UTC (rev 4713)
@@ -0,0 +1,191 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+# treat all warnings about uninitialised values as errors
+use warnings FATAL => qw(uninitialized);
+
+################################################################################
+#
+# @file shdd2
+#
+# @brief The main function of setup harddisks 2 - the tool to configure the
+# partitioning from within FAI.
+#
+# This is an implementation from scratch to properly support LVM and RAID. The
+# input format is documented in @ref shdd2-parser
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+# command line parameter handling
+use Getopt::Std;
+
+# the variables for getopt
+our ( $opt_X, $opt_f );
+
+# parse the command line
+&getopts('Xf:') || die <<EOF;
+USAGE: [-X]                     no test, your harddisks will be formated
+                                default: only test, no real formating
+       [-f<config-filename>]    default: parse classes
+EOF
+
+# $disklist must be provided by the environment
+defined( $ENV{disklist} ) or die "Environment variable disklist is not set";
+
+################################################################################
+#
+# @brief Really write any changes to disk
+#
+################################################################################
+$FAI::no_dry_run = 0;
+($opt_X) and $FAI::no_dry_run = 1;
+($opt_X) or warn "shdd2 is running in test-only mode!\n";
+
+# include all subparts
+unshift @INC, "/usr/share/fai/storage-magic/";
+require "init.pm";
+require "volumes.pm";
+require "parser.pm";
+require "sizes.pm";
+require "commands.pm";
+require "fstab.pm";
+require "exec.pm";
+
+# the config source file
+my $config_file = undef;
+
+# use the config file, if given
+if ($opt_f) {
+  open( $config_file, $opt_f ) or die "Failed to open config file $opt_f\n";
+}
+
+# see which class file to use
+else {
+  foreach my $classfile ( reverse split( /\s+/, $ENV{"classes"} ) ) {
+    next unless ( -r "$ENV{'FAI'}/disk_config/$classfile" );
+    open( $config_file, "$ENV{'FAI'}/disk_config/$classfile" );
+    last;
+  }
+}
+
+# if we could not find any matching class file, bail out
+defined($config_file) or die "No matching disk_config found\n";
+
+# start the parsing - thereby $FAI::configs is filled
+&FAI::run_parser($config_file);
+
+# read the sizes and partition tables of all disks listed in $FAI::disks
+&FAI::get_current_disks;
+
+# see whether there are any existing LVMs
+# load the dm-mod module first, otherwise the LVM tools won't work
+`modprobe dm-mod`;
+&FAI::get_current_lvm;
+
+# see whether there are any existing RAID devices
+# load the md-mod module first, otherwise there is nothing that can be detected
+`modprobe md-mod`;
+&FAI::get_current_raid;
+
+# for debugging purposes to print the hash structures
+use Data::Dumper;
+
+# debugging only: print the current contents of $FAI::current_config
+if ($FAI::debug) {
+  print "Current disk layout\n";
+
+  # make sure perl doesn't warn about it being used only once
+  our %current_config;
+  print Dumper \%current_config;
+
+  print "Current LVM layout\n";
+
+  # make sure perl doesn't warn about it being used only once
+  our %current_lvm_config;
+  print Dumper \%current_lvm_config;
+
+  print "Current RAID layout\n";
+
+  # make sure perl doesn't warn about it being used only once
+  our %current_raid_config;
+  print Dumper \%current_raid_config;
+}
+
+# compute the new LVM and partition sizes; do the partition sizes first to have
+# them available for the the volume group size estimation
+&FAI::compute_partition_sizes;
+&FAI::compute_lv_sizes;
+
+# debugging only: print the current contents of $FAI::configs
+if ($FAI::debug) {
+  print "Desired disk layout\n";
+  print Dumper \%FAI::configs;
+}
+
+# generate the command script
+&FAI::build_disk_commands;
+&FAI::build_raid_commands;
+&FAI::build_lvm_commands;
+
+# run all commands
+# debugging only: print the command script
+($FAI::debug) and print "$_\n" foreach (@FAI::commands);
+
+# run the command (if $FAI::no_dry_run is set)
+&FAI::execute_command_std($_) foreach (@FAI::commands);
+
+# generate the proposed fstab contents
+my @fstab = &FAI::generate_fstab( \%FAI::configs );
+
+# debugging only; print fstab
+($FAI::debug) and print "$_\n" foreach (@fstab);
+
+# write the proposed contents of fstab to $LOGDIR/fstab, if $FAI::no_dry_run is set
+if ($FAI::no_dry_run) {
+  # write fstab to $LOGDIR/fstab
+  open( FSTAB, ">$ENV{LOGDIR}/fstab" )
+    or die "Failed to open $ENV{LOGDIR}/fstab for writing\n";
+  print FSTAB "$_\n" foreach (@fstab);
+  close FSTAB;
+}
+
+# write variables to $LOGDIR/disk_var.sh
+# debugging
+($FAI::debug) and print "$_=$FAI::disk_var{$_}\n"
+  foreach ( keys %FAI::disk_var );
+
+# do it, if $FAI::no_dry_run is set
+if ($FAI::no_dry_run)
+{
+  open( DISK_VAR, ">$ENV{LOGDIR}/disk_var.sh" )
+    or die "Unable to write to file $ENV{LOGDIR}/disk_var.sh\n";
+  print DISK_VAR "$_=$FAI::disk_var{$_}\n" foreach ( keys %FAI::disk_var );
+  close DISK_VAR;
+}
+




More information about the Fai-commit mailing list