nmaggioni / r710-fan-controller

A Python script and SystemD service to control Dell PowerEdges' fans depending on CPU(s) temperatures.
MIT License
140 stars 39 forks source link

CPU temperature command, Kg key for IPMIv2 authentication, RPi Service and Additional Speeds/Temps #11

Closed MillerLoren closed 8 months ago

MillerLoren commented 2 years ago

Hi there! So this tool has been great but I had to make a few tweaks to get it to work and while I was at it I improved a bit as well(at least I think so). I wanted to share my process to get this to work on a Raspberry Pi 4b host running Raspberry Pi OS to control a Dell Poweredge R710 running Proxmox 7.1-12.

First Proxmox would not show cpu values from the suggested "sysctl -a" command. Instead I needed to install im-sensors on the Dell server and run "sensors -u" through ssh. The entire command being:

ssh root@dell_hostname 'sensors -u | grep input | grep -E -o "[0-9]+\.[0-9]+"'

Next the ipmitool would not work and would return an error: "RAKR 4 message has invalid integrity check value". No matter where I looked I could not find this specific error, at least nothing that wasn't behind a paywall. I then came across a different tutorial here: https://jacobjangles.com/hushing-the-dell-r710-fans/ that had an additional flag on the ipmitool. After looking into it, it made sense that I needed to define the Kg key using the "-y " flag. So adding that flag and hex key to the config file and subprocess.check_output allowed the commands to go through.

    cmd = ["ipmitool"]
    if state[host['name']]['is_remote']:
        cmd += ['-I', 'lanplus']
        cmd += ['-H', host['remote_ipmi_credentials']['host']]
        cmd += ['-U', host['remote_ipmi_credentials']['username']]
        cmd += ['-P', host['remote_ipmi_credentials']['password']]
        cmd += ['-y', host['remote_ipmi_credentials']['hexkey']]
    cmd += (args.split(' '))

The last issue that I had while running this was that because Raspberry Pi OS runs the systemctl service as root the service would fail. I needed to add both "User=pi" and "WorkingDirectory=/opt/fan_control" to the /etc/systemd/system/fan-control.service to get it to work after that.

[Unit]
Description=Temperature-based fan speed controller
After=network.target

[Service]
User=pi
Type=simple
WorkingDirectory=/opt/fan_control
ExecStart=/opt/fan_control/venv/bin/python3 -u /opt/fan_control/fan_control.py
Restart=always
RestartSec=15

[Install]
WantedBy=multi-user.target

Then I ran the following for the change to take effect:

sudo systemctl daemon-reload
sudo systemctl enable fan-control.service
sudo systemctl start fan-control.service

Once I got the tool running I updated it to use some recommended commands for Python3 and changed it to use more speed/temp combos.

I saw it is recommended now to use "subprocess.run" instead of "subprocess.check_output", but I am not sure the difference or why. I update it though which makes line 41 of fan_control.py:

subprocess.run(cmd, check=True, capture_output=True, text=True)

There is also a hex conversion in python that simplified some of the code on line 74:

        wanted_percentage_hex = hex(wanted_percentage)

For my own use case I also included another command to get the ambient temperature from the Dell server just to help average the temperature for the entire server using the following command:

ipmitool -I lanplus -H idrac_hostname -U ipmi-user -P password -y hex_key sdr type temperature | grep Ambient | grep -Po '\| ([0-9]+) degrees' | grep -Po '[0-9]+'

The last change I made was adding a for loop for temp/speed checks instead of a set 3. Just so I could have a finer control over the speeds. So after removing lines 107-110 I updated the code originally on lines 172-195 to the following:

    for idx, x in enumerate(host['temperatures']):
        idx_under = idx - 1
        idx_over = idx + 1
        if idx == 0:
            if (
                temp_average <= x and
                checkHysteresis(temp_average, idx, host)
            ):
                set_fan_speed(idx, host)
                return True
        elif idx_over == len(host['temperatures']):
            if x < temp_average:
                set_fan_control("automatic", host)
                return True
        elif (
            host['temperatures'][idx_under] < temp_average <= x and
            checkHysteresis(temp_average, idx_under, host)
        ):
            set_fan_speed(idx_under, host)
            return True
        elif (
            x < temp_average <= host['temperatures'][idx_over] and
            checkHysteresis(temp_average, idx, host)
        ):
            set_fan_speed(idx, host)
            return True

I know this may not be the best way to do this and I am not an expert in Python but maybe it will help someone else or help improve the development of the tool as it really has been great so far!

nmaggioni commented 2 years ago

Thanks for your input, the ipmitool error sounds like it's specific to your setup but the rest could be useful to somebody else sooner or later. I'll pin this issue for the moment.