wiki:Development/Code/TemplateToolkit

Using Template::Toolkit in Quattor components

Introduction

In some cases, most of the code deals with printing correctly to a file, and it could be simplified a lot by using a basic template instead of zillions of print statements.

For instance, the accounts component could do the following to fully generate /etc/passwd:

# WARNING: UNTESTED!!!

use Template;
use CAF::FileWriter;

my $fh = CAF::FileWriter->new("/etc/passwd", log => $self);
my $tree = $config->getElementGetTree();
# This one is needed for Template::Toolkit to work with the CAF::File*
my $tpl = Template->new();
$tpl->process(\*DATA, $tree, $fh);
$fh->close();

__DATA__

[% FOREACH user in users.pairs %]
[% user.key %]:x:[% user,value.uid %]:[% user.value.gid %]:[% user.value.comment %]:[% user.value.homeDir %]:[% user.value.shell %]
[% END %]

The code would thus only choose what accounts are valid or invalid in the system.

The advantage in some sitiations is that the representation is completely isolated from the logic that will generate it. And it may allow for a closer correspondency with the schema, if wanted.

Sometimes one template won't be enough: there will be several files of several types to print (f.i, nagios-related configuration). We still have to see how to ship different templates.

Also, we have to be careful so that we don't replace ugly code with even uglier templates!!

Proposal for the Template Toolkit

Template storage

Since there may be several components using the Template toolkit, I suggest to store them under /usr/share/quattor/templates. We'd then create one subdir for each component, like this:

  • /usr/share/quattor/templates/accounts/
  • /usr/share/quattor/templates/sudo/

Does it cause any confusion with the Pan templates (mis-)installed under /usr/share/doc? If so, we can choose /usr/share/quattor/tt.

Invoking the templates

All components using Template::Toolkit will need the smae configuration, namely; the INCLUDE_PATH (that should be configurable) and the ability to access variables that start with an underscore. It's easiest to encapsulate all this in the base class. Then, our component would just request this base class the Template object:

my ($self, $config) = @_;

my $t = $config->getElement("/software/components/foo")->getTree();
my $tpl = $self->template();
my $fh = CAF::FileWriter->open("/etc/file", log => $self);
print $fh "#A common header\n";
$tpl->process("component/template.tt", $t, $fh);

Alternatively, the base class may do the parsing on our behalf (is this preferred?):

my $t = ...->getTree();
my $fh = CAF::FileWriter->open(...);
$self->process_template("component/template.tt", $t, $fh);
Last modified 10 years ago Last modified on Apr 15, 2014, 10:20:54 PM