#!/usr/bin/perl -w use strict; use POSIX qw(ceil floor); package FAI; sub get_current_disks { foreach my $disk (@FAI::disks) { if ( !( $disk =~ m{^/} ) ) { $disk = "/dev/" . $disk; } $FAI::current_config{$disk}{"partitions"} = {}; my @parted_print = (); while (1) { my $error = &FAI::execute_command( "$FAI::parted_binary $disk unit TB print", \@parted_print, 0 ); if ( $error eq "parted_2" ) #this error happens when the disk has no disk label. then, parted provides no information about the disk { $error = &FAI::execute_command("$FAI::parted_binary $disk mklabel msdos"); next; } if ( $error ne "" ) { my $response = &FAI::get_error( $error, "response" ); if ( $response eq "die" ) { die &FAI::get_error( $error, "message" ); } if ( $response eq "warn" ) { warn &FAI::get_error( $error, "message" ); } } last; } my $parted_fs_start = 0; my $parted_fs_end = 0; foreach my $line (@parted_print) { if ( $line =~ /^Disk geometry/ ) { next; } elsif ( $line =~ /^Disk label type: (.*)$/ ) { $FAI::current_config{$disk}{"disklabel"} = $1; next; } elsif ( $line =~ /^Number/ ) { $parted_fs_start = 0; $parted_fs_end = 0; my @chars = split( "", $line ); foreach my $char (@chars) { $parted_fs_end++; if ( $char eq "F" ) { $parted_fs_start = $parted_fs_end; } elsif ( $char eq "m" && $parted_fs_start > 0 ) { last; } } $parted_fs_start--; $parted_fs_end -= $parted_fs_start; } else { $line =~ /^(\d+)/; my $id = $1; $line =~ /^.{$parted_fs_start}(.{$parted_fs_end})/; my $fs = $1; $FAI::current_config{$disk}{"partitions"}{$id}{"filesystem"} = $fs; } } @parted_print = (); &FAI::execute_command_std( "$FAI::parted_binary $disk unit B print", \@parted_print, 0 ); foreach my $line (@parted_print) { if ( $FAI::current_config{$disk}{"disklabel"} eq "msdos" ) { if ( $line =~ /^(\d+)*\s+(\d+)B\s+(\d+)B\s+(\d+)B\s+(primary|logical|extended)/i ) { $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; if ( $5 eq "extended" ) { $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 1; } else { $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 0; } } } else { if ( $line =~ /^(\d+)*\s+(\d+)B\s+(\d+)B\s+(\d+)B/i ) { $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; $FAI::current_config{$disk}{"partitions"}{$1}{"is_extended"} = 0; } } if ( $line =~ /^Disk geometry for.*(\d+)B - (\d+)B/i ) { $FAI::current_config{$disk}{"begin_byte"} = $1; $FAI::current_config{$disk}{"end_byte"} = $2; } } @parted_print = (); &FAI::execute_command_std( "$FAI::parted_binary $disk unit chs print", \@parted_print, 0 ); foreach my $line (@parted_print) { if ( $line =~ /^(\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; } if ( $line =~ /^Disk geometry for.*(\d+),(\d+),(\d+) - (\d+),(\d+),(\d+)/i ) { $FAI::current_config{$disk}{"begin_cylinder"} = $1; $FAI::current_config{$disk}{"begin_head"} = $2; $FAI::current_config{$disk}{"begin_sector"} = $3; $FAI::current_config{$disk}{"end_cylinder"} = $4; $FAI::current_config{$disk}{"end_head"} = $5; $FAI::current_config{$disk}{"end_sector"} = $6; } } } } sub compute_sizes { foreach my $config ( keys %FAI::configs ) { if ( $config eq "RAID" || $config =~ /^VG_/ ) { # compute the sizes of lvms next; } elsif ( $config =~ /^PHY_(.*)$/ ) { my $extended = -1; my $disk = $1; my $redist_space = 0; my $min_req_space = 0; my $min_req_total_space = 0; my @redist_list = (); my $redist_factor = 0; my $range_start = 0; foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) { if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"extended"} == 1 ) { ( $extended == -1 ) or die "more than 1 extended partition\n"; ( $part_id <= 4 ) or die "extended partition wouldn't be a primary one\n"; $extended = $part_id; $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} = 0; } elsif ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"preserve"} == 0 ) { if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =~ /^(\d+%?)-(\d+%?)$/ ) { my $start = $1; my $end = $2; if ( $start =~ /^(\d+)%$/ ) { $start = POSIX::floor( $FAI::current_config{$disk}{"end_byte"} * $1 / 100 ); } else { $start = $start * 1024.0 * 1024.0; } if ( $end =~ /^(\d+)%$/ ) { $end = POSIX::ceil( $FAI::current_config{$disk}{"end_byte"} * $1 / 100 ); } else { $end = $end * 1024.0 * 1024.0; } $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} = $start . "-" . $end; if ( $end == $start ) { $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"eff_size"} = $start; } else { $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"eff_size"} = -1; $redist_space += $end - $start; } $min_req_space += $start; $min_req_total_space += $start; } else { die "invalid range\n"; } } else { if ( !defined( $FAI::current_config{$disk}{"partitions"}{$part_id} ) ) { die "$part_id can't be preserved, it does not exist.\n"; } $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} = $FAI::current_config{$disk}{"partitions"}{$part_id}{"count_byte"}; $min_req_total_space += $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"}; if ( scalar(@redist_list) > 0 ) { my $redist_factor = ( $FAI::current_config{$disk}{"partitions"}{$part_id} {"begin_byte"} - 1 - $range_start - $min_req_space ) / $redist_space; print "redist factor is $redist_factor\n"; foreach my $part_id (@redist_list) { ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"eff_size"} == -1 ) or die "internal error\n"; if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"range"} =~ /^(\d+%?)-(\d+%?)$/ ) { my $start = $1; my $end = $2; $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"eff_size"} = POSIX::floor( $start + ( ( $end - $start ) * $redist_factor ) ); } else { die "invalid range\n"; } } } @redist_list = (); $range_start = $FAI::current_config{$disk}{"partitions"}{$part_id}{"end_byte"} + 1; $redist_space = 0; $min_req_space = 0; } } if ( scalar(@redist_list) > 0 ) { my $redist_factor = ( $FAI::current_config{$disk}{"end_byte"} - 1 - $range_start - $min_req_space ) / $redist_space; print "redist factor is $redist_factor\n"; foreach my $part_id (@redist_list) { ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"} {"eff_size"} == -1 ) or die "internal error\n"; if ( $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"range"} =~ /^(\d+%?)-(\d+%?)$/ ) { my $start = $1; my $end = $2; $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"} = POSIX::floor( $start + ( ( $end - $start ) * $redist_factor ) ); } else { die "invalid range\n"; } } } if ( $min_req_total_space > $FAI::current_config{$disk}{"end_byte"} ) { die "Disk is too small - at least $min_req_space is required\n"; } if ( $FAI::configs{$config}{"disklabel"} ne "msdos" && $extended > -1 ) { die "extended partitions are not supported by this disklabel\n"; } if ( $FAI::configs{$config}{"disklabel"} eq "msdos" && $extended > -1 ) { my $extended_size = 0; foreach my $part_id ( sort keys %{ $FAI::configs{$config}{"partitions"} } ) { next if ( $part_id <= 4 ); $extended_size += $FAI::configs{$config}{"partitions"}{$part_id}{"size"}{"eff_size"}; } $FAI::configs{$config}{"partitions"}{$extended}{"size"}{"eff_size"} = $extended_size; } } else { die "Internal error (invalid config entry $config).\n"; } } } 1;