Skip to content

Instantly share code, notes, and snippets.

@Jiab77
Last active April 17, 2024 20:43
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save Jiab77/0778ef11a441f49df62e2b65f3daef76 to your computer and use it in GitHub Desktop.
Save Jiab77/0778ef11a441f49df62e2b65f3daef76 to your computer and use it in GitHub Desktop.
Setup nut and netdata on Ubuntu

Setup nut and netdata on Ubuntu

In this document, I will explain how to setup nut (Network UPS Tools) on Ubuntu 18.04 and 20.04.

It is basically the next chapter of my previous gist, Upgrade nut on Ubuntu 18.04.

I'll only document USB connected UPS and not the other supported connection modes.

Install required dependencies

Nut

sudo apt install nut

Netdata

bash <(curl -Ss https://my-netdata.io/kickstart.sh) all --dont-wait --disable-telemetry

Must read

I'd seriously recommend you to take the time to explore this book written by Roger Price that contains tons of useful information about how to implement and configure nut.

Detect connected UPS

Here is how you can detect your connected UPS. I'll mainly focus on the USB connection and not the other connection modes.

# Scan all available devices (default)
$ nut-scanner

# Scan USB devices only
$ nut-scanner -U

# Scan USB devices only but print in ups.conf format
$ nut-scanner -UN

Use the following commands to add the details about the connected UPS in the /etc/nut/ups.conf file:

# Add some spacing and UPS details
echo -e "\n# Detected UPS from USB" | sudo tee -a /etc/nut/ups.conf

# Add detected UPS details
nut-scanner -UNq 2>/dev/null | sudo tee -a /etc/nut/ups.conf

# Check the result
sudo cat /etc/nut/ups.conf

You should get something similar:

# Detected UPS from USB
[nutdev1]
	driver = "usbhid-ups"
	port = "auto"
	vendorid = "051D"
	productid = "0002"
	bus = "001"

You can now change [nutdev1] by something more representative to your UPS and also add the desc field. You'll need sudo to edit the config files.

# Edit the config file
sudo nano /etc/nut/ups.conf

Then change the previously generated config that way:

# Detected UPS from USB
[APCBU1400]
	driver = "usbhid-ups"
	port = "auto"
	desc = "APC Back-UPS 1400VA / 700w"
	vendorid = "051D"
	productid = "0002"
	bus = "001"

Create monitoring connection

Once the UPS defined in the /etc/nut/ups.conf file, we must define the monitoring user that will used to read the data from the UPS and define the actions according to them.

The created user is not related to the standard UNIX users, it will only exist in the nut context.

Here is how to create it:

# Install password generator
sudo apt install pwgen

# Generate strong password (but avoid using special chars)
export NUT_RAND_PW=$(pwgen -snc -B 128 | awk '{ print $1 }')

# Create monitoring user dynamicaly
echo -e "\n[monitor]\n\tpassword = ${NUT_RAND_PW}\n\tupsmon master\n" | sudo tee -a /etc/nut/upsd.users

# Generate connection string
# You will have to copy it in the 'upsmon.conf' file later
echo -e "\nMONITOR APCBU1400@localhost 1 monitor $NUT_RAND_PW master\n"

# Remove generated password from environment
unset NUT_RAND_PW

You should get something similar:

$ export NUT_RAND_PW=$(pwgen -snc -B 128 | awk '{ print $1 }')
$ echo -e "\n[monitor]\n\tpassword = ${NUT_RAND_PW}\n\tupsmon master\n" | sudo tee -a /etc/nut/upsd.users

[monitor]
	password = [REDACTED]
	upsmon master

$ echo -e "\nMONITOR APCBU1400@localhost 1 monitor $NUT_RAND_PW master\n"

MONITOR APCBU1400@localhost 1 monitor [REDACTED] master

$ unset NUT_RAND_PW

Now we have to add the generated connection string into the /etc/nut/upsmon.conf file:

sudo nano /etc/nut/upsmon.conf

after the examples of the MONITOR section:

# Examples: 
#
# MONITOR myups@bigserver 1 monmaster blah master
# MONITOR su700@server.example.com 1 upsmon secretpass slave
# MONITOR myups@localhost 1 upsmon pass master	(or slave)

MONITOR APCBU1400@localhost 1 monitor [REDACTED] master

# --------------------------------------------------------------------------

Improve UPS commands scheduling script

By default the /bin/upssched-cmd script is pretty basic and does not contains enough commands:

#! /bin/sh
#
# This script should be called by upssched via the CMDSCRIPT directive.
# 
# Here is a quick example to show how to handle a bunch of possible
# timer names with the help of the case structure.
#
# This script may be replaced with another program without harm.
#
# The first argument passed to your CMDSCRIPT is the name of the timer
# from your AT lines.

case $1 in
	upsgone)
		logger -t upssched-cmd "The UPS has been gone for awhile"
		;;
	*)
		logger -t upssched-cmd "Unrecognized command: $1"
		;;
esac

Now, create a copy of the original script:

# Create a copy
sudo cp -v /bin/upssched-cmd /bin/upssched-cmd.bak

# Edit the current version
sudo nano /bin/upssched-cmd

And change the script to that:

#! /bin/sh
#
# This script should be called by upssched via the CMDSCRIPT directive.
# 
# Here is a quick example to show how to handle a bunch of possible
# timer names with the help of the case structure.
#
# This script may be replaced with another program without harm.
#
# The first argument passed to your CMDSCRIPT is the name of the timer
# from your AT lines.

# Dynamic name assignment
UPS=$( upsc -l 2>/dev/null )

# Or manual name assignement
# UPS="YOUR-UPS-DEFINED-NAME-IN-UPS.CONF"

STATUS=$( upsc $UPS ups.status )
CHARGE=$( upsc $UPS battery.charge )
CHMSG="[$STATUS]:$CHARGE%"

case $1 in
	online) MSG="$UPS, $CHMSG - power supply has been restored." ;;
	onbatt) MSG="$UPS, $CHMSG - power failure - save your work!" ;;
	lowbatt) MSG="$UPS, $CHMSG - shutdown now!" ;;
	upsgone) MSG="The UPS $UPS has been gone for awhile" ;;
	*) MSG="$UPS, $CHMSG - Unrecognized command: $1" ;;
esac
logger -i -t upssched-cmd $MSG

# Comment out the line below for workstations
# notify-send-all "$MSG"

Configure commands scheduling

Now that we have improved the existing commands scheduling scripts, we have to edit the /etc/nut/upssched.conf file and change or set some values:

# Define PIPE file
sudo sed -e 's|# PIPEFN /run/|PIPEFN /run/|' -i /etc/nut/upssched.conf

# Define Lock file
sudo sed -e 's|# LOCKFN /run/|LOCKFN /run/|' -i /etc/nut/upssched.conf

# Add commands defined in the scripts
echo -e "\n# Custom commands" | sudo tee -a /etc/nut/upssched.conf
echo -e "AT ONLINE $(upsc -l 2>/dev/null)@localhost EXECUTE online\nAT ONBATT $(upsc -l 2>/dev/null)@localhost EXECUTE onbatt\nAT LOWBATT $(upsc -l 2>/dev/null)@localhost EXECUTE lowbatt" | sudo tee -a /etc/nut/upssched.conf

# Comment out this line if it didn't worked with the 'upsc' command above
# echo -e "AT ONLINE APCBU1400@localhost EXECUTE online\nAT ONBATT APCBU1400@localhost EXECUTE onbatt\nAT LOWBATT APCBU1400@localhost EXECUTE lowbatt" | sudo tee -a /etc/nut/upssched.conf

# Check the result
sudo nano /etc/nut/upssched.conf

Start nut services

Now that we have defined the UPS in the /etc/nut/ups.conf file and the monitoring user in the /etc/nut/upsd.users then finally set the monitoring connection in /etc/nut/upsmon.conf, we must now change the running mode which is by default set to none and would not let systemd services to run correctly. We'll set it to the standalone mode for now.

# Change the running mode
sudo sed -e 's/MODE=none/MODE=standalone/' -i /etc/nut/nut.conf

# Check the result
sudo nano /etc/nut/nut.conf

You can now restart all the nut related systemd services:

for S in nut-client.service nut-driver.service nut-monitor.service nut-server.service ; do sudo systemctl restart $S ; done

Wait few seconds to let them start then check their status:

for S in nut-client.service nut-driver.service nut-monitor.service nut-server.service ; do systemctl status $S -l ; done

You should see something similar:

$ for S in nut-client.service nut-driver.service nut-monitor.service nut-server.service ; do systemctl status $S -l ; done
● nut-monitor.service - Network UPS Tools - power device monitor and shutdown controller
   Loaded: loaded (/lib/systemd/system/nut-monitor.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2021-07-02 07:49:48 CEST; 1min 8s ago
  Process: 8731 ExecStart=/sbin/upsmon (code=exited, status=0/SUCCESS)
 Main PID: 8733 (upsmon)
    Tasks: 2 (limit: 4915)
   Memory: 3.0M
   CGroup: /system.slice/nut-monitor.service
           ├─8732 /lib/nut/upsmon
           └─8733 /lib/nut/upsmon

jui 02 07:49:48 [REDACTED] upsmon[8731]: Using power down flag file /etc/killpower
jui 02 07:49:48 [REDACTED] systemd[1]: nut-monitor.service: Can't open PID file /run/nut/upsmon.pid (yet?) 
jui 02 07:49:48 [REDACTED] upsmon[8732]: Startup successful
jui 02 07:49:48 [REDACTED] systemd[1]: nut-monitor.service: Supervising process 8733 which is not our child
jui 02 07:49:48 [REDACTED] systemd[1]: Started Network UPS Tools - power device monitor and shutdown contro
jui 02 07:49:48 [REDACTED] upsmon[8733]: Login on UPS [APCBU1400@localhost] failed - got [ERR ACCESS-DENIED
jui 02 07:49:53 [REDACTED] upsmon[8733]: Poll UPS [APCBU1400@localhost] failed - Write error: Broken pipe
jui 02 07:49:53 [REDACTED] upsmon[8733]: Communications with UPS APCBU1400@localhost lost
jui 02 07:49:58 [REDACTED] upsmon[8733]: Communications with UPS APCBU1400@localhost established
jui 02 07:49:58 [REDACTED] upsmon[8733]: UPS APCBU1400@localhost on line power
● nut-driver.service - Network UPS Tools - power device driver controller
   Loaded: loaded (/lib/systemd/system/nut-driver.service; static; vendor preset: enabled)
   Active: active (running) since Fri 2021-07-02 07:49:48 CEST; 1min 16s ago
  Process: 8704 ExecStart=/sbin/upsdrvctl start (code=exited, status=0/SUCCESS)
 Main PID: 8725 (usbhid-ups)
    Tasks: 1 (limit: 4915)
   Memory: 1.1M
   CGroup: /system.slice/nut-driver.service
           └─8725 /lib/nut/usbhid-ups -a APCBU1400

jui 02 07:49:48 [REDACTED] systemd[1]: Starting Network UPS Tools - power device driver controller...
jui 02 07:49:48 [REDACTED] upsdrvctl[8704]: Using subdriver: APC HID 0.96
jui 02 07:49:48 [REDACTED] upsdrvctl[8704]: Network UPS Tools - Generic HID driver 0.41 (2.7.4)
jui 02 07:49:48 [REDACTED] upsdrvctl[8704]: USB communication driver 0.33
jui 02 07:49:48 [REDACTED] upsdrvctl[8704]: Network UPS Tools - UPS driver controller 2.7.4
jui 02 07:49:48 [REDACTED] usbhid-ups[8725]: Startup successful
jui 02 07:49:48 [REDACTED] systemd[1]: Started Network UPS Tools - power device driver controller.
● nut-monitor.service - Network UPS Tools - power device monitor and shutdown controller
   Loaded: loaded (/lib/systemd/system/nut-monitor.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2021-07-02 07:49:48 CEST; 1min 16s ago
  Process: 8731 ExecStart=/sbin/upsmon (code=exited, status=0/SUCCESS)
 Main PID: 8733 (upsmon)
    Tasks: 2 (limit: 4915)
   Memory: 3.0M
   CGroup: /system.slice/nut-monitor.service
           ├─8732 /lib/nut/upsmon
           └─8733 /lib/nut/upsmon

jui 02 07:49:48 [REDACTED] upsmon[8731]: Using power down flag file /etc/killpower
jui 02 07:49:48 [REDACTED] systemd[1]: nut-monitor.service: Can't open PID file /run/nut/upsmon.pid (yet?) 
jui 02 07:49:48 [REDACTED] upsmon[8732]: Startup successful
jui 02 07:49:48 [REDACTED] systemd[1]: nut-monitor.service: Supervising process 8733 which is not our child
jui 02 07:49:48 [REDACTED] systemd[1]: Started Network UPS Tools - power device monitor and shutdown contro
jui 02 07:49:48 [REDACTED] upsmon[8733]: Login on UPS [APCBU1400@localhost] failed - got [ERR ACCESS-DENIED
jui 02 07:49:53 [REDACTED] upsmon[8733]: Poll UPS [APCBU1400@localhost] failed - Write error: Broken pipe
jui 02 07:49:53 [REDACTED] upsmon[8733]: Communications with UPS APCBU1400@localhost lost
jui 02 07:49:58 [REDACTED] upsmon[8733]: Communications with UPS APCBU1400@localhost established
jui 02 07:49:58 [REDACTED] upsmon[8733]: UPS APCBU1400@localhost on line power
● nut-server.service - Network UPS Tools - power devices information server
   Loaded: loaded (/lib/systemd/system/nut-server.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2021-07-02 07:49:52 CEST; 1min 16s ago
  Process: 8787 ExecStart=/sbin/upsd (code=exited, status=0/SUCCESS)
 Main PID: 8800 (upsd)
    Tasks: 1 (limit: 4915)
   Memory: 1.1M
   CGroup: /system.slice/nut-server.service
           └─8800 /lib/nut/upsd

jui 02 07:49:52 [REDACTED] upsd[8787]: fopen /run/nut/upsd.pid: No such file or directory
jui 02 07:49:52 [REDACTED] upsd[8787]: listening on 127.0.0.1 port 3493
jui 02 07:49:52 [REDACTED] upsd[8787]: listening on ::1 port 3493
jui 02 07:49:52 [REDACTED] upsd[8787]: listening on 127.0.0.1 port 3493
jui 02 07:49:52 [REDACTED] upsd[8787]: listening on ::1 port 3493
jui 02 07:49:52 [REDACTED] upsd[8787]: Connected to UPS [APCBU1400]: usbhid-ups-APCBU1400
jui 02 07:49:52 [REDACTED] upsd[8787]: Connected to UPS [APCBU1400]: usbhid-ups-APCBU1400
jui 02 07:49:52 [REDACTED] upsd[8800]: Startup successful
jui 02 07:49:52 [REDACTED] systemd[1]: Started Network UPS Tools - power devices information server.
jui 02 07:49:58 [REDACTED] upsd[8800]: User monitor@::1 logged into UPS [APCBU1400]

If yes, then you can read the data available from your UPS that way:

# Use 'upsc -l' to get the name of the UPS
# Then pass it as self argument to read the UPS data
upsc $(upsc -l 2>/dev/null) 2>/dev/null

It should output something similar but with different values according to your UPS:

$ upsc $(upsc -l 2>/dev/null) 2>/dev/null
battery.charge: 100
battery.charge.low: 10
battery.charge.warning: 50
battery.date: 2001/09/25
battery.mfr.date: 2020/09/17
battery.runtime: 2604
battery.runtime.low: 120
battery.type: PbAc
battery.voltage: 27.3
battery.voltage.nominal: 24.0
device.mfr: American Power Conversion
device.model: Back-UPS XS 1400U 
device.serial: [REDACTED]  
device.type: ups
driver.name: usbhid-ups
driver.parameter.bus: 001
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.productid: 0002
driver.parameter.synchronous: no
driver.parameter.vendorid: 051D
driver.version: 2.7.4
driver.version.data: APC HID 0.96
driver.version.internal: 0.41
input.sensitivity: medium
input.transfer.high: 280
input.transfer.low: 155
input.voltage: 234.0
input.voltage.nominal: 230
ups.beeper.status: enabled
ups.delay.shutdown: 20
ups.firmware: 926.T2 .I
ups.firmware.aux: T2 
ups.load: 14
ups.mfr: American Power Conversion
ups.mfr.date: 2020/09/17
ups.model: Back-UPS XS 1400U 
ups.productid: 0002
ups.realpower.nominal: 700
ups.serial: [REDACTED]  
ups.status: OL
ups.test.result: No test initiated
ups.timer.reboot: 0
ups.timer.shutdown: -1
ups.vendorid: 051d

Configure Netdata

You can now configure Netdata and add the name of your UPS in the configuration file:

# Go to the netdata config folder
cd /etc/netdata

# Run the editing script
sudo ./edit-config charts.d/nut.conf

You don't really need to change anything as normally the defaults are good enough to simply having to restart the service and nothing more:

sudo systemctl restart netdata ; systemctl status netdata -l

Now you can navigate to http://localhost:19999 and search for the UPS section on the right, you should see something like that:

image

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment