[Fai-commit] r4949 - people/michael/features/setup_harddisks_2/implementation/lib trunk/lib/setup-storage
lange at alioth.debian.org
lange at alioth.debian.org
Thu Jun 12 11:34:21 UTC 2008
Author: lange
Date: 2008-06-12 11:34:19 +0000 (Thu, 12 Jun 2008)
New Revision: 4949
Added:
trunk/lib/setup-storage/Commands.pm
Removed:
people/michael/features/setup_harddisks_2/implementation/lib/commands.pm
Log:
moving setup-storage to trunk
Deleted: people/michael/features/setup_harddisks_2/implementation/lib/commands.pm
===================================================================
--- people/michael/features/setup_harddisks_2/implementation/lib/commands.pm 2008-06-12 11:34:18 UTC (rev 4948)
+++ people/michael/features/setup_harddisks_2/implementation/lib/commands.pm 2008-06-12 11:34:19 UTC (rev 4949)
@@ -1,909 +0,0 @@
-#!/usr/bin/perl -w
-
-#*********************************************************************
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# A copy of the GNU General Public License is available as
-# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
-# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
-# can also obtain it by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#*********************************************************************
-
-use strict;
-
-################################################################################
-#
-# @file commands.pm
-#
-# @brief Build the required commands in @FAI::commands using the config stored
-# in %FAI::configs
-#
-# $Id$
-#
-# @author Christian Kern, Michael Tautschnig, Sebastian Hetze, Andreas Schuldei
-# @date Sun Jul 23 16:09:36 CEST 2006
-#
-################################################################################
-
-package FAI;
-
-################################################################################
-#
-# @brief Build the mkfs commands for the partition pointed to by $partition
-#
-# @param $device Device name of the target partition
-# @param $partition Reference to partition in the config hash
-#
-################################################################################
-sub build_mkfs_commands {
-
- my ($device, $partition) = @_;
-
- defined ($partition->{filesystem})
- or &FAI::internal_error("filesystem is undefined");
- my $fs = $partition->{filesystem};
-
- return if ($fs eq "-");
-
- my ($create_options) = $partition->{fs_options} =~ m/createopts="([^"]+)"/;
- my ($tune_options) = $partition->{fs_options} =~ m/tuneopts="([^"]+)"/;
- $create_options = $partition->{fs_options} unless $create_options;
- print "create_options: $create_options\n" if ($FAI::debug && $create_options);
- print "tune_options: $tune_options\n" if ($FAI::debug && $tune_options);
-
- # check for encryption requests
- $device = &FAI::encrypt_device($device, $partition);
-
- # create the file system with options
- my $create_tool = "mkfs.$fs";
- ($fs eq "swap") and $create_tool = "mkswap";
- ($fs eq "xfs") and $create_options = "$create_options -f" unless ($create_options =~ m/-f/);
- my $pre_encrypt = "exist_$device";
- $pre_encrypt = "encrypt_$device" if ($partition->{encrypt});
- &FAI::push_command( "$create_tool $create_options $device", $pre_encrypt,
- "has_fs_$device" );
-
- # possibly tune the file system - this depends on whether the file system
- # supports tuning at all
- return unless $tune_options;
- my $tune_tool;
- ($fs eq "ext2" || $fs eq "ext3") and $tune_tool = "tune2fs";
- ($fs eq "reiserfs") and $tune_tool = "reiserfstune";
- die "Don't know how to tune $fs\n" unless $tune_tool;
-
- # add the tune command
- &FAI::push_command( "$tune_tool $tune_options $device", "has_fs_$device",
- "has_fs_$device" );
-}
-
-################################################################################
-#
-# @brief Encrypt a device and change the device name before formatting it
-#
-# @param $device Original device name of the target partition
-# @param $partition Reference to partition in the config hash
-#
-# @return Device name, may be the same as $device
-#
-################################################################################
-sub encrypt_device {
-
- my ($device, $partition) = @_;
-
- return $device unless $partition->{encrypt};
-
- # encryption requested, rewrite the device name
- my $enc_dev_name = $device;
- $enc_dev_name =~ "s#/#_#g";
- my $enc_dev_short_name = "crypt$enc_dev_name";
- $enc_dev_name = "/dev/mapper/$enc_dev_short_name";
- my $keyfile = "$ENV{LOGDIR}/$enc_dev_short_name";
-
- # generate a key for encryption
- &FAI::push_command(
- "head -c 2048 /dev/urandom | head -n 47 | tail -n 46 | od | tee $keyfile",
- "", "keyfile_$device" );
-
- # prepare encryption
- &FAI::push_command(
- "yes YES | cryptsetup luksFormat $device $keyfile -c aes-cbc-essiv:sha256 -s 256",
- "exist_$device,keyfile_$device", "crypt_format_$device" );
- &FAI::push_command(
- "cryptsetup luksOpen $device $enc_dev_short_name --key-file $keyfile",
- "crypt_format_$device", "encrypted_$device" );
-
- # add entries to crypttab
- push @FAI::crypttab, "$enc_dev_short_name\t$device\t$keyfile\tluks";
-
- return $enc_dev_name;
-}
-
-################################################################################
-#
-# @brief Set the partition type $t on a device $d. This is a no-op if $d is not
-# a physical device
-#
-# @param $d Device name
-# @param $t Type (e.g., lvm or raid)
-#
-################################################################################
-sub set_partition_type_on_phys_dev {
-
- my ($d, $t) = @_;
- my ($i_p_d, $disk, $part_no) = &FAI::phys_dev($d);
- return unless $i_p_d;
- # make sure this device really exists (we can't check for the partition
- # as that may be created later on
- (-b $disk) or die "Specified disk $disk does not exist in this system!\n";
- # set the raid flag
- &FAI::push_command( "parted -s $disk set $part_no $t on", "exist_$d",
- "type_${t}_$d" );
-}
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to create any RAID devices
-#
-################################################################################
-sub build_raid_commands {
-
- foreach my $config (keys %FAI::configs) { # loop through all configs
- # no LVM or physical devices here
- next if ($config =~ /^VG_./ || $config =~ /^PHY_./);
- ($config eq "RAID") or &FAI::internal_error("Invalid config $config");
-
- # create all raid devices
- foreach my $id (&numsort(keys %{ $FAI::configs{$config}{volumes} })) {
-
- # keep a reference to the current volume
- my $vol = (\%FAI::configs)->{$config}->{volumes}->{$id};
- # the desired RAID level
- my $level = $vol->{mode};
-
- warn "RAID implementation is incomplete - preserve is not supported\n" if
- ($vol->{preserve});
-
- # prepend "raid", if the mode is numeric-only
- $level = "raid$level" if ($level =~ /^\d+$/);
-
- # the list of RAID devices
- my @devs = keys %{ $vol->{devices} };
- my @eff_devs = ();
- my @spares = ();
- my $pre_req;
-
- # set proper partition types for RAID
- foreach my $d (@devs) {
- if ($vol->{devices}->{$d}->{spare}) {
- push @spares, $d;
- } else {
- push @eff_devs, $d;
- }
- # skip devices marked missing
- next if $vol->{devices}{$d}{missing};
- &FAI::set_partition_type_on_phys_dev($d, "raid");
- if ((&FAI::phys_dev($d))[0]) {
- $pre_req .= ",type_raid_$d";
- } else {
- $pre_req .= ",exist_$d";
- }
- }
- my $pre_req_no_comma = $pre_req;
- $pre_req_no_comma =~ s/^,//;
- # wait for udev to set up all devices
- &FAI::push_command( "udevsettle --timeout=10", $pre_req_no_comma,
- "settle_for_mdadm_create$id" );
-
- # create the command
- if (0 == $id) {
- $pre_req = "settle_for_mdadm_create$id$pre_req";
- } else {
- $pre_req = "settle_for_mdadm_create$id,exist_/dev/md" . ( $id - 1 ) . $pre_req;
- }
- &FAI::push_command(
- "yes | mdadm --create /dev/md$id --level=$level --force --run --raid-devices="
- . scalar(@eff_devs) . " --spare-devices=" . scalar(@spares) . " "
- . join(" ", @eff_devs) . " " . join(" ", @spares),
- "$pre_req", "exist_/dev/md$id" );
-
- # create the filesystem on the volume
- &FAI::build_mkfs_commands("/dev/md$id",
- \%{ $FAI::configs{$config}{volumes}{$id} });
- }
- }
-}
-
-################################################################################
-#
-# @brief Erase the LVM signature from a list of devices that should be prestine
-# in order to avoid confusion of the lvm tools
-#
-################################################################################
-sub erase_lvm_signature {
-
- my ($devices_aref) = @_;
- # first remove the dm_mod module to prevent ghost lvm volumes
- # from existing
- # push @FAI::commands, "modprobe -r dm_mod";
- # zero out (broken?) lvm signatures
- # push @FAI::commands, "dd if=/dev/zero of=$_ bs=1 count=1"
- # foreach ( @{$devices_aref} );
- my $device_list = join (" ", @{$devices_aref});
- $FAI::debug and print "Erased devices: $device_list\n";
- &FAI::push_command( "pvremove -ff -y $device_list", "", "pv_sigs_removed" );
-
- # reload module
- # push @FAI::commands, "modprobe dm_mod";
-}
-
-################################################################################
-#
-# @brief Create the volume group $config, unless it exists already; if the
-# latter is the case, only add/remove the physical devices
-#
-# @param $config Config entry
-#
-################################################################################
-sub create_volume_group {
-
- my ($config) = @_;
- ($config =~ /^VG_(.+)$/) and ($1 ne "--ANY--") or &FAI::internal_error("Invalid config $config");
- my $vg = $1; # the actual volume group
-
- # create the volume group, if it doesn't exist already
- if (!defined ($FAI::current_lvm_config{$vg})) {
- # create all the devices
- my @devices = keys %{ $FAI::configs{$config}{devices} };
- &FAI::erase_lvm_signature(\@devices);
- &FAI::push_command( "pvcreate $_", "pv_sigs_removed,exist_$_",
- "pv_done_$_" ) foreach (@devices);
- # create the volume group
- my $pre_dev = join(",pv_done_", @devices);
- $pre_dev =~ s/^,//;
- &FAI::push_command( "vgcreate $vg " . join (" ", @devices), "$pre_dev",
- "vg_created_$vg" );
- # we are done
- return;
- }
-
- # otherwise add or remove the devices for the volume group, run pvcreate
- # where needed
- # the list of devices to be created
- my %new_devs = ();
-
- # create an undefined entry for each new device
- @new_devs{ keys %{ $FAI::configs{$config}{devices} } } = ();
-
- my @new_devices = keys %new_devs;
-
- # &FAI::erase_lvm_signature( \@new_devices );
-
- # create all the devices
- &FAI::push_command( "pvcreate $_", "exist_$_", "pv_done_$_" ) foreach (@new_devices);
-
- # extend the volume group by the new devices (includes the current ones)
- my $pre_dev = join(",pv_done_", @new_devices);
- $pre_dev =~ s/^,//;
- &FAI::push_command( "vgextend $vg " . join (" ", @new_devices), "$pre_dev",
- "vg_extended_$vg" );
-
- # 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 (@new_devices);
-
- # run vgreduce to get them removed
- if (scalar (keys %rm_devs)) {
- $pre_dev = join(",pv_done_", keys %rm_devs);
- &FAI::push_command( "vgreduce $vg " . join (" ", keys %rm_devs),
- "vg_extended_$vg$pre_dev", "vg_created_$vg" );
- } else {
- &FAI::push_command( "true", "vg_extended_$vg", "vg_created_$vg" );
- }
-}
-
-################################################################################
-#
-# @brief Create the volume group $config, unless it exists already; if the
-# latter is the case, only add/remove the physical devices
-#
-# @param $config Config entry
-#
-################################################################################
-sub setup_logical_volumes {
-
- my ($config) = @_;
- ($config =~ /^VG_(.+)$/) and ($1 ne "--ANY--") or &FAI::internal_error("Invalid config $config");
- my $vg = $1; # the actual volume group
-
- my $lv_rm_pre = "";
- my $lv_resize_pre = "";
- # 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
- if (defined ( $FAI::configs{$config}{volumes}{$lv})
- && ($FAI::configs{$config}{volumes}{$lv}{size}{preserve} == 1)) {
- $lv_resize_pre .= ",lv_resize_$vg/$lv" if
- $FAI::configs{$config}{volumes}{$lv}{size}{resize};
- next;
- }
-
- &FAI::push_command( "lvremove -f $vg/$lv", "vg_enabled_$vg", "lv_rm_$vg/$lv");
- $lv_rm_pre .= ",lv_rm_$vg/$lv";
- }
- $lv_rm_pre =~ s/^,//;
- $lv_resize_pre =~ s/^,//;
-
- # now create or resize the configured logical volumes
- foreach my $lv (keys %{ $FAI::configs{$config}{volumes} }) {
- # reference to the size of the current logical volume
- my $lv_size = (\%FAI::configs)->{$config}->{volumes}->{$lv}->{size};
- # skip preserved partitions, but ensure that they exist
- if ($lv_size->{preserve}) {
- defined ($FAI::current_lvm_config{$vg}{volumes}{$lv})
- or die "Preserved volume $vg/$lv does not exist\n";
- next;
- }
-
- # resize the volume
- if ($lv_size->{resize}) {
- defined ($FAI::current_lvm_config{$vg}{volumes}{$lv})
- or die "Resized volume $vg/$lv does not exist\n";
-
- if ($lv_size->{eff_size} <
- $FAI::current_lvm_config{$vg}{volumes}{$lv}{size})
- {
- &FAI::push_command( "parted -s /dev/$vg/$lv resize 1 0 " . $lv_size->{eff_size} . "B",
- "vg_enabled_$vg,$lv_rm_pre", "lv_shrink_$vg/$lv" );
- &FAI::push_command( "lvresize -L " . $lv_size->{eff_size} . " $vg/$lv",
- "vg_enabled_$vg,$lv_rm_pre,lv_shrink_$vg/$lv", "lv_created_$vg/$lv" );
- } else {
- &FAI::push_command( "lvresize -L " . $lv_size->{eff_size} . " $vg/$lv",
- "vg_enabled_$vg,$lv_rm_pre", "lv_grow_$vg/$lv" );
- &FAI::push_command( "parted -s /dev/$vg/$lv resize 1 0 " . $lv_size->{eff_size} . "B",
- "vg_enabled_$vg,$lv_rm_pre,lv_grow_$vg/$lv", "exist_/dev/$vg/$lv" );
- }
-
- next;
- }
-
- # create a new volume
- &FAI::push_command( "lvcreate -n $lv -L " . $lv_size->{eff_size} . " $vg",
- "vg_enabled_$vg,$lv_rm_pre", "exist_/dev/$vg/$lv" );
-
- # create the filesystem on the volume
- &FAI::build_mkfs_commands("/dev/$vg/$lv",
- \%{ $FAI::configs{$config}{volumes}{$lv} });
- }
-}
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to setup the LVM
-# creates the volume groups, the logical volumes and the filesystems
-#
-################################################################################
-sub build_lvm_commands {
-
- # loop through all configs
- foreach my $config (keys %FAI::configs) {
-
- # no physical devices or RAID here
- next if ($config =~ /^PHY_./ || $config eq "RAID");
- ($config =~ /^VG_(.+)$/) or &FAI::internal_error("Invalid config $config");
- next if ($1 eq "--ANY--");
- my $vg = $1; # the volume group
-
- # set proper partition types for LVM
- &FAI::set_partition_type_on_phys_dev($_, "lvm")
- foreach (keys %{ $FAI::configs{$config}{devices} });
- my $type_pre = join(",type_lvm_", keys %{ $FAI::configs{$config}{devices} });
- $type_pre =~ s/^,//;
- # wait for udev to set up all devices
- &FAI::push_command( "udevsettle --timeout=10", "$type_pre",
- "settle_for_vgchange_$vg" );
-
- # create the volume group or add/remove devices
- &FAI::create_volume_group($config);
- # enable the volume group
- &FAI::push_command( "vgchange -a y $vg",
- "settle_for_vgchange_$vg,vg_created_$vg", "vg_enabled_$vg" );
-
- # perform all necessary operations on the underlying logical volumes
- &FAI::setup_logical_volumes($config);
- }
-}
-
-################################################################################
-#
-# @brief Return an ordered list of partitions that must be preserved
-#
-# @param $config Config entry
-#
-################################################################################
-sub get_preserved_partitions {
-
- my ($config) = @_;
- ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
- my $disk = $1; # the device to be configured
-
- # the list of partitions that must be preserved
- my @to_preserve = ();
-
- # find partitions that should be preserved or resized
- foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
- # reference to the current partition
- my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
- next unless ($part->{size}->{preserve} || $part->{size}->{resize});
-
- # 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)
- $part->{maps_to_existing} = $part_id;
-
- # add $part_id to the list of preserved partitions
- push @to_preserve, $part_id;
-
- }
-
- # sort the list of preserved partitions
- @to_preserve = &numsort(@to_preserve);
-
- # add the extended partition as well, if logical partitions must be
- # preserved; and mark it as resize
- if ($FAI::configs{$config}{disklabel} eq "msdos") {
- # we assume there are no logical partitions
- my $has_logical = 0;
- my $extended = -1;
-
- # now check all entries; the array is sorted
- foreach my $part_id (@to_preserve) {
- # the extended partition may already be listed; then, the id of the
- # extended partition must not change
- if ($FAI::current_config{$disk}{partitions}{$part_id}{is_extended}) {
- (defined ($FAI::configs{$config}{partitions}{$extended}{size}{extended})
- && defined ($FAI::current_config{$disk}{partitions}{$extended}{is_extended})
- && $FAI::configs{$config}{partitions}{$extended}{size}{extended}
- && $FAI::current_config{$disk}{partitions}{$extended}{is_extended})
- or die "ID of extended partition changes\n";
-
- # make sure resize is set
- $FAI::configs{$config}{partitions}{$part_id}{size}{resize} = 1;
- $extended = $part_id;
- last;
- }
-
- # there is some logical partition
- if ($part_id > 4) {
- $has_logical = 1;
- last;
- }
- }
-
- # if the extended partition is not listed yet, find and add it now; note
- # that we need to add the existing one
- if ($has_logical && -1 == $extended) {
- foreach my $part_id (&numsort(keys %{ $FAI::current_config{$disk}{partitions} })) {
-
- # no extended partition
- next unless
- $FAI::current_config{$disk}{partitions}{$part_id}{is_extended};
-
- # find the configured extended partition to set the mapping
- foreach my $p (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
- # reference to the current partition
- my $part = (\%FAI::configs)->{$config}->{partitions}->{$p};
- next unless $part->{size}->{extended};
-
- # make sure resize is set
- $part->{size}->{resize} = 1;
-
- # store the id for further checks
- $extended = $p;
-
- # add a mapping entry to the existing extended partition
- $part->{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 = &numsort(@to_preserve);
-
- last;
- }
- }
-
- # a sanity check: if there are logical partitions, the extended must
- # have been added
- (0 == $has_logical || -1 != $extended)
- or &FAI::internal_error("Required extended partition not detected for preserve");
- }
-
- return @to_preserve;
-}
-
-################################################################################
-#
-# @brief Recreate the preserved partitions once the partition table has been
-# flushed
-#
-# @param $config Config entry
-# @param $to_preserve Reference to list of preserved/resized partitions
-#
-################################################################################
-sub rebuild_preserved_partitions {
-
- my ($config, $to_preserve) = @_;
- ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
- my $disk = $1; # the device to be configured
-
- # once we rebuild partitions, their ids are likely to change; this counter
- # helps keeping track of this
- my $part_nr = 0;
-
- # now rebuild all preserved partitions
- foreach my $part_id (@{$to_preserve}) {
- # get the existing id
- my $mapped_id =
- $FAI::configs{$config}{partitions}{$part_id}{maps_to_existing};
-
- # get the original starts and ends
- my $start =
- $FAI::current_config{$disk}{partitions}{$mapped_id}{begin_byte};
- my $end =
- $FAI::current_config{$disk}{partitions}{$mapped_id}{end_byte};
-
- # the type of the partition defaults to primary
- my $part_type = "primary";
- if ( $FAI::configs{$config}{disklabel} eq "msdos" ) {
-
- # change the partition type to extended or logical as appropriate
- if ( $FAI::configs{$config}{partitions}{$part_id}{size}{extended} == 1 ) {
- $part_type = "extended";
- } elsif ( $part_id > 4 ) {
- $part_type = "logical";
- $part_nr = 4 if ( $part_nr < 4 );
- }
- }
-
- # restore the partition type, if any
- my $fs =
- $FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem};
-
- # 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;
-
- my $post = "exist_" . &FAI::make_device_name($disk, $part_nr);
- $post = "rebuilt_" . &FAI::make_device_name($disk, $part_nr) if
- $FAI::configs{$config}{partitions}{$part_id}{size}{resize};
- # build a parted command to create the partition
- &FAI::push_command( "parted -s $disk mkpart $part_type $fs ${start}B
- ${end}B", "cleared1_$disk", $post );
- }
-}
-
-################################################################################
-#
-# @brief Create the volume group $config, unless it exists already; if the
-# latter is the case, only add/remove the physical devices
-#
-# @param $config Config entry
-#
-################################################################################
-sub setup_partitions {
-
- my ($config) = @_;
- ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
- my $disk = $1; # the device to be configured
-
- # the list of partitions that must be preserved
- my @to_preserve = &FAI::get_preserved_partitions($config);
-
- # 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
- &FAI::push_command( "parted -s $disk mklabel " .
- $FAI::configs{$config}{disklabel}, "exist_$disk", "cleared1_$disk" );
-
- &FAI::rebuild_preserved_partitions($config, \@to_preserve);
-
- my $pre_all_resize = "";
-
- # resize partitions while checking for dependencies
- foreach my $part_id (reverse &numsort(@to_preserve)) {
- # reference to the current partition
- my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
- # get the existing id
- my $mapped_id = $part->{maps_to_existing};
- # get the intermediate partition id
- my $p = $FAI::current_config{$disk}{partitions}{$mapped_id}{new_id};
- # anything to be done?
- $pre_all_resize .= ",exist_" . &FAI::make_device_name($disk, $p) unless
- $part->{size}->{resize};
- next unless $part->{size}->{resize};
-
- $pre_all_resize .= ",resized_" . &FAI::make_device_name($disk, $p);
- my $deps = "";
- # now walk all other partitions requiring a resize to check for overlaps
- foreach my $part_other (reverse &numsort(@to_preserve)) {
- # don't compare to self
- next if ($part_id == $part_other);
- # reference to the current partition
- my $part_other_ref = (\%FAI::configs)->{$config}->{partitions}->{$part_other};
- # anything to be done?
- next unless $part_other_ref->{size}->{resize};
- # get the existing id
- my $mapped_id_other = $part_other_ref->{maps_to_existing};
- # get the intermediate partition id
- my $p_other = $FAI::current_config{$disk}{partitions}{$mapped_id_other}{new_id};
- # check for overlap
- next if($part->{begin_byte} >
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte});
- next if($part->{end_byte} <
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte});
- # overlap detected - add dependency, but handle extended<->logical with
- # special care, even though this does not catch all cases (sometimes it
- # will fail nevertheless
- if ($part->{size}->{extended} && $part_other > 4) {
- if($part->{begin_byte} >
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte}) {
- $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
- }
- elsif($part->{end_byte} <
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte}) {
- $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
- }
- }
- elsif ($part_id > 4 && $part_other_ref->{size}->{extended}) {
- if($part->{begin_byte} <
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte}) {
- $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
- }
- elsif($part->{end_byte} >
- $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte}) {
- $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
- }
- } else {
- $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
- }
- }
-
- # get the new starts and ends
- my $start = $part->{start_byte};
- my $end = $part->{end_byte};
-
- # build an appropriate command
- # ntfs requires specific care
- if ($FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem} eq
- "ntfs") {
- # check, whether ntfsresize is available
- &FAI::in_path("ntfsresize") or die "ntfsresize not found in PATH\n";
- # ntfs partition can't be moved
- ($start == $FAI::current_config{$disk}{partitions}{$mapped_id}{begin_byte})
- or &FAI::internal_error("ntfs partition supposed to move");
- # ntfsresize requires device names
- my $eff_size = $part->{size}->{eff_size};
-
- # wait for udev to set up all devices
- &FAI::push_command( "udevsettle --timeout=10", "rebuilt_" .
- &FAI::make_device_name($disk, $p) . $deps, "settle_for_resize_" .
- &FAI::make_device_name($disk, $p) );
- &FAI::push_command( "yes | ntfsresize -s $eff_size " .
- &FAI::make_device_name($disk, $p), "settle_for_resize_" .
- &FAI::make_device_name($disk, $p), "ntfs_ready_for_rm_" .
- &FAI::make_device_name($disk, $p) );
- &FAI::push_command( "parted -s $disk rm $p", "ntfs_ready_for_rm_" .
- &FAI::make_device_name($disk, $p), "resized_" .
- &FAI::make_device_name($disk, $p) );
- } else {
- &FAI::push_command( "parted -s $disk resize $p ${start}B ${end}B",
- "rebuilt_" . &FAI::make_device_name($disk, $p), "resized_" .
- &FAI::make_device_name($disk, $p) );
- }
-
- }
-
- # write the disklabel again to drop the partition table and create a new one
- # that has the proper ids
- &FAI::push_command( "parted -s $disk mklabel " .
- $FAI::configs{$config}{disklabel}, "cleared1_$disk$pre_all_resize",
- "cleared2_$disk" );
-
- my $prev_id = -1;
- # generate the commands for creating all partitions
- foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
- # reference to the current partition
- my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
- # get the existing id
- my $mapped_id = $part->{maps_to_existing};
-
- # get the new starts and ends
- my $start = $part->{start_byte};
- my $end = $part->{end_byte};
-
- # the type of the partition defaults to primary
- my $part_type = "primary";
- if ($FAI::configs{$config}{disklabel} eq "msdos") {
-
- # change the partition type to extended or logical as appropriate
- if ($part->{size}->{extended} == 1) {
- $part_type = "extended";
- } elsif ($part_id > 4) {
- $part_type = "logical";
- }
- }
-
- my $fs = $part->{filesystem};
- $fs = "" unless defined($fs);
- $fs = "linux-swap" if ($fs eq "swap");
- $fs = "fat32" if ($fs eq "vfat");
- $fs = "fat16" if ($fs eq "msdos");
- $fs = $FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem}
- if ($part->{size}->{preserve} || $part->{size}->{resize});
- $fs = "" if ($fs eq "-");
-
- my $pre = "";
- $pre = ",exist_" . &FAI::make_device_name($disk, $prev_id) if ($prev_id > -1);
- # build a parted command to create the partition
- &FAI::push_command( "parted -s $disk mkpart $part_type $fs ${start}B
- ${end}B", "cleared2_$disk$pre", "exist_" . &FAI::make_device_name($disk,
- $part_id) );
- $prev_id = $part_id;
- }
-
- # set the bootable flag, if requested at all
- if ($FAI::configs{$config}{bootable} > -1) {
- &FAI::push_command( "parted -s $disk set " .
- $FAI::configs{$config}{bootable} . " boot on", "exist_" .
- &FAI::make_device_name($disk, $FAI::configs{$config}{bootable}),
- "boot_set_$disk" );
- }
-}
-
-
-################################################################################
-#
-# @brief Using the configurations from %FAI::configs, a list of commands is
-# built to setup the partitions
-#
-################################################################################
-sub build_disk_commands {
-
- # loop through all configs
- foreach my $config ( keys %FAI::configs ) {
- # no RAID or LVM devices here
- next if ($config eq "RAID" || $config =~ /^VG_./);
- ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
- my $disk = $1; # the device to be configured
-
- # create partitions on non-virtual configs
- &FAI::setup_partitions($config) unless ($FAI::configs{$config}{virtual});
-
- # generate the commands for creating all filesystems
- foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
- # reference to the current partition
- my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
-
- # skip preserved/resized/extended partitions
- next if ($part->{size}->{preserve} == 1
- || $part->{size}->{resize} == 1 || $part->{size}->{extended} == 1);
-
- # create the filesystem on the device
- &FAI::build_mkfs_commands( &FAI::make_device_name($disk, $part_id), $part );
- }
- }
-}
-
-################################################################################
-#
-# @brief Whatever happened, write the previous partition table to the disk again
-#
-################################################################################
-sub restore_partition_table {
-
- # loop through all existing configs
- foreach my $disk (keys %FAI::current_config) {
-
- # write the disklabel again to drop the partition table
- &FAI::execute_command("parted -s $disk mklabel "
- . $FAI::current_config{$disk}{disklabel}, 0, 0);
-
- # generate the commands for creating all partitions
- foreach my $part_id (&numsort(keys %{ $FAI::current_config{$disk}{partitions} })) {
- # reference to the current partition
- my $curr_part = (\%FAI::current_config)->{$disk}->{partitions}->{$part_id};
-
- # get the starts and ends
- my $start = $curr_part->{begin_byte};
- my $end = $curr_part->{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 ($curr_part->{is_extended}) {
- $part_type = "extended";
- } elsif ($part_id > 4) {
- $part_type = "logical";
- }
- }
-
- # restore the partition type, if any
- my $fs = $curr_part->{filesystem};
-
- # build a parted command to create the partition
- &FAI::execute_command("parted -s $disk mkpart $part_type $fs ${start}B ${end}B");
- }
- warn "Partition table of disk $disk has been restored\n";
- }
-
- die "setup-storage failed, but the partition tables have been restored\n";
-}
-
-################################################################################
-#
-# @brief Try to order the queued commands to satisfy all dependencies
-#
-################################################################################
-sub order_commands {
- my @pre_deps = ();
- my $i = 1;
- my $pushed = -1;
-
- while ($i < $FAI::n_c_i) {
- my $all_matched = 1;
- foreach (split(/,/, $FAI::commands{$i}{pre})) {
- next if scalar(grep(m{^$_$}, @pre_deps));
- $all_matched = 0;
- last;
- }
- if ($all_matched) {
- defined($FAI::commands{$i}{post}) and push @pre_deps, split(/,/, $FAI::commands{$i}{post});
- $pushed = -1;
- $i++;
- next;
- }
- if (-1 == $pushed) {
- $pushed = $FAI::n_c_i;
- }
- elsif ($i == $pushed) {
- die "Cannot satisfy pre-depends for " . $FAI::commands{$i}{cmd} . ": " .
- $FAI::commands{$i}{pre} . " -- system left untouched.\n";
- }
- &FAI::push_command( $FAI::commands{$i}{cmd}, $FAI::commands{$i}{pre},
- $FAI::commands{$i}{post} );
- delete $FAI::commands{$i};
- }
-}
-
-1;
-
Copied: trunk/lib/setup-storage/Commands.pm (from rev 4948, people/michael/features/setup_harddisks_2/implementation/lib/commands.pm)
===================================================================
--- trunk/lib/setup-storage/Commands.pm (rev 0)
+++ trunk/lib/setup-storage/Commands.pm 2008-06-12 11:34:19 UTC (rev 4949)
@@ -0,0 +1,909 @@
+#!/usr/bin/perl -w
+
+#*********************************************************************
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# A copy of the GNU General Public License is available as
+# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
+# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html. You
+# can also obtain it by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#*********************************************************************
+
+use strict;
+
+################################################################################
+#
+# @file commands.pm
+#
+# @brief Build the required commands in @FAI::commands using the config stored
+# in %FAI::configs
+#
+# $Id$
+#
+# @author Christian Kern, Michael Tautschnig, Sebastian Hetze, Andreas Schuldei
+# @date Sun Jul 23 16:09:36 CEST 2006
+#
+################################################################################
+
+package FAI;
+
+################################################################################
+#
+# @brief Build the mkfs commands for the partition pointed to by $partition
+#
+# @param $device Device name of the target partition
+# @param $partition Reference to partition in the config hash
+#
+################################################################################
+sub build_mkfs_commands {
+
+ my ($device, $partition) = @_;
+
+ defined ($partition->{filesystem})
+ or &FAI::internal_error("filesystem is undefined");
+ my $fs = $partition->{filesystem};
+
+ return if ($fs eq "-");
+
+ my ($create_options) = $partition->{fs_options} =~ m/createopts="([^"]+)"/;
+ my ($tune_options) = $partition->{fs_options} =~ m/tuneopts="([^"]+)"/;
+ $create_options = $partition->{fs_options} unless $create_options;
+ print "create_options: $create_options\n" if ($FAI::debug && $create_options);
+ print "tune_options: $tune_options\n" if ($FAI::debug && $tune_options);
+
+ # check for encryption requests
+ $device = &FAI::encrypt_device($device, $partition);
+
+ # create the file system with options
+ my $create_tool = "mkfs.$fs";
+ ($fs eq "swap") and $create_tool = "mkswap";
+ ($fs eq "xfs") and $create_options = "$create_options -f" unless ($create_options =~ m/-f/);
+ my $pre_encrypt = "exist_$device";
+ $pre_encrypt = "encrypt_$device" if ($partition->{encrypt});
+ &FAI::push_command( "$create_tool $create_options $device", $pre_encrypt,
+ "has_fs_$device" );
+
+ # possibly tune the file system - this depends on whether the file system
+ # supports tuning at all
+ return unless $tune_options;
+ my $tune_tool;
+ ($fs eq "ext2" || $fs eq "ext3") and $tune_tool = "tune2fs";
+ ($fs eq "reiserfs") and $tune_tool = "reiserfstune";
+ die "Don't know how to tune $fs\n" unless $tune_tool;
+
+ # add the tune command
+ &FAI::push_command( "$tune_tool $tune_options $device", "has_fs_$device",
+ "has_fs_$device" );
+}
+
+################################################################################
+#
+# @brief Encrypt a device and change the device name before formatting it
+#
+# @param $device Original device name of the target partition
+# @param $partition Reference to partition in the config hash
+#
+# @return Device name, may be the same as $device
+#
+################################################################################
+sub encrypt_device {
+
+ my ($device, $partition) = @_;
+
+ return $device unless $partition->{encrypt};
+
+ # encryption requested, rewrite the device name
+ my $enc_dev_name = $device;
+ $enc_dev_name =~ "s#/#_#g";
+ my $enc_dev_short_name = "crypt$enc_dev_name";
+ $enc_dev_name = "/dev/mapper/$enc_dev_short_name";
+ my $keyfile = "$ENV{LOGDIR}/$enc_dev_short_name";
+
+ # generate a key for encryption
+ &FAI::push_command(
+ "head -c 2048 /dev/urandom | head -n 47 | tail -n 46 | od | tee $keyfile",
+ "", "keyfile_$device" );
+
+ # prepare encryption
+ &FAI::push_command(
+ "yes YES | cryptsetup luksFormat $device $keyfile -c aes-cbc-essiv:sha256 -s 256",
+ "exist_$device,keyfile_$device", "crypt_format_$device" );
+ &FAI::push_command(
+ "cryptsetup luksOpen $device $enc_dev_short_name --key-file $keyfile",
+ "crypt_format_$device", "encrypted_$device" );
+
+ # add entries to crypttab
+ push @FAI::crypttab, "$enc_dev_short_name\t$device\t$keyfile\tluks";
+
+ return $enc_dev_name;
+}
+
+################################################################################
+#
+# @brief Set the partition type $t on a device $d. This is a no-op if $d is not
+# a physical device
+#
+# @param $d Device name
+# @param $t Type (e.g., lvm or raid)
+#
+################################################################################
+sub set_partition_type_on_phys_dev {
+
+ my ($d, $t) = @_;
+ my ($i_p_d, $disk, $part_no) = &FAI::phys_dev($d);
+ return unless $i_p_d;
+ # make sure this device really exists (we can't check for the partition
+ # as that may be created later on
+ (-b $disk) or die "Specified disk $disk does not exist in this system!\n";
+ # set the raid flag
+ &FAI::push_command( "parted -s $disk set $part_no $t on", "exist_$d",
+ "type_${t}_$d" );
+}
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to create any RAID devices
+#
+################################################################################
+sub build_raid_commands {
+
+ foreach my $config (keys %FAI::configs) { # loop through all configs
+ # no LVM or physical devices here
+ next if ($config =~ /^VG_./ || $config =~ /^PHY_./);
+ ($config eq "RAID") or &FAI::internal_error("Invalid config $config");
+
+ # create all raid devices
+ foreach my $id (&numsort(keys %{ $FAI::configs{$config}{volumes} })) {
+
+ # keep a reference to the current volume
+ my $vol = (\%FAI::configs)->{$config}->{volumes}->{$id};
+ # the desired RAID level
+ my $level = $vol->{mode};
+
+ warn "RAID implementation is incomplete - preserve is not supported\n" if
+ ($vol->{preserve});
+
+ # prepend "raid", if the mode is numeric-only
+ $level = "raid$level" if ($level =~ /^\d+$/);
+
+ # the list of RAID devices
+ my @devs = keys %{ $vol->{devices} };
+ my @eff_devs = ();
+ my @spares = ();
+ my $pre_req;
+
+ # set proper partition types for RAID
+ foreach my $d (@devs) {
+ if ($vol->{devices}->{$d}->{spare}) {
+ push @spares, $d;
+ } else {
+ push @eff_devs, $d;
+ }
+ # skip devices marked missing
+ next if $vol->{devices}{$d}{missing};
+ &FAI::set_partition_type_on_phys_dev($d, "raid");
+ if ((&FAI::phys_dev($d))[0]) {
+ $pre_req .= ",type_raid_$d";
+ } else {
+ $pre_req .= ",exist_$d";
+ }
+ }
+ my $pre_req_no_comma = $pre_req;
+ $pre_req_no_comma =~ s/^,//;
+ # wait for udev to set up all devices
+ &FAI::push_command( "udevsettle --timeout=10", $pre_req_no_comma,
+ "settle_for_mdadm_create$id" );
+
+ # create the command
+ if (0 == $id) {
+ $pre_req = "settle_for_mdadm_create$id$pre_req";
+ } else {
+ $pre_req = "settle_for_mdadm_create$id,exist_/dev/md" . ( $id - 1 ) . $pre_req;
+ }
+ &FAI::push_command(
+ "yes | mdadm --create /dev/md$id --level=$level --force --run --raid-devices="
+ . scalar(@eff_devs) . " --spare-devices=" . scalar(@spares) . " "
+ . join(" ", @eff_devs) . " " . join(" ", @spares),
+ "$pre_req", "exist_/dev/md$id" );
+
+ # create the filesystem on the volume
+ &FAI::build_mkfs_commands("/dev/md$id",
+ \%{ $FAI::configs{$config}{volumes}{$id} });
+ }
+ }
+}
+
+################################################################################
+#
+# @brief Erase the LVM signature from a list of devices that should be prestine
+# in order to avoid confusion of the lvm tools
+#
+################################################################################
+sub erase_lvm_signature {
+
+ my ($devices_aref) = @_;
+ # first remove the dm_mod module to prevent ghost lvm volumes
+ # from existing
+ # push @FAI::commands, "modprobe -r dm_mod";
+ # zero out (broken?) lvm signatures
+ # push @FAI::commands, "dd if=/dev/zero of=$_ bs=1 count=1"
+ # foreach ( @{$devices_aref} );
+ my $device_list = join (" ", @{$devices_aref});
+ $FAI::debug and print "Erased devices: $device_list\n";
+ &FAI::push_command( "pvremove -ff -y $device_list", "", "pv_sigs_removed" );
+
+ # reload module
+ # push @FAI::commands, "modprobe dm_mod";
+}
+
+################################################################################
+#
+# @brief Create the volume group $config, unless it exists already; if the
+# latter is the case, only add/remove the physical devices
+#
+# @param $config Config entry
+#
+################################################################################
+sub create_volume_group {
+
+ my ($config) = @_;
+ ($config =~ /^VG_(.+)$/) and ($1 ne "--ANY--") or &FAI::internal_error("Invalid config $config");
+ my $vg = $1; # the actual volume group
+
+ # create the volume group, if it doesn't exist already
+ if (!defined ($FAI::current_lvm_config{$vg})) {
+ # create all the devices
+ my @devices = keys %{ $FAI::configs{$config}{devices} };
+ &FAI::erase_lvm_signature(\@devices);
+ &FAI::push_command( "pvcreate $_", "pv_sigs_removed,exist_$_",
+ "pv_done_$_" ) foreach (@devices);
+ # create the volume group
+ my $pre_dev = join(",pv_done_", @devices);
+ $pre_dev =~ s/^,//;
+ &FAI::push_command( "vgcreate $vg " . join (" ", @devices), "$pre_dev",
+ "vg_created_$vg" );
+ # we are done
+ return;
+ }
+
+ # otherwise add or remove the devices for the volume group, run pvcreate
+ # where needed
+ # the list of devices to be created
+ my %new_devs = ();
+
+ # create an undefined entry for each new device
+ @new_devs{ keys %{ $FAI::configs{$config}{devices} } } = ();
+
+ my @new_devices = keys %new_devs;
+
+ # &FAI::erase_lvm_signature( \@new_devices );
+
+ # create all the devices
+ &FAI::push_command( "pvcreate $_", "exist_$_", "pv_done_$_" ) foreach (@new_devices);
+
+ # extend the volume group by the new devices (includes the current ones)
+ my $pre_dev = join(",pv_done_", @new_devices);
+ $pre_dev =~ s/^,//;
+ &FAI::push_command( "vgextend $vg " . join (" ", @new_devices), "$pre_dev",
+ "vg_extended_$vg" );
+
+ # 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 (@new_devices);
+
+ # run vgreduce to get them removed
+ if (scalar (keys %rm_devs)) {
+ $pre_dev = join(",pv_done_", keys %rm_devs);
+ &FAI::push_command( "vgreduce $vg " . join (" ", keys %rm_devs),
+ "vg_extended_$vg$pre_dev", "vg_created_$vg" );
+ } else {
+ &FAI::push_command( "true", "vg_extended_$vg", "vg_created_$vg" );
+ }
+}
+
+################################################################################
+#
+# @brief Create the volume group $config, unless it exists already; if the
+# latter is the case, only add/remove the physical devices
+#
+# @param $config Config entry
+#
+################################################################################
+sub setup_logical_volumes {
+
+ my ($config) = @_;
+ ($config =~ /^VG_(.+)$/) and ($1 ne "--ANY--") or &FAI::internal_error("Invalid config $config");
+ my $vg = $1; # the actual volume group
+
+ my $lv_rm_pre = "";
+ my $lv_resize_pre = "";
+ # 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
+ if (defined ( $FAI::configs{$config}{volumes}{$lv})
+ && ($FAI::configs{$config}{volumes}{$lv}{size}{preserve} == 1)) {
+ $lv_resize_pre .= ",lv_resize_$vg/$lv" if
+ $FAI::configs{$config}{volumes}{$lv}{size}{resize};
+ next;
+ }
+
+ &FAI::push_command( "lvremove -f $vg/$lv", "vg_enabled_$vg", "lv_rm_$vg/$lv");
+ $lv_rm_pre .= ",lv_rm_$vg/$lv";
+ }
+ $lv_rm_pre =~ s/^,//;
+ $lv_resize_pre =~ s/^,//;
+
+ # now create or resize the configured logical volumes
+ foreach my $lv (keys %{ $FAI::configs{$config}{volumes} }) {
+ # reference to the size of the current logical volume
+ my $lv_size = (\%FAI::configs)->{$config}->{volumes}->{$lv}->{size};
+ # skip preserved partitions, but ensure that they exist
+ if ($lv_size->{preserve}) {
+ defined ($FAI::current_lvm_config{$vg}{volumes}{$lv})
+ or die "Preserved volume $vg/$lv does not exist\n";
+ next;
+ }
+
+ # resize the volume
+ if ($lv_size->{resize}) {
+ defined ($FAI::current_lvm_config{$vg}{volumes}{$lv})
+ or die "Resized volume $vg/$lv does not exist\n";
+
+ if ($lv_size->{eff_size} <
+ $FAI::current_lvm_config{$vg}{volumes}{$lv}{size})
+ {
+ &FAI::push_command( "parted -s /dev/$vg/$lv resize 1 0 " . $lv_size->{eff_size} . "B",
+ "vg_enabled_$vg,$lv_rm_pre", "lv_shrink_$vg/$lv" );
+ &FAI::push_command( "lvresize -L " . $lv_size->{eff_size} . " $vg/$lv",
+ "vg_enabled_$vg,$lv_rm_pre,lv_shrink_$vg/$lv", "lv_created_$vg/$lv" );
+ } else {
+ &FAI::push_command( "lvresize -L " . $lv_size->{eff_size} . " $vg/$lv",
+ "vg_enabled_$vg,$lv_rm_pre", "lv_grow_$vg/$lv" );
+ &FAI::push_command( "parted -s /dev/$vg/$lv resize 1 0 " . $lv_size->{eff_size} . "B",
+ "vg_enabled_$vg,$lv_rm_pre,lv_grow_$vg/$lv", "exist_/dev/$vg/$lv" );
+ }
+
+ next;
+ }
+
+ # create a new volume
+ &FAI::push_command( "lvcreate -n $lv -L " . $lv_size->{eff_size} . " $vg",
+ "vg_enabled_$vg,$lv_rm_pre", "exist_/dev/$vg/$lv" );
+
+ # create the filesystem on the volume
+ &FAI::build_mkfs_commands("/dev/$vg/$lv",
+ \%{ $FAI::configs{$config}{volumes}{$lv} });
+ }
+}
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to setup the LVM
+# creates the volume groups, the logical volumes and the filesystems
+#
+################################################################################
+sub build_lvm_commands {
+
+ # loop through all configs
+ foreach my $config (keys %FAI::configs) {
+
+ # no physical devices or RAID here
+ next if ($config =~ /^PHY_./ || $config eq "RAID");
+ ($config =~ /^VG_(.+)$/) or &FAI::internal_error("Invalid config $config");
+ next if ($1 eq "--ANY--");
+ my $vg = $1; # the volume group
+
+ # set proper partition types for LVM
+ &FAI::set_partition_type_on_phys_dev($_, "lvm")
+ foreach (keys %{ $FAI::configs{$config}{devices} });
+ my $type_pre = join(",type_lvm_", keys %{ $FAI::configs{$config}{devices} });
+ $type_pre =~ s/^,//;
+ # wait for udev to set up all devices
+ &FAI::push_command( "udevsettle --timeout=10", "$type_pre",
+ "settle_for_vgchange_$vg" );
+
+ # create the volume group or add/remove devices
+ &FAI::create_volume_group($config);
+ # enable the volume group
+ &FAI::push_command( "vgchange -a y $vg",
+ "settle_for_vgchange_$vg,vg_created_$vg", "vg_enabled_$vg" );
+
+ # perform all necessary operations on the underlying logical volumes
+ &FAI::setup_logical_volumes($config);
+ }
+}
+
+################################################################################
+#
+# @brief Return an ordered list of partitions that must be preserved
+#
+# @param $config Config entry
+#
+################################################################################
+sub get_preserved_partitions {
+
+ my ($config) = @_;
+ ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
+ my $disk = $1; # the device to be configured
+
+ # the list of partitions that must be preserved
+ my @to_preserve = ();
+
+ # find partitions that should be preserved or resized
+ foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
+ # reference to the current partition
+ my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
+ next unless ($part->{size}->{preserve} || $part->{size}->{resize});
+
+ # 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)
+ $part->{maps_to_existing} = $part_id;
+
+ # add $part_id to the list of preserved partitions
+ push @to_preserve, $part_id;
+
+ }
+
+ # sort the list of preserved partitions
+ @to_preserve = &numsort(@to_preserve);
+
+ # add the extended partition as well, if logical partitions must be
+ # preserved; and mark it as resize
+ if ($FAI::configs{$config}{disklabel} eq "msdos") {
+ # we assume there are no logical partitions
+ my $has_logical = 0;
+ my $extended = -1;
+
+ # now check all entries; the array is sorted
+ foreach my $part_id (@to_preserve) {
+ # the extended partition may already be listed; then, the id of the
+ # extended partition must not change
+ if ($FAI::current_config{$disk}{partitions}{$part_id}{is_extended}) {
+ (defined ($FAI::configs{$config}{partitions}{$extended}{size}{extended})
+ && defined ($FAI::current_config{$disk}{partitions}{$extended}{is_extended})
+ && $FAI::configs{$config}{partitions}{$extended}{size}{extended}
+ && $FAI::current_config{$disk}{partitions}{$extended}{is_extended})
+ or die "ID of extended partition changes\n";
+
+ # make sure resize is set
+ $FAI::configs{$config}{partitions}{$part_id}{size}{resize} = 1;
+ $extended = $part_id;
+ last;
+ }
+
+ # there is some logical partition
+ if ($part_id > 4) {
+ $has_logical = 1;
+ last;
+ }
+ }
+
+ # if the extended partition is not listed yet, find and add it now; note
+ # that we need to add the existing one
+ if ($has_logical && -1 == $extended) {
+ foreach my $part_id (&numsort(keys %{ $FAI::current_config{$disk}{partitions} })) {
+
+ # no extended partition
+ next unless
+ $FAI::current_config{$disk}{partitions}{$part_id}{is_extended};
+
+ # find the configured extended partition to set the mapping
+ foreach my $p (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
+ # reference to the current partition
+ my $part = (\%FAI::configs)->{$config}->{partitions}->{$p};
+ next unless $part->{size}->{extended};
+
+ # make sure resize is set
+ $part->{size}->{resize} = 1;
+
+ # store the id for further checks
+ $extended = $p;
+
+ # add a mapping entry to the existing extended partition
+ $part->{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 = &numsort(@to_preserve);
+
+ last;
+ }
+ }
+
+ # a sanity check: if there are logical partitions, the extended must
+ # have been added
+ (0 == $has_logical || -1 != $extended)
+ or &FAI::internal_error("Required extended partition not detected for preserve");
+ }
+
+ return @to_preserve;
+}
+
+################################################################################
+#
+# @brief Recreate the preserved partitions once the partition table has been
+# flushed
+#
+# @param $config Config entry
+# @param $to_preserve Reference to list of preserved/resized partitions
+#
+################################################################################
+sub rebuild_preserved_partitions {
+
+ my ($config, $to_preserve) = @_;
+ ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
+ my $disk = $1; # the device to be configured
+
+ # once we rebuild partitions, their ids are likely to change; this counter
+ # helps keeping track of this
+ my $part_nr = 0;
+
+ # now rebuild all preserved partitions
+ foreach my $part_id (@{$to_preserve}) {
+ # get the existing id
+ my $mapped_id =
+ $FAI::configs{$config}{partitions}{$part_id}{maps_to_existing};
+
+ # get the original starts and ends
+ my $start =
+ $FAI::current_config{$disk}{partitions}{$mapped_id}{begin_byte};
+ my $end =
+ $FAI::current_config{$disk}{partitions}{$mapped_id}{end_byte};
+
+ # the type of the partition defaults to primary
+ my $part_type = "primary";
+ if ( $FAI::configs{$config}{disklabel} eq "msdos" ) {
+
+ # change the partition type to extended or logical as appropriate
+ if ( $FAI::configs{$config}{partitions}{$part_id}{size}{extended} == 1 ) {
+ $part_type = "extended";
+ } elsif ( $part_id > 4 ) {
+ $part_type = "logical";
+ $part_nr = 4 if ( $part_nr < 4 );
+ }
+ }
+
+ # restore the partition type, if any
+ my $fs =
+ $FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem};
+
+ # 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;
+
+ my $post = "exist_" . &FAI::make_device_name($disk, $part_nr);
+ $post = "rebuilt_" . &FAI::make_device_name($disk, $part_nr) if
+ $FAI::configs{$config}{partitions}{$part_id}{size}{resize};
+ # build a parted command to create the partition
+ &FAI::push_command( "parted -s $disk mkpart $part_type $fs ${start}B
+ ${end}B", "cleared1_$disk", $post );
+ }
+}
+
+################################################################################
+#
+# @brief Create the volume group $config, unless it exists already; if the
+# latter is the case, only add/remove the physical devices
+#
+# @param $config Config entry
+#
+################################################################################
+sub setup_partitions {
+
+ my ($config) = @_;
+ ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
+ my $disk = $1; # the device to be configured
+
+ # the list of partitions that must be preserved
+ my @to_preserve = &FAI::get_preserved_partitions($config);
+
+ # 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
+ &FAI::push_command( "parted -s $disk mklabel " .
+ $FAI::configs{$config}{disklabel}, "exist_$disk", "cleared1_$disk" );
+
+ &FAI::rebuild_preserved_partitions($config, \@to_preserve);
+
+ my $pre_all_resize = "";
+
+ # resize partitions while checking for dependencies
+ foreach my $part_id (reverse &numsort(@to_preserve)) {
+ # reference to the current partition
+ my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
+ # get the existing id
+ my $mapped_id = $part->{maps_to_existing};
+ # get the intermediate partition id
+ my $p = $FAI::current_config{$disk}{partitions}{$mapped_id}{new_id};
+ # anything to be done?
+ $pre_all_resize .= ",exist_" . &FAI::make_device_name($disk, $p) unless
+ $part->{size}->{resize};
+ next unless $part->{size}->{resize};
+
+ $pre_all_resize .= ",resized_" . &FAI::make_device_name($disk, $p);
+ my $deps = "";
+ # now walk all other partitions requiring a resize to check for overlaps
+ foreach my $part_other (reverse &numsort(@to_preserve)) {
+ # don't compare to self
+ next if ($part_id == $part_other);
+ # reference to the current partition
+ my $part_other_ref = (\%FAI::configs)->{$config}->{partitions}->{$part_other};
+ # anything to be done?
+ next unless $part_other_ref->{size}->{resize};
+ # get the existing id
+ my $mapped_id_other = $part_other_ref->{maps_to_existing};
+ # get the intermediate partition id
+ my $p_other = $FAI::current_config{$disk}{partitions}{$mapped_id_other}{new_id};
+ # check for overlap
+ next if($part->{begin_byte} >
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte});
+ next if($part->{end_byte} <
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte});
+ # overlap detected - add dependency, but handle extended<->logical with
+ # special care, even though this does not catch all cases (sometimes it
+ # will fail nevertheless
+ if ($part->{size}->{extended} && $part_other > 4) {
+ if($part->{begin_byte} >
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte}) {
+ $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
+ }
+ elsif($part->{end_byte} <
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte}) {
+ $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
+ }
+ }
+ elsif ($part_id > 4 && $part_other_ref->{size}->{extended}) {
+ if($part->{begin_byte} <
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{begin_byte}) {
+ $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
+ }
+ elsif($part->{end_byte} >
+ $FAI::current_config{$disk}{partitions}{$mapped_id_other}{end_byte}) {
+ $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
+ }
+ } else {
+ $deps .= ",resized_" . &FAI::make_device_name($disk, $p_other);
+ }
+ }
+
+ # get the new starts and ends
+ my $start = $part->{start_byte};
+ my $end = $part->{end_byte};
+
+ # build an appropriate command
+ # ntfs requires specific care
+ if ($FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem} eq
+ "ntfs") {
+ # check, whether ntfsresize is available
+ &FAI::in_path("ntfsresize") or die "ntfsresize not found in PATH\n";
+ # ntfs partition can't be moved
+ ($start == $FAI::current_config{$disk}{partitions}{$mapped_id}{begin_byte})
+ or &FAI::internal_error("ntfs partition supposed to move");
+ # ntfsresize requires device names
+ my $eff_size = $part->{size}->{eff_size};
+
+ # wait for udev to set up all devices
+ &FAI::push_command( "udevsettle --timeout=10", "rebuilt_" .
+ &FAI::make_device_name($disk, $p) . $deps, "settle_for_resize_" .
+ &FAI::make_device_name($disk, $p) );
+ &FAI::push_command( "yes | ntfsresize -s $eff_size " .
+ &FAI::make_device_name($disk, $p), "settle_for_resize_" .
+ &FAI::make_device_name($disk, $p), "ntfs_ready_for_rm_" .
+ &FAI::make_device_name($disk, $p) );
+ &FAI::push_command( "parted -s $disk rm $p", "ntfs_ready_for_rm_" .
+ &FAI::make_device_name($disk, $p), "resized_" .
+ &FAI::make_device_name($disk, $p) );
+ } else {
+ &FAI::push_command( "parted -s $disk resize $p ${start}B ${end}B",
+ "rebuilt_" . &FAI::make_device_name($disk, $p), "resized_" .
+ &FAI::make_device_name($disk, $p) );
+ }
+
+ }
+
+ # write the disklabel again to drop the partition table and create a new one
+ # that has the proper ids
+ &FAI::push_command( "parted -s $disk mklabel " .
+ $FAI::configs{$config}{disklabel}, "cleared1_$disk$pre_all_resize",
+ "cleared2_$disk" );
+
+ my $prev_id = -1;
+ # generate the commands for creating all partitions
+ foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
+ # reference to the current partition
+ my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
+ # get the existing id
+ my $mapped_id = $part->{maps_to_existing};
+
+ # get the new starts and ends
+ my $start = $part->{start_byte};
+ my $end = $part->{end_byte};
+
+ # the type of the partition defaults to primary
+ my $part_type = "primary";
+ if ($FAI::configs{$config}{disklabel} eq "msdos") {
+
+ # change the partition type to extended or logical as appropriate
+ if ($part->{size}->{extended} == 1) {
+ $part_type = "extended";
+ } elsif ($part_id > 4) {
+ $part_type = "logical";
+ }
+ }
+
+ my $fs = $part->{filesystem};
+ $fs = "" unless defined($fs);
+ $fs = "linux-swap" if ($fs eq "swap");
+ $fs = "fat32" if ($fs eq "vfat");
+ $fs = "fat16" if ($fs eq "msdos");
+ $fs = $FAI::current_config{$disk}{partitions}{$mapped_id}{filesystem}
+ if ($part->{size}->{preserve} || $part->{size}->{resize});
+ $fs = "" if ($fs eq "-");
+
+ my $pre = "";
+ $pre = ",exist_" . &FAI::make_device_name($disk, $prev_id) if ($prev_id > -1);
+ # build a parted command to create the partition
+ &FAI::push_command( "parted -s $disk mkpart $part_type $fs ${start}B
+ ${end}B", "cleared2_$disk$pre", "exist_" . &FAI::make_device_name($disk,
+ $part_id) );
+ $prev_id = $part_id;
+ }
+
+ # set the bootable flag, if requested at all
+ if ($FAI::configs{$config}{bootable} > -1) {
+ &FAI::push_command( "parted -s $disk set " .
+ $FAI::configs{$config}{bootable} . " boot on", "exist_" .
+ &FAI::make_device_name($disk, $FAI::configs{$config}{bootable}),
+ "boot_set_$disk" );
+ }
+}
+
+
+################################################################################
+#
+# @brief Using the configurations from %FAI::configs, a list of commands is
+# built to setup the partitions
+#
+################################################################################
+sub build_disk_commands {
+
+ # loop through all configs
+ foreach my $config ( keys %FAI::configs ) {
+ # no RAID or LVM devices here
+ next if ($config eq "RAID" || $config =~ /^VG_./);
+ ($config =~ /^PHY_(.+)$/) or &FAI::internal_error("Invalid config $config");
+ my $disk = $1; # the device to be configured
+
+ # create partitions on non-virtual configs
+ &FAI::setup_partitions($config) unless ($FAI::configs{$config}{virtual});
+
+ # generate the commands for creating all filesystems
+ foreach my $part_id (&numsort(keys %{ $FAI::configs{$config}{partitions} })) {
+ # reference to the current partition
+ my $part = (\%FAI::configs)->{$config}->{partitions}->{$part_id};
+
+ # skip preserved/resized/extended partitions
+ next if ($part->{size}->{preserve} == 1
+ || $part->{size}->{resize} == 1 || $part->{size}->{extended} == 1);
+
+ # create the filesystem on the device
+ &FAI::build_mkfs_commands( &FAI::make_device_name($disk, $part_id), $part );
+ }
+ }
+}
+
+################################################################################
+#
+# @brief Whatever happened, write the previous partition table to the disk again
+#
+################################################################################
+sub restore_partition_table {
+
+ # loop through all existing configs
+ foreach my $disk (keys %FAI::current_config) {
+
+ # write the disklabel again to drop the partition table
+ &FAI::execute_command("parted -s $disk mklabel "
+ . $FAI::current_config{$disk}{disklabel}, 0, 0);
+
+ # generate the commands for creating all partitions
+ foreach my $part_id (&numsort(keys %{ $FAI::current_config{$disk}{partitions} })) {
+ # reference to the current partition
+ my $curr_part = (\%FAI::current_config)->{$disk}->{partitions}->{$part_id};
+
+ # get the starts and ends
+ my $start = $curr_part->{begin_byte};
+ my $end = $curr_part->{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 ($curr_part->{is_extended}) {
+ $part_type = "extended";
+ } elsif ($part_id > 4) {
+ $part_type = "logical";
+ }
+ }
+
+ # restore the partition type, if any
+ my $fs = $curr_part->{filesystem};
+
+ # build a parted command to create the partition
+ &FAI::execute_command("parted -s $disk mkpart $part_type $fs ${start}B ${end}B");
+ }
+ warn "Partition table of disk $disk has been restored\n";
+ }
+
+ die "setup-storage failed, but the partition tables have been restored\n";
+}
+
+################################################################################
+#
+# @brief Try to order the queued commands to satisfy all dependencies
+#
+################################################################################
+sub order_commands {
+ my @pre_deps = ();
+ my $i = 1;
+ my $pushed = -1;
+
+ while ($i < $FAI::n_c_i) {
+ my $all_matched = 1;
+ foreach (split(/,/, $FAI::commands{$i}{pre})) {
+ next if scalar(grep(m{^$_$}, @pre_deps));
+ $all_matched = 0;
+ last;
+ }
+ if ($all_matched) {
+ defined($FAI::commands{$i}{post}) and push @pre_deps, split(/,/, $FAI::commands{$i}{post});
+ $pushed = -1;
+ $i++;
+ next;
+ }
+ if (-1 == $pushed) {
+ $pushed = $FAI::n_c_i;
+ }
+ elsif ($i == $pushed) {
+ die "Cannot satisfy pre-depends for " . $FAI::commands{$i}{cmd} . ": " .
+ $FAI::commands{$i}{pre} . " -- system left untouched.\n";
+ }
+ &FAI::push_command( $FAI::commands{$i}{cmd}, $FAI::commands{$i}{pre},
+ $FAI::commands{$i}{post} );
+ delete $FAI::commands{$i};
+ }
+}
+
+1;
+
More information about the Fai-commit
mailing list