[Fai-commit] r4293 - in people/michael/features/setup_harddisks_2: examples implementation
michael-guest at alioth.debian.org
michael-guest at alioth.debian.org
Thu May 31 07:27:20 UTC 2007
Author: michael-guest
Date: 2007-05-31 07:27:20 +0000 (Thu, 31 May 2007)
New Revision: 4293
Added:
people/michael/features/setup_harddisks_2/implementation/shdd2-volumes
Removed:
people/michael/features/setup_harddisks_2/implementation/NOTES
Modified:
people/michael/features/setup_harddisks_2/examples/model
people/michael/features/setup_harddisks_2/examples/vil
people/michael/features/setup_harddisks_2/implementation/Makefile
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-lib
people/michael/features/setup_harddisks_2/implementation/shdd2-parser
people/michael/features/setup_harddisks_2/implementation/shdd2-sizes
Log:
Here we go: setup_harddisks_2 seems to work; I'll send more information to
linux-fai-devel soon
Modified: people/michael/features/setup_harddisks_2/examples/model
===================================================================
--- people/michael/features/setup_harddisks_2/examples/model 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/examples/model 2007-05-31 07:27:20 UTC (rev 4293)
@@ -1,4 +1,4 @@
-disk_config hda preserve:6,7 disklabel:msdos bootable:3
+disk_config hda preserve:6,7 disklabel:msdos bootable:3 fstabkey:label
primary /boot 20 ext3 rw
primary swap 1000 swap sw
primary / 12000 ext3 rw -b 2048
Modified: people/michael/features/setup_harddisks_2/examples/vil
===================================================================
--- people/michael/features/setup_harddisks_2/examples/vil 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/examples/vil 2007-05-31 07:27:20 UTC (rev 4293)
@@ -1,14 +1,8 @@
-# disk configuration for a 6 disk configuration
-
-# This is set up much like everything else;
-
-# <type> <mountpoint> <size in mb> <mount options> <filesystem> [extra options]
-
disk_config sda
-primary / 256 - -
-primary swap 1024 - -
-primary - 0- - -
+primary - 256 - -
+primary swap 1024 swap sw
+primary - 0- - -
disk_config sdb
primary - 0- - -
Modified: people/michael/features/setup_harddisks_2/implementation/Makefile
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/Makefile 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/Makefile 2007-05-31 07:27:20 UTC (rev 4293)
@@ -7,7 +7,8 @@
shdd2-init \
shdd2-lib \
shdd2-parser \
- shdd2-sizes
+ shdd2-sizes \
+ shdd2-volumes
tidy:
perltidy -bl -i=2 $(FILES)
Deleted: people/michael/features/setup_harddisks_2/implementation/NOTES
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/NOTES 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/NOTES 2007-05-31 07:27:20 UTC (rev 4293)
@@ -1,12 +0,0 @@
-libparse-recdescent-perl must get installed in nfsroot
-
-auto mode
-
-query commands
-
-allow for some arbitrary predefined list instead of $ENV{disklist}
-
-split into functions, size computation must be fixed
-
-getopt processing
-
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2 2007-05-31 07:27:20 UTC (rev 4293)
@@ -20,6 +20,9 @@
use strict;
+# treat all warnings about uninitialised values as errors
+use warnings FATAL => qw(uninitialized);
+
################################################################################
#
# @file shdd2
@@ -37,43 +40,171 @@
#
################################################################################
+################################################################################
+# TODO list
+# - parted, libparse-recdescent-perl must get installed in nfsroot (add Depends:
+# to fai-nfsroot package)
+#
+# - closes #380629, #330915, #277045, #356862
+# - no-bug: #364763
+#
+# - auto mode (something like auto:server, auto:desktop?)
+# - man page
+#
+# - resize should imply resizing the filesystem as well (unless this is done by
+# parted already, needs to be checked)
+# - more error messages must be caught by shdd2-exec
+# - how to detect old-style config files? migration strategies?
+# - implement disklabels other than msdos and gpt
+################################################################################
+
+
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 "
+USAGE: [-X] no test, your harddisks will be formated
+ default: only test, no real formating
+ [-f<config-filename>] default: parse classes
+";
+
+# $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;
+&FAI::run_parser($config_file);
# read the sizes and partition tables of all disks listed in $FAI::disks
&FAI::get_current_disks;
-# compute the new partition sizes
-&FAI::compute_sizes;
+# 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;
-# TODO - debugging only: print the current contents of $FAI::configs
-&FAI::print_hash( \%FAI::configs );
+# 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;
-# generate the command script
-&FAI::build_commands;
+# for debugging purposes to print the hash structures
+use Data::Dumper;
-# TODO - debugging only: print the command script
-foreach my $cmd (@FAI::commands)
+# debugging only: print the current contents of $FAI::current_config
+if ( $FAI::debug )
{
- print $cmd . "\n";
+ 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;
+}
- # &FAI::execute_command($cmd);
+# 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 );
-# TODO - debugging only; print fstab
-printf "$_\n" foreach (@fstab);
+# 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;
+}
+
+
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-commands
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-commands 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-commands 2007-05-31 07:27:20 UTC (rev 4293)
@@ -37,61 +37,242 @@
################################################################################
#
+# @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";
+
+ if ( $partition->{"filesystem"} eq "-" )
+ {
+ return;
+ }
+ elsif ( $partition->{"filesystem"} eq "swap" )
+ {
+ push @FAI::commands, "mkswap " . $partition->{"fs_options"} . " $device";
+ }
+ else
+ {
+ push @FAI::commands, "mkfs." . $partition->{"filesystem"} . " " .
+ $partition->{"fs_options"} . " $device";
+ }
+}
+
+################################################################################
+#
# @brief Using the configurations from %FAI::configs, a list of commands is
-# build
+# built to create any RAID devices
#
# The list is @FAI::commands
#
################################################################################
-sub build_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_(.+)$/ );
- # TODO implement RAID/LVM support
- if ( $config eq "RAID" || $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"} };
+ # 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} } );
+ }
+ }
+}
+
- # TODO
- next;
+################################################################################
+#
+# @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";
}
- # configure a physical device
- elsif ( $config =~ /^PHY_(.*)$/ )
+ # create the volume group, if it doesn't exist already
+ if ( ! defined( $FAI::current_lvm_config{$vg} ) )
{
+ # create all the devices, unless they already exist (then, pvdisplay will
+ # succeed)
+ push @FAI::commands, "pvdisplay $_ || pvcreate $_" foreach ( keys
+ %{ $FAI::configs{$config}{"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"} }} = ();
+ # create all the devices, unless they already exist (then, pvdisplay will
+ # succeed)
+ push @FAI::commands, "pvdisplay $_ || pvcreate $_" foreach ( keys
+ %new_devs );
+ # 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";
+ }
- # the device to be configured
- my $disk = $1;
+ # 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} } );
+ }
+ }
- # the list of partitions that must be preserved
- my @to_preserve = ();
+ }
+}
- # the index of the existing extended partiton
- my $extended = -1;
+################################################################################
+#
+# @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
+{
- # find any existing extended partition on msdos disklabels
- if ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" )
- {
+ # loop through all configs
+ foreach my $config ( keys %FAI::configs )
+ {
+ # no RAID devices here
+ next if ( $config eq "RAID" );
- # loop over all existing partitions
- foreach my $part_id (
- sort keys %{ $FAI::current_config{$disk}{"partitions"} } )
- {
- next
- unless ( $FAI::current_config{$disk}{"partitions"}{$part_id}
- {"is_extended"} == 1 );
+ # no LVM here
+ next if ( $config =~ /^VG_(.+)$/ );
- # TODO: should be handled properly
- ( $extended == -1 ) or die "INTERNAL ERROR: Can't handle more than 1
- extended partition\n";
+ # configure a physical device
+ ( $config =~ /^PHY_(.+)$/ ) or die
+ "INTERNAL ERROR: Invalid config\n";
- # set the id of the extended partition
- $extended = $part_id;
- }
- }
+ # 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"} } )
@@ -104,164 +285,338 @@
|| $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} ==
1 );
- # preserved partitions must exist already
+ # 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;
- # in case $part_id is a logical partition, the corresponding extended
- # partition must be preserved as well
- if ( $extended > -1
- && $part_id > 4
- && $FAI::current_config{$disk}{"disklabel"} eq "msdos" )
- {
- push @to_preserve, $extended;
-
- # set $extended to -1 to avoid adding it multiple times; do not rely
- # on $extended later on!
- $extended = -1;
- }
}
# sort the list of preserved partitions
@to_preserve = sort { $a <=> $b } @to_preserve;
-
- # check, whether a new disk label is required
- if ( $FAI::configs{$config}{'disklabel'} ne
- $FAI::current_config{$disk}{'disklabel'} )
+
+ # 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;
+ }
+ }
- # A new disk label may only be written if no partitions need to be
- # preserved
- ( scalar(@to_preserve) > 0 )
- and die "Can't change disklabel, partitions are to be preserved!\n";
+ # 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 command to set the disklabel
- push @FAI::commands, "$FAI::system_commands{'parted'} $disk mklabel"
- . $FAI::configs{$config}{'disklabel'};
+ # 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";
}
- else
+
+ # 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, "$FAI::system_commands{'parted'} $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 disk label is unchanged, now all partitions must be removed unless
- # they are to be preserved
- foreach my $part_id (
- sort keys %{ $FAI::current_config{$disk}{"partitions"} } )
+ # the type of the partition defaults to primary
+ my $part_type = "primary";
+ if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
{
- # partition $part_id is to be preserved, skip it
- if ( $to_preserve[0] == $part_id )
+ # change the partition type to extended or logical as appropriate
+ if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
+ {"extended"} == 1 )
{
-
- # and remove it from the list
- shift @to_preserve;
- next;
+ $part_type = "extended";
}
+ elsif ( $part_id > 4 )
+ {
+ $part_type = "logical";
+ $part_nr = 4 if ( $part_nr < 4 );
+ }
+ }
- # add a command to remove $part_id
- push @FAI::commands,
- "$FAI::system_commands{'parted'} $disk rm $part_id";
- }
+ # 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, "$FAI::system_commands{'parted'} $disk mkpart "
+ . "$part_type $start" . "B " . $end . "B";
}
- # the byte count where the next parition should start
- my $next_start = 0;
-
- # generate the commands for resizing or creating partitions
- foreach
- my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
+ # 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 );
- # check, whether $part_id must be preserved; we have checked it for
- # existence above already
+ # 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"}{"preserve"} ==
- 1 )
+ $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} >
+ $FAI::current_config{$disk}{"partitions"}{$mapped_id}{"count_byte"} )
{
-
- # the next partition may start after the preserved partition
- $next_start =
- $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} + 1
- unless ( $FAI::configs{$config}{"disklabel"} eq "msdos"
- && $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
- {"extended"} == 1 );
+ 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"};
- # check, whether $part_id must be resized; we have checked it for
- # existence above already
- elsif (
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"resize"} ==
- 1 )
- {
+ # build an appropriate command
+ push @FAI::commands,
+ "$FAI::system_commands{'parted'} $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"};
- # resizing a partition, which must be formatted later makes no sense
- ( $FAI::current_config{$disk}{"partitions"}{"filesystem"} ne
- $FAI::configs{$config}{"partitions"}{$part_id}{"filesystem"} )
- and die
-"Filesystem change has been requested for $part_id, resizing makes no sense\n";
+ # build an appropriate command
+ push @FAI::commands,
+ "$FAI::system_commands{'parted'} $disk resize $p $start"
+ . "B " . $end . "B";
+ }
+
+ # write the disklabel again to drop the partition table
+ push @FAI::commands, "$FAI::system_commands{'parted'} $disk mklabel "
+ . $FAI::configs{$config}{'disklabel'};
- # compute end byte of the resized partition
- my $part_end =
- $FAI::configs{$config}{"partitions"}{"size"}{"eff_size"} +
- $next_start - 1;
+ # generate the commands for creating all partitions
+ foreach
+ my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
+ {
- # build an appropriate command
- push @FAI::commands,
- "$FAI::system_commands{'parted'} $disk resize $part_id $next_start"
- . "B "
- . $part_size . "B";
+ # 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"};
- # set $next_start unless this is an extended partition
- $next_start = $part_size + 1
- unless ( $FAI::configs{$config}{"disklabel"} eq "msdos"
- && $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
- {"extended"} == 1 );
- }
-
- # $part_id neither needs to be resized nor preserved, but newly created
- else
+ # the type of the partition defaults to primary
+ my $part_type = "primary";
+ if ( $FAI::configs{$config}{"disklabel"} eq "msdos" )
{
- # 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 )
{
-
- # 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_type = "extended";
}
+ elsif ( $part_id > 4 )
+ {
+ $part_type = "logical";
+ }
+ }
- # compute end byte of the new partition
- my $part_end =
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} +
- $next_start - 1;
-
- # build a parted command to create the partition
- push @FAI::commands, "$FAI::system_commands{'parted'} $disk mkpart
- $part_type $next_start" . "B " . $part_size . "B";
- $next_start = $part_size + 1
- unless ( $FAI::configs{$config}{"disklabel"} eq "msdos"
- && $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
- {"extended"} == 1 );
- }
+ # build a parted command to create the partition
+ push @FAI::commands, "$FAI::system_commands{'parted'} $disk mkpart "
+ . "$part_type $start" . "B " . $end . "B";
}
+
+ # set the bootable flag, if requested at all
+ push @FAI::commands, "$FAI::system_commands{'parted'} $disk set "
+ . $FAI::configs{$config}{"bootable"} . " boot on" if
+ ( $FAI::configs{$config}{"bootable"} > -1 );
+
+ # sleep a little to wait for udev to set up all devices
+ push @FAI::commands, "sleep 1";
}
- else
+
+ # generate the commands for creating all filesystems
+ foreach
+ my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
{
- die "INTERNAL ERROR: Invalid config\n";
+ # 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( "$FAI::system_commands{'parted'} $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( "$FAI::system_commands{'parted'} $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;
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-exec
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-exec 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-exec 2007-05-31 07:27:20 UTC (rev 4293)
@@ -52,7 +52,7 @@
$FAI::error_codes = [
{
error => "parted_1",
- message => "Parted produced error. Couldn't remove Partition",
+ message => "Parted produced error. Couldn't remove partition\n",
stderr_regex =>
".*Error: Could not stat device rm - No such file or directory.*",
stdout_regex => "",
@@ -61,15 +61,15 @@
},
{
error => "parted_2",
- message => "Parted produced error. Could not read disk label.",
+ message => "Parted produced error. Could not read disk label.\n",
stderr_regex => ".*Error: Unable to open .* - unrecognised disk label.*",
stdout_regex => "",
program => "parted",
- response => "die",
+ response => "warn",
},
{
error => "parted_3",
- message => "Parted produced error. Could not open disk",
+ message => "Parted produced error. Could not open disk\n",
stderr_regex =>
".*Error: Could not stat device .* - No such file or directory.*",
stdout_regex => "",
@@ -77,13 +77,29 @@
response => "die"
},
{
- error => "port_1",
- message => "test_error",
- stderr_regex => ".*Error: .* Port test not found.*",
+ error => "parted_4",
+ message => "parted not found\n",
+ stderr_regex => ".*(parted: command not found|/sbin/parted: No such file or directory)",
stdout_regex => "",
- program => "port",
+ 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",
+ },
];
################################################################################
@@ -99,10 +115,8 @@
{
my ($error) = @_;
my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
- foreach my $m (@treffer) #returns the first found error message.
- {
- return "$m->{'message'}";
- }
+ # returns the first found error message.
+ return $treffer[ 0 ]->{'message'};
}
################################################################################
@@ -119,10 +133,8 @@
{
my ( $error, $field ) = @_;
my @treffer = grep { $_->{error} eq "$error" } @$FAI::error_codes;
- foreach my $m (@treffer) #returns the first found error message.
- {
- return "$m->{$field}";
- }
+ # returns the first found error message.
+ return $treffer[ 0 ]->{$field};
}
################################################################################
#
@@ -145,21 +157,17 @@
{
my ( $command, $stdout_ref, $stderr_ref ) = @_;
my $err = &execute_command( $command, $stdout_ref, $stderr_ref );
- unless ( $err eq "" )
+ if ( $err ne "" )
{
my $response = &get_error( $err, "response" );
my $message = &get_error( $err, "message" );
- if ( $response eq "die" )
- {
- die $message;
- }
+ $response->() if ( ref( $response ) );
- if ( $response eq "warn" )
- {
- warn $message;
- }
+ die $message if ( $response eq "die" );
+ warn $message if ( $response eq "warn" );
+
return $err;
}
return "";
@@ -176,7 +184,7 @@
# output of the bash command
#
# @reference stderr_ref reference to a list, that should contain the standard
-# errer output of the bash command
+# error output of the bash command
#
# @return the identifier of the error
#
@@ -189,27 +197,22 @@
my @stdout = ();
my $stderr_line = "";
my $stdout_line = "";
- my $run = 0;
- if ( defined( $ENV{"NO_DRY_RUN"} ) && !( $ENV{"NO_DRY_RUN"} =~ /^\s*$/ ) )
- {
- $run = 1;
- }
#make tempfile, get perl filehandle and filename of the file
- ( my $stderr_fh, my $stderr_filename ) = File::Temp::tempfile();
- ( my $stdout_fh, my $stdout_filename ) = File::Temp::tempfile();
+ ( 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 ( $run == 1 )
+ 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
{
- printf
-"would run command $command, to run in NO_DRY_MODE, please set environment variable NO_DRY_RUN to 1 \n";
+ print "would run command $command; to have them executed, use -X \n";
}
# read the tempfile into lists, each element of the list one line
@@ -220,31 +223,22 @@
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
- if ( scalar(@stderr) > 0 )
- {
- $stderr_line = $stderr[0];
- }
+ $stderr_line = $stderr[0] if ( scalar(@stderr) > 0 );
#see last comment
- if ( scalar(@stdout) > 0 )
- {
- $stdout_line = $stdout[0];
- }
+ $stdout_line = $stdout[0] if ( scalar(@stdout) > 0 );
#if an array is passed to the function, it is filled with the stdout
- if ( 'ARRAY' eq ref($stdout_ref) )
- {
- @$stdout_ref = @stdout;
- }
+ @$stdout_ref = @stdout if ( 'ARRAY' eq ref($stdout_ref) );
#see above
- if ( 'ARRAY' eq ref($stderr_ref) )
- {
- @$stderr_ref = @stderr;
- }
+ @$stderr_ref = @stderr if ( 'ARRAY' eq ref($stderr_ref) );
- #get the error, eventually happened
+ #get the error, if there was any
foreach my $err (@$FAI::error_codes)
{
if (
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-fstab
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-fstab 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-fstab 2007-05-31 07:27:20 UTC (rev 4293)
@@ -38,105 +38,210 @@
################################################################################
#
# @brief this function generates the fstab file from our representation of the
-# partitions to be created. Call this function only with one parameter,
-# the rest is used for recursion.
+# partitions to be created.
#
-# @reference hash_ref to our representation of the partitions to be created
-# @param modus help variable for the recursion. holds the value wheter we are
-# opertationing in physical partition, lvm or raid mode
+# @reference config Reference to our representation of the partitions to be
+# created
#
-# @param device help variable, holds the actual device
+# @return list of fstab lines
#
-# @reference listref help variable, holding the reference to the list which
-# contains the lines of the fstab file to be generated
-#
-# @param call_count holds the actual recursion depth
-#
-# @return 0
-#
################################################################################
sub generate_fstab
{
+ # config structure is the only input
+ my ( $config ) = @_;
- # MT: TODO - CONT HERE
- my ( $hash_ref, $modus, $device, $listref, $call_count ) = @_;
+ # the file to be returned, a list of lines
+ my @fstab = ();
- #first check if we have already a list, when not, create one
- if ( !$listref )
+ # walk through all configured parts
+ # the order of entries is most likely wrong, it is fixed at the end
+ foreach my $c ( keys %$config )
{
- my @newlist = ();
- $listref = \@newlist;
- }
+ # 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 "/" );
-#check if call_count is set, if not, the function is called the first time, set it
- if ( !$call_count )
- {
- $call_count = 1;
- }
- else
- {
-
- #if we go deaper, increment it
- $call_count++;
- }
-
- # go through all drives, lvms, raids
- foreach my $key ( keys %$hash_ref )
- {
-
- if ( ref( $hash_ref->{$key} ) && $key =~ /^PHY_(.*)$/ )
+ # 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" } = $device . $p_ref->{"number"}
+ 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 = ();
+ # write the logical volume path as the first entry
+ push @fstab_line, "/dev/$device/$l";
+ # 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 "/" );
- #set the modus to physical drive partition
- $modus = "PHY";
- $device = $1;
+ # 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/$device/$l" if ( $l_ref->{"mountpoint"} eq
+ "/" );
+ # add to the swaplist, if the filesystem is swap
+ $FAI::disk_var{ "SWAPLIST" } .= " /dev/$device/$l" if (
+ $l_ref->{"filesystem"} eq "swap" );
+ }
}
-
- # check if we are at the important place for the fstab file
- if ( ref( $hash_ref->{$key} ) && $key eq "partitions" )
+ elsif ( $c eq "RAID" )
{
-
-# every partition means one line in fstab, let's get the information about each one
- foreach my $partition ( keys %{ $hash_ref->{$key} } )
+ # create a line in the output file for each device
+ foreach my $r ( sort keys %{ $config->{$c}->{"volumes"} } )
{
- my $p_ref = $hash_ref->{$key}->{$partition};
- if ( !$p_ref->{'size'}->{'extended'} )
- {
- my @fstab_line = ();
- push @fstab_line, $device . $partition;
- push @fstab_line, $p_ref->{'mountpoint'};
- push @fstab_line, $p_ref->{'filesystem'};
- push @fstab_line, $p_ref->{'mount_options'};
- push @fstab_line, 0;
- if ( $p_ref->{'mountpoint'} eq "/root" )
- {
- push @fstab_line, 1;
- }
- else
- {
- push @fstab_line, 2;
- }
+ # 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 "/" );
-#lets join the columns of one line with tabs, and the push it to our fstab line array
- push @$listref, join( "\t", @fstab_line );
- }
-
+ # 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" );
}
-
}
- if ( ref( $hash_ref->{$key} ) )
+ else
{
- &generate_fstab( $hash_ref->{$key}, $modus, $device, $listref,
- $call_count );
+ die "INTERNAL ERROR: Unexpected key $c\n";
}
+ }
- }
- if ( $call_count eq 1 )
+ # cleanup the swaplist (remove leading space)
+ $FAI::disk_var{ "SWAPLIST" } =~ s/^\s+//;
+
+ # 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" );
- #if we are at highes level, lets return the listref
- return @$listref;
+ 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;
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-init
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-init 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-init 2007-05-31 07:27:20 UTC (rev 4293)
@@ -37,15 +37,12 @@
################################################################################
#
-# @brief Enable debugging by setting $FAI::debug to a value greater than 0
+# @brief Enable debugging by setting $debug to a value greater than 0
#
################################################################################
-$FAI::debug = 1;
+$FAI::debug = 0;
+defined( $ENV{debug} ) and $FAI::debug = $ENV{debug};
-# TODO - REMOVE THIS
-$ENV{disklist} = "hda
-";
-
################################################################################
#
# @brief The lists of disks of the system
@@ -55,15 +52,67 @@
################################################################################
#
+# @brief The variables later written to disk_var.sh
+#
+################################################################################
+%FAI::disk_var = ();
+$FAI::disk_var{ "SWAPLIST" } = "";
+
+################################################################################
+#
# @brief The hash of all configurations specified in the disk_config file
#
# The structure is as follows:
+# PHY_<DEVICE>
+# virtual (0|1)
+# disklabel STRING
+# bootable -1..n
+# partitions
+# <1..n>
+# size
+# extended (0|1)
+# preserve (0|1)
+# resize (0|1)
+# range
+# eff_size
+# number 1..n
+# maps_to_existing 1..n
+# start_byte
+# end_byte
+# mountpoint
+# mount_options
+# filesystem
+# fs_options
+# label
# VG_<NAME>
-# TODO
-# PHY_<DEVICE>
-# TODO
+# devices
+# estimated_size
+# volumes
+# <logical-volume-name>
+# size
+# preserve (0|1)
+# resize (0|1)
+# range
+# eff_size
+# mountpoint
+# mount_options
+# filesystem
+# fs_options
+# label
# RAID
-# TODO
+# volumes
+# <0..n>
+# mode
+# devices
+# /dev/<device-name>
+# options
+# spare (0|1)
+# missing (0|1)
+# mountpoint
+# mount_options
+# filesystem
+# fs_options
+# label
#
################################################################################
%FAI::configs = ();
@@ -74,13 +123,53 @@
#
# The structure is as follows:
# <DEVICE>
-# TODO
+# bios_cylinders
+# bios_heads
+# bios_sectors_per_track
+# sector_size
+# disklabel
+# begin_byte
+# end_byte
+# partitions
+# <1..n>
+# begin_byte
+# end_byte
+# count_byte
+# is_extended
+# filesystem
#
################################################################################
%FAI::current_config = ();
################################################################################
#
+# @brief The current LVM configuration
+#
+# The structure is as follows:
+# <VG>
+# physical_volumes
+# size
+# volumes
+# <lv-name>
+# size
+#
+################################################################################
+%FAI::current_lvm_config = ();
+
+################################################################################
+#
+# @brief The current RAID configuration
+#
+# The structure is as follows:
+# <0..n>
+# devices
+# mode
+#
+################################################################################
+%FAI::current_raid_config = ();
+
+################################################################################
+#
# @brief The list of commands to be executed
#
################################################################################
@@ -90,12 +179,12 @@
#
# @brief A map of commands
#
-# TODO check for availability, exact version?
+# checks for availability within $PATH are performed from within the parser
#
################################################################################
%FAI::system_commands = (
- "mdadm" => "/sbin/mdadm",
- "parted" => "/sbin/parted -s"
+ "mdadm" => "mdadm",
+ "parted" => "parted -s"
);
1;
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-lib
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-lib 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-lib 2007-05-31 07:27:20 UTC (rev 4293)
@@ -90,8 +90,12 @@
{
# if there is another hash, recursively call the function with this hash
- if ( ref( $hash_ref->{$key} ) )
+ if ( ref( $hash_ref->{$key} ) eq 'ARRAY' )
{
+ print "$k KEY: $key VAL: " . join( " ", @{ $hash_ref->{$key} } ) . "\n";
+ }
+ elsif ( ref( $hash_ref->{$key} ) )
+ {
print "$k KEY: $key VAL: hash\n";
&print_hash( $hash_ref->{$key}, $k );
}
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-parser
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-parser 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-parser 2007-05-31 07:27:20 UTC (rev 4293)
@@ -50,12 +50,16 @@
# /* preserve partitions */
# | resize:[[:digit:]]+(,[[:digit:]]+)*
# /* attempt to resize partitions */
-# | disklabel:(msdos|sun)
+# | disklabel:(msdos|gpt)
# /* write a disklabel - default is msdos */
# | bootable:[[:digit:]]+
# /* mark a partition bootable, default is / */
# | virtual
# /* do not assume the disk to be a physical device, use with xen */
+# | fstabkey:(device|label|uuid)
+# /* when creating the fstab, the key used for defining the device
+# may be the device (/dev/xxx), a label given using -L, or the uuid
+# */
#
# volume ::= <type> <mountpoint> <size> <filesystem> <mount_options> <fs_options>
# | vg <name> <size>
@@ -145,8 +149,8 @@
if ( $disk =~ /^\d+$/ )
{
- # $disk-1 must not be valid index in the map of all disks in the system
- ( $FAI::disks < $disk )
+ # $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
@@ -172,6 +176,7 @@
"virtual" => 0,
"disklabel" => "msdos",
"bootable" => -1,
+ "fstabkey" => "device",
"partitions" => {}
};
}
@@ -352,6 +357,7 @@
{
$return = 1;
}
+ | <error>
line: <skip: qr/[ \t]*/> "\\n"
| <skip: qr/[ \t]*/> comment "\\n"
@@ -385,12 +391,18 @@
}
| /^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 ] );
}
@@ -406,23 +418,34 @@
# set the resize flag for all ids
$FAI::configs{ $FAI::device }{ "partitions" }{ $_ }{ "size" }{ "resize" } = 1 foreach ( split( ",", $1 ) );
}
- | /^disklabel:(msdos|sun|gpt|mac)/
+ | /^disklabel:(msdos|gpt)/
{
# set the disk label - actually not only the above, but all types
- # supported by parted could be allowed
+ # 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
- # TODO not yet implemented
$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' name devices
| /^raid([0156])\s+/
@@ -466,6 +489,9 @@
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};
@@ -479,28 +505,42 @@
| 'swap'
{
# this partition is swap space, not mounted
- $FAI::partition_pointer->{ "mountpoint" } = "swap";
+ $FAI::partition_pointer->{ "mountpoint" } = "none";
}
| m{^/\S*}
{
# set the mount point
$FAI::partition_pointer->{ "mountpoint" } = $item[ 1 ];
+ # mark the bootable partition, if it is not set yet and this is a PHY_
+ # device
+ if ( $item[ 1 ] eq "/" && $FAI::device =~ /^PHY_(.+)$/ &&
+ $FAI::configs{$FAI::device}{"bootable"} == -1 )
+ {
+ $FAI::configs{$FAI::device}{"bootable"} =
+ $FAI::partition_pointer->{"number"};
+ # 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" };
+ }
}
- name: m{^([^/\s\-]+)\s+}
+ name: m{^([^/\s\-]+)}
{
- # make sure this line is part of an LVM configuration
- ( $FAI::device =~ /^VG_/ ) or
- die "vg is invalid in a non LVM-context.\n";
# 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+%?(-(\d+%?)?)?)(:resize)?\s+/
@@ -533,18 +573,13 @@
| /^preserve(\d+)\s+/
{
# set the preserve flag, if the index of the partition hasn't changed
- if( $1 == $FAI::partition_pointer->{ "number" } )
- {
- $FAI::partition_pointer->{ "size" }->{ "preserve" } = 1;
- }
- else
- {
- die "partition number of to-be-preserved partition $1 changed to $FAI::partition_pointer->{number}\n";
- }
+ ( $1 == $FAI::partition_pointer->{ "number" } ) or die
+ "Partition number of to-be-preserved partition $1 changed to $FAI::partition_pointer->{number}\n";
+ $FAI::partition_pointer->{ "size" }->{ "preserve" } = 1;
}
| <error: invalid partition size near "$text">
- devices: /^([^\d,:\s\-][^,:\s]*(:(spare|missing))*(,[^,:\s]+(:(spare|missing))*)*)\s+/
+ devices: /^([^\d,:\s\-][^,:\s]*(:(spare|missing))*(,[^,:\s]+(:(spare|missing))*)*)/
{
# split the device list by ,
foreach my $dev ( split( ",", $1 ) )
@@ -579,9 +614,11 @@
}
else
{
- push @{ $FAI::configs{$FAI::device}{"devices"} }, $dev;
+ # create an empty hash for each device
+ $FAI::configs{$FAI::device}{"devices"}{$dev} = {};
}
}
+ 1;
}
| <error: invalid device spec "$text">
@@ -600,11 +637,12 @@
}
| /^\S+/
{
- ( &FAI::in_path("mkfs.$item[1]") == 1 ) or die "unknown/invalid filesystem type $item[1]";
+ ( &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: /.*/
+ fs_options: /[^;\n]*/
{
$FAI::partition_pointer->{ "fs_options" } = $item[ 1 ];
}
@@ -613,21 +651,28 @@
################################################################################
#
-# @brief Parse the data from <STDIN> using @ref $FAI::Parser
+# @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 <STDIN> to a single string (not a list), thus $/ has to be unset
+ # read <$IN> to a single string (not a list), thus $/ has to be unset
my $ifs = $/;
undef $/;
- my $input = <STDIN>;
+ my $input = <$IN>;
$/ = $ifs;
- # print the contents of <STDIN> for debugging purposes
+ # 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";
}
Modified: people/michael/features/setup_harddisks_2/implementation/shdd2-sizes
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-sizes 2007-05-23 15:49:57 UTC (rev 4292)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-sizes 2007-05-31 07:27:20 UTC (rev 4293)
@@ -24,8 +24,7 @@
#
# @file shdd2-sizes
#
-# @brief A set of functions to obtain the current partition table and to
-# compute the size of the partitions to be created
+# @brief Compute the size of the partitions and volumes to be created
#
# $Id$
#
@@ -40,293 +39,207 @@
################################################################################
#
-# @brief Collect the current partition information from all disks listed both
-# in $FAI::disks and $FAI::configs{PHY_<disk>}
+# @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 get_current_disks
+sub estimate_size
{
+ my ( $dev ) = @_;
- # backup value of $ENV{"NO_DRY_RUN"}
- my $no_dry_run = "";
- defined( $ENV{"NO_DRY_RUN"} ) and $no_dry_run = $ENV{"NO_DRY_RUN"};
+ # 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";
- # obtain the current state of all disks
- foreach my $disk (@FAI::disks)
+ # 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 = "";
- # create full paths
- ( $disk =~ m{^/} ) or $disk = "/dev/$disk";
+ # 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 );
- # make sure, $disk is a proper block device
- ( -b $disk ) or die "$disk is not a block special device!\n";
-
- # skip this disk, if it is not listed in $FAI::configs
- defined( $FAI::configs{"PHY_$disk"} ) or next;
-
- # 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
- $ENV{"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(
- $FAI::system_commands{"parted"} . " $disk unit TiB print",
- \@parted_print, 0 );
-
- # reset NO_DRY_RUN
- $ENV{"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" )
+ # 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 );
+ }
- # write the disk label as configured
- $error =
- &FAI::execute_command( $FAI::system_commands{"parted"}
- . " $disk mklabel "
- . $FAI::configs{$disk}{"disklabel"} );
-
- # set NO_DRY_RUN to perform read-only commands always
- $ENV{"NO_DRY_RUN"} = "1";
-
- # retry partition-table print
- $error =
- &FAI::execute_command(
- $FAI::system_commands{"parted"} . " $disk unit TiB print",
- \@parted_print, 0 );
-
- # reset NO_DRY_RUN
- $ENV{"NO_DRY_RUN"} = $no_dry_run;
+ 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" );
}
-
- # check, whether there is still an error
- if ( $error ne "" )
+ else
{
- 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" );
+ # 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";
+ }
+}
-# 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.
+# @brief Compute the desired sizes of logical volumes
#
-# 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
-# 6 14715058176B 30449986559B 15734928384B logical ext3
-# 7 30450018816B 32547432959B 2097414144B logical ext3
-# 8 32547465216B 82343278079B 49795812864B logical ext3
-#
+################################################################################
+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;
- # As shown above, the file system might be blank, if its type is not known
- # to parted. Thus the exact columns of "File system" have to be extracted
- # These two variables keep the indices
- my $parted_fs_before = 0;
- my $parted_fs_len = 0;
+ # the volumes that require redistribution of free space
+ my @redist_list = ();
- # Parse the output line by line
- foreach my $line (@parted_print)
+ # 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;
- # print the line read - for debugging purposes only
- ( $FAI::debug > 0 ) and print "$line";
+ # start may be given in percents of the size, rewrite it to megabytes
+ $start = POSIX::floor( $vg_size * $1 / 100 ) if ( $start =~ /^(\d+)%$/);
- # now we test line by line - some of them may be ignored
- if ( $line =~ /^Disk /
- || $line =~ /^\s*$/
- || $line =~ /^WARNING: You are not superuser/
- || $line =~ /^Sector / )
- {
- next;
- }
+ # end may be given in percents of the size, rewrite it to megabytes
+ $end = POSIX::ceil( $vg_size * $1 / 100 ) if ( $end =~ /^(\d+)%$/);
- # read and store the current disk label
- elsif ( $line =~ /^Partition Table: (.*)$/ )
- {
- $FAI::current_config{$disk}{"disklabel"} = $1;
- }
+ # make sure that $end >= $start
+ ( $end >= $start ) or die "INTERNAL ERROR: end < start\n";
- # the line containing the table headers
- elsif ( $line =~ /^(Number.*\s+)(File system\s+)\S+/ )
+ # 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 )
{
-
- # the number of characters before File system
- $parted_fs_before = length($1) + 1;
-
- # the length of the File system column
- $parted_fs_len = length($2);
+ # write the size back to the configuration
+ $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} = $start;
}
-
- # one of the partitions
else
{
-
- # we must have seen the header, otherwise probably the format has
- # changed
- ( $parted_fs_len > 0 )
- or die "INTERNAL ERROR: Table header not seen yet\n";
-
- # get the partition number
- $line =~ /^\s*(\d+)/;
- my $id = $1;
-
- # extract the set of characters
- $line =~ /^.{$parted_fs_before}(.{$parted_fs_len})/;
- 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;
+ # add this volume to the redistribution list
+ push @redist_list, $lv;
}
}
- # set NO_DRY_RUN to perform read-only commands always
- $ENV{"NO_DRY_RUN"} = "1";
+ # 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";
- # reset the output list
- @parted_print = ();
+ # the extension factor
+ my $redist_factor = 0;
+ $redist_factor = ( $vg_size - $min_space ) / ( $max_space - $min_space ) if
+ ( $max_space > $min_space );
- # obtain the partition table using bytes as units
- # TODO: when to use _std, when should one use execute_command
- my $error =
- &FAI::execute_command_std(
- "$FAI::system_commands{'parted'}} $disk unit B print",
- \@parted_print, 0 );
-
- # reset NO_DRY_RUN
- $ENV{"NO_DRY_RUN"} = $no_dry_run;
-
- # check, whether an error has occured
- # TODO: is this necessary?
- if ( $error ne "" )
+ # update all sizes that are still ranges
+ foreach my $lv ( @redist_list )
{
- 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" );
- }
+ # 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;
- # Parse the output of the byte-wise partition table
- foreach my $line (@parted_print)
- {
-
- # The size of the disk
- if ( $line =~ /^Disk .*: (\d+)B$/i )
- {
- $FAI::current_config{$disk}{"begin_byte"} = 0;
- $FAI::current_config{$disk}{"end_byte"} = $1;
- }
-
- # 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
- );
-
- # set the corresponding entries
- $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;
+ # write the final size
+ $FAI::configs{$config}{"volumes"}{$lv}{"size"}{"eff_size"} = $start + ( (
+ $end - $start ) * $redist_factor );
}
-
- # set NO_DRY_RUN to perform read-only commands always
- $ENV{"NO_DRY_RUN"} = "1";
-
- # reset the output list
- @parted_print = ();
-
- # obtain the partition table using bytes as units
- # TODO: when to use _std, when should one use execute_command
- my $error =
- &FAI::execute_command_std(
- "$FAI::system_commands{'parted'}} $disk unit chs print",
- \@parted_print, 0 );
-
- # reset NO_DRY_RUN
- $ENV{"NO_DRY_RUN"} = $no_dry_run;
-
- # check, whether an error has occured
- # TODO: is this necessary?
- 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 CHS partition table
- foreach my $line (@parted_print)
- {
-
- # The partition geometry
- if ( $line =~ /^\s*(\d+)\s+(\d+),(\d+),(\d+)\s+(\d+),(\d+),(\d+)/i )
- {
- $FAI::current_config{$disk}{"partitions"}{$1}{"begin_cylinder"} = $2;
- $FAI::current_config{$disk}{"partitions"}{$1}{"begin_head"} = $3;
- $FAI::current_config{$disk}{"partitions"}{$1}{"begin_sector"} = $4;
- $FAI::current_config{$disk}{"partitions"}{$1}{"end_cylinder"} = $5;
- $FAI::current_config{$disk}{"partitions"}{$1}{"end_head"} = $6;
- $FAI::current_config{$disk}{"partitions"}{$1}{"end_sector"} = $7;
- }
-
- # The disk geometry
- if ( $line =~ /^Disk .*: (\d+),(\d+),(\d+)$/i )
- {
- $FAI::current_config{$disk}{"begin_cylinder"} = 0;
- $FAI::current_config{$disk}{"begin_head"} = 0;
- $FAI::current_config{$disk}{"begin_sector"} = 0;
- $FAI::current_config{$disk}{"end_cylinder"} = $1;
- $FAI::current_config{$disk}{"end_head"} = $2;
- $FAI::current_config{$disk}{"end_sector"} = $3;
- }
- }
-
}
}
@@ -336,306 +249,472 @@
# thereof.
#
################################################################################
-sub compute_sizes
+sub compute_partition_sizes
{
# loop through all device configurations
foreach my $config ( keys %FAI::configs )
{
- if ( $config eq "RAID" || $config =~ /^VG_/ )
- {
+ # 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_(.+)$/ );
- # TODO compute the sizes of lvms, RAIDs
- next;
- }
+ # 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 );
- # device is an effective disk
- elsif ( $config =~ /^PHY_(.*)$/ )
+ # 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;
+ }
+ }
- # initialise variables
- # the id of the extended partition to be created, if required
- my $extended = -1;
+ # the space required on the disk
+ my $min_req_total_space = 0;
- # the device name of the disk
- my $disk = $1;
+ # 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"};
- # remaining free space is to be shared by those partitions whose size is a
- # range; $redist_space keeps track of the maximum space to be
- # redistributed
- my $redist_space = 0;
+ # the MBR requires space, too
+ $min_req_total_space += $FAI::current_config{$disk}{"bios_sectors_per_track"} *
+ $FAI::current_config{$disk}{"sector_size"};
+ }
- # minimum space required by all partitions, i.e., the lower ends of the
- # ranges
- # $min_req_space counts up to the next preserved partition
- my $min_req_space = 0;
+ # 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"};
+ }
- # $min_req_total_space counts for the entire disk
- my $min_req_total_space = 0;
+ # the list of partitions that we need to find start and end bytes for
+ my @worklist = ( sort keys %{ $FAI::configs{$config}{"partitions"} } );
- # list of partition ids that require redistribution
- my @redist_list = ();
+ while ( scalar( @worklist ) > 0 )
+ {
+ # work on the first entry of the list
+ my $part_id = $worklist[ 0 ];
- # the start of a range of non-preserved disk space
- my $range_start = 0;
-
- # loop through all configured partitions in a sorted manner
- foreach
- my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
+ # the partition $part_id must be preserved
+ if (
+ $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} ==
+ 1 )
{
- # find/handle the extended partition, if any
- if (
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} ==
- 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";
- # make sure that there is only one extended partition
- ( $extended == -1 )
- or die "INTERNAL ERROR: More than 1 extended partition\n";
+ ( $next_start >
+ $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"} )
+ and die
+ "Previous partitions overflow begin of preserved partition $part_id\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 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"};
- # set the local variable to this id
- $extended = $part_id;
+ # 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"};
- # initialise the size of the extended partition to 0
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
- 0;
- }
+ # 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"};
- # the partition $pard_id need not be preserved
- elsif (
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} ==
- 0 )
+ # 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 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;
+ # 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";
- # start may be given in is percents of the size
- if ( $start =~ /^(\d+)%$/ )
- {
+ # 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);
- # rewrite it to bytes
- $start =
- POSIX::floor(
- $FAI::current_config{$disk}{"end_byte"} * $1 / 100 );
- }
- else
+ # 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"};
- # it is given in megabytes, make it bytes
- $start = $start * 1024.0 * 1024.0;
+ # set the next start to the start of the extended partition
+ $next_start =
+ $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"};
}
- # end may be given in is percents of the size
- if ( $end =~ /^(\d+)%$/ )
- {
+ }
+
+ # 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";
+ }
- # rewrite it to bytes
- $end =
- POSIX::ceil( $FAI::current_config{$disk}{"end_byte"} * $1 / 100 );
- }
- else
- {
+ # 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";
- # it is given in megabytes, make it bytes
- $end = $end * 1024.0 * 1024.0;
- }
+ # make sure that there is only one extended partition
+ ( $extended == -1 || 1 == scalar( @worklist ) )
+ or die "INTERNAL ERROR: More than 1 extended partition\n";
- # write back the size spec in bytes
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =
- $start . "-" . $end;
+ # ensure that it is a primary partition
+ ( $part_id <= 4 ) or die
+ "INTERNAL ERROR: Extended partition wouldn't be a primary one\n";
- # check, whether the size is fixed
- if ( $end == $start )
+ # 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 );
- # then set eff_size to a proper value
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
- $start;
+ 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"};
}
- else
- {
+
+ ( $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;
- # make sure that $end > $start
- ( $end > $start ) or die "INTERNAL ERROR: end < start\n";
+ # start may be given in percents of the size
+ if ( $start =~ /^(\d+)%$/ )
+ {
- # effective size is not yet known, use -1 to indicate this
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
- -1;
+ # rewrite it to bytes
+ $start = POSIX::floor(
+ $FAI::current_config{$disk}{"size"} * $1 / 100 );
+ }
+ else
+ {
- # add the valid range to the space to be redistributed among the
- # non-fixed sizes
- $redist_space += $end - $start;
+ # it is given in megabytes, make it bytes
+ $start = $start * 1024.0 * 1024.0;
+ }
- # add this $part_id to the redistribution list
- push @redist_list, $part_id;
- }
+ # end may be given in percents of the size
+ if ( $end =~ /^(\d+)%$/ )
+ {
- # add the minimum size to the required space (locally)
- $min_req_space += $start;
+ # rewrite it to bytes
+ $end =
+ POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
+ }
+ else
+ {
- # add the minimum size to the required space (entire disk)
- $min_req_total_space += $start;
+ # 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";
- # partition must be preserved
- else
+ # 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;
- # 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";
+ # 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;
- # 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"};
+ # maximum useful space
+ my $max_space = 0;
- # 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"};
-
- # check, whether there are partitions that require redistribution
- if ( scalar(@redist_list) > 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;
- # compute the available space and the resulting multiplier of all
- # ranges
- my $free_space =
- $FAI::current_config{$disk}{"partitions"}{$part_id}
- {"begin_byte"} - 1 - $range_start - $min_req_space;
- ( $free_space >= 0 ) or die "INTERNAL ERROR: negative free space\n";
+ # start may be given in percents of the size
+ if ( $min_size =~ /^(\d+)%$/ )
+ {
- # the multiplier for the amount added to the lower bounds of the ranges
- my $redist_factor = 1.0;
- ( $free_space < $redist_space )
- and $redist_factor = $free_space / $redist_space;
- ( $FAI::debug > 0 ) and print "redist factor is $redist_factor\n";
+ # rewrite it to bytes
+ $min_size = POSIX::floor(
+ $FAI::current_config{$disk}{"size"} * $1 / 100 );
+ }
+ else
+ {
- # redistribute the free space
- foreach my $part (@redist_list)
- {
+ # 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+)%$/ )
+ {
- # make sure the entry requires redistribution
- ( $FAI::configs{$config}{"partitions"}{$part}{"size"}
- {"eff_size"} == -1 )
- or die "INTERNAL ERROR: invalid entry in redist_list\n";
+ # rewrite it to bytes
+ $max_size =
+ POSIX::ceil( $FAI::current_config{$disk}{"size"} * $1 / 100 );
+ }
+ else
+ {
- # re-check that the size is indeed a range and obtain the start
- # and end-points
- ( $FAI::configs{$config}{"partitions"}{$part}{"size"}{"range"} =~
- /^(\d+%?)-(\d+%?)$/ )
- or die "INTERNAL ERROR: invalid range spec\n";
+ # it is given in megabytes, make it bytes
+ $max_size *= 1024.0 * 1024.0;
+ }
- # store the start and end
- my $start = $1;
- my $end = $2;
+ # 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"};
+ }
- # set the new effective size according to $redist_factor
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}
- {"eff_size"} =
- POSIX::floor( $start + ( ( $end - $start ) * $redist_factor ) );
+ $min_req_space += $min_size;
+ $max_space += $max_size;
}
}
- # empty $redist_list;
- @redist_list = ();
+ # 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 );
- # set the range start past the preserved partition
- $range_start =
- $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} + 1;
+ my $available_space = $end_of_range - $next_start + 1;
- # reset $redist space and $min_req_space to 0
- $redist_space = 0;
- $min_req_space = 0;
+ # 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"};
+ }
- # check, whether there are partitions that require redistribution
- if ( scalar(@redist_list) > 0 )
- {
+ # partition starts at where we currently are
+ $FAI::configs{$config}{"partitions"}{$part_id}{"start_byte"} =
+ $next_start;
- # compute the available space and the resulting multiplier of all
- # ranges
- my $free_space =
- $FAI::current_config{$disk}{"partitions"}{$part_id}{"begin_byte"} -
- 1 - $range_start - $min_req_space;
- ( $free_space >= 0 ) or die "INTERNAL ERROR: negative free space\n";
+ # the end may need some alignment, depending on the disk label
+ my $end_byte = $next_start + $start - 1;
- # the multiplier for the amount added to the lower bounds of the ranges
- my $redist_factor = 1.0;
- ( $free_space < $redist_space )
- and $redist_factor = $free_space / $redist_space;
- ( $FAI::debug > 0 ) and print "redist factor is $redist_factor\n";
+ # 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"} );
+ }
- # redistribute the free space
- foreach my $part (@redist_list)
+ # 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"};
+ }
- # make sure the entry requires redistribution
- ( $FAI::configs{$config}{"partitions"}{$part}{"size"}{"eff_size"} ==
- -1 )
- or die "INTERNAL ERROR: invalid entry in redist_list\n";
+ # 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;
- # re-check that the size is indeed a range and obtain the start
- # and end-points
- ( $FAI::configs{$config}{"partitions"}{$part}{"size"}{"range"} =~
- /^(\d+%?)-(\d+%?)$/ )
- or die "INTERNAL ERROR: invalid range spec\n";
+ # then set eff_size to a proper value
+ $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
+ $start;
- # store the start and end
- my $start = $1;
- my $end = $2;
+ # 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 new effective size according to $redist_factor
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} =
- POSIX::floor( $start + ( ( $end - $start ) * $redist_factor ) );
- }
+ # 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}{"end_byte"} )
- and die
- "Disk is too small - at least $min_req_total_space is required\n";
+ # 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";
+ # 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";
- # compute the size of the extended partition, if any
- next
- unless ( $FAI::configs{$config}{"disklabel"} eq "msdos"
- && $extended > -1 );
- foreach
- my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } )
- {
-
- # logical partitions have an id > 4
- next if ( $part_id <= 4 );
-
- # add the effective size of $part_id to the size of the extended partition
- $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"eff_size"} +=
- $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"};
- }
+ # 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";
}
- # an invalid config entry has been found
- else
- {
- die "INTERNAL ERROR: invalid config entry $config.\n";
- }
}
}
Copied: people/michael/features/setup_harddisks_2/implementation/shdd2-volumes (from rev 4292, people/michael/features/setup_harddisks_2/implementation/shdd2-sizes)
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/shdd2-volumes (rev 0)
+++ people/michael/features/setup_harddisks_2/implementation/shdd2-volumes 2007-05-31 07:27:20 UTC (rev 4293)
@@ -0,0 +1,552 @@
+#!/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.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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(
+ $FAI::system_commands{"parted"} . " $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{$disk}{"disklabel"} ) )
+ {
+ # write the disk label as configured
+ $error =
+ &FAI::execute_command( $FAI::system_commands{"parted"}
+ . " $disk mklabel msdos" );
+ }
+ else
+ {
+ # write the disk label as configured
+ $error =
+ &FAI::execute_command( $FAI::system_commands{"parted"}
+ . " $disk mklabel "
+ . $FAI::configs{$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(
+ $FAI::system_commands{"parted"} . " $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
+ if ( $line =~ /^Disk /
+ || $line =~ /^\s*$/
+ || $line =~ /^WARNING: You are not superuser/ )
+ {
+ next;
+ }
+
+ # determine the logical sector size
+ elsif ( $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(
+ "$FAI::system_commands{'parted'} $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(
+ "$FAI::system_commands{'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";
+
+ }
+}
+
+################################################################################
+#
+# @brief Collect the current LVM configuration
+#
+################################################################################
+sub get_current_lvm
+{
+ # backup value of $FAI::no_dry_run
+ my $no_dry_run = $FAI::no_dry_run;
+
+ # the list to hold the output of vgdisplay commands as parsed below
+ my @vgdisplay_print = ();
+
+ # set no_dry_run to perform read-only commands always
+ $FAI::no_dry_run = 1;
+
+ # try to obtain the list of volume groups
+ my $error = &FAI::execute_command_std( "vgdisplay -s", \@vgdisplay_print, 0 );
+
+ # the expected output (if any) contains lines like the following
+ #
+ # $ vgdisplay -s
+ # "XENU" 453.36 GB [451.93 GB used / 1.43 GB free]
+
+ # parse the output line by line and call vgdisplay -v <VG>
+ foreach my $line ( @vgdisplay_print )
+ {
+ ( $line =~ /^\s*"(\S+)"\s+\d+\.\d+ GB\s+\[\d+\.\d+ GB\s+used \/ \d+\.\d+ GB\s+free\]$/ )
+ or die "Unexpected vgdisplay output $line";
+
+ # the name of the volume group
+ my $vg = $1;
+
+ # initialise the hash entry
+ $FAI::current_lvm_config{$vg}{"physical_volumes"} = ();
+
+ # get the detailed configuration for $vg
+ my @vgdisplay_v_print = ();
+
+ # try to obtain the detailed information for the volume group $vg
+ my $error = &FAI::execute_command_std( "vgdisplay -v $vg", \@vgdisplay_v_print, 0 );
+
+ # the expected output (if any) looks like this:
+# $ vgdisplay -v XENU
+# Using volume group(s) on command line
+# Finding volume group "XENU"
+# --- Volume group ---
+# VG Name XENU
+# System ID
+# Format lvm2
+# Metadata Areas 4
+# Metadata Sequence No 65
+# VG Access read/write
+# VG Status resizable
+# MAX LV 0
+# Cur LV 53
+# Open LV 46
+# Max PV 0
+# Cur PV 4
+# Act PV 4
+# VG Size 453.36 GB
+# PE Size 4.00 MB
+# Total PE 116060
+# Alloc PE / Size 115693 / 451.93 GB
+# Free PE / Size 367 / 1.43 GB
+# VG UUID 09JCPv-v2RU-NWEZ-ilNA-mNLk-Scw3-aURtE6
+#
+# --- Logical volume ---
+# LV Name /dev/XENU/mole_
+# VG Name XENU
+# LV UUID WBcBDw-1z2J-F3b2-FGAk-u7Ki-IEgF-lMEURK
+# LV Write Access read/write
+# LV Status available
+# # open 1
+# LV Size 1000.00 MB
+# Current LE 250
+# Segments 1
+# Allocation inherit
+# Read ahead sectors 0
+# Block device 254:0
+#
+# --- Physical volumes ---
+# PV Name /dev/sda8
+# PV UUID 4i7Tpi-k9io-Ud44-gWJd-nSuG-hbh7-CE1m43
+# PV Status allocatable
+# Total PE / Free PE 29015 / 0
+#
+# PV Name /dev/sda9
+# PV UUID VXSxq1-vEwU-5VrY-QVC8-3Wf1-AY45-ayD9KY
+# PV Status allocatable
+# Total PE / Free PE 29015 / 0
+#
+
+ # parse the output to select the interesting parts
+ # there are 3 main groups: the volume group, logical volumes and physical
+ # volumes; use mode to indicate this
+ my $mode = "";
+ # we need to remember the logical volume name across the lines
+ my $lv_name = "";
+ # do the line-wise parsing
+ foreach my $line_v ( @vgdisplay_v_print )
+ {
+ $mode = "vg" if ( $line_v =~ /^\s*--- Volume group ---\s*$/ );
+ $mode = "lv" if ( $line_v =~ /^\s*--- Logical volume ---\s*$/ );
+ $mode = "pv" if ( $line_v =~ /^\s*--- Physical volumes ---\s*$/ );
+ $mode = "" if ( $mode ne "pv" && $line_v =~ /^\s*$/ );
+ next if ( $mode eq "" );
+
+ # Now select the interesting information for each mode
+ if ( $mode eq "vg" )
+ {
+ # for a volume group only the size is needed
+ if ( $line_v =~ /^\s*Alloc PE \/ Size\s+\d+ \/ (\d+\.\d+) ([MG]B)\s*$/ )
+ {
+ # extract the floatingpoint value
+ $FAI::current_lvm_config{$vg}{"size"} = $1;
+ # make it megabytes
+ $FAI::current_lvm_config{$vg}{"size"} *= 1024 if ( $2 eq "GB" );
+ }
+ }
+ elsif ( $mode eq "lv" )
+ {
+ # we need the name and the size of each existing logical volume
+ if ( $line_v =~ /^\s*LV Name\s+\/dev\/\Q$vg\E\/(\S+)\s*$/ )
+ {
+ $lv_name = $1;
+ }
+ elsif ( $line_v =~ /^\s*LV Size\s+(\d+\.\d+) ([MG]B)\s*$/ )
+ {
+ # extract the floatingpoint value
+ $FAI::current_lvm_config{$vg}{"volumes"}{$lv_name}{"size"} = $1;
+ # make it megabytes
+ $FAI::current_lvm_config{$vg}{"volumes"}{$lv_name}{"size"} *= 1024 if ( $2 eq "GB" );
+ }
+ }
+ elsif ( $mode eq "pv" )
+ {
+ # get the physical devices that are part of this volume group
+ if ( $line_v =~ /^\s*PV Name\s+(\S+)\s*$/ )
+ {
+ push @{ $FAI::current_lvm_config{$vg}{"physical_volumes"} }, $1;
+ }
+ }
+
+ }
+ }
+
+ # reset no_dry_run
+ $FAI::no_dry_run = $no_dry_run;
+}
+
+################################################################################
+#
+# @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;
+
More information about the Fai-commit
mailing list