Creating a LAVA boot action method
A boot action method in LAVA is the component in the LAVA dispatcher that flashes and boots a device during test runs. This page details the procedure used to create a boot action method.
Prerequisites
- Install LAVA and ensure it runs on your development machine
- Clone the LAVA development repository from https://git.lavasoftware.org/lava/lava.git
- Familiarize yourself with the LAVA contribution guide
This guide has code blocks to illustrate the code for the OpenOCD boot action method, which is still in a pull request pending approval. The procedure is expected to be similar when creating your own boot method.
Adding the 'plumbing'
- Look up the command line invoked when flashing and booting your device, if you normally just invoke a command to flash and boot. For example, if you are developing in Zephyr, a trick is to look up the command run by '
make flash
' to get an idea of what is being done. - Write the python file associated with your boot action method to define some python classes to invoke the command line (or to follow some other boot procedure), and put it in
lava_dispatcher/actions/boot
. In the file, you create the functions that correspond to some 'actions' to flash and boot the device, which LAVA can then add to an execution pipeline that is run when a test job is submitted. The parameters to the command line are typically passed in/inferred from the parameters defined in the device type template, which is covered in step 5. There are many examples of boot action methods in this directory, and it is best to study them as examples to get started. A couple of good ones arepyocd.py
andcmsis-dap.py
. In addition there is some official documentation in LAVA: - Add an entry for your boot method in
lava_dispatcher/actions/boot/strategies.py
from lava_dispatcher.actions.boot.openocd import OpenOCD
This tells LAVA that there is a boot action method named 'OpenOCD' defined in
lava_dispatcher/actions/boot/openocd.py
. Add a schema for your boot method in
lava_common/schemas/boot/<name of your boot method>.py
from voluptuous import Msg, Required from lava_common.schemas import boot def schema(): base = {Required("method"): Msg("openocd", "'method' should be 'openocd'")} return {**boot.schema(), **base}
- Add support for the boot action method in the device type template of your choice. For example, the template for the CC3220SF is located in
lava_scheduler_app/tests/device-types/cc3220SF.jinja
2. Modifications are made to add openocd as a supported boot action method:openocd: parameters: command: # Can set 'openocd_bin_override' in device dictionary to # override location of OpenOCD executable to point to TI OpenOCD # if necessary {{ openocd_bin_override|default('openocd') }} options: file: - board/ti_cc3220sf_launchpad.cfg # Set 'openocd_scripts_dir_override' in device dictionary to # point to TI OpenOCD scripts if necessary search: [{{ openocd_scripts_dir_override }}] command: - init - targets - 'reset halt' - 'flash write_image erase {BINARY}' - 'reset halt' - 'verify_image {BINARY}' - 'reset run' - shutdown debug: 2
This basically defines a dictionary of the parameters that are passed to the boot action method to invoke the openocd command line.
- Document the boot method in
doc/v2/actions-boot.rst
Modify lava-docker-compose to try it out
Inspect your
lava-docker-compose/.env
file. It shows the version tag of the LAVA repository that is used by the container images.DC_POSTGRES_IMAGE=postgres:11.2-alpine DC_SERVER_IMAGE=lavasoftware/lava-server:2019.04 DC_DISPATCHER_IMAGE=lavasoftware/lava-dispatcher:2019.04
Ideally, you want to ensure your cloned LAVA repository is pointing to the same version. Then you can share your work with the containers by adding to the 'volumes' section for each container in the
docker-compose.yaml
file. For example, say your cloned repository is located under the directorylava-work
, which is located right alongside thelava-docker-compose
directory. if you want to share your changes from thelava_scheduler_app
andlava_common
directories with the lava-dispatcher container, you can adding volume bindings as follow:lava-dispatcher: ... volumes: ... # Example for development # If you wanted to point to a local git checkout of lava for development # of lava_dispatcher, you can uncomment out the lines below and # set the 'source:' to point to where your lava checkout is # The example assumes its relative in ../lava # - type: bind source: ../lava-work/lava_dispatcher target: /usr/lib/python3/dist-packages/lava_dispatcher - type: bind source: ../lava-work/lava_common target: /usr/lib/python3/dist-packages/lava_common
Depending on how far off your cloned repository is from the version tag '2019.04', you may need to share more directories to ensure the various LAVA-related directories under
/usr/lib/python3/dist-packag
es in each container are in sync. Runtime errors may result if different containers are referencing vastly different code.Create a job that uses the new boot method, e.g.
lava_openocd.job
in our example for OpenOCD. The key thing is to specify your boot method under the boot section:- boot: method: openocd timeout: minutes: 1
Kick off the new job as you normally would:
lavacli -i dispatcher jobs submit lava_openocd.job
If you did everything correctly, the test job would run successfully to completion, and invoke your boot action method in the process.
Validation and submission
- Add a unit test for your boot action method:
Install lava-dev and python3-django-testscenarios
sudo apt install lava-dev python3-django-testscenarios
If the above does not work because lava-dev is not found, take a look at https://docs.lavasoftware.org/lava/installing_on_debian.html regarding LAVA repositories. If you are on Ubuntu, which is not officially supported, you may need to add this line in your
/etc/apt/sources.list
file:deb [allow-insecure=yes allow-downgrade-to-insecure=yes] http://deb.debian.org/debian stretch-backports main
Then run
sudo apt update sudo apt install lava-dev
It is going to complain that the package is insecure, but I haven't found a way around this (suggestions welcome). The installation still goes through however.
- Install a few more dependencies:
pip3 install --user django-tables2 pip3 install --user djangorestframework sudo apt-get install python-psycopg2 sudo apt-get install libpq-dev pip3 install --user psycopg2 pip3 install --user django-restricted-resource sudo apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev sudo pip3 install --user pyldap pip3 install --user pytest-tap
- In the cloned LAVA repository, create a test under
lava_dispatcher/tests/test_<boot method name>.py
:There are many examples of tests in the same directory. Feel free to take a look at them to come up with something that makes sense for your boot action method. Some useful documentation regarding how to add unit tests is here. In essence, the role of the unit test is to create a job using a test device dictionary file (import unittest from lava_dispatcher.tests.utils import infrastructure_error from lava_dispatcher.tests.test_basic import Factory, StdoutTestCase from lava_common.exceptions import InfrastructureError from lava_dispatcher.utils.shell import which def check_openocd(): try: which("openocd") return False except InfrastructureError: return True class OpenOCDFactory(Factory): """ Not Model based, this is not a Django factory. Factory objects are dispatcher based classes, independent of any database objects. """ @unittest.skipIf(infrastructure_error("openocd"), "openocd not installed") def create_cc3230SF_job(self, filename): return self.create_job("cc3220SF-02.jinja2", filename) class TestOpenOCDAction(StdoutTestCase): @unittest.skipIf(check_openocd(), "openocd not available") def test_openocd_pipeline(self): factory = OpenOCDFactory() job = factory.create_cc3230SF_job("sample_jobs/cc3220SF-openocd.yaml") job.validate() description_ref = self.pipeline_reference("openocd.yaml", job=job) self.assertEqual(description_ref, job.pipeline.describe(False)) # Check BootOpenOCDRetry action action = job.pipeline.actions[1].internal_pipeline.actions[0] self.assertEqual(action.name, "boot-openocd-image") # Check FlashOpenOCDAction action = ( job.pipeline.actions[1] .internal_pipeline.actions[0] .internal_pipeline.actions[0] ) self.assertEqual(action.name, "flash-openocd") self.assertEqual(len(action.base_command), 20) self.assertEqual(action.base_command[0], "openocd") self.assertEqual(action.base_command[1], "-f") self.assertEqual(action.base_command[2], "board/ti_cc3220sf_launchpad.cfg") self.assertEqual(action.base_command[3], "-d2") self.assertEqual(action.base_command[19], "shutdown") action = ( job.pipeline.actions[1] .internal_pipeline.actions[0] .internal_pipeline.actions[1] ) self.assertEqual(action.name, "connect-device")
lava_scheduler_app/tests/devices/cc3220SF-02.jinja2
in the code above), and sample test job definition (lava_dispatcher/tests/sample_jobs/cc3220SF-openocd.yaml
), and then verify that internal variables of the various actions executed as part of the execution pipeline of the test get set to the expected values. As part of creating your test, you should create a file that defines the expected pipeline for the test job (lava_dispatcher/tests/pipeline_refs/openocd.yaml
), so that the unit test can validate against it, in addition to creating your own test device dictionary and/or sample test job definition. The lava-dispatcher section here has some commands for either running your specific unit test or running all unit tests for the dispatcher. Here's how it looks on a successful run:
$ python3 -m unittest -v -c -f lava_dispatcher.tests.test_openocd test_openocd_pipeline (lava_dispatcher.tests.test_openocd.TestOpenOCDAction) ... dpkg-query: no packages found matching lava-dispatcher DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): snapshots.linaro.org:443 DEBUG:urllib3.connectionpool:https://snapshots.linaro.org:443 "HEAD /components/kernel/zephyr/master/zephyr/cc3220sf_launchxl/3885/tests/subsys/logging/log_list/logging.log_list/zephyr/zephyr.elf HTTP/1.1" 302 0 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): publishing-ie-linaro-org.s3.amazonaws.com:443 DEBUG:urllib3.connectionpool:https://publishing-ie-linaro-org.s3.amazonaws.com:443 "HEAD /snapshots/components/kernel/zephyr/master/zephyr/cc3220sf_launchxl/3885/tests/subsys/logging/log_list/logging.log_list/zephyr/zephyr.elf?Signature=A80F8UEVFVAwHtZZ%2FwWL0dEZ5L8%3D&Expires=1560818349&AWSAccessKeyId=AKIAIJR2J6C42QCU7ITA HTTP/1.1" 403 0 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): snapshots.linaro.org:443 DEBUG:urllib3.connectionpool:https://snapshots.linaro.org:443 "GET /components/kernel/zephyr/master/zephyr/cc3220sf_launchxl/3885/tests/subsys/logging/log_list/logging.log_list/zephyr/zephyr.elf HTTP/1.1" 302 0 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): publishing-ie-linaro-org.s3.amazonaws.com:443 DEBUG:urllib3.connectionpool:https://publishing-ie-linaro-org.s3.amazonaws.com:443 "GET /snapshots/components/kernel/zephyr/master/zephyr/cc3220sf_launchxl/3885/tests/subsys/logging/log_list/logging.log_list/zephyr/zephyr.elf?Signature=kzYqsz96Pm%2Bc0Kp3ycqGyQzTq2o%3D&Expires=1560818351&AWSAccessKeyId=AKIAIJR2J6C42QCU7ITA HTTP/1.1" 200 572552 ok ---------------------------------------------------------------------- Ran 1 test in 4.035s OK
- Submit a PR for your changes to lavasoftware.org, as per the contribution guide.