-
Notifications
You must be signed in to change notification settings - Fork 31
LNST Task API
After some time of writing LNST tasks in XML you probably keep repeating:
"XML is such a bad language to write sequence of commands, it's so limited. This should be done differently!!"
And you're quite right. We got tired of this, too. Therefore we've added a new feature that allows you to use your own python program that drives the test execution with ability to access all of the LNST infrastructure - machine properties, command execution, template functions, background execution - from the program. Besides the LNST infrastructure you can benefit from the Python language facilities as loops, conditional executions and whatever Python provides in it's library.
Note that the machine requirements description still needs to be provided as an XML code in the recipe.
Let's assume that your Python program you're using for the task execution is called task_check_ping.py. To include it in the recipe use following code:
<lnstrecipe>
<machines>
<machine id="1">
<interfaces>
<eth network="ttnet" id="testiface"/>
<addresses>
<address value="192.168.100.240/24"/>
</addresses>
</eth>
</interfaces>
</machine>
<machine id="2">
<interfaces>
<eth network="ttnet" id="testiface" type="eth">
<addresses>
<address value="192.168.100.215/24"/>
</addresses>
</eth>
</interfaces>
</machine>
</machines>
<!-- This is it! -->
<task python="task_check_ping.py"/>
</lnstrecipe> # Mandatory import, the ctl handle contains the API
from lnst.Controller.Task import ctl
# Get handles for the machines
m1 = ctl.get_machine("1")
m2 = ctl.get_machine("2")
# Set a config option (persistent=True)
m1.config("/proc/sys/net/ipv4/conf/all/forwarding", "0", True)
# run a shell command
devname = m2.get_devname("testiface")
m1.run("echo %s" % devname, timeout=30)
# prepare a module for execution
ping_mod = ctl.get_module("IcmpPing", addr=m2.get_ip("testiface", 0),
count=40, interval=0.2, limit_rate=95)
# run the module twice on machine one
ping_test = m1.run(ping_mod, timeout=30)
ping_test = m1.run(ping_mod, bg=True, timeout=30)
# make the controller wait
ctl.wait(5)
# interrupt the process
ping_test.intr()As you can see all that you need to do to access the LNST API is simply importing the Controller handle
from lnst.Controller.Task import ctlLet's have a closer look on the API itself.
The controller handle provides following methods
-
get_machine(self, machine_id)to get a machine API handle for the machine from the recipe spec with a specific id -
get_module(self, name, **kwargs)to get a test module API handle -
wait()to make controller wait for a specific amount of seconds
The get_machine() method returns a handle that is needed when you want to access the information about the interfaces or run a command on the machine. See the Machine API for details on how to do that or look at the examples below.
The get_module() simply returns the Test Module handle that can be run on a test machine. This method takes an optional keyword argument list to initialize the Test Module.
Finally the wait() method is an equivalent for the <ctl_wait> tag in the recipe xml. It takes one parameter seconds and it's value tells the controller how long it should wait before it continues in the task execution.
m1 = ctl.get_machine("1")
m2 = ctl.get_machine("2")
ifc_ip = m2.get_ip("testiface")
ifc_hwaddr = m2.get_hwaddr("testiface")
m1.run("ping -c 5" + ifc_ip)
m1.run("arp -n | grep -i " + ifc_hwaddr)Following example shows the IcmpPing module initialization and it's execution on the test machine.
m1 = ctl.get_machine("1")
m2 = ctl.get_machine("2")
ping_module = ctl.get_module("IcmpPing", addr=m2.get_ip("testiface", 0),
count=40, interval=0.2, limit_rate=95)
m1.run(ping_module) logging.info("I'll wait for 10 seconds")
ctl.wait(10)
logging.info("Task execution continues ...")Machine API provides following methods
-
config(self, option, value, persistent=False)to configure values under /proc or /sys directories -
run(self, what, **kwargs)to run commands or test modules on the test machines -
get_devname(self, interface_id)to get the name of the interface on the test machine, e.g. eth0 -
get_hwaddr(self, interface_id)to get the hardware address of the interface, e.g. 00:11:22:33:44:55:FF -
get_ip(self, interface_id, addr_number=0)to get the IP address of the interface, e.g. 192.168.1.10
The config() is the equivalent of the <config> command used in the recipe xml. It takes three parameters, the option defining the path to the file under /proc or /sys directory, the value containing the value to set the option to and persistent flag to make the value persistent between individual tasks.
The run() is the equivalent of the <run> command. The parameter what is used to pass either Test Module handle obtained thorugh the Machine API's get_module() method or string containing a command to be run on the command line. See examples below.
Last three methods get_devname(), get_hwaddr(), get_ip() are used to retrieve information about configured test interfaces. All of them take one common parameter interface_id to identify the interface id that is set in the recipe xml. Method get_ip() takes an optional parameter addr_number that is used as index to the address list if more than one address has been set for the interface.