diff --git a/smart-status/CHANGELOG b/smart-status/CHANGELOG index 8e105a2bf..4f2655fd1 100644 --- a/smart-status/CHANGELOG +++ b/smart-status/CHANGELOG @@ -9,4 +9,4 @@ ATA mode is now used by default on CentOS and Redhat Enterprise versions 5 and a ---- Changes since 1.440 ---- SCSI drives are visible in the System and Server Status module. The SMART status monitor now has an option to only alert if the error count on a drive has increased. -Improved support for 3ware drives, so that the underlying disks are now detected and can be reported on separately. +Improved support for 3ware and HP RAID devices, so that the underlying disks are now detected and can be reported on separately. diff --git a/smart-status/action.cgi b/smart-status/action.cgi index fa70184d8..52c62646a 100755 --- a/smart-status/action.cgi +++ b/smart-status/action.cgi @@ -9,7 +9,7 @@ $mode = $in{'short'} ? "short" : @drives = &list_smart_disks_partitions(); ($d) = grep { $_->{'device'} eq $in{'drive'} && - $_->{'3ware'} == $in{'3ware'} } @drives; + $_->{'subdisk'} == $in{'subdisk'} } @drives; print &text($mode."_doing", $d->{'desc'}),"\n"; if ($mode eq "short") { ($ok, $out) = &short_test($in{'drive'}, $d); diff --git a/smart-status/index.cgi b/smart-status/index.cgi index 067c62e34..63c509ba9 100755 --- a/smart-status/index.cgi +++ b/smart-status/index.cgi @@ -36,14 +36,16 @@ if ($config{'mode'} == 1 || $in{'drive'}) { print &ui_form_start("index.cgi"); print "$text{'index_show'}\n"; print &ui_select("drive", $in{'drive'}, - [ map { [ $_->{'device'}, + [ map { [ $_->{'device'}.":".$_->{'subdisk'}, $_->{'desc'}.($_->{'model'} ? " ($_->{'model'})" : "") ] } @drives ], 1, 0, 0, 0, "onChange='form.submit()'"); print &ui_submit($text{'index_ok'}),"\n"; print &ui_form_end(); if ($in{'drive'}) { - ($d) = grep { $_->{'device'} eq $in{'drive'} } @drives; + ($device, $subdisk) = split(/:/, $in{'drive'}); + ($d) = grep { $_->{'device'} eq $device && + $_->{'subdisk'} == $subdisk } @drives; &show_drive($d); } } @@ -61,9 +63,13 @@ sub show_drive { print &ui_form_start("action.cgi"); print &ui_hidden("drive", $_[0]->{'device'}); -print &ui_hidden("3ware", $_[0]->{'3ware'}); -print &ui_table_start(&text('index_drive', "$_[0]->{'device'}"), - undef, 2); +print &ui_hidden("subdisk", $_[0]->{'subdisk'}); +local $h = defined($_[0]->{'subdisk'}) ? + &text('index_drivesub', "$_[0]->{'device'}", + $_[0]->{'subdisk'}) : + &text('index_drive', "$_[0]->{'device'}"); +print &ui_table_start($h, "width=100%", 4, + [ "width=30%", undef, "width=30%", undef ]); local $st = &get_drive_status($_[0]->{'device'}, $_[0]); print &ui_table_row($text{'index_desc'}, $_[0]->{'desc'}); @@ -87,18 +93,32 @@ if ($st->{'support'} && $st->{'enabled'}) { ""); } print &ui_table_row($text{'index_check'}, - $st->{'check'} ? $text{'yes'} : "$text{'no'}"); - if ($config{'attribs'}) { - print &ui_table_hr(); - local $a; - foreach $a (@{$st->{'attribs'}}) { - next if ($a->[0] =~ /UDMA CRC Error Count/i); # too long - print &ui_table_row($a->[0], - $a->[2] =~ /^\s*(seconds|minutes|hours|days|months|years|weeks)\s*/i || !$a->[2] ? $a->[1]." ".$a->[2] : $a->[2]); - } - } + $st->{'check'} ? $text{'yes'} : + "$text{'no'}"); } print &ui_table_end(); + +# Show extra attributes +if ($config{'attribs'} && @{$st->{'attribs'}}) { + print &ui_hidden_table_start($text{'index_attrs'}, "width=100%", 2, + "attrs", 1, [ "width=30%" ]); + foreach my $a (@{$st->{'attribs'}}) { + next if ($a->[0] =~ /UDMA CRC Error Count/i); # too long + print &ui_table_row($a->[0], + $a->[2] =~ /^\s*(seconds|minutes|hours|days|months|years|weeks)\s*/i || !$a->[2] ? $a->[1]." ".$a->[2] : $a->[2]); + } + print &ui_hidden_table_end(); + } + +# Show raw data from smartctl +if ($config{'attribs'} && $st->{'raw'}) { + print &ui_hidden_table_start($text{'index_raw'}, "width=100%", 2, + "raw", @{$st->{'attribs'}} ? 0 : 1); + print &ui_table_row(undef, + "
".&html_escape($st->{'raw'})."
", 2); + print &ui_hidden_table_end(); + } + if ($st->{'support'} && $st->{'enabled'}) { print &ui_form_end([ [ "short", $text{'index_short'} ], [ "ext", $text{'index_ext'} ], diff --git a/smart-status/lang/en b/smart-status/lang/en index 578fb4240..a9b261599 100644 --- a/smart-status/lang/en +++ b/smart-status/lang/en @@ -6,6 +6,7 @@ index_show=Show status of drive: index_ok=Show index_eidescsi=No IDE or SCSI drives were found on your system. index_drive=Status of drive $1 +index_drivesub=Status of drive $1, disk $2 index_desc=Location index_size=Drive size index_model=Make and model @@ -17,6 +18,8 @@ index_ext=Extended Self Test index_data=Data Collection Test index_errors=Errors logged index_ecount=$1 errors detected +index_attrs=Additional SMART attributes +index_raw=Full SMART status report index_return=module index monitor_type=SMART Drive Check diff --git a/smart-status/smart-status-lib.pl b/smart-status/smart-status-lib.pl index 1d4d59d7c..9b4008622 100644 --- a/smart-status/smart-status-lib.pl +++ b/smart-status/smart-status-lib.pl @@ -33,41 +33,65 @@ May include faked-up 3ware devices =cut sub list_smart_disks_partitions { -local @drives = grep { $_->{'type'} eq 'ide' || - $_->{'type'} eq 'scsi' } &fdisk::list_disks_partitions(); local @rv; local $threecount = 0; -foreach my $d (@drives) { - if ($d->{'type'} eq 'scsi' && $d->{'model'} =~ /3ware/i) { +foreach my $d (&fdisk::list_disks_partitions()) { + if (($d->{'type'} eq 'scsi' || $d->{'type'} eq 'raid') && + $d->{'model'} =~ /3ware/i) { # Actually a 3ware RAID device .. but we want to probe the # underlying real disks, so add fake devices for them - my $count = &count_3ware_disks($d); + my $count = &count_subdisks($d, "3ware"); for(my $i=0; $i<$count; $i++) { push(@rv, { 'device' => '/dev/twe'.$threecount, 'prefix' => '/dev/twe'.$threecount, 'desc' => '3ware physical disk '.$i, 'type' => 'scsi', - '3ware' => $i, + 'subtype' => '3ware', + 'subdisk' => $i, }); } $threecount++; } - else { + elsif ($d->{'device'} =~ /^\/dev\/cciss\/(.*)$/) { + # HP Smart Array .. add underlying disks + my $count = &count_subdisks($d, "cciss"); + for(my $i=0; $i<$count; $i++) { + push(@rv, { 'device' => $d->{'device'}, + 'prefix' => $d->{'device'}, + 'desc' => 'HP Smart Array physical disk '.$i, + 'type' => 'scsi', + 'subtype' => 'cciss', + 'subdisk' => $i, + }); + } + } + elsif ($d->{'type'} eq 'scsi' || $d->{'type'} eq 'ide') { + # Some other disk push(@rv, $d); } } return sort { $a->{'device'} cmp $b->{'device'} || - $a->{'3ware'} <=> $b->{'3ware'} } @rv; + $a->{'subdisk'} <=> $b->{'subdisk'} } @rv; } -=head2 count_3ware_disks(&drive) +=head2 count_subdisks(&drive, type) -Returns the number of physical disks on some 3ware RAID device. +Returns the number of sub-disks for a hardware RAID device, by calling +smartctl on them until failure. =cut -sub count_3ware_disks +sub count_subdisks { -return 4; # XXX +local ($d, $type) = @_; +local $count = 0; +while(1) { + local $cmd = "$config{'smartctl'} -d $type,$count ". + quotemeta($d->{'device'}); + &execute_command($cmd); + last if ($?); + $count++; + } +return $count; } =head2 get_drive_status(device-name, [&drive]) @@ -157,6 +181,7 @@ if ($config{'attribs'}) { # Fetch other attributes local ($lastline, @attribs); local $doneknown = 0; + $rv{'raw'} = ""; open(OUT, "$config{'smartctl'} $extra_args -a $qd |"); while() { s/\r|\n//g; @@ -188,6 +213,7 @@ if ($config{'attribs'}) { $rv{'errors'} = $1; } $lastline = $_; + $rv{'raw'} .= $_."\n"; } close(OUT); $rv{'attribs'} = \@attribs; @@ -292,8 +318,8 @@ if (!$drive) { &list_smart_disks_partitions(); } local $extra_args = $config{'extra'}; -if ($drive && defined($drive->{'3ware'})) { - $extra_args .= " -d 3ware,$drive->{'3ware'}"; +if ($drive && defined($drive->{'subdisk'})) { + $extra_args .= " -d $drive->{'subtype'},$drive->{'subdisk'}"; } elsif ($config{'ata'}) { $extra_args .= " -d ata"; diff --git a/smart-status/status_monitor.pl b/smart-status/status_monitor.pl index 0cf68d182..72a7adca5 100644 --- a/smart-status/status_monitor.pl +++ b/smart-status/status_monitor.pl @@ -24,7 +24,7 @@ if (!-r $_[1]->{'drive'}) { } local @drives = &list_smart_disks_partitions(); local ($d) = grep { $_->{'device'} eq $_[1]->{'drive'} && - $_->{'3ware'} eq $_[1]->{'3ware'} } @drives; + $_->{'subdisk'} eq $_[1]->{'subdisk'} } @drives; if (!$d) { # Not in list?! return { 'up' => -1, @@ -74,13 +74,13 @@ sub status_monitor_dialog local $rv; local @drives = &list_smart_disks_partitions(); local ($inlist) = grep { $_->{'device'} eq $_[1]->{'drive'} && - $_->{'3ware'} eq $_[1]->{'3ware'} } @drives; + $_->{'subdisk'} eq $_[1]->{'subdisk'} } @drives; $inlist = 1 if (!$_[1]->{'drive'}); $rv .= &ui_table_row($text{'monitor_drive'}, &ui_select("drive", !$_[1]->{'drive'} ? $drives[0]->{'device'} : - $inlist ? $inlist->{'drive'}.':'.$inlist->{'3ware'} : + $inlist ? $inlist->{'drive'}.':'.$inlist->{'subdisk'} : undef, - [ (map { [ $_->{'device'}.':'.$_->{'3ware'}, + [ (map { [ $_->{'device'}.':'.$_->{'subdisk'}, $_->{'desc'}.($_->{'model'} ? " ($_->{'model'})" : "") ] } @drives), [ "", $text{'monitor_other'} ] ]). @@ -98,11 +98,11 @@ return $rv; sub status_monitor_parse { if ($_[2]->{'drive'}) { - ($_[1]->{'drive'}, $_[1]->{'3ware'}) = split(/:/, $_[2]->{'drive'}); + ($_[1]->{'drive'}, $_[1]->{'subdisk'}) = split(/:/, $_[2]->{'drive'}); } else { $_[1]->{'drive'} = $_[2]->{'other'}; - $_[1]->{'3ware'} = undef; + $_[1]->{'subdisk'} = undef; $_[1]->{'drive'} =~ /^\S+$/ || &error($text{'monitor_edrive'}); } $_[1]->{'errors'} = $_[2]->{'errors'};