converted from bash to perl, and made it servoing to setpoints based on component temperatures

This commit is contained in:
Tim Connors 2019-12-20 17:40:17 +11:00
parent a61a333f48
commit b9d75f2b9a
8 changed files with 198 additions and 75 deletions

View file

@ -1,23 +0,0 @@
#!/usr/bin/env bash
# ----------------------------------------------------------------------------------
# Script for setting manual fan speed to 2160 RPM (on my R710)
#
# Requires:
# ipmitool  apt-get install ipmitool
# slacktee.sh  https://github.com/course-hero/slacktee
# ----------------------------------------------------------------------------------
# IPMI SETTINGS:
# Modify to suit your needs.
# DEFAULT IP: 192.168.0.120
IPMIHOST=10.0.100.20
IPMIUSER=root
IPMIPW=calvin
IPMIEK=0000000000000000000000000000000000000000
printf "Activating manual fan speeds! (2160 RPM)" | systemd-cat -t R710-IPMI-TEMP
echo "Activating manual fan speeds! (2160 RPM)" | slacktee.sh -t "R710-IPMI-TEMP [$(hostname)]"
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x00
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x02 0xff 0x09

View file

@ -1,41 +0,0 @@
#!/bin/bash
# ----------------------------------------------------------------------------------
# Script for checking the temperature reported by the ambient temperature sensor,
# and if deemed too high send the raw IPMI command to enable dynamic fan control.
#
# Requires:
# ipmitool  apt-get install ipmitool
# slacktee.sh  https://github.com/course-hero/slacktee
# ----------------------------------------------------------------------------------
# IPMI SETTINGS:
# Modify to suit your needs.
# DEFAULT IP: 192.168.0.120
IPMIHOST=10.0.100.20
IPMIUSER=root
IPMIPW=calvin
IPMIEK=0000000000000000000000000000000000000000
# TEMPERATURE
# Change this to the temperature in celcius you are comfortable with.
# If the temperature goes above the set degrees it will send raw IPMI command to enable dynamic fan control
MAXTEMP=27
# This variable sends a IPMI command to get the temperature, and outputs it as two digits.
# Do not edit unless you know what you do.
TEMP=$(ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK sdr type temperature |grep Ambient |grep degrees |grep -Po '\d{2}' | tail -1)
if [[ $TEMP > $MAXTEMP ]];
then
printf "Warning: Temperature is too high! Activating dynamic fan control! ($TEMP C)" | systemd-cat -t R710-IPMI-TEMP
echo "Warning: Temperature is too high! Activating dynamic fan control! ($TEMP C)" | /usr/bin/slacktee.sh -t "R710-IPMI-TEMP [$(hostname)]"
ipmitool -I lanplus -H $IPMIHOST -U $IPMIUSER -P $IPMIPW -y $IPMIEK raw 0x30 0x30 0x01 0x01
else
# healthchecks.io
curl -fsS --retry 3 https://hchk.io/XXX >/dev/null 2>&1
printf "Temperature is OK ($TEMP C)" | systemd-cat -t R710-IPMI-TEMP
echo "Temperature is OK ($TEMP C)"
fi

View file

@ -0,0 +1,165 @@
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw( apply );
my $static_speed_low=0x04;
my $static_speed_high=0x26;
my $ipmi_inlet_sensorname="Inlet Temp";
my $default_threshold=32; # the ambient temperature we use above
# which we default back to letting the drac
# control the fans
my $base_temp = 30; # no fans when below this temp
my $desired_temp1 = 40; # aim to keep the temperature below this
my $desired_temp2 = 50; # really ramp up fans above this
my $desired_temp3 = 55; # really ramp up fans above this
my $demand1 = 30; # demand at temp1
my $demand2 = 100; # demand at temp2
my $demand3 = 200; # demand at temp3
# check inlet temp every minute, hddtemp every minute (but FIXME:
# ensure doesn't spinup spundown disks), and sensors every few seconds
my @ambient_ipmitemps=();
my @hddtemps=();
my @coretemps=();
my @cputemps=();
my $current_mode;
my $lastfan;
sub average {
my (@temps) = (@_);
my $div = @temps;
my $tot = 0;
for (my $i = 0; $i < @temps ; $i++) {
$tot += $temps[$i];
}
my $avg = sprintf "%.2f", $tot/$div;
return $avg;
}
sub max {
my ($v1, $v2) = (@_);
if ($v1 > $v2) {
return $v1;
} else {
return $v2;
}
}
sub set_fans_default {
if (!defined $current_mode or $current_mode ne "default") {
$current_mode="default";
$lastfan=undef;
print "--> enable dynamic fan control\n";
system("ipmitool raw 0x30 0x30 0x01 0x01");
}
}
sub set_fans_servo {
my ($ambient_temp, $_cputemps, $_coretemps, $_hddtemps) = (@_);
my (@cputemps) = @$_cputemps;
my (@coretemps) = @$_coretemps;
my (@hddtemps) = @$_hddtemps;
if (!defined $current_mode or $current_mode ne "set") {
$current_mode="set";
print "--> disable dynamic fan control\n";
system("ipmitool raw 0x30 0x30 0x01 0x00");
}
# two thirds weighted CPU temps vs hdd temps, but if the HDD temps
# creep above this value, use them exclusively (more important to
# keep them cool than the CPUs)
my $weighted_temp = max(average(
average(@cputemps), average(@coretemps), average(@hddtemps)),
average(@hddtemps));
print "weighted_temp = $weighted_temp ; ambient_temp $ambient_temp\n";
# FIXME: probably want to take into account ambient temperature - if
# the difference between weighted_temp and ambient_temp is small
# because ambient_temp is large, then less need to run the fans
# because there's still low power demands
my $demand = 0; # want demand to be a reading from 0-100% of
# $static_speed_low - $static_speed_high
if ($weighted_temp > $base_temp and
$weighted_temp < $desired_temp1) {
# slope m = (y2-y1)/(x2-x1)
# y - y1 = (x-x1)(y2-y1)/(x2-x1)
# y1 = 0 ; x1 = base_temp ; y2 = demand1 ; x2 = desired_temp1
# x = weighted_temp
$demand = 0 + ($weighted_temp - $base_temp) * ($demand1 - 0)/($desired_temp1 - $base_temp);
} elsif ($weighted_temp >= $desired_temp2) {
# y1 = demand1 ; x1 = desired_temp1 ; y2 = demand2 ; x2 = desired_temp2
$demand = $demand2 + ($weighted_temp - $desired_temp2) * ($demand3 - $demand2)/($desired_temp3 - $desired_temp2);
} elsif ($weighted_temp >= $desired_temp1) {
# y1 = demand1 ; x1 = desired_temp1 ; y2 = demand2 ; x2 = desired_temp2
$demand = $demand1 + ($weighted_temp - $desired_temp1) * ($demand2 - $demand1)/($desired_temp2 - $desired_temp1);
}
$demand = int($static_speed_low + $demand/100*($static_speed_high-$static_speed_low));
if ($demand>255) {
$demand=255;
}
if (!defined $lastfan or $demand < $lastfan or $demand > $lastfan + 1) {
$lastfan = $demand;
$demand = sprintf("0x%x", $demand);
# print "demand = $demand\n";
print "--> ipmitool raw 0x30 0x30 0x02 0xff $demand\n";
system("ipmitool raw 0x30 0x30 0x02 0xff $demand");
}
}
$SIG{TERM} = $SIG{INT} = sub { my $signame = shift ; $SIG{$signame} = 'DEFAULT' ; set_fans_default ; kill $signame, $$ };
my $last_reset_hddtemps=time;
my $last_reset_ambient_ipmitemps=time;
while () {
if (!@hddtemps) {
@hddtemps=`hddtemp /dev/sd? | grep [0-9]`
}
if (!@ambient_ipmitemps) {
@ambient_ipmitemps=`ipmitool sdr type temperature | grep "$ipmi_inlet_sensorname" | grep [0-9]`
}
@coretemps=`sensors | grep [0-9]`;
@cputemps=grep {/^Package id/} @coretemps;
@coretemps=grep {/^Core/} @coretemps;
chomp @cputemps;
chomp @coretemps;
chomp @ambient_ipmitemps;
chomp @hddtemps;
@cputemps = apply { s/.*: *([^ ]*)°C.*/$1/ } @cputemps;
@coretemps = apply { s/.*: *([^ ]*)°C.*/$1/ } @coretemps;
@ambient_ipmitemps = apply { s/.*\| ([^ ]*) degrees C.*/$1/ } @ambient_ipmitemps;
@hddtemps = apply { s/.*: *([^ ]*)°C.*/$1/ } @hddtemps;
print "\n";
print "cputemps=", join (" ; ", @cputemps), "\n";
print "coretemps=", join (" ; ", @coretemps), "\n";
print "ambient_ipmitemps=", join (" ; ", @ambient_ipmitemps), "\n";
print "hddtemps=", join (" ; ", @hddtemps), "\n";
my $ambient_temp = average(@ambient_ipmitemps);
# FIXME: hysteresis
if ($ambient_temp > $default_threshold) {
set_fans_default();
} else {
set_fans_servo($ambient_temp, \@cputemps, \@coretemps, \@hddtemps);
}
if (time - $last_reset_hddtemps > 60) {
@hddtemps=();
}
if (time - $last_reset_ambient_ipmitemps > 60) {
@ambient_ipmitemps=();
}
sleep 3;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1,22 +1,46 @@
# Safety BASH script
I made a BASH script to check the temperature, and if it's higher than XX (27 degrees C in my case) it sends a raw command to restore automatic fan control.
# fan speed controller for dell R710, R520 etc
I'm running this on an Ubuntu VM on ESXi (on the R710 box), but it should be able to run as long as you have ipmitools. It could be you need to modify the logging, to make it work with whatever your system use.
Dells don't like having third party cards installed, and ramp up the
fan speed to jetliner taking off. But you can override this.
I run the script via CRON every 5 minutes from my Ubuntu Server VM running on ESXi.
This speed controller uses ipmi raw commands that seem to be similar
across a wide range of dell server generations (google searches for
`ipmitool raw 0x30 0x30 0x01 0x00` show it works for R710, R730, T130,
and I run this on my R520
`*/5 * * * * /bin/bash /path/to/script/R710-IPMITemp.sh > /dev/null 2>&1`
This script monitoring the ambient air temperature (you will likely
need to modify the $ipmi_inlet_sensorname variable to find the correct
sensor), the hdd temperatures, the core and socket temperatures
(weighted so one core shooting up if all the others are still cold -
let the heatsink do its job).
Notice that I use [healthchecks.io](https://healthchecks.io) in the script to notify if the temp goes to high (it would also trigger if the internet goes down for some reason). Remember to get your own check URL if you want it, or else just remove the curl command.
It uses setpoints and temperature ranges you can tune to your heart's
content. I use it to keep the fans low but increasing to a soft
volume up to 40 degrees, ramp it up quickly to 50degrees, then very
quickly towards full speed much beyond that.
I'm also currently testing out [slacktee.sh](https://github.com/course-hero/slacktee) to get notifications in my slack channel.
It's got a signal handler so it defaults to default behaviour when
killed by SIGINT/SIGTERM.
I run it on my proxmox hypervisor directly, hence not needing any ipmi
passwords. I will start and stop it through proxmox's systemd system
once I have it firmly debugged.
I wrote it the night before Australia's hottest December day on record
(hey we like our coal fondling prime-ministers). It seems to be
coping so far now that it has reached that predicted peak (I don't
believe it's only 26 in my un-air conditioned study).
![Socket and ambient temperature on 20Dec2019](ipmi_temp-pinpoint=1576762993,1576823788.png)
![Hdd temp](hddtemp_smartctl-pinpoint=1576762993,1576823788.png)
![Core temp](sensors_temp-pinpoint=1576762993,1576823788.png)
![Resultant Fan speed](ipmi_fans-pinpoint=1576762993,1576823788.png)
The Scripts [Reddit thread](https://www.reddit.com/r/homelab/comments/779cha/manual_fan_control_on_r610r710_including_script/)
*****
# Howto: Setting the fan speed of the Dell R610/R710
# Howto: Manually setting the fan speed of the Dell R610/R710
1. Enable IPMI in iDrac
2. Install ipmitool on linux, win or mac os
@ -61,8 +85,6 @@ _Note: The RPM may differ from model to model_
**Disclaimer**
I'm by no means good at IPMI, BASH scripting or regex, etc. but it seems to work fine for me.
TLDR; I take _NO_ responsibility if you mess up anything.
*****

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB