1
===========================
2
plainbox-template-units (7)
3
===========================
8
This page documents the Plainbox template units syntax and runtime behavior
13
The template unit is a variant of Plainbox unit types. A template is a skeleton
14
for defining additional units, typically job definitions. A template is defined
15
as a typical RFC822-like Plainbox unit (like a typical job definition) with the
16
exception that all the fields starting with the string ``template-`` are
17
reserved for the template itself while all the other fields are a definition of
18
all the eventual instances of the template.
20
Template-Specific Fields
21
------------------------
23
There are four fields that are specific to the template unit:
26
Name of the unit type this template will generate. By default job
27
definition units are generated (as if the field was specified with the
28
value of ``job``) eventually but other values may be used as well.
30
This field is optional.
32
``template-resource``:
33
Name of the resource job (if it is a compatible resource identifier) to use
34
to parametrize the template. This must either be a name of a resource job
35
available in the namespace the template unit belongs to *or* a valid
36
resource identifier matching the definition in the ``template-imports``
39
This field is mandatory.
42
A resource import statement. It can be used to refer to arbitrary resource
43
job by its full identifier and (optionally) give it a short variable name.
45
The syntax of each imports line is::
47
IMPORT_STMT :: "from" <NAMESPACE> "import" <PARTIAL_ID>
48
| "from" <NAMESPACE> "import" <PARTIAL_ID>
51
The short syntax exposes ``PARTIAL_ID`` as the variable name available
52
within all the fields defined within the template unit. If it is not a
53
valid variable name then the second form must be used.
55
This field is sometimes optional. It becomes mandatory when the resource
56
job definition is from another provider namespace or when it is not a valid
57
resource identifier and needs to be aliased.
60
A resource program that limits the set of records from which template
61
instances will be made. The syntax of this field is the same as the syntax
62
of typical job definition unit's ``requires`` field, that is, it is a
65
When defined, the expression is evaluated once for each resource object and
66
if it evaluates successfully to a True value then that particular resource
67
object is used to instantiate a new unit.
69
This field is optional.
74
When a template is instantiated, a single record object is used to fill in the
75
parametric values to all the applicable fields. Each field is formatted using
76
the python formatting language. Within each field the record is exposed as the
77
variable named by the ``template_resource`` field. Record data is exposed as
78
attributes of that object.
80
The special parameter ``__index__`` can be used to iterate over the devices
81
matching the ``template-filter`` field.
83
Migrating From Local Jobs
84
-------------------------
86
Migration from local jobs is mostly straightforward. Apart from one gotcha the
87
process is as follows:
89
1. Look at the data that was used to *instantiate* job definitions by the old
90
local job. Write them down.
91
2. Ensure that all of the instantiated template data is exposed by exactly one
92
resource. This may be the commonly-used checkbox ``device`` resource job or
93
any custom resource job but it has to be all contained in one resource. Data
94
that used to be computed partially by the resource and partially by the
95
local job needs to be computed as additional attributes (fields) of the
97
3. Replace the boilerplate of the local job (typically a ``cat``, here-document
98
piped to ``run-templates`` and ``filter-templates``) with the equivalent
99
``template-resource`` and ``template-filter`` fields.
100
4. Remove the indentation so that all of the job definition is aligned to the
101
left of the paragraph.
102
5. Re-validate the provider to ensure that everything looks okay.
103
6. Re-test the job by running it.
105
The only gotcha is related to step two. It is very common for local jobs to do
106
some additional computation. For example many storage tests compute the path
107
name of some ``sysfs`` file. This has to be converted to a readily-available
108
path that is provided by the resource job.
110
Another thing to remember is that Plainbox templates use Python syntax for
111
their fields. That means that some characters have to be escaped. For instance,
112
``${PLAINBOX_SESSION_SHARE}/test-file`` has to be escaped to
113
``${{PLAINBOX_SESSION_SHARE}}/test-file``.
121
The following example contains a simplified template that instantiates to a
122
simple storage test. The test is only instantiated for devices that are
123
considered *physical*. In this example we don't want to spam the user with a
124
long list of loopback devices. This is implemented by exposing that data in the
125
resource job itself::
130
echo 'path: /dev/sda'
131
echo 'has_media: yes'
134
echo 'path: /dev/cdrom'
138
echo 'path: /dev/loop0'
139
echo 'has_media: yes'
142
The template defines a test-storage-``XXX`` test where ``XXX`` is replaced by
143
the path of the device. Only devices which are *physical* according to some
144
definition are considered for testing. This means that the record related to
145
``/dev/loop0`` will be ignored and will not instantiate a test job for that
146
device. This feature can be coupled with the existing resource requirement to
147
let the user know that we did see their CD-ROM device but it was not tested as
148
there was no inserted media at the time::
151
template-resource: device
152
template-filter: device.physical == 'yes'
153
requires: device.has_media == 'yes'
154
id: test-storage-{path}
156
command: perform-testing-on --device {path}
161
Here is a real life example from a provider. We have the following local job
162
that generates a job for each hard drive available on the system::
165
_summary: Check stats changes for each disk
167
requires: device.category == 'DISK'
169
This test generates some disk activity and checks the stats to ensure drive
170
activity is being recorded properly.
172
cat <<'EOF' | run_templates -t -s 'udev_resource | filter_templates -w "category=DISK"'
174
category_id: 2013.com.canonical.plainbox::disk
175
id: disk/stats_`ls /sys$path/block`
178
device.path == "$path"
179
block_device.`ls /sys$path/block`_state != 'removable'
181
command: disk_stats_test `ls /sys$path/block | sed 's|!|/|'`
182
description: This test checks disk stats, generates some activity and rechecks stats to verify they've changed. It also verifies that disks appear in the various files they're supposed to.
185
After migration to a template unit job, it looks like this::
188
template-resource: device
189
template-filter: device.category == 'DISK'
191
category_id: 2013.com.canonical.plainbox::disk
192
id: disk/stats_{name}
194
device.path == "{path}"
195
block_device.{name}_state != 'removable'
197
command: disk_stats_test {name}
198
_description: This test checks {name} disk stats, generates some activity and rechecks stats to verify they've changed. It also verifies that disks appear in the various files they're supposed to.
200
The ``template-resource`` used here (``device``) refers to a resource job using the ``udev_resource`` script to get information about the system. The ``udev_resource`` script returns a list of items with attributes such as ``path`` and ``name``, so we can use these directly in our template.
202
We end up with a shorter (from 19 lines to 11!) and more readable template.