R710-Fan-Control/smart-intercept-spindown
Tim Connors 7c831f1f52 Make use of smart-intercept-spindown and smartctlnvme
Migrate smart-intercept-spindown and smartctlnvme into this repo from
my ansible-initial-server-setup playbooks, so they can be published in
a self-contained repo.  Ansible then distributes these files anyway
via including this repo.

This enables us to get rid of the spindown test code we duplicated
here, plus ensure we're actually distributing smartctlnvme
2025-09-13 04:20:15 +10:00

132 lines
5.8 KiB
Bash
Executable file

#!/bin/bash
# For those disks like some of my SAS drives where `smartctl
# --nocheck=standby` and `hdparm -C` actually spins up the disk,
# detect whether a disk is spun down with sdparm commands, and don't
# invoke smartctl if it is. Otherwise, some of our other disks like
# our 16TB SATA drives on Dell Perc controllers give unreliable status to `sdparm
# --command=sense`, but `smartctl --nocheck=standby` does seem to
# reliably detect their spundown state anyway, so we continue to
# supply `--nocheck=standby` to cover this case.
# While we're here, we might as well try transform the mimimal output
# smartctl gives us on SAS drives into something we can parse too, so
# we can at least get temperatures for hddtemp_smartctl's use. We
# also detect nvme drives and pass off a request to smartctlnvme for
# it to give us smartctl style output on them.
final_arg="${@: -1}"
convert_sas() {
read_initial=true
read_data=false
device_model=
device_product=
local exit_code=0
while read line ; do
case "$read_initial,$read_data,$line" in
true,false,*DATA\ SECTION*)
read_initial=false
read_data=true
echo "Device Model: $device_vendor $device_product"
echo "$line"
echo "ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE"
;;
true,false,Vendor:*)
device_vendor="$( echo "$line" | sed 's/Vendor: *//' )"
;;
true,false,Product:*)
device_product="$( echo "$line" | sed 's/Product: *//' )"
;;
true*)
echo "$line"
;;
false,true,SMART\ Health\ Status:*)
if [[ "$line" == *OK ]] ; then
exit_code=0
else
exit_code=8
fi
# echo "$line"
;;
false,true,Current\ Drive\ Temperature:*)
temp="$( echo "$line" | sed 's/.*Temperature: *// ; s/ C$//' )"
;;
false,true,Drive\ Trip\ Temperature:*)
temp_threshold="$( echo "$line" | sed 's/.*Drive Trip Temperature: *// ; s/ C$//' )"
hundred_minus_temp="$( printf "%03.f" "$( echo "100 - $temp" | bc -l )" )"
echo "194 Temperature_Celsius null $hundred_minus_temp $hundred_minus_temp 000 Old_age Always - $temp (Min/Max 0/$temp_threshold)"
;;
false,true,Accumulated\ power\ on\ time,\ hours:minutes:*)
hours="$( echo "$line" | sed 's/.*hours:minutes // ; s/:.*//' )"
# minutes="$( echo "$line" | sed 's/.*hours:minutes // ; s/.*://' )"
# hours="$( echo "scale=2 ; $hours + $minutes/60" | bc -l )"
echo "9 Power_On_Hours null 100 100 000 Old_age Always - $hours"
;;
false,true,Specified\ cycle\ count\ over\ device\ lifetime:*)
start_stop_cycles_threshold="$( echo "$line" | sed 's/.*Specified cycle count over device lifetime: *//' )"
;;
false,true,Accumulated\ start-stop\ cycles:*)
start_stop_cycles="$( echo "$line" | sed 's/.*Accumulated start-stop cycles: *//' )"
hundred_minus_cycles="$( printf "%03.f" "$( echo "100 - 100*$start_stop_cycles/$start_stop_cycles_threshold" | bc -l )" )"
echo "12 Power_Cycle_Count null $hundred_minus_cycles $hundred_minus_cycles 000 Pre-fail Always - $start_stop_cycles"
;;
false,true,Specified\ load-unload\ count\ over\ device\ lifetime:*)
load_unload_cycles_threshold="$( echo "$line" | sed 's/.*Specified load-unload count over device lifetime: *//' )"
;;
false,true,Accumulated\ load-unload\ cycles:*)
load_unload_cycles="$( echo "$line" | sed 's/.*Accumulated load-unload cycles: *//' )"
hundred_minus_cycles="$( printf "%03.f" "$( echo "100 - 100*$load_unload_cycles/$load_unload_cycles_threshold" | bc -l )" )"
echo "193 Load_Cycle_Count null $hundred_minus_cycles $hundred_minus_cycles 000 Pre-fail Always - $load_unload_cycles"
;;
false,true,Elements\ in\ grown\ defect\ list:*)
defects="$( echo "$line" | sed 's/.*Elements in grown defect list: //' )"
echo "5 Reallocated_Sector_Ct null 100 100 001 Pre-fail Always - $defects"
;;
false,true,Non-medium\ error\ count:*)
errors="$( echo "$line" | sed 's/.*Non-medium error count: //' )"
echo "1 Raw_Read_Error_Rate null 100 100 001 Pre-fail Always - $errors"
;;
esac
done <<< "$1"
return $exit_code
}
if [ "1" = --version ] ; then
# allow munin smart_ check to test for support of --nocheck=standby to ensure it doesnt' then run hdparm
exec smartctl "$@"
fi
if sdparm --command=sense "$final_arg" 2>/dev/null | grep Standby; then
# exit code success rather than code 2 like smartctl by default
# Could do similar output with hdparm -C, but smartctl
# --nocheck=standby,0 correctly detects that
exit
fi
smart_output=$( smartctl --nocheck=standby,0 "$@" )
exit_code=$?
case "$smart_output" in
*Transport*protocol:*SAS*)
convert_sas "$smart_output"
convert_exit=$?
# FIXME: should work out a proper exit code precedence rule
if (( $convert_exit > $exit_code )) ; then
exit $convert_exit
fi
exit $exit_code
;;
*NVMe*Version*)
# to transform smartctl nvme output to more resemble
# traditional output so existing munin plugins can monitor it
exec /usr/local/bin/smartctlnvme "$@"
;;
*)
echo "$smart_output"
;;
esac
exit $exit_code