diff --git a/snmp/smart b/snmp/smart index 44b7a31..1f68ccb 100755 --- a/snmp/smart +++ b/snmp/smart @@ -1,5 +1,5 @@ #!/usr/bin/env perl -#Copyright (c) 2017, Zane C. Bowers-Hadley +#Copyright (c) 2019, Zane C. Bowers-Hadley #All rights reserved. # #Redistribution and use in source and binary forms, with or without modification, @@ -38,14 +38,17 @@ will be /etc/snmp/smart.config. Alternatively you can also specific a config via Anything starting with a # is comment. The format for variables is $variable=$value. Empty lines are ignored. Spaces and tabes at either the start or end of a line are ignored. Any -line with out a = or # are treated as a disk. +line with out a matched variable or # are treated as a disk. #This is a comment cache=/var/cache/smart smartctl=/usr/local/sbin/smartctl useSN=0 ada0 - ada1 + da5 /dev/da5 -d sat + twl0,0 /dev/twl0 -d 3ware,0 + twl0,1 /dev/twl0 -d 3ware,1 + twl0,2 /dev/twl0 -d 3ware,2 The variables are as below. @@ -54,8 +57,13 @@ The variables are as below. useSN = If set to 1, it will use the disks SN for reporting instead of the device name. 1 is the default. 0 will use the device name. +A disk line is can be as simple as just a disk name under /dev/. Such as in the config above +The line "ada0" would resolve to "/dev/ada0" and would be called with no special argument. If +a line has a space in it, everything before the space is treated as the disk name and is what +used for reporting and everything after that is used as the argument to be passed to smartctl. + If you want to guess at the configuration, call it with -g and it will print out what it thinks -it should be. +it should be. =cut @@ -73,10 +81,9 @@ my $useSN=1; $Getopt::Std::STANDARD_HELP_VERSION = 1; sub main::VERSION_MESSAGE { - print "SMART SNMP extend 0.0.0\n"; + print "SMART SNMP extend 0.1.0\n"; }; - sub main::HELP_MESSAGE { print "\n". "-u Update '".$cache."'\n". @@ -108,75 +115,70 @@ if ( defined( $opts{g} ) ){ $cache='cache='.$cache."\n"; } - my %found_disks; - - #check for drives named /dev/sd* - my @matches=glob('/dev/sd*'); - @matches=grep(!/[0-9]/, @matches); - my $matches_int=0; - while ( defined( $matches[$matches_int] ) ){ - my $device=$matches[$matches_int]; - system( $smartctl.' -A '.$device.' > /dev/null' ); - if ( $? == 0 ){ - $device =~ s/\/dev\///; - $found_disks{$device}=1; - } - - $matches_int++; - } - - #check for drives named /dev/ada* - @matches=glob('/dev/ada*'); - @matches=grep(!/[ps]/, @matches); - $matches_int=0; - while ( defined( $matches[$matches_int] ) ){ - my $device=$matches[$matches_int]; - system( $smartctl.' -A '.$device.' > /dev/null' ); - if ( $? == 0 ){ - $device =~ s/\/dev\///; - $found_disks{$device}=1; - } - - $matches_int++; - } - - #check for drives named /dev/da* - @matches=glob('/dev/da*'); - @matches=grep(!/[ps]/, @matches); - $matches_int=0; - while ( defined( $matches[$matches_int] ) ){ - my $device=$matches[$matches_int]; - system( $smartctl.' -A '.$device.' > /dev/null' ); - if ( $? == 0 ){ - $device =~ s/\/dev\///; - $found_disks{$device}=1; - } - - $matches_int++; - } + # used for checking if a disk has been found more than once + my %found_disks_names; + my @argumentsA; #have smartctl scan and see if it finds anythings not get found my $scan_output=`$smartctl --scan-open`; my @scan_outputA=split(/\n/, $scan_output); + + # remove non-SMART devices sometimes returned @scan_outputA=grep(!/ses[0-9]/, @scan_outputA); # not a disk, but may or may not have SMART attributes @scan_outputA=grep(!/pass[0-9]/, @scan_outputA); # very likely a duplicate and a disk under another name - $matches_int=0; - while ( defined( $scan_outputA[$matches_int] ) ){ - my $device=$scan_outputA[$matches_int]; - $device =~ s/ .*//; - system( $smartctl.' -A '.$device.' > /dev/null' ); - if ( $? == 0 ){ - $device =~ s/\/dev\///; - $found_disks{$device}=1; - } - - $matches_int++; + @scan_outputA=grep(!/cd[0-9]/, @scan_outputA); # CD drive + if ( $^O eq 'freebsd' ){ + @scan_outputA=grep(!/sa[0-9]/, @scan_outputA); # tape drive + @scan_outputA=grep(!/ctl[0-9]/, @scan_outputA); # CAM target layer + }elsif( $^O eq 'linux' ){ + @scan_outputA=grep(!/st[0-9]/, @scan_outputA); # SCSI tape drive + @scan_outputA=grep(!/ht[0-9]/, @scan_outputA); # ATA tape drive } - + + # make the first pass, figuring out what all we have and trimming comments + foreach my $arguments ( @scan_outputA ){ + my $name = $arguments; + + $arguments =~ s/ \#.*//; # trim the comment out of the argument + $name =~ s/ .*//; + $name =~ s/\/dev\///; + if (defined( $found_disks_names{$name} )){ + $found_disks_names{$name}++; + }else{ + $found_disks_names{$name}=0; + } + + push( @argumentsA, $arguments ); + + } + + # second pass, putting the lines together + my %current_disk; + my $drive_lines=''; + foreach my $arguments ( @argumentsA ){ + my $name = $arguments; + $name =~ s/ .*//; + $name =~ s/\/dev\///; + + if ( $found_disks_names{$name} == 0 ){ + # If no other devices, just name it after the base device. + $drive_lines=$drive_lines.$name." ".$arguments."\n"; + }else{ + # if more than one, start at zero and increment, apennding comma number to the base device name + if (defined( $current_disk{$name} )){ + $current_disk{$name}++; + }else{ + $current_disk{$name}=0; + } + $drive_lines=$drive_lines.$name.",".$current_disk{$name}." ".$arguments."\n"; + } + + } + print "useSN=0\n".'smartctl='.$smartctl."\n". - $cache. - join( "\n", keys(%found_disks) )."\n"; - + $cache. + $drive_lines; + exit 0; } @@ -200,27 +202,32 @@ my @configA=split(/\n/, $config_file); my $configA_int=0; while ( defined( $configA[$configA_int] ) ){ my $line=$configA[$configA_int]; + chomp($line); $line=~s/^[\t\s]+//; $line=~s/[\t\s]+$//; my ( $var, $val )=split(/=/, $line, 2); + my $matched; if ( $var eq 'cache' ){ $cache=$val; + $matched=1; } - + if ( $var eq 'smartctl' ){ $smartctl=$val; + $matched=1; } if ( $var eq 'useSN' ){ $useSN=$val; + $matched=1; } - + if ( !defined( $val ) ){ - push(@disks, $var); + push(@disks, $line); } - + $configA_int++; } @@ -244,11 +251,22 @@ if ( ! defined( $opts{u} ) ){ } my $toReturn=''; -my $int=0; -while ( defined($disks[$int]) ) { - my $disk=$disks[$int]; +foreach my $line ( @disks ){ + my $disk; + my $name; + if ( $line =~ /\ / ){ + ($name, $disk)=split(/\ /, $line, 2); + }else{ + $disk=$line; + $name=$line; + } my $disk_sn=$disk; - my $output=`$smartctl -A /dev/$disk`; + my $output; + if ( $disk =~ /\// ){ + $output=`$smartctl -A $disk`; + }else{ + $output=`$smartctl -A /dev/$disk`; + } my %IDs=( '5'=>'null', '10'=>'null', @@ -267,7 +285,7 @@ while ( defined($disks[$int]) ) { '231'=>'null', '233'=>'null', ); - + my @outputA=split( /\n/, $output ); my $outputAint=0; while ( defined($outputA[$outputAint]) ) { @@ -281,7 +299,7 @@ while ( defined($disks[$int]) ) { my $id=$lineA[0]; # single int raw values - if ( + if ( ( $id == 5 ) || ( $id == 10 ) || ( $id == 173 ) || @@ -319,8 +337,8 @@ while ( defined($disks[$int]) ) { ) { my ( $temp )=split(/\ /, $raw); $IDs{$id}=$temp; - } - + } + } $outputAint++; @@ -337,9 +355,9 @@ while ( defined($disks[$int]) ) { my $short=scalar grep(/Short/, @outputA); my $conveyance=scalar grep(/Conveyance/, @outputA); my $selective=scalar grep(/Selective/, @outputA); - + # get the drive serial number, if needed - my $disk_id=$disk; + my $disk_id=$name; if ( $useSN ){ while (`$smartctl -i /dev/$disk` =~ /Serial Number:(.*)/g) { $disk_id = $1; @@ -351,7 +369,6 @@ while ( defined($disks[$int]) ) { .','.$IDs{'190'} .','.$IDs{'194'}.','.$IDs{'196'}.','.$IDs{'197'}.','.$IDs{'198'}.','.$IDs{'199'}.','.$IDs{'231'}.','.$IDs{'233'}.','. $completed.','.$interrupted.','.$read_failure.','.$unknown_failure.','.$extended.','.$short.','.$conveyance.','.$selective."\n"; - $int++; } if ( ! $noWrite ){