initial commit of file from CVS for e-smith-base on Thu 26 Oct 11:24:52 BST 2023
This commit is contained in:
303
root/sbin/e-smith/console-menu-items/manageRAID.pl
Normal file
303
root/sbin/e-smith/console-menu-items/manageRAID.pl
Normal file
@@ -0,0 +1,303 @@
|
||||
package esmith::console::manageDiskRedundancy;
|
||||
use strict;
|
||||
use warnings;
|
||||
use esmith::console;
|
||||
use Locale::gettext;
|
||||
use Taint::Util;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
use constant DEBUG_MANAGE_RAID => 0;
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $self = {
|
||||
name => gettext("Manage disk redundancy"),
|
||||
order => 45,
|
||||
};
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub name
|
||||
{
|
||||
return $_[0]->{name};
|
||||
}
|
||||
|
||||
sub order
|
||||
{
|
||||
return $_[0]->{order};
|
||||
}
|
||||
|
||||
sub doit
|
||||
{
|
||||
my ($self, $console, $db) = @_;
|
||||
my ($rc, $choice);
|
||||
|
||||
use POSIX qw(strftime);
|
||||
|
||||
SCAN:
|
||||
my $today = strftime "%A %B %e, %Y %H:%M:%S", localtime;
|
||||
my $title = gettext("Disk redundancy status as of") . " " . $today,
|
||||
my $text = gettext("Current RAID status:") . "\n\n" .
|
||||
join("", get_raid_status()) . "\n\n";
|
||||
my %devices = get_raid_details();
|
||||
|
||||
warn $text if DEBUG_MANAGE_RAID;
|
||||
warn "devices: " . Dumper(\%devices) . "\n" if DEBUG_MANAGE_RAID;
|
||||
|
||||
unless (scalar %devices)
|
||||
{
|
||||
$text = gettext("There are no RAID devices configured");
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Determine the status of each array
|
||||
my @unclean = ();
|
||||
my @recovering = ();
|
||||
my @failed = ();
|
||||
my %used_disks = ();
|
||||
|
||||
for my $dev (keys %devices)
|
||||
{
|
||||
$used_disks{$_}++ for (@{$devices{$dev}{UsedDisks}});
|
||||
|
||||
if ($devices{$dev}{FailedDevices} > 0) {
|
||||
push @failed, "$dev => " . $devices{$dev}{FailedDevices};
|
||||
}
|
||||
|
||||
if ($devices{$dev}{State} =~ /recovering|resync/) {
|
||||
push @recovering, "$dev => " . $devices{$dev}{State};
|
||||
next;
|
||||
}
|
||||
|
||||
next if ($devices{$dev}{State} =~ /^(clean|active)\s*$/);
|
||||
push @unclean, "$dev => " . $devices{$dev}{State};
|
||||
}
|
||||
|
||||
warn "unclean: @unclean\n" if DEBUG_MANAGE_RAID;
|
||||
warn "recovering: @recovering\n" if DEBUG_MANAGE_RAID;
|
||||
warn "failed: @failed\n" if DEBUG_MANAGE_RAID;
|
||||
warn "used_disks: " . Dumper(\%used_disks) . "\n" if DEBUG_MANAGE_RAID;
|
||||
|
||||
# Check for any spare disks we could add
|
||||
my %free_disks = map {$_ => 1} get_disks();
|
||||
delete $free_disks{$_} for keys %used_disks;
|
||||
warn "free_disks: " . Dumper(\%free_disks) . "\n" if DEBUG_MANAGE_RAID;
|
||||
|
||||
# Report status and return if recovering
|
||||
if (scalar @recovering)
|
||||
{
|
||||
$text .= gettext("A RAID resynchronization is in progress.");
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Report status and return if arrays are inconsistent
|
||||
if ((scalar @unclean && scalar @unclean != scalar keys %devices) || (scalar @failed && scalar @failed != scalar keys %devices))
|
||||
{
|
||||
$text .= gettext("Only some of the RAID devices are unclean or contain failed disks.") .
|
||||
"\n\n" .
|
||||
gettext("Manual intervention may be required.") . "\n\n";
|
||||
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Report status if arrays are clean and continue if a spare disk is available or there's only one disk in the system
|
||||
unless (scalar @unclean || scalar @failed)
|
||||
{
|
||||
$text .= gettext("All RAID devices are in a clean state.");
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return unless scalar keys %free_disks > 0 || scalar keys %used_disks == 1;
|
||||
}
|
||||
|
||||
# Report status if all arrays are dirty and continue
|
||||
if ((scalar @unclean && scalar @unclean == scalar keys %devices) || (scalar @failed && scalar @failed == scalar keys %devices))
|
||||
{
|
||||
$text .= gettext("All RAID devices are in an unclean state or contain failed disks.");
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
}
|
||||
|
||||
# Summarise disk assignments
|
||||
my $disk_status = gettext("Current disk status:") . "\n\n";
|
||||
$disk_status .= gettext("Installed disks") . ": " .
|
||||
join(" ", get_disks()) . "\n";
|
||||
$disk_status .= gettext("Used disks") . ": " .
|
||||
join(" ", keys %used_disks) . "\n";
|
||||
$disk_status .= gettext("Free disks") . ": " .
|
||||
join(" ", keys %free_disks) . "\n";
|
||||
|
||||
# Spare disk scenarios
|
||||
# Scenario 1 - single disk or degraded array with no spare - warn
|
||||
if ((scalar @unclean || scalar @failed || scalar keys %used_disks == 1) && scalar keys %free_disks == 0)
|
||||
{
|
||||
$text = $disk_status .
|
||||
"\n\n" .
|
||||
gettext("To ensure continued redundancy, please shut down, install another drive of the same capacity and then return to this screen.");
|
||||
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Scenario 2 - no spares and not degraded so something has gone wrong
|
||||
if (scalar keys %free_disks == 0)
|
||||
{
|
||||
$text = $disk_status .
|
||||
"\n\n" .
|
||||
gettext("Your RAID devices are in an inconsistent state, and no spare drives were detected. You may need to manually remove a failed drive from your arrays using mdadm.");
|
||||
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Scenario 3 - multiple spares
|
||||
if (scalar keys %free_disks > 1)
|
||||
{
|
||||
$text = $disk_status .
|
||||
"\n\n" .
|
||||
gettext("Multiple spare drives have been detected. This utility can only add one drive at a time. Please either shut down and remove all but one of your spare drives, or configure your array manually.");
|
||||
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
return;
|
||||
}
|
||||
|
||||
# Scenario 4 - single spare ready to add
|
||||
$text = $disk_status .
|
||||
"\n\n" .
|
||||
gettext("There is an unused disk drive in your system. Do you want to add it to the existing RAID array(s)?") .
|
||||
"\n\n" .
|
||||
gettext("WARNING: ALL DATA ON THE NEW DISK WILL BE DESTROYED!") .
|
||||
"\n";
|
||||
|
||||
($rc, $choice) = $console->yesno_page(title => $title, text => $text, defaultno => 1);
|
||||
return unless ($rc == 0);
|
||||
|
||||
my @cmd = ("/sbin/e-smith/add_drive_to_raid", "-f", join("", keys %free_disks));
|
||||
my $cmd_out = qx( @cmd 2>&1 );
|
||||
untaint $cmd_out;
|
||||
|
||||
if ($? == 0) {
|
||||
$text = "\nSuccessfully added /dev/" . join("", keys %free_disks) . " to RAID!";
|
||||
} else {
|
||||
$text = gettext("The command failed:") . " @cmd" .
|
||||
"\n\n" . $cmd_out . "\n\n";
|
||||
}
|
||||
|
||||
($rc, $choice) = $console->message_page(title => $title, text => $text);
|
||||
goto SCAN;
|
||||
}
|
||||
|
||||
sub get_raid_status
|
||||
{
|
||||
die gettext("Couldn't open") . " /proc/mdstat:$!\n"
|
||||
unless (open(MDSTAT, "/proc/mdstat"));
|
||||
|
||||
my @mdstat;
|
||||
|
||||
while (<MDSTAT>)
|
||||
{
|
||||
push @mdstat, "$1\n" if (/(.*\w.*)/);
|
||||
}
|
||||
close MDSTAT;
|
||||
return @mdstat;
|
||||
}
|
||||
|
||||
sub get_raid_details
|
||||
{
|
||||
my @devices = ();
|
||||
|
||||
die gettext("Couldn't call") . " mdadm: $!\n"
|
||||
unless open(MDADM, "/sbin/mdadm --detail --scan|");
|
||||
|
||||
while (<MDADM>)
|
||||
{
|
||||
push @devices, $1 if ( m:ARRAY (/dev/md/\w+): )
|
||||
}
|
||||
close MDADM;
|
||||
|
||||
my %devices;
|
||||
|
||||
for my $dev (@devices)
|
||||
{
|
||||
die gettext("Couldn't call") . " mdadm --detail $dev: $!\n"
|
||||
unless open(MDADM, "/sbin/mdadm --detail $dev|");
|
||||
|
||||
while ( <MDADM> )
|
||||
{
|
||||
if ( /\s*(.*)\s+:\s+(\d+)\s+\(.*\)\s*/ )
|
||||
{
|
||||
my ($key, $value) = ($1, $2);
|
||||
$key =~ s/\s//g;
|
||||
|
||||
# Allow for different mdadm output formats for DeviceSize
|
||||
$key =~ s/UsedDevSize/DeviceSize/;
|
||||
|
||||
$devices{$dev}{$key} = $value;
|
||||
}
|
||||
elsif ( /\s*(.*)\s+:\s+(.*)\s*/ )
|
||||
{
|
||||
my ($key, $value) = ($1, $2);
|
||||
$key =~ s/\s//g;
|
||||
$devices{$dev}{$key} = $value;
|
||||
}
|
||||
|
||||
if ( m:\s+(\d+)\s+(\d+)\s+(\d+).*/dev/([\w\/]+): )
|
||||
{
|
||||
$devices{$dev}{$1} = $_;
|
||||
my $used_disk = $4;
|
||||
if (/(rd|ida|cciss|i2o)\//) {
|
||||
$used_disk =~ s/p\d+$//;
|
||||
} else {
|
||||
$used_disk =~ s/\d+//;
|
||||
}
|
||||
push (@{$devices{$dev}{UsedDisks}}, $used_disk);
|
||||
}
|
||||
}
|
||||
close MDADM;
|
||||
}
|
||||
|
||||
return %devices;
|
||||
}
|
||||
|
||||
sub get_partitions
|
||||
{
|
||||
die gettext("Couldn't read") . " /proc/partitions: $!\n"
|
||||
unless open (PARTITIONS, "/proc/partitions");
|
||||
|
||||
my %parts;
|
||||
|
||||
while (<PARTITIONS>)
|
||||
{
|
||||
if ( /\s+(\d+)\s+(\d+)\s+(\d+)\s+([\w\/]+)\s+/ )
|
||||
{
|
||||
my $name = $4;
|
||||
|
||||
$parts{$name}{major} = $1;
|
||||
$parts{$name}{minor} = $2;
|
||||
$parts{$name}{blocks} = $3;
|
||||
}
|
||||
}
|
||||
close PARTITIONS;
|
||||
|
||||
return %parts;
|
||||
}
|
||||
|
||||
sub get_disks
|
||||
{
|
||||
my %parts = get_partitions();
|
||||
|
||||
my @disks;
|
||||
|
||||
for (keys %parts)
|
||||
{
|
||||
push @disks, $_ unless (/[0-9]$/);
|
||||
push @disks, $_ if (/(rd|ida|cciss|i2o)\// && ! /p\d+$/);
|
||||
}
|
||||
|
||||
return @disks;
|
||||
}
|
||||
|
||||
return new esmith::console::manageDiskRedundancy;
|
Reference in New Issue
Block a user