Script: Supermicro Host Fan control on on Napp-It (or maybe any Solaris OS?)

Notice: Page may contain affiliate links for which we may earn a small commission through services like Amazon Affiliates or Skimlinks.

helsyeah

Active Member
Aug 22, 2015
111
30
28
43
Hey all.

I was inspired by the work that the folks over at FreeNas were doing to managed the fan speeds on their NAS based on the temps of their HDD's. (Mentioned at STH here & references at FreeNas here)

So I took the same approach and created an updated Perl script to work on Omnios with the intent of letting my Napp-IT VM have full control over the a host fan speeds.

There are a couple key differences in this script over the one created for FreeNas (besides the OS differences):
  • I wanted to provide a means for defining a regular "fan curve" for HDD/CPU temps in the script, thus making use of a couple hash tables.
  • This script is designed to run full time & be setup to run on OS start. I used a bash script in init.d to launch the script at OS launch.
  • This script monitors CPU temperature more frequently than HDD temperature, and can adjust fan speeds more quickly based on changes in CPU temperature.
Most of the details on how to use the script are in the script comments, so see those for basic instructions on how to use it.

Couple comments on the script: its a pretty quick and dirty script. There is essentially no error handling, so test it carefully!

I hope this helps someone out, and without further ado, here it is (in its current form):

Code:
#!/usr/bin/perl
use Time::HiRes;

# This script is designed to work with a Solaris based VM and control the fan speeds on a
# Supermicro x9/x10(and maybe x11) host where the VM is running.
# Specifically this was built on a NAPP-IT storage appliance VM run on ESXI.
# The script is also designed around using a Supermicro board with two fan control (0x00 & 0x01) zones.
# The script may need modification for your particular hardware.

# This script also assumes that ipmitool has been installed on the OS
#    The following worked to install ipmitool on Omnios:
#       pkg install /system/management/ipmitool
#       pkg install /driver/ipmi

# This script should be added to a init.d via a bash script so it will run when the OS starts.

# WARNINGS:
# - If the same zone is used for both HDD & CPU fan speed control,
#   the CPU temp will always be the one applied.
# - There's very little (almost no) error handling in this script,
#   so test any modifications you make thoroughly!

$debug = 1;    # 0 for no debug. 1..2 for verbosity
$ipmilogin = "-I lanplus -H 192.168.2.160 -U ADMIN -P ADMIN"; # Set your IPMI IP address, username & password here.
$logfilepath = "/mir01/shares/TOOLS"; # set the path where you want any log files to generated.

$hdfanzone = 0x00;
$cpufanzone = 0x01;

$hdtempcheckperiod = 90; # This is the number of seconds to check DRIVE temp, and possibly adjust drive fan speeds.
# Fan Temp to Speed curves are defined in a hash table in the following format:
# TEMP => FAN SPEED %
# Example: 37 => 85
#   This means that when a temp hits 37 Celcius, the fans will go to 85% speed.
%hdfancurve = (
               38 => 100,
               37 => 85,
               36 => 60,
               35 => 40
              );
            
$cputempcheckperiod = 2; # This is the number of seconds to be checking the CPU temp and possibly adjusting the CPU fan speeds.
# Fan Temp to Speed curves are defined in a hash table in the following format:
# TEMP => FAN SPEED%
# Example: 37 => 85
#   This means that when a temp hits 37 Celcius, the fans will go to 85% speed.
%cpufancurve = (
                55 => 100,
                50 => 90,
                45 => 70,
                40 => 50,
                35 => 30
                );




# Set all fan control to Standard (0) to start.
# other options are:
# Full = 1
# Optimal = 2
# Heavy IO = 4
# FYI: Standard, Optimal & Heavy IO seem to regain auto-control fan speed
# if the script stops manually setting the speeds. Full does not seem to do this

`ipmitool $ipmilogin raw 0x30 0x45 0x01 0`;
sleep 1;

# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# DO NOT MODIFY BELOW THIS LINE (UNLESS YOU KNOW WHAT YOUR ARE DOING!)
# --------------------------------------------------------------------
$lastcputime = 0;
$lasthdtime = 0;

$loop_sleep = 0.5;

use POSIX qw(strftime);

@disks = `echo | format | /usr/gnu/bin/grep -oP '(?<=[0-9]\. )[0-9a-zA-Z]+(?= <)'`;
chomp @disks;

do {
    $datestr = strftime '%Y%m%d', localtime;
    $logfile = "$logfilepath/TempLog_$datestr.log";
    $datestring = strftime "%F %H:%M:%S", localtime;
    open (LOGFILE, ">>$logfile");
  
    $currenttime = Time::HiRes::time();

    if (($currenttime - $lasthdtime) >= $hdtempcheckperiod) {
        if( $debug > 0 ) {print "Checking HD Temps\n"};
        # ******************************************************
        # *************** Get Highest HD Temp ******************
        $highest_hd_temp = 0;
        foreach $disk (@disks)
        {
            $command = "/usr/sbin/smartctl -a -d sat,12 -T permissive /dev/rdsk/$disk | /usr/gnu/bin/grep -Po '(Temperature_Celsius.*?-\\s+?)\\K[0-9\\.]+'";
            $temp = `$command`;
            chomp $temp;
            if ($temp > $highest_hd_temp) {$highest_hd_temp = $temp;}
        }
      
        if( $debug > 0 ) {
            print "Highest HD Temperature: $highest_hd_temp\n";
            print LOGFILE "$datestring - Highest HD Temperature: $highest_hd_temp\n";
        }
        # ******************************************************
        # ******************************************************
  
        $hdfansetting = 0;
        # set fan setting to minimum first
        foreach $hdtempkey (keys %hdfancurve) {
            if ($hdfansetting > $hdtempkey || $hdfansetting == 0) {$hdfansetting = $hdtempkey};
        }
      
        foreach $hdtempkey (keys %hdfancurve) {
            if ($hdtempkey >= $hdfansetting && $highest_hd_temp >= $hdtempkey) {
                $hdfansetting = $hdtempkey;
            }      
        }

        $hddfanspeed = $hdfancurve{$hdfansetting};
        $currentfanspeed = getdutycycle($hdfanzone);

        if ($hddfanspeed != $currentfanspeed)
        {
            if( $debug > 0 ) {
                print "Setting drives to $hddfanspeed%\n";
                print LOGFILE "$datestring - Setting drives to $hddfanspeed%\n";
            }
            `ipmitool $ipmilogin raw 0x30 0x70 0x66 0x01 $hdfanzone $hddfanspeed`;
        }
        else
        {
            if( $debug > 1 ) {
                print "Drive fans already at $hddfanspeed%\n";
                print LOGFILE "$datestring - Drive fans already at $hddfanspeed%\n";
            }
        }
        $lasthdtime = Time::HiRes::time();
    }
  
    $currenttime = Time::HiRes::time();

    if (($currenttime - $lastcputime) >= $cputempcheckperiod) {
        if( $debug > 0 ) {print "Checking CPU Temps\n"};
        $cputemp = 0;
        # there's probably a faster way to get cpu temp via ipmitool raw commands, but this works for now
        $cputemp = `ipmitool $ipmilogin sensor | /usr/gnu/bin/grep -Po '(CPU Temp\\s+?\\|\\s+?)\\K[0-9\\.]+'`;
        chomp $cputemp;
        if( $debug > 0 ) {
            print "CPU Temperature: $cputemp\n";
            print LOGFILE "$datestring - CPU Temperature: $cputemp\n";
        }
      
        $cpufansetting = 0;
        # set fan setting to minimum first
        foreach $cputempkey (keys %cpufancurve) {
            if ($cpufansetting > $cputempkey || $cpufansetting == 0) {$cpufansetting = $cputempkey};
        }
      
        foreach $cputempkey (keys %cpufancurve) {
            if ($cputempkey >= $cpufansetting && $cputemp >= $cputempkey) {
                $cpufansetting = $cputempkey;
            }  
        }
      
        $cpufanspeed = $cpufancurve{$cpufansetting};
        $currentfanspeed = getdutycycle($cpufanzone);
      
        if ($cpufanspeed != $currentfanspeed)
        {
            if( $debug > 0 ) {
                print "Setting CPU fan(s) to $cpufanspeed%\n";
                print LOGFILE "$datestring - Setting CPU fan to $cpufanspeed%\n";
            }
            `ipmitool $ipmilogin raw 0x30 0x70 0x66 0x01 $cpufanzone $cpufanspeed`;
        }
        else
        {
            if( $debug > 1 ) {
                print "CPU fan(s) already at $cpufanspeed%\n";
                print LOGFILE "$datestring - CPU fans already at $cpufanspeed%\n";
            }
        }

        $lastcputime = Time::HiRes::time();
    }
  

    close (LOGFILE);
  
    if( $loop_sleep > 0 ) {
        if( $debug > 2 ) {
            print "sleeping...\n";
        }
        sleep $loop_sleep;
    }
} while( $loop_sleep );

sub ltrim {
    $s = shift;
    $s =~ s/^\s+//;
    return $s;
}
sub rtrim {
    $s = shift;
    $s =~ s/\s+$//;
    return $s;
}
sub  trim {
    $s = shift;
    $s =~ s/^\s+|\s+$//g;
    return $s;
}
sub getdutycycle {
    $fanzone = shift;
    $hexspeed = `ipmitool $ipmilogin raw 0x30 0x70 0x66 0x00 $fanzone`;
    chomp $hexspeed;
    $hexspeed = trim($hexspeed);
    $speed = hex($hexspeed);
    return $speed;  
}
 

nle

Member
Oct 24, 2012
204
11
18
Nice work! What is the advantages with this over rather than just let esxi handle the fans?
 

helsyeah

Active Member
Aug 22, 2015
111
30
28
43
Nice work! What is the advantages with this over rather than just let esxi handle the fans?
To my knowledge, ESXI doesn't offer the same level of control and granularity on temperature ranges and fan speeds. Also, since HDD's are typically attached to a controller that is passed-through to a VM, ESXI might not have access to SMART temp data in order to set appropriate speeds.

Now, I, to be fair, I am a complete ESXI noob when it comes to fan speed control, so if someone knows how to set ESXI to control fan speeds based on HDD temps as well as CPU/Chassis temps, I'd love to hear about it!