|
#!/bin/bash |
|
set -euf -o pipefail |
|
|
|
IPMITOOL_ARGS=() |
|
DEFAULT_FANMODE="optimal" |
|
DEFAULT_FANSPEED=( 100 100 ) |
|
DEFAULT_FANSLEEP=5 |
|
AUTOFAN_SOURCES=() |
|
AUTOFAN_PENALTY=() |
|
AUTOFAN_BASE=() |
|
AUTOFAN_MIN=() |
|
AUTOFAN_MAX=() |
|
|
|
# Optional: Override configuration vars |
|
if [[ -f /usr/local/etc/supermicro.cfg ]] |
|
then |
|
#shellcheck disable=SC1091 |
|
source /usr/local/etc/supermicro.cfg |
|
fi |
|
|
|
function argc |
|
{ |
|
if [[ "$1" -lt "$2" ]] |
|
then usage |
|
fi |
|
} |
|
|
|
function usage |
|
{ |
|
cat << EOF >&2 |
|
Usage: $0 <COMMAND> [<ARGS> ...] |
|
|
|
Display current fan settings: |
|
$0 fan |
|
|
|
Update fan settings: |
|
$0 fan standard|optimal|pue[2]|heavy[io] |
|
$0 fan full [<PERCENT> ...] |
|
$0 fan default |
|
|
|
Adjust fan settings based on sensor temperatures: |
|
$0 fan auto |
|
EOF |
|
exit 1 |
|
} |
|
|
|
function stderr |
|
{ |
|
printf '%s\n' "$@" 1>&2 |
|
} |
|
|
|
function intval |
|
{ |
|
local i |
|
|
|
i=${1//[[:space:]]} |
|
i=${i#"${i%%[!0]*}"} |
|
|
|
printf '%d' "$i" 2>/dev/null || : |
|
} |
|
|
|
function hex2int |
|
{ |
|
printf '%d' "0x${1//[[:space:]]}" 2>/dev/null || : |
|
} |
|
|
|
function int2hex |
|
{ |
|
printf '0x%02x' "$1" |
|
} |
|
|
|
function execIPMI |
|
{ |
|
ipmitool -c "${IPMITOOL_ARGS[@]}" "$@" |
|
} |
|
|
|
function sendRawCommand |
|
{ |
|
execIPMI raw "$@" | sed '/./,$!d' |
|
} |
|
|
|
function getFanModeName |
|
{ |
|
local name="Standard" |
|
|
|
case "$1" in |
|
1) name="Full" ;; |
|
2) name="Optimal" ;; |
|
3) name="PUE2" ;; |
|
4) name="HeavyIO" ;; |
|
esac |
|
|
|
printf '%s' "$name" |
|
} |
|
|
|
function getFanModeKey |
|
{ |
|
getFanModeName "${1,,}" |
|
} |
|
|
|
function getFanModeID |
|
{ |
|
local -i fanmode=0 |
|
|
|
case "${1,,}" in |
|
full) fanmode=1 ;; |
|
optimal) fanmode=2 ;; |
|
pue*) fanmode=3 ;; |
|
heavy*|io) fanmode=4 ;; |
|
esac |
|
|
|
printf '%d' "$fanmode" |
|
} |
|
|
|
function getFanMode |
|
{ |
|
hex2int "$(sendRawCommand 0x30 0x45 0x00)" |
|
} |
|
|
|
function setFanMode |
|
{ |
|
local -i fanmode |
|
local -i current |
|
local hex |
|
|
|
fanmode=$(getFanModeID "$1") |
|
current=$(getFanMode) |
|
|
|
if (( current != fanmode )) |
|
then |
|
hex=$(int2hex "$fanmode") |
|
stderr "Setting new fan mode: $hex ($(getFanModeName "$fanmode"))" |
|
sendRawCommand 0x30 0x45 0x01 "$hex" |
|
|
|
if [[ "$fanmode" -eq "$(getFanModeID "full")" ]] |
|
then |
|
sleep "$DEFAULT_FANSLEEP" |
|
fi |
|
else |
|
stderr "Current fan mode not changed: $(int2hex "$current") ($(getFanModeName "$current"))" |
|
fi |
|
} |
|
|
|
function getFanSpeed |
|
{ |
|
hex2int "$(sendRawCommand 0x30 0x70 0x66 0x00 "$(int2hex "$1")")" |
|
} |
|
|
|
function setFanSpeed |
|
{ |
|
local zone |
|
local value |
|
|
|
zone=$(int2hex "$1") |
|
value=$(int2hex "$2") |
|
|
|
stderr "Setting fan speed of zone $zone: $value ($2%)" |
|
sendRawCommand 0x30 0x70 0x66 0x01 "$zone" "$value" |
|
} |
|
|
|
function loadTemperatureValues |
|
{ |
|
local result k v |
|
result=$(execIPMI sdr type Temperature) |
|
declare -g -A temperatureValues |
|
|
|
while IFS=',' read -r k v _ |
|
do |
|
k=${k,,} |
|
k=${k/% */} |
|
k=${k/%[a-z][0-9]/} |
|
v=$(intval "$v") |
|
|
|
if [[ -z "${temperatureValues[$k]-}" || "${temperatureValues[$k]}" -lt "$v" ]] |
|
then |
|
temperatureValues[$k]="$v" |
|
fi |
|
done <<< "$result" |
|
|
|
for k in "${AUTOFAN_SOURCES[@]}" |
|
do |
|
k=${k,,} |
|
k=${k/% */} |
|
k=${k/%[a-z][0-9]/} |
|
v=$("smCustomSource${k^^}" ||:) |
|
v=$(intval "$v") |
|
|
|
if [[ -z "${temperatureValues[$k]-}" || "${temperatureValues[$k]}" -lt "$v" ]] |
|
then |
|
temperatureValues[$k]="$v" |
|
fi |
|
done |
|
} |
|
|
|
function processFanSpeedRules |
|
{ |
|
local mode |
|
mode=$1 |
|
shift |
|
|
|
local key |
|
key=$1 |
|
shift |
|
|
|
local -i value |
|
value=$(intval "$1") |
|
shift |
|
|
|
for line in "$@" |
|
do |
|
IFS=" " read -r sensor temperature zones <<< "$line" |
|
temperature=$(intval "$temperature") |
|
sensor=${sensor,,} |
|
|
|
if [[ "$sensor" == "$key" ]] |
|
then |
|
if [[ "$mode" == "min" && "$value" -le "$temperature" ]] || [[ "$mode" == "max" && "$value" -ge "$temperature" ]] |
|
then |
|
if [[ "$mode" == "min" && -n "${AUTOFAN_BASE[*]-}" ]] |
|
then saveFanSpeedZones "${AUTOFAN_BASE[*]}" |
|
else saveFanSpeedZones "$zones" |
|
fi |
|
|
|
fanSpeedFixed=1 |
|
return |
|
fi |
|
|
|
printf -v "fanSpeed${mode^}Temp" '%s' "$temperature" |
|
read -r -a "fanSpeedZones${mode^}" <<< "$zones" |
|
return |
|
fi |
|
done |
|
} |
|
|
|
function saveFanSpeedZones |
|
{ |
|
local -i i; i=0 |
|
for speed in $1 |
|
do |
|
if [[ "$speed" -gt 0 ]] && [[ -z "${fanSpeedZones[$i]-}" || "${fanSpeedZones[$i]}" -lt "$speed" ]] |
|
then |
|
saveFanSpeedZone "$i" "$speed" |
|
fi |
|
|
|
i=$(( i + 1 )) |
|
done |
|
} |
|
|
|
function saveFanSpeedZone |
|
{ |
|
fanSpeedZones[$1]=$2 |
|
fanSpeedChanged=1 |
|
} |
|
|
|
function adjustFanSpeeds |
|
{ |
|
declare -g -i fanSpeedFixed |
|
declare -g -A fanSpeedZones |
|
declare -g -a fanSpeedZonesMin |
|
declare -g -a fanSpeedZonesMax |
|
declare -g -i fanSpeedChanged |
|
declare -g -i fanSpeedPenalty |
|
declare -i fanSpeedMinTemp |
|
declare -i fanSpeedMaxTemp |
|
local -i time |
|
local -i percent |
|
local -i value |
|
local -i start |
|
local -i end |
|
local final |
|
local dow |
|
local s |
|
local e |
|
|
|
fanSpeedPenalty=100 |
|
time=$(intval "$(date +%H%M)") |
|
for rule in "${AUTOFAN_PENALTY[@]}" |
|
do |
|
IFS=" " read -r dow s e percent final <<< "$rule" |
|
start=$(intval "$s") |
|
end=$(intval "$e") |
|
|
|
if [[ "$dow" != "*" ]] |
|
then |
|
dow=$(intval "$dow") |
|
|
|
if [[ "$dow" -ne "$(date +%u)" && "$dow" -ne "$(date +%w)" ]] |
|
then |
|
continue |
|
fi |
|
fi |
|
|
|
if [[ "$end" -lt "$start" && "$time" -le "$end" ]] \ |
|
|| [[ "$end" -lt "$start" && "$time" -ge "$start" ]] \ |
|
|| [[ "$time" -ge "$start" && "$time" -le "$end" ]] |
|
then |
|
fanSpeedPenalty=$(intval "$percent") |
|
|
|
if [[ -n "$final" ]] |
|
then |
|
break |
|
fi |
|
fi |
|
done |
|
|
|
fanSpeedChanged=0 |
|
loadTemperatureValues |
|
|
|
for key in "${!temperatureValues[@]}" |
|
do |
|
value=${temperatureValues[$key]} |
|
|
|
fanSpeedZonesMin=() |
|
fanSpeedZonesMax=() |
|
fanSpeedMinTemp=0 |
|
fanSpeedMaxTemp=0 |
|
fanSpeedFixed=0 |
|
|
|
processFanSpeedRules min "$key" "$value" "${AUTOFAN_MIN[@]}" |
|
processFanSpeedRules max "$key" "$value" "${AUTOFAN_MAX[@]}" |
|
|
|
if [[ "$fanSpeedFixed" -ne 1 ]] |
|
then |
|
c1=$(intval "${#fanSpeedZonesMin[@]}") |
|
c2=$(intval "${#fanSpeedZonesMax[@]}") |
|
c=$(( c1 > c2 ? c1 : c2 )) |
|
|
|
for (( i=0; i < c; i++ )) |
|
do |
|
minSpeed=$(intval "${fanSpeedZonesMin[$i]-0}") |
|
maxSpeed=$(intval "${fanSpeedZonesMax[$i]-0}") |
|
|
|
if [[ "$minSpeed" -gt 0 && "$maxSpeed" -gt 0 ]] |
|
then |
|
speed=$(bc -l <<< "result = ($maxSpeed - $minSpeed) / 100 * (100 / (($fanSpeedMaxTemp - $fanSpeedMinTemp) / ($value - $fanSpeedMinTemp))) + $minSpeed; scale=0; result/1") |
|
|
|
if [[ "$speed" -gt 0 ]] && [[ -z "${fanSpeedZones[$i]-}" || "${fanSpeedZones[$i]}" -lt "$speed" ]] |
|
then |
|
saveFanSpeedZone "$i" "$speed" |
|
fi |
|
fi |
|
done |
|
fi |
|
done |
|
|
|
if (( fanSpeedChanged )) |
|
then |
|
setFanMode "full" |
|
for key in "${!fanSpeedZones[@]}" |
|
do |
|
value=$(bc -l <<< "result = ${fanSpeedZones[$key]-0} / 100 * $fanSpeedPenalty; if ( result < 1 ) result=1; scale=0; result/1") |
|
value=$(( value > 100 ? 100 : value )) |
|
|
|
if [[ "$value" -gt 0 ]] |
|
then |
|
setFanSpeed "$key" "$value" |
|
fi |
|
done |
|
fi |
|
} |
|
|
|
argc $# 1 |
|
MODE=${1,,} |
|
shift |
|
|
|
case "$MODE" in |
|
|
|
fan) |
|
if [[ "$#" -eq 0 ]] |
|
then |
|
mode=$(getFanMode) |
|
echo "Current fan mode: $(int2hex "$mode") ($(getFanModeName "$mode"))" |
|
|
|
c=${#DEFAULT_FANSPEED[@]} |
|
for (( i=0; i < c; i++ )) |
|
do |
|
speed=$(getFanSpeed "$i") |
|
echo "Fan speed of zone $(int2hex "$i"): $(int2hex "$speed") ($speed%)" |
|
done |
|
|
|
exit 0 |
|
fi |
|
|
|
#argc $# 1 |
|
mode=${1,,} |
|
|
|
if [[ "$mode" == "default" ]] |
|
then |
|
setFanMode "$DEFAULT_FANMODE" |
|
exit $? |
|
elif [[ "$mode" == "auto" ]] |
|
then |
|
adjustFanSpeeds |
|
exit $? |
|
fi |
|
|
|
fanmode=$(getFanModeID "$mode") |
|
setFanMode "$(getFanModeKey "$fanmode")" |
|
shift |
|
|
|
if [[ "$fanmode" -eq $(getFanModeID "full") ]] |
|
then |
|
FANZONES=( "$@" ) |
|
c=$(( ${#FANZONES[@]} - 1 )) |
|
|
|
if (( c >= 0 )) |
|
then |
|
for (( i=0; i <= c; i++ )) |
|
do |
|
v=$(intval "${FANZONES[i]}") |
|
|
|
if (( v > 0 )) |
|
then |
|
setFanSpeed "$i" "${FANZONES[i]}" |
|
fi |
|
done |
|
else |
|
c=$(( ${#DEFAULT_FANSPEED[@]} - 1 )) |
|
for (( i=0; i <= c; i++ )) |
|
do |
|
setFanSpeed "$i" "${DEFAULT_FANSPEED[i]}" |
|
done |
|
fi |
|
fi |
|
;; |
|
|
|
*) |
|
usage |
|
;; |
|
esac |