Monitoring Hard Drive Health with Smartctl and PHP

Hard drives are nothing more than consumables. At least, that’s certainly true when you’re dealing with large disk arrays, especially those operating under heavy load. In my case, these are enclosures without a hardware RAID controller. So, I’d like to share my implementation of a simple disk health monitoring system that helps me spot the moment when it’s time to replace a «tired» element in the array before everything else starts to fall apart. (And, by the way, I’ve had this happen before.) Sometimes I’m completely too lazy to look for ready-made solutions when I need to create something simple and straightforward. In this case, it’s easier for me to just roll my own. At least then I’ll know for sure who’s to blame for the non-working state. ;)) The goal: create a simple page with a clear display of the status of all hard drives from all managed storage devices. We’ll need the php5-cli, php5-curl, and smartmontools packages. Now let’s look at the script for parsing information about HDD #!/usr/bin/php $sname = 'server_name'; exec("ls /dev/sd*|grep -v [0-9]", $out); #Ищем что-нибудь похожее на винты foreach($out as $entry){ $arr[] = smart($entry); } $data = base64_encode(json_encode($arr)); # Тут, внезапно, мы отправляем данные, получение которых описано ниже. # Дабы не просрать ничего, упаковываем всё в json и в base64 if( $curl = curl_init() ) { curl_setopt($curl, CURLOPT_URL, 'http://192.168.10.10/hdd.php'); curl_setopt($curl, CURLOPT_RETURNTRANSFER,true); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, "server=".$sname."&status=".$data."&s=".md5($data)); $out = curl_exec($curl); echo $out; curl_close($curl); } function smart($dev,$atr = true){ # непосредственно чтение и парсинг $cmd = '/usr/sbin/smartctl -AHi '.$dev; # получаем данные об устройстве exec($cmd, $out); # designate array cells $kw = 'Serial Number'; $serial = array_values(preg_grep("/{$kw}/i",$out)); $kw = 'SMART overall-health self-assessment test result'; $health = array_values(preg_grep("/{$kw}/i",$out)); $serial = substr(strrchr(str_replace(' ', '', $serial[0]),":"),1); $health = substr(strrchr(str_replace(' ', '', $health[0]),":"),1); $diag['DEV'] = str_replace('/dev/','',$dev); $diag['SN'] = $serial; $diag['HT'] = $health; $kw = 'ATTRIBUTE_NAME'; $lstr = array_keys(preg_grep("/{$kw}/i",$out)); $table = explode(' ', preg_replace('/\s\s+/', ' ', ltrim($out[$lstr[0]]))); $i=0; # reading smart attributes foreach($out as $string){ if($i > $lstr[0]){ $arr = explode(' ', preg_replace('/\s\s+/', ' ', ltrim($string))); if(count($arr) == count ($table)) $attribs[] = array_combine($table,$arr); } $i++; } if($atr == true) $diag['ATTR'] = $attribs; return $diag; } ?> [quads id=2] As you can see, the script collects all the data about the found block devices (without their partitions) and sends it to the server where the second part, that is, the receiving script, will be located. And here is its content if($_POST['s'] == md5($_POST['status'])){ $body = "

".$_POST['server']." HDD monitor ( ".date('d.m.Y H:i:s')." )
"; $data = base64_decode($_POST['status']); $data = json_decode($data); foreach($data as $hdd){ if($hdd->HT == "PASSED"){ $color = 'green'; } else { $color = 'red'; } $info =''; foreach($hdd->ATTR as $attrib){ $info .= $attrib->ATTRIBUTE_NAME.": ".$attrib->RAW_VALUE."\n"; } $body .= "

DEV: ".$hdd->DEV."
".$hdd->SN."

"; } $body .= "

"; file_put_contents("/var/www/status/".$_POST['server'].".html", $body); } ?> When requested by the so-called sensor, the receiver creates pages in the status folder with the names servers taken from the request itself. Add the "sensor" script to CRON and enjoy. THAT'S IT! :)