Template Systems are well known in Webprogramming. But they are useful for sysadmins too. In File:FaiTS-0.01.tar.gz i try to combine the class concept of fai with templates.
My goal is to create a config file for a router, /etc/network/interfaces, during a fai installation. Here is, what the target file finally should look like:
# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8) # The loopback interface auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 172.16.240.1 netmask 255.255.255.0 network 172.16.240.0 broadcast 172.16.240.255 auto eth1 iface eth1 inet static address 192.168.1.5 netmask 255.255.255.0 network 192.168.1.0 broadcast 192.168.1.255 auto eth2 iface eth2 inet static address 22.214.171.124 netmask 255.255.255.0 network 126.96.36.199 broadcast 188.8.131.52 gateway 184.108.40.206
Here is how a template for this file could look like. The way i've implemented it now, template-toolkit  is used as template engine. And the template-files are located in a directory tree /fai/templates, similar to the /fai/files tree in fai:
# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8) # The loopback interface auto lo iface lo inet loopback [% FOREACH name = interfaces.keys.sort %] [% i = interfaces.$name -%] auto [% name %] iface [% name %] inet static address [% i.address %] netmask [% i.netmask %] [% IF i.network %] network [% i.network %] [% END %] [% IF i.broadcast %] broadcast [% i.broadcast %] [% END %] [% IF i.gateway %] gateway [% i.gateway %] [% END %] [% END %]
Everything between the [% %] brackets ist template-toolkit syntax. In this case, a variable named interfaces contains a list of interface definitions. Each interface-definition is assigned a to a varable named i. Each instance of i contains the parameters address and netmask, some instances also contain parameters network, broadcast, gateway.
But where to get the data from, to fill in these variables? An nice format for flat files is YAML [ http://www.yaml.org ]:
interfaces: eth0: address: 172.16.240.5 netmask: 255.255.255.0 network: 172.16.240.0 broadcast: 172.16.240.255 eth1: address: 192.168.1.5 netmask: 255.255.255.0 network: 192.168.1.0 broadcast: 192.168.1.255
interfaces: eth0: address: 172.16.240.1 eth2: address: 220.127.116.11 netmask: 255.255.255.0 network: 18.104.22.168 broadcast: 22.214.171.124 gateway: 126.96.36.199
class based merge
Hey, what did i win so far? Didn't i have to write one config file before ( see man interfaces (5) ), and now i have to write two files ( .yml and .tt )?
Yes, but now you're more flexible: only .yml-files that belog two your install client's class are choosen. Data in these files is aggregated / merged together. Than, again respecting fai classes, template files are choosen, where this data is filled in.
And .yml files are only the first data-source implemented. Other alternatives are an LDAP-directory, a database or the scripts called in task_class or task_configure. Each backend adding more data.
And of course I'm not limited to only fill one template with this data. I could create a template /templates/etc/udev/rules.d/30-net_persistent_names.rules/ROUTER.tt, and /templates/etc/hosts/ROUTER.tt, and so on.
Cool. Is this already implemented?
yes. i've coded a proof of concept using Template-Toolkit, YAML and Hash::Merge.
File:FaiTS-0.01.tar.gz it and type:
aptitude install libclone-perl libyaml-perl libtemplate-perl tar xzf FaiTS-0.01.tar.gz cd FaiTS-0.01/ perl Makefile.PL make test
If you get lots of errors and a final "All tests successful" then the code should work ...
To see something happen, you can set CLEANUP to 0 in line 27 of the file t/FaiTS.t and then run
If you then type
and you'll see the .yml' (YAML) files with config data, .tt ( Template-Toolkit) templates and a target directory with a freshly created etc/network/interfaces file.
There are two "executables" in this package:
First i started only with bin/fai-ts.pl. But i don't like skripts getting to long, so i tried to source out as much functions as possible to a perl module lib/FaiTS.pm. For every subroutine in lib/FaiTS.pm there is a test in t/FaiTS.t. This is to further document the module, and to make sure that future changes to lib/FaiTS.pm don't break things.
Dokumentation is contained in the source as POD and accessable via perldoc:
perldoc lib/FaiTS.pm perldoc bin/fai-ts.pl