#!/usr/bin/pwsh # By MH 2020, MIT-License # TODO: Logging (Sucess, Error), Windows-Support param ( [switch]$smart_disco, [switch]$smart_check, [switch]$smart_with_external ) ### Misc Helper Functions function Get-IsUSBDrive { param ( [ValidateNotNullOrEmpty()] [string]$DeviceFile ) $x = lsblk -d $DeviceFile -o SUBSYSTEMS | Out-String -stream | Select-String usb $x.Matches.Length -gt 0 } ### Smart-Helper Functions class SmartDev { [String]$DeviceFile [String]$Name [String]$Type } # First letter lower-case for compat with zabbix-expected Json class SmartDiscoEntry { [String]$name [String]$type [String]$model [String]$sn [int]$rotations [int]$lbs [int]$isExternal [int]$isSSD } class SmartInfoEntry { [String]$Name [String]$DeviceFile [String]$Health [int]$PowerOnHours [int]$PowerCycleCount [double]$Temperature } function Get-SmartDevsFromScan { $Res=@() foreach ($ln in (smartctl -n standby --scan | Out-String -stream)) { $Obj = New-Object SmartDev $parts=(-Split $ln) $Obj.DeviceFile=$parts[0] $Obj.Name=($parts[0] -Split "/")[2] $Obj.Type=(($ln -Split ",")[1] -Split " ")[1] $Res += $Obj } return $Res } function Get-SmartDisco { $Res = @() foreach ($dev in Get-SmartDevsFromScan) { $isUSB=Get-IsUSBDrive -DeviceFile $dev.DeviceFile $model = "" # TODO Support SSD $isSSD=0 if ( $isUSB -eq $False ) { $j = smartctl -n standby -a $dev.DeviceFile --json | ConvertFrom-Json $model = $j.model_name $sn = $j.serial_number $rotrate = $j.rotation_rate $lbs = $j.logical_block_size } else { $model = $null $sn = $null $rotrate = $null $lbs = $null } $Obj=New-Object SmartDiscoEntry $Obj.name=$dev.Name $Obj.type=$dev.Type $Obj.model=$model $Obj.sn=$sn $Obj.rotations=$rotrate $Obj.lbs=$lbs $Obj.isExternal=$isUSB ? 1 : 0 $Obj.isSSD=$isSSD $Res += $Obj } return $Res } function Get-SmartInfo { param ( [Parameter(Mandatory=$true)] [SmartDev]$device ) $Res = New-Object SmartInfoEntry $Res.DeviceFile = $device.DeviceFile $Res.Name = $device.Name smartctl -s on $device.DeviceFile | out-null $st=$(smartctl -n standby -q errorsonly -H $device.DeviceFile) if ( $st.Trim.Length -eq 0 ) { $st = "OK" } $j=smartctl -n standby -a $device.DeviceFile --json | ConvertFrom-Json $Res.Health = $st $Res.PowerOnHours = $j.power_on_time.hours $Res.PowerCycleCount = $j.power_cycle_count $Res.Temperature = $j.temperature.current return $Res } function Get-SmartInfos { param ( [bool]$withExternalDevs ) $Res = @() foreach ($dev in Get-SmartDevsFromScan) { $isUSB=Get-IsUSBDrive -DeviceFile $dev.DeviceFile if ( $isUSB -And -Not $withExternalDevs ) { continue } smartctl -s on $dev.DeviceFile | out-null $si = Get-SmartInfo -device $dev $Res += $si } return $Res } ### Monitoring functions function Send-SmartDiscoToZabbix { $disco = Get-SmartDisco $jsons=$disco | ConvertTo-Json -Compress zabbix_sender -vv -k "8o_smartcheck.disco_devs" -o '$jsons' -c /etc/zabbix/zabbix_agentd.conf } function Send-SmartInfosToZabbix { param ( [bool]$withExternalDevs ) &{ foreach( $dev in Get-SmartInfos -withExternalDevs $withExternalDevs ) { # Better solution? $dn = $dev.Name Write-Output "- 8o_smartcheck.[${dn}.health] $($dev.Health)" Write-Output "- 8o_smartcheck.[${dn}.power_on_hours] $($dev.PowerOnHours)" Write-Output "- 8o_smartcheck.[${dn}.power_cycle_count] $($dev.PowerCycleCount)" Write-Output "- 8o_smartcheck.[${dn}.temperature] $($dev.Temperature)" } } | zabbix_sender -vv -i - -c /etc/zabbix/zabbix_agentd.conf } $exitcode=0 if ( $smart_disco ) { Write-Output "Doing SMART-Disco " Send-SmartDiscoToZabbix } if ( $smart_check ) { Write-Output "Doing SMART-Check withExternal: ${smart_with_external} " Send-SmartInfosToZabbix -withExternalDevs $smart_with_external } Write-Output "Done" exit $exitcode and here is the original Bash-version: #!/bin/bash # By MH 2020, MIT-License _smart_is_usb() { rem=$(lsblk -d "$1" -o SUBSYSTEMS | grep "usb" | wc -l) if [[ "${rem}" == "0" ]]; then echo 0 else echo 1 fi } _smart_getval() { echo -e "$1" | grep -i "^${2} = " | cut -d "=" -f 2 | cut -d '"' -f 2 } _smart_getval_i() { res=$(echo -e "$1" | grep -i "^${2} = " | cut -d "=" -f 2 | cut -d ' ' -f 2 | tr -d ";") if [[ "$res" == "" ]]; then res=0 fi echo "${res}" } smart_disco() { c=0 devs="" while read ln; do dev=$(echo ${ln} | cut -d " " -f 1 | sed -e "s/\/dev\///g") typ=$(echo ${ln} | cut -d " " -f 3) # Removable # rem=$(lsblk -d /dev/${dev} -o SUBSYSTEMS | grep "usb" | wc -l) rem=$(_smart_is_usb "/dev/${dev}") if [[ "${rem}" == "0" ]]; then smart="$(smartctl -n standby -a /dev/${dev} --json=g)" model="$(_smart_getval "${smart}" "json.model_name")" sn="$(_smart_getval "${smart}" "json.serial_number")" rot="$(_smart_getval_i "${smart}" "json.rotation_rate")" lbs="$(_smart_getval_i "${smart}" "json.logical_block_size")" #SSD: TODO isSSD=0 fi if (( c == 1 )); then devs="${devs},\n" fi c=1 devs="${devs} {\n" if [[ "${rem}" == "0" ]]; then devs="${devs} \"name\": \"${dev}\",\n" devs="${devs} \"type\": \"${typ}\",\n" devs="${devs} \"model\": \"${model}\",\n" devs="${devs} \"sn\": \"${sn}\",\n" devs="${devs} \"rotations\": ${rot},\n" devs="${devs} \"lbs\": ${lbs},\n" devs="${devs} \"isExternal\": ${rem},\n" devs="${devs} \"isSSD\": ${isSSD}\n" else devs="${devs} \"name\": \"${dev}\",\n" devs="${devs} \"isExternal\": ${rem}\n" fi devs="${devs} }" done < <(smartctl -n standby --scan) json2="\n{" json2="${json2} \"devices\": [\n" json2="${json2} ${devs}\n" json2="${json2} ]\n" json2="${json2}}" echo -e "$json2" # Remove newline for use as parameter sentval="$(echo -e $json2)" #json=$(smartctl --json --scan) #json=$(echo "${json}" | sed -e "s/\/dev\///g") #echo $json zabbix_sender -vv -k "8o_smartcheck.disco_devs" -o "${sentval}" -c /etc/zabbix/zabbix_agentd.conf 2>&1 | logger -t "zabbix-smart_disco" } smart_check() { (for dev in $(smartctl --scan | sed -e "s/\/dev\///g" | cut -d " " -f 1 ); do rem=$(_smart_is_usb "/dev/${dev}") if [[ "${rem}" == "1" ]] && [[ "${SMART_WITH_EXTERNAL}" == "0" ]]; then #echo "X" 1>&2 continue; fi smartctl -s on /dev/${dev} > /dev/null status="$(smartctl -n standby -q errorsonly -H /dev/${dev})" if [[ "${status}" == "" ]]; then status="OK" fi smart="$(smartctl -n standby -a /dev/${dev} --json=g)" poh="$(_smart_getval_i "${smart}" "json.power_on_time.hours")" poc="$(_smart_getval_i "${smart}" "json.power_cycle_count")" temp="$(_smart_getval_i "${smart}" "json.temperature.current")" #zabbix_sender -vv -k "8o_smartcheck.[${dev}.health]" -o "${status}" -c /etc/zabbix/zabbix_agentd.conf \ # 2>&1 | logger -t "zabbix-smart_health" echo "- 8o_smartcheck.[${dev}.health] ${status}" echo "- 8o_smartcheck.[${dev}.power_on_hours] ${poh}" echo "- 8o_smartcheck.[${dev}.power_cycle_count] ${poc}" echo "- 8o_smartcheck.[${dev}.temperature] ${temp}" #echo "8o_smartcheck.[${dev}.health]" #echo ${dev} done) | zabbix_sender -vv -i - -c /etc/zabbix/zabbix_agentd.conf 2>&1 | logger -t "zabbix-smart_health" } SMART_WITH_EXTERNAL=0 if [ "$1" == "--smart-with-external" ]; then SMART_WITH_EXTERNAL=1 fi if [ "$1" == "--disco" ]; then smart_disco else smart_check fi