NUT configuration management with Augeas ======================================== Introduction ------------ Configuration has long been one of the two main NUT weaknesses. This is mostly due to the framework nature of NUT, and its many components and features, which make NUT configuration a very complex task. In order to address this point, NUT now provides configuration tools and manipulation abstraction, to anybody who want to manipulate NUT configuration, through Augeas lenses and modules. From link:http://augeas.net[Augeas homepage]: "Augeas is a configuration editing tool. It parses configuration files in their native formats and transforms them into a tree. Configuration changes are made by manipulating this tree and saving it back into native config files." In other words, Augeas is the dreamed Registry, with all the advantages (such as a uniform interface and tools), and the added bonus of being free/libre open source software and letting liberty on configuration file format. Requirements ------------ To be able to use Augeas with NUT, you will need to install Augeas, and also the NUT provided lenses, which describe NUT configuration files format. Augeas ~~~~~~ Having link:http://augeas.net[Augeas] installed. You will need at least version 0.5.1 (prior versions may work too, reports are welcome). As an example, on Debian and derivatives, do the following: $ apt-get install augeas-lenses augeas-tools And optionally: $ apt-get install libaugeas0 libaugeas-dev python-augeas On RedHat and derivatives, you have to install the packages 'augeas' and 'augeas-libs'. [[augeas_user]] NUT lenses and modules for Augeas ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These are the *.aug files in the present directory. You can either install the files to the right location on your system, generally in '/usr/share/augeas/lenses/', or use these from NUT source directory ('nut/scripts/augeas'). The latter is to be preferred for the time being. Create a test sandbox --------------------- NOTE: for now, it's easier to include an existing /etc/nut/ directory. $ export AUGEAS_ROOT=./augeas-sandbox $ mkdir $AUGEAS_ROOT $ sudo cp -pr /etc/nut $AUGEAS_ROOT $ sudo chown -R $(id -nu):$(id -ng) $AUGEAS_ROOT Start testing and using ----------------------- Augeas provides many tools and link:http://augeas.net/download.html[languages bindings] (Python, Perl, Java, PHP, Ruby, ...), still with the same simple logic. This chapter will only illustrate some of these. Refer to the language binding's help and link:http://augeas.net/docs/index.html[Augeas documentation] for more information. Shell ~~~~~ Start an augeas shell using: $ augtool -b NOTE: if you have not installed NUT lenses, add '-I/path/to/nut/scripts/augeas'. From there, you can perform different actions like: - list existing nut related files: augtool> ls /files/etc/nut/ nut.conf/ = (none) upsd.users/ = (none) upsmon.conf = (none) ups.conf/ = (none) upsd.conf/ = (none + or using: + augtool> match /files/etc/nut/* /files/etc/nut/nut.conf = (none) /files/etc/nut/upsd.users = (none) /files/etc/nut/upsmon.conf = (none) /files/etc/nut/ups.conf = (none) /files/etc/nut/upsd.conf = (none) NOTE: if you don't see anything, you may search for error messages by using: + augtool> ls /augeas/files/etc/nut/*/errors and augtool> get /augeas/files/etc/nut/ups.conf/error/message /augeas/files/etc/nut/ups.conf/error/message = Permission denied - create a new device entry (in ups.conf), called 'augtest': augtool> set /files/etc/nut/ups.conf/augtest/driver dummy-ups augtool> set /files/etc/nut/ups.conf/augtest/port auto augtool> save - list the devices using the 'usbhid-ups' driver: augtool> match /files/etc/nut/ups.conf/*/driver dummy-ups C ~ A library is available for C programs, along with pkg-config support. You can get the compilation and link flags using the following code in your configure script or Makefile: CFLAGS="`pkg-config --silence-errors --cflags augeas`" LDFLAGS="`pkg-config --silence-errors --libs augeas`" Here is an code sample using this library for NUT configuration: -------------------------------------------------------------------------------- augeas *a = aug_init(NULL, NULL, AUG_NONE); ret = aug_match(a, "/files/etc/nut/*", &matches_p); ret = aug_set(a, "/files/etc/nut/ups.conf/augtest/driver", "dummy-ups"); ret = aug_set(a, "/files/etc/nut/ups.conf/augtest/port", "auto"); ret = aug_save(a); -------------------------------------------------------------------------------- Python ~~~~~~ The `augeas` class abstracts access to the configuration files. $ python Python 2.5.1 (r251:54863, Apr 8 2008, 01:19:33) [GCC 4.3.0 20080404 (Red Hat 4.3.0-6)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import augeas >>> a = augeas.augeas() >>> a.match("/files/etc/nut/*") ['/files/etc/nut/upsd.users', '/files/etc/nut/upsmon.conf', '/files/etc/nut/ups.conf', '/files/etc/nut/upsd.conf'] >>> a.set("/files/etc/nut/ups.conf/augtest/driver", "dummy-ups") True >>> a.set("/files/etc/nut/ups.conf/augtest/port", "auto") True >>> a.save() True >>> $ grep -A 2 augtest /etc/nut/ups.conf [augtest] driver=dummy-ups port=auto Perl ~~~~ The Perl binding is available through CPAN and packages. use Config::Augeas; my $aug = Config::Augeas->new( root => $aug_root ) ; my @a = $aug->match("/files/etc/nut/*") ; my $nb = $aug->count_match("/files/etc/nut/*") ; $aug->set("/files/etc/nut/ups.conf/augtest/driver", "dummy-ups") ; $aug->set("/files/etc/nut/ups.conf/augtest/port", "auto") ; $aug->save ; Test the conformity testing module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Existing configuration files can be tested for conformity. To do so, use: $ augparse -I ./ ./test_nut.aug Complete configuration wizard example ------------------------------------- Here is a Python example that generate a complete and usable standalone configuration: -------------------------------------------------------------------------------- import augeas device_name="dev1" driver_name="usbhid-ups" port_name="auto" a = augeas.augeas() # Generate nut.conf a.set("/files/etc/nut/nut.conf/MODE", "standalone") # Generate ups.conf # FIXME: chroot, driverpath? a.set(("/files/etc/nut/ups.conf/%s/driver" % device_name), driver_name) a.set(("/files/etc/nut/ups.conf/%s/port" % device_name), port_name) # Generate upsd.conf a.set("/files/etc/nut/upsd.conf/#comment[1]", "just to touch the file!") # Generate upsd.users user = "admin" a.set(("/files/etc/nut/upsd.users/%s/password" % user), "dummypass") a.set(("/files/etc/nut/upsd.users/%s/actions/SET" % user), "") # FIXME: instcmds lens should be fixed, as per the above rule a.set(("/files/etc/nut/upsd.users/%s/instcmds" % user), "ALL") monuser = "monuser" monpasswd = "******" a.set(("/files/etc/nut/upsd.users/%s/password" % monuser), monpasswd) a.set(("/files/etc/nut/upsd.users/%s/upsmon" % monuser), "primary") # Generate upsmon.conf a.set("/files/etc/nut/upsmon.conf/MONITOR/system/upsname", device_name) # Note: we prefer to omit localhost, not to be bound to a specific # entry in /etc/hosts, and thus be more generic #a.set("/files/etc/nut/upsmon.conf/MONITOR/system/hostname", "localhost") a.set("/files/etc/nut/upsmon.conf/MONITOR/powervalue", "1") a.set("/files/etc/nut/upsmon.conf/MONITOR/username", monuser) a.set("/files/etc/nut/upsmon.conf/MONITOR/password", monpasswd) a.set("/files/etc/nut/upsmon.conf/MONITOR/type", "primary") # FIXME: glitch on the generated content a.set("/files/etc/nut/upsmon.conf/SHUTDOWNCMD", "/sbin/shutdown -h +0") # save config a.save() a.close() --------------------------------------------------------------------------------