-
Notifications
You must be signed in to change notification settings - Fork 32
Recipe XML Format
This page needs reviewing!
This document describes the format of recipe XML files accepted by lnst-ctl. This is more of a reference guide than a manual or how-to. If you want to know how to structure your very first recipe, please refer to How to Structure LNST Recipe.
Here is a map of the XML format. To get a description of a tag, click on its name.
<lnstrecipe>
|
LNST recipes have the following structure:
<lnstrecipe>
<machines>
...
</machines>
<task>
<task>
...
</lnstrecipe>
You need to provide information about the machines required for the test. All these information are listed under the <machines>
tag. After that you can specify one or more tasks to be executed.
Each task is enclosed in a <task>
tag. Think of tasks as test cases.
Root element required in every valid LNST recipe. | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<machines> <task>
|
Optional: | None |
There must be exactly one <machines>
tag provided in every valid LNST recipe. The tag contains an enumeration of machine details (in the form of <machine>
tags) required to perform the test.
Contains a list of machines required to perform the test. | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<machine>
|
Optional: | None |
<machines>
<machine id="1">...</machine>
<machine id="peanut">...</machine>
...
</machines>
Each machine tag can contains two sections, parameters within the <params>
tag and enumeration of the its interfaces (<interfaces>
tag). The former is optional, the latter mandatory. You can request a number of parameters for the machine to have, such as a type and version of the operating system, even some hardware details.
Specification of a machine that is required to perform the test. | ||
Attributes: | Required: | [string] id: An unique identifier of the machine |
Optional: | None | |
Children: | Required: |
<interfaces>
|
Optional: |
<params>
|
The parameters of a machine. | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<param>
|
Optional: | None |
A single parameter of a machine. | ||
Attributes: | Required: |
[string] name: The name of the parameter [string] value: The required value |
Optional: | None | |
Children: | Required: | None |
Optional: | None |
<machine id="peanut">
<params>
<param name="os" value="rhel6.2"/>
<param name="type" value="server"/>
</params>
<interfaces>...</interfaces>
</machine>
The second (and far more complicated) part of machine specification is the network configuration. In this section of the recipe, you need to specify what interfaces you would like for the machine to have and how they should be configured. You will be able to refer to the herein configured devices from the tasks.
List of interfaces of a machine with their configuration | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: | one of following:<eth> <bond> <team> <vlan> <bridge> <macvlan>
|
Optional: | None |
Test interface configuration | ||
Attributes: | Required: | [string] id: unique identifier of the interface [string] network: the network segment this network device is connected to; Note: required for <eth> only
|
Optional: | None | |
Children: | Required: | None |
Optional: |
<addresses> <options> Note: applicable to all except <eth> <params> Note: applicable to <eth> only <slaves> Note: applicable to all except <eth>
|
Address can be assigned to all interface types.
A list of addresses to be assigned to this interface | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<address>
|
Optional: | None |
An address | ||
Attributes: | Required: | [string] value: IP or IP6 address |
Optional: | None | |
Children: | Required: | None |
Optional: | None |
Some interface types have configurable options. Use the option child tag to set them.
A list of configuration options | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<option>
|
Optional: | None |
An option | ||
Attributes: | Required: |
[string] name: option name [string] value: option value; Note: Value can also be passed as tag content |
Optional: | None | |
Children: | Required: | None |
Optional: | None |
The parameters tag allows you to request specific details about the interface, such as the driver, NIC model, etc.
The parameters of an interface. | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<param>
|
Optional: | None |
A single parameter of an interface. | ||
Attributes: | Required: |
[string] name: The name of the parameter [string] value: The required value |
Optional: | None | |
Children: | Required: | None |
Optional: | None |
Interfaces that are not associated with any physical device (don't have a phys_id assigned) usually have slave interfaces assigned.
A list of slave interfaces | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: |
<slave>
|
Optional: | None |
Slave interface | ||
Attributes: | Required: | [string] id: slave identifier |
Optional: | None | |
Children: | Required: | None |
Optional: | None |
LNST is even able to configure multiple network devices into bonds and teams. It can also perform VLAN and bridging configuration for you.
We will have a closer look at the configuration of different interface types in the following sections.
When setting up a plain Ethernet interface you need to specify an id, type, and a network. The network attribute tells LNST to which network segment is this interface connected to. You also need to configure an address using the addresses tag. See the example below:
<interfaces>
<eth id="testiface" network="net1">
<addresses>
<address value="192.168.100.226/24"/>
</addresses>
</eth>
<eth id="2" network="net1">
<addresses>
<address value="192.168.100.240/24"/>
</addresses>
</eth>
</interfaces>
The configuration of a bond is a little more tricky. Firstly, you need to have two ethernet interfaces which will be part of the bond.
<eth id="test_if1" network="net1"/>
<eth id="test_if2" network="net2"/>
After that, it is necessary to create a new interface of type bond. Then you will need to specify the slave interfaces that will be part of the bond:
<slaves>
<slave id="test_if1"/>
<slave id="test_if2"/>
</slaves>
The bonding driver also has a lot of configurable parameters. You can specify any of them using the option tag. For the full list of available parameters, please refer to the original bonding driver documentation.
<options>
<option name="mode" value="0"/>
<option name="miimon" value="100"/>
</options>
<machine id="testmachine1">
<interfaces>
<eth id="test_if1" network="net1"/>
<eth id="test_if2" network="net1"/>
<bond id="test_bond">
<slaves>
<slave id="test_if1"/>
<slave id="test_if2"/>
</slaves>
<options>
<option name="mode" value="0"/>
<option name="miimon" value="100"/>
</options>
<addresses>
<address value="192.168.200.1/24"/>
</addresses>
</bond>
</interfaces>
</machine>
First you need to initialize the interface which will be used to configure the VLAN:
<eth id="test_if1" network="net1"/>
The interface is "enslaved" under the newly created VLAN interface using the [#element:slaves ] tag:
<slaves>
<slave id="test_if1"/>
</slaves>
The only supported option for VLANs is vlan_tci. It can be configured as follows:
<options>
<option name="vlan_tci" value="10"/>
</options>
<machine id="testmachine1">
<interfaces>
<eth id="1" network="net1"/>
<vlan id="testifc1"/>
<options>
<option name="vlan_tci" value="10"/>
</options>
<slaves>
<slave id="1"/>
</slaves>
<addresses>
<address value="192.168.200.2/24"/>
</addresses>
</vlan>
</interfaces>
</machine>
First you need to initialize the underlying interface which will be used to configure the MAC-VLAN:
<eth id="test_if1" network="net1"/>
The interface is "enslaved" under the newly created MAC-VLAN interface using the [#element:slaves ] tag:
<slaves>
<slave id="test_if1"/>
</slaves>
<machine id="testmachine1">
<interfaces>
<eth id="1" network="net1"/>
<vlan id="testifc1"/>
<options>
<option name="vlan_tci" value="10"/>
</options>
<slaves>
<slave id="1"/>
</slaves>
<addresses>
<address value="192.168.200.2/24"/>
</addresses>
</vlan>
</interfaces>
</machine>
First you need to initialize the interfaces which will be used to configure the team:
<eth id="1" phys_id="1"/>
<eth id="2" phys_id="2"/>
<eth id="3" phys_id="3"/>
The interfaces are "enslaved" under the newly created team interface using the [#element:slaves ] tag:
<slaves>
<slave id="1"/>
<slave id="2"/>
<slave id="3"/>
</slaves>
You can put a JSON (JavaScript Object Notation) configuration for the team device directly in the LNST recipe:
<options>
<option name="teamd_config">
{
"hwaddr": "00:11:22:33:44:55",
"runner": {"name": "roundrobin"}
}
</option>
</options>
<interfaces>
<eth id="1" network="net1"/>
<eth id="2" network="net1"/>
<eth id="3" network="net1"/>
<team id="testiface">
<options>
<option name="teamd_config">
{
"hwaddr": "00:11:22:33:44:55",
"runner": {"name": "roundrobin"}
}
</option>
</options>
<slaves>
<slave id="1"/>
<slave id="2"/>
<slave id="3"/>
</slaves>
<addresses>
<address value="{$testip}"/>
<address value="{$testip6}"/>
</addresses>
</team>
</interfaces>
First you need to initialize the interfaces which will be used to configure the bridge:
<eth id="1" network="net1"/>
<eth id="2" network="net1"/>
<eth id="3" network="net1"/>
The interfaces are "enslaved" under the newly created team interface using the [#element:slaves ] tag:
<slaves>
<slave id="1"/>
<slave id="2"/>
<slave id="3"/>
</slaves>
In case of the bridge the addresses are configured directly on the interfaces.
<interfaces>
<eth id="1" network="net1">
<addresses>
<address value="192.168.1.101"/>
</addresses>
</eth>
<eth id="2" network="net2">
<addresses>
<address value="192.168.1.102"/>
</addresses>
</eth>
<bridge id="lnstbr">
<slaves>
<slave id="1"/>
<slave id="2"/>
</slaves>
</bridge>
</interfaces>
Tasks can be thought of as test cases. There should be one task for one case. There may be one or more tasks in each recipe.
A sequence of commands forming a single test case | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: | None |
Optional: | one of following:
<run> <ctl_wait> <config> <wait> <intr> <kill>
|
<task>
<command machine_id="1" timeout="30" type="exec" value="sleep 5" />
<command machine_id="1" timeout="30" type="test" value="IcmpPing">
<options>
<option name="addr" value="{ip(2,1)}"/>
<option name="count" value="40"/>
<option name="interval" value="0.2"/>
<option name="limit_rate" value="95"/>
</options>
</command>
<command machine_id="1" timeout="30" type="test" value="IcmpPing" bg_id="1">
<options>
<option name="addr" value="{ip(2,1)}"/>
<option name="count" value="40"/>
<option name="interval" value="0.2"/>
<option name="limit_rate" value="95"/>
</options>
</command>
<command type="ctl_wait" value="5"/>
<command machine_id="1" timeout="30" type="intr" value="1"/>
</task>
There are several basic command types. Specifics of each one of them are described further bellow.
Test interface configuration | ||
Attributes: | Required: * [_string_] **machine_id** machine to run this command on || ||[_enum_] **type** ||command type BR **Values**: _exec_, _ctl_wait_, _intr_, _kill_, _system_config_, _test_, _wait_ || || **Optional:**||[_string_] **bg_id** ||execute command in background with a specific **bg_id** (it will be used later to interrupt or kill it) || || ||[_int_] **timeout** ||failsafe timeout to terminate command execution in case it hangs || || ||[_string_] **option** ||some command types may accept an option BR **Valid for:** _system_config_ || || ||[_string_] **value** ||some command types can accept a value BR **Valid for:** _exec_, _ctl_wait_, _intr_, _kill_, _system_config_, _test_, _wait_ || || ||[_string_] **option** ||some command types may accept an option BR **Valid for:** _system_config_ || | |
Children: | Required: | None |
Optional: |
<options> |
Set of command options | ||
Attributes: | Required: | None |
Optional: | None | |
Children: | Required: | None |
Optional: |
<options> |
Option assignment | ||
Attributes: | Required:
[string] name option name (this is test-specific) || ||[_string_] value ||option value BR Note: If the value is longer, you can omit this attribute and put it in the contents of the tag. || |
|
Optional: | None | |
Children: | Required: | None |
Optional: | None |
<command machine_id="1" timeout="30" type="exec" value="sleep 5" />
Commands execute in sequential order from the controller's perspective, unless you specify any of them to run in the background. To do so, you need to add a bg_id attribute to the command you want to run in the background. After doing so, you need to match the background command with one of the termination commands -- a kill, an interrupt, or a wait.
Using kill to terminate the command will end its life with SIGKILL. In the following example, the controller will execute a ping test on machine 1 in the background. It will wait for 5 seconds and then kill the ping_cmd.
<command machine_id="1" type="test" value="IcmpPing" bg_id="ping_cmd">
<options>
<option name="addr" value="{ip(2,1)}"/>
<option name="count" value="40"/>
</options>
</command>
<command type="ctl_wait" value="5"/>
<command machine_id="1" type="kill" value="ping_cmd"/>
Interrupt is very similar to kill. But it will send SIGINT instead.
<command machine_id="1" type="intr" value="ping_cmd"/>
Wait will not signal the background process, but instead it will wait for it to terminate.
<command machine_id="1" type="wait" value="ping_cmd"/>
This command is exceptional in the sense that it is not executed on any of the slaves. Instead it is run on the controller machine itself. Therefore it does not require a machine_id. This command can be used to make the controller wait for some time while the slaves are executing commands in the background.
<!-- Controller will wait for 5 seconds -->
<command type="ctl_wait" value="5"/>
Type exec allows you to actually execute shell commands on slave machines directly from the recipe. Needless to say, this type of command is intended to be used sporadically for short commands (such as a bash script or third party tool execution). Please do not write programs or scripts using this type of command. Also, if you need to configure a system property using /proc or /sys, please use [#system_config system_config] instead. It will take care of the clean-up at the end of the recipe. It is a good idea to set a timeout, because the command could hang.
<command machine_id="1" type="exec" timeout="30" value="tcpdump -i {devname(2, 1)}"/>
If you have any third-party tools set up with LNST, you can use the from attribute to specify which one LNST should use.
<command machine_id="1" type="exec" timeout="30" from="multicast" value="./igmp_query -a 192.168.1.101"/>
[=#system_config] System config is the native way for making changes in system configuration through /proc/ or /sys/ interface. There are two versions of the command. One-line or multi-line command that will allow you to set more options at once. Both versions are demonstrated in the following example:
<command machine_id="1" type="system_config" option="/proc/sys/net/ipv4/igmp_max_memberships" value="5" persistent="true"/>
<command machine_id="1" type="system_config">
<options>
<option name="/proc/sys/net/ipv4/igmp_max_memberships" value="5"/>
<option name="/proc/sys/net/ipv4/conf/{devname(2,1)}/force_igmp_version" value="2"/>
</options>
</command>
The persistent attribute to the first tag denotes, that that option should not be cleaned up at the end of the task. Use this if you need to set the options persistently through multiple tasks or in case the option cannot be reverted back easily (some files in /proc/ return different values than they expect, so LNST will fail to reset them to the previous state. You will need to revert the value back to the original by yourself.
This class is used for executing test modules of LNST. Some modules, such as Tests/TestIcmpPing, Tests/TestIperf, or Tests/TestMulticast are direclty a part of LNST distribution, but you can add more of your custom or any other third-party modules as well. Each module can accept a different set of options through the [#element:cmd_options ] tag. Please refer to the documentation of the respective module for more information. It is also a good idea to provide a timeout, because there could be a bug in the module. See the example of IcmpPing below, other test modules are documented Tests:
<command machine_id="1" type="test" value="IcmpPing" timeout="30">
<options>
<option name="addr" value="{ip(2,1)}"/>
<option name="count" value="40"/>
<option name="interval" value="0.2"/>
<option name="limit_rate" value="95"/>
</options>
</command>
On top of the standard XML syntax, LNST Recipe format is extended with support of, so called, templates. Templates are always enclosed within a pair of curly braces {}. Templates can be used ONLY within an attribute value or within a tag's text content. Templates therefore will not work when used as a name of a tag or attribute!
There are two types of templates that will be discussed in the sections bellow: aliases and template functions.
Aliases can be thought of as macros or variables (although they cannot be changed). You can define an alias absolutely anywhere within the document structure (as long as it does not invalidate the XML) by using the [#element:define ] tag.
<define>
<alias name="multicast_group" value="239.1.2.3"/>
<alias name="port" value="1337"/>
<alias name="test_duration" value="10"/>
<alias name="send_delay" value="0.1"/>
<alias name="nonexistent_ip" value="127.0.0.200"/>
</define>
After you define your aliases, you will be able to reference them using the following notation: {$alias_name}
. See the example bellow:
<command bg_id="1" machine_id="1" timeout="30" type="test" value="Multicast">
<options>
<option name="setup" value="send_simple"/>
<option name="address" value="{$multicast_group}"/>
<option name="port" value="{$port}"/>
<option name="duration" value="{$test_duration}"/>
<option name="delay" value="{$send_delay}"/>
</options>
</command>
Alias definition's area of effect is within the tag it was defined in and in all subsequent child tags. It will be discarded as soon as the parent tag is closed.
Template functions are conceptually quite similar to aliases, but instead of an assigned value, they represent an action that will be executed when the recipe is parsed. They can be used the same way as aliases - inside of curly braces. Currently, there are three template functions available in LNST:
[=#ip]
Returns an IP address for a specific machine and interface. Third argument is optional and can be used when there are multiple IP addresses associated with a single interface.
[=#hwaddr]
Returns hardware address of specified interface.
[=#devname]
Returns the device name of a specified interface.
All three above mentioned functions can be used for retrieving information from machine configs, therefore calling them is only valid within a task. See the following example on how this template can be used:
{{{#!div style="background: #ffffff; border: 1px ridge; border-color: #d7d7d7; margin: 23px"
<pre>
<command type="system_config" option="/proc/sys/net/ipv4/conf/<span style="background-color:#ffc0cb">{devname(1,1)}</span>/forwarding" value="1" machine_id="1" />
<command type="test" value="Multicast" machine_id="1" timeout="30">
<options>
<option name="setup" value="max_groups" />
<option name="interface" value="<span style="background-color:#ffc0cb">{ip(1,1)}</span>" />
<option name="condition" value="max_groups > 0" />
</options>
</command>
</pre>
}}}
Aliases can be passed to functions as parameters, but the preprocessor does not support nesting (e.g. function cannot have another function passed as an argument).
LNST also offers a possibility of splitting the recipe into several files. It's very helpful if you want to re-use the code in different recipes. This can be achieved by supplying source argument to an arbitrary tag. The contents of that tag then will be loaded from the file specified in the attribute's value. For instance, the following machine configuration will be loaded from file peanut.xml:
{{{#!div style="background: #ffffff; border: 1px ridge; border-color: #d7d7d7; margin: 23px"
<pre>
<machine <span style="background-color:#90ee90">source="machine_configs/peanut.xml"</span> />
</pre>
}}}
Example (peanut.xml): {{{#!div style="background: #ffffff; border: 1px ridge; border-color: #d7d7d7; margin: 23px"
<pre>
<machine id="1">
<params>
<param name="os" value="rhel6.2" />
</params>
<interfaces <span style="background-color:#add8e6">source="example_recipes/interfaces1.xml"</span> />
</machine>
</pre>
}}}
Note that parts of the included machine config are again external.