Tous les fichiers de configuration et leurs chemins correspondent à une distribution sur base RedHat (RHEL, CentOS, Fedora). Il faut certainement adapter pour une Debian-based.


Tout d'abord, il faut bien évidemment installer NUT (Network UPS Tools) et son client. Dans un terminal en root, un simple

# yum install nut nut-client

fera l'affaire.
Pas besoin de faire appel à des dépôts tiers, c'est accessible dans les dépôts de base.

L'avantage de cette méthode c'est que le groupe et l'utilisateur spécifique sont automatiquement créés, ainsi que le "service" et les fichiers de configuration.

Reste qu'il va falloir un peu travailler sur ceux-ci pour que ça fonctionne.


Étape 1 : les fichiers de configuration

On a de la chance, ils sont tous au même endroit : dans /etc/ups/

Commençons par ups.conf, le fichier qui va décrire votre onduleur.

[ellipse]
 	driver = usbhid-ups
	port = auto
	desc = "MGE 600 USBS"

Quelques explications :

  • ellipse est le petit nom que j'ai décidé de donner à mon onduleur
  • usbhid-ups est le nom du driver pour les onduleurs MGE en USB
  • auto pour le port, toujours à cause de l'USB
  • desc, euh c'est une description, c'est tout


Ensuite, le fichier de configuration du démon : upsd.conf

ACL all 0.0.0.0/0
ACL localhost 127.0.0.1/32
ACL reseau 192.168.0.0/24

ACCEPT reseau
ACCEPT localhost
REJECT all

MAXAGE 25

Grosso modo, on autorise l'accès uniquement à partir de l'hôte et du réseau local.

L'option MAXAGE est là pour prévenir un bug récurrent sur certains onduleur MGE Ellipse en USB.


Maintenant, les utilisateurs autorisés à accéder au démon, dans upsd.users :

# Network UPS Tools: Example upsd.users
 
	[admin]
		password = GrandMaitre
		allowfrom = localhost reseau
		actions = SET
		instcmds = ALL
#
 
#
# --- Configuring for upsmon
#
# To add a user for your upsmon, use this example:
#
	[fedups]
		password  = votre_mdp
		allowfrom = localhost reseau
		upsmon master

En premier, l'utilisateur qui aura le pouvoir de gérer l'onduleur. À utiliser pour passer des valeurs à l'onduleur ou directement des commandes. Personnellement je ne m'en sers quasiment jamais.

Le deuxième est celui que va utiliser le moniteur upsmon. J'ai fait très original pour le choix du nom : fedups pour fedora et ups, à vous de trouver mieux. Notez bien le mot de passe, il faudra le renseigner plus loin.

Arrivé ici on a un service qui va dialoguer avec notre onduleur ... et c'est tout, rien ne se produira. Ça c'est le rôle de la suite.


Surveillons, surveillons ou le fichier de configuration upsmon.conf

# Network UPS Tools: example upsmon configuration
#
# This file contains passwords, so keep it secure.
 
# --------------------------------------------------------------------------
RUN_AS_USER nut
 
 
# --------------------------------------------------------------------------
MONITOR ellipse@localhost 1 fedups votre_mdp master
 
 
MINSUPPLIES 1
 
 
SHUTDOWNCMD "/sbin/shutdown -h +0"
 
# --------------------------------------------------------------------------
# NOTIFYCMD <command>
#
# upsmon calls this to send messages when things happen
#
# This command is called with the full text of the message as one argument.
# The environment string NOTIFYTYPE will contain the type string of
# whatever caused this event to happen.
#
# Note that this is only called for NOTIFY events that have EXEC set with
# NOTIFYFLAG.  See NOTIFYFLAG below for more details.
#
# Making this some sort of shell script might not be a bad idea.  For more
# information and ideas, see pager.txt in the docs directory.
#
# Example:
NOTIFYCMD /usr/sbin/upssched
 
 
POLLFREQ 5
 
POLLFREQALERT 5
 
HOSTSYNC 15
 
DEADTIME 15
 
POWERDOWNFLAG /etc/killpower
 
# --------------------------------------------------------------------------
# NOTIFYMSG - change messages sent by upsmon when certain events occur
#
# You can change the default messages to something else if you like.
#
# NOTIFYMSG <notify type> "message"
#
NOTIFYMSG ONLINE	"UPS %s on line power"
NOTIFYMSG ONBATT	"UPS %s on battery"
NOTIFYMSG LOWBATT	"UPS %s battery is low"
NOTIFYMSG FSD		"UPS %s: forced shutdown in progress"
NOTIFYMSG COMMOK	"Communications with UPS %s established"
NOTIFYMSG COMMBAD	"Communications with UPS %s lost"
NOTIFYMSG SHUTDOWN	"Auto logout and shutdown proceeding"
NOTIFYMSG REPLBATT	"UPS %s battery needs to be replaced"
NOTIFYMSG NOCOMM	"UPS %s is unavailable"
NOTIFYMSG NOPARENT	"upsmon parent process died - shutdown impossible"
#
# Note that %s is replaced with the identifier of the UPS in question.
#
# Possible values for <notify type>:
#
# ONLINE   : UPS is back online
# ONBATT   : UPS is on battery
# LOWBATT  : UPS has a low battery (if also on battery, it's "critical")
# FSD      : UPS is being shutdown by the master (FSD = "Forced Shutdown")
# COMMOK   : Communications established with the UPS
# COMMBAD  : Communications lost to the UPS
# SHUTDOWN : The system is being shutdown
# REPLBATT : The UPS battery is bad and needs to be replaced
# NOCOMM   : A UPS is unavailable (can't be contacted for monitoring)
# NOPARENT : The process that shuts down the system has died (shutdown impossible)
 
# --------------------------------------------------------------------------
# NOTIFYFLAG - change behavior of upsmon when NOTIFY events occur
#
# By default, upsmon sends walls (global messages to all logged in users)
# and writes to the syslog when things happen.  You can change this.
#
# NOTIFYFLAG <notify type> <flag>[+<flag>][+<flag>] ...
#
NOTIFYFLAG ONLINE	SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT	SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT	SYSLOG+WALL+EXEC
# NOTIFYFLAG FSD	SYSLOG+WALL
NOTIFYFLAG COMMOK	SYSLOG+WALL
NOTIFYFLAG COMMBAD	SYSLOG+WALL
NOTIFYFLAG SHUTDOWN	SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT	SYSLOG+WALL
# NOTIFYFLAG NOCOMM	SYSLOG+WALL
# NOTIFYFLAG NOPARENT	SYSLOG+WALL
#
# Possible values for the flags:
#
# SYSLOG - Write the message in the syslog 
# WALL   - Write the message to all users on the system
# EXEC   - Execute NOTIFYCMD (see above) with the message
# IGNORE - Don't do anything
#
# If you use IGNORE, don't use any other flags on the same line.
 
 
RBWARNTIME 43200
 
NOCOMMWARNTIME 300
 
FINALDELAY 5

Dans l'ordre, on indique à upsmon de tourner en tant qu'utilisateur nut (il est créé par défaut). Ensuite, on indique le ou les onduleurs à surveiller (ligne MONITOR). Vous noterez la relation avec le fichier précédent.

Le fichier est commenté, je passe donc sur le reste. Une petite exception sur le NOTIFYFLAG : Tout ce qui est noté EXEC provoquera une action configurable autre qu'un simple avertissement ou log. C'est donc là que tout se tiendra.


Le dernier fichier de configuration : upssched.conf

Vous avez peut-être noté la ligne :

NOTIFYCMD /usr/sbin/upssched

dans le fichier précédent. C'est ce script qui va déclencher tout le reste et le fichier upssched.conf est ... son fichier de configuration !

# Network UPS Tools - upssched.conf sample file
 
CMDSCRIPT /opt/bin/upssched-cmd
 
PIPEFN /var/run/nut/upssched/upssched.pipe
LOCKFN /var/run/nut/upssched/upssched.lock
# ============================================================================
#
# AT <notifytype> <upsname> <command>
#
# Define a handler for a specific event <notifytype> on UPS <upsname>.
#
# <upsname> can be the special value * to apply this handler to every
# possible value of <upsname>.
#
# Run the command <command> via your CMDSCRIPT when it happens. 
#
# Note that any AT that matches both the <notifytype> and the <upsname> 
# for the current event will be used.
 
#   -----------------------------------------------------------------------
#
# - EXECUTE <command>
#
#   Immediately pass <command> as an argument to CMDSCRIPT.
#
#   Example:
#   If any UPS (*) reverts to utility power, then execute
#   'ups-back-on-line' via CMDSCRIPT.
#
#   AT ONLINE * EXECUTE ups-back-on-line
AT ONBATT * EXECUTE ups-on-battery
AT ONLINE * EXECUTE ups-back-on-line

CMDSCRIPT indique l'emplacement du script qui va être appelé et auquel on va passer les arguments command. Personnellement, j'utilise une version modifiée du script généré par MGE OPS (euh pardon, Personal Solution Pac).

J'ai laissé les commentaires sur AT et EXECUTE, le fichier est entièrement commenté à la base. Pas besoin de déclarer une action pour l'extinction, cela a été fait dans le fichier précédent (SHUTDOWNCMD). Dans mon cas, upsmon va passer le relais à upssched à deux occasions :

  • l'onduleur est en ligne donc branché (ONLINE)
  • l'onduleur est sur batterie donc le secteur a mystérieusement disparu (ONBATT)

Et upssched va passer le relais au script défini dans CMDSCRIPT.

Autrement dit, on a un démon qui tourne en arrière plan et qui gère la communication avec l'onduleur via l'interface et le port décrit dans ups.conf. Ce démon peut être écouté par les utilisateurs défini dans upsd.users et les hôtes définis dans upsd.conf. Ensuite, on fait tourner le programme de monitoring upsmon qui va passer le relais au script /usr/bin/upssched pour la gestion des évènements. Ce dernier va ensuite faire intervenir le script défini dans CMDSCRIPT pour réaliser des actions ou autre.
Qui a dit qu'on aurait pu faire plus simple ?

Attendez, c'est pas fini !!!


Étape 2 : Traitons tout ça

C'est le rôle du script /opt/bin/upssched-cmd basé sur celui de MGE OPS PSP. Je vous livre celui-ci tel quel avec des essais de notification commentés qui ne marchent pas mais ça me sert de pense-bête.

#!/bin/bash
########################################################
# Generated by MGE UPS SYSTEMS - Personal Solution Pac #
#       Support script for NUT - upssched utility      #
########################################################
# release: 0.1                                         #
########################################################
 
# Determine the various paths
if [ -d /etc/nut ]
then
	sysConfPath="/etc/nut/"
else
	if [ -d /etc/ups/ ]
	then
		sysConfPath="/etc/ups/"
	fi
fi
if [ -d /var/run/nut/ ]
then
	statePath="/var/run/nut/"
else
	if [ -d /var/state/ups/ ]
	then
		statePath="/var/state/ups/"
	else
		if [ -d /var/lib/ups/ ]
		then
			statePath="/var/lib/ups/"
		fi
	fi
fi
if [ -f /usr/sbin/upsmon ]
then
	binPath="/usr/bin/"
	sbinPath="/usr/sbin/"
else
	if [ -f /sbin/upsmon ]
	then
		binPath="/bin/"
		sbinPath="/sbin/"
	fi
fi
 
# Source the wizard.conf file to obtain
# specific configuration (ie shutdown criteria/config)
if [ -f ${sysConfPath}wizard.conf ]
then
	. ${sysConfPath}wizard.conf
else
	shutdownBatteryLevel=30
	shutdownBatteryRuntime=-1
fi
 
# Internal constants
False="0"
True="1"
Ups="ellipse@localhost"
 
case $1 in
	# Handles shutdown timer criteria (when triggered,
	# timer has expired, so immediately shutdown)
	ups-on-battery-timer)
		logger -t upssched-cmd "Shutdown timer reached: Calling upsmon -c fsd"
		${sbinPath}upsmon -c fsd
	;;
	# Handles shutdown criteria based on battery runtime or charge
	ups-on-battery)
		sd_batt_level=`expr $shutdownBatteryLevel "!=" -1`
		sd_batt_runtime=`expr $shutdownBatteryRuntime "!=" -1`
		#Affiche la perte du secteur sur l'écran
		#/usr/bin/notify-send -u critical -i /usr/share/icons/ellipsemax.png Onduleur "Perte du secteur"
		# Envoie un SMS pour information
		/usr/bin/php /opt/bin/sms.php "Onduleur" "Perte du Secteur"
 
		if [ $sd_batt_level == $False ] && [ $sd_batt_runtime == $False ]
		then
        	echo "No criteria enabled, exiting"
			exit 0
		else
			while(true)
			do
				# Test if we are still needed
				ups_online="`${binPath}upsc $Ups ups.status | grep OL`"
				if [ -n "$ups_online" ]
				then
					#Affiche le retour du secteur sur l'écran
					#/usr/bin/notify-send -u critical -i /usr/share/icons/ellipsemax.png Onduleur "Retour du secteur"
					# Envoie un SMS pour information
					/usr/bin/php /opt/bin/sms.php "Onduleur" "Retour Secteur"
					logger -t upssched-cmd "UPS is back online. Aborting..."
					exit 0
				else
					# Processing criteria "battery charge"
					if [ $sd_batt_level == $True ]
					then
						# Retrieve and compare current battery charge
						battCharge=`eval '${binPath}upsc $Ups battery.charge'`
 
						# And compare it with the criteria
						if [ `expr $battCharge "<=" $shutdownBatteryLevel` == $True ]
						then
							#Affiche l'arrêt pour niveau batterie atteint sur l'écran
							#/usr/bin/notify-send -u critical -i /usr/share/icons/ellipsemax.png Onduleur "Niveau de Batterie restant atteint : On ferme"
							# Envoie un SMS pour information
							/usr/bin/php /opt/bin/sms.php "Onduleur" "Arrêt : Niveau Batterie atteint"
							logger -t upssched-cmd "BatteryLevel reached ($shutdownBatteryLevel %): Calling upsmon -c fsd"
							${sbinPath}upsmon -c fsd
						fi
					fi # "battery charge" criteria
 
					# Processing criteria "battery runtime"
					if [ $sd_batt_runtime == $True ]
					then
						# Retrieve current remaining runtime
						remRuntime=`eval '${binPath}upsc $Ups battery.runtime'`
 
						# And compare it with the criteria
						if [ `expr $remRuntime "<=" $shutdownBatteryRuntime` == $True ]
						then
							#Affiche l'arrêt pour temps de batterie restant atteint sur l'écran
							#/usr/bin/notify-send -u critical -i /usr/share/icons/ellipsemax.png Onduleur "Temps de Batterie restant atteint : On ferme"
							# Envoie un SMS pour information
							/usr/bin/php /opt/bin/sms.php "Onduleur" "Arrêt : Temps Batterie atteint"
							logger -t upssched-cmd "BatteryRuntime reached ($shutdownBatteryRuntime s): calling upsmon -c fsd"
							${sbinPath}upsmon -c fsd
						fi
					fi # "battery runtime" criteria
				fi # ... still needed
 
				# wait 5 seconds before continuing the loop
				sleep 5
			done
		fi
	;;
	commbad)
		#Affiche la perte de communication sur l'écran
		#/usr/bin/notify-send -u low -i /usr/share/icons/ellipsemax.png Onduleur "Perte de la communication avec l'onduleur"
	;;
	commok)
		#Affiche le retour de communication sur l'écran
		#/usr/bin/notify-send -u low -i /usr/share/icons/ellipsemax.png Onduleur "Retour de la communication avec l'onduleur"
	;;
	ups-back-on-line)
		#Affiche le retour du secteur sur l'écran
		#/usr/bin/notify-send -u critical -i /usr/share/icons/ellipsemax.png Onduleur "Retour du secteur"
		# Envoie un SMS pour information
		/usr/bin/php /opt/bin/sms.php "Onduleur" "Retour Secteur"
		# Remove possible timers
		/bin/rm -f ${statePath}ups-on-battery
		/bin/rm -f ${statePath}ups-on-battery-timer
	;;
	*)
		logger -t upssched-cmd "Unrecognized command: $1"
	;;
esac

Le fichier wizard.conf généré par MGE OPS PSP n'existe pas dans mon cas, j'ai donc codé en dur les valeurs qui m'intéressent :
shutdownBatteryLevel=30, l'onduleur provoquera l'extinction du PC en arrivant à moins de 30% de batterie restante
shutdownBatteryRuntime=-1, pas de condition sur le temps de maintien de tension sous batterie
Ups="ellipse@localhost", on retrouve le petit nom de mon onduleur

Pour le reste, il suffit de spécifier la ou les commandes que l'on veut exécuter pour chaque cas. En pratique, hors mes essais de notification via notify-send qui foirent, je me contente d'exécuter un script PHP qui envoie un SMS sur mon portable me décrivant ce qui se passe.

Je reviendrai sur la procédure utilisée dans un futur billet.

Et comme je souhaite aussi être prévenu du redémarrage de mon ordinateur s'il a été arrêté et que le courant revient (il est configuré pour redémarrer suite à une coupure de l'alimentation dans le BIOS), j'ai aussi inclus la ligne suivante dans le fichier /etc/rc.d/rc.local :

/usr/bin/php /opt/bin/sms.php "Ordinateur" "Démarrage terminé"


Tout ça a l'air très lourd pour gérer un simple onduleur mais cela prend tout son sens lorsqu'il s'agit de gérer très finement de nombreux modules.