.. _`django-rest-framework`:
   https://www.django-rest-framework.org

REST API is available with /api URL. The API is based on
`django-rest-framework`_. All of the docs from django-rest-framework apply.
LAVA uses token based authentication. To obtain a token POST request has to
be made to /api/<version>/token/ endpoint. The request has to contain
username and password fields. These are the same as used for web UI
authentication. Call returns either existing AuthToken or creates new one.
The tokens used for REST API and XML-RPC API are the same objects. Example:

.. code-block:: none

    $ curl -d '{"username":"john.doe", "password":"FooBar"}' -H "Content-Type: application/json" -X POST "https://master.lavasoftware.org/api/v0.1/token/"

Reply will contain a token that identifies the user when using the REST API:

.. code-block:: json

    {"token":"ezwpm1wytdwwnbbu90e6eo02bligzw21b0ibyc1ikbc19zkin6639f3wodce5u9oc3lndoqn0asfewrw0bclfii4mgtweokrxa0mztohj46n2rdi0qinezsbobfauqf0"}

v0.2
====

Currently there are 9 endpoints available:

- /api/v0.2/aliases/
- /api/v0.2/jobs/
- /api/v0.2/devicetypes/
- /api/v0.2/devices/
- /api/v0.2/permissions/devicetypes
- /api/v0.2/permissions/devices
- /api/v0.2/system/
- /api/v0.2/tags/
- /api/v0.2/workers/

In addition TestJob object (/api/v0.2/jobs/id/) contains following routes:

 - logs (/api/v0.2/jobs/<job_id>/logs)
 - metadata (/api/v0.2/jobs/<job_id>/metadata)

Following nested routes are available. The allow for filtering in suites and tests:

- suites (/api/v0.2/jobs/<job_id>/suites)
- tests (/api/v0.2/jobs/<job_id>/tests)

The results are also available in JUnit, TAP13, CSV or YAML formats at:

- junit (/api/v0.2/jobs/<job_id>/junit/)
- tap13 (/api/v0.2/jobs/<job_id>/tap13/)
- CSV (/api/v0.2/jobs/<job_id>/csv)
- yaml (/api/v0.2/jobs/<job_id>/yaml)

Extra actions
-------------

Additional actions are available on the following endpoints with GET request:

- device dictionary (/api/v0.2/devices/<device_hostname>/dictionary/)
- device type health check (/api/v0.2/devicetypes/<device_type_name>/health_check/)
- device type template (/api/v0.2/devicetypes/<device_type_name>/template/)
- worker configuration (/api/v0.2/workers/<worker>/config/)
- worker environment (/api/v0.2/workers/<worker>/env/)

Additional actions are available on the following endpoints with POST request:

- device dictionary (/api/v0.2/devices/<device_hostname>/dictionary/)
- device type health check (/api/v0.2/devicetypes/<device_type_name>/health_check/)
- device type template (/api/v0.2/devicetypes/<device_type_name>/template/)
- worker configuration (/api/v0.2/workers/<worker>/config/)
- worker environment (/api/v0.2/workers/<worker>/env/)
- job resubmit (/api/v0.2/jobs/<job_id>/resubmit)
- job validate (/api/v0.2/jobs/validate)
    * required parameters:
        + definition (string)
    * optional parameters:
        + strict (bool)

Objects in all endpoints can be filtered and sorted as described
in django-rest-framework docs: http://www.django-rest-framework.org/api-guide/filtering/
Searching is currently disabled.

Examples specific to LAVA objects:

Filtering
---------

.. code-block:: none

    https://validation.linaro.org/restapi/v0.2/jobs/?health_check=true

Filtering fields

.. _testjob_rest_object:

TestJob object
~~~~~~~~~~~~~~

- submitter (can be traversed to :ref:`user-rest-object`)
- viewing_groups (can be traversed to :ref:`group-rest-object`)
- description (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- health_check (bool field)
    * exact
- requested_device_type (can be traversed to :ref:`devicetype-rest-object`)
- tags (can be traversed to :ref:`tag-rest-object`)
- actual_device (can be traversed to :ref:`device-rest-object`)
- submit_time (datetime field)
    * exact
    * lt
    * gt
- start_time (datetime field)
    * exact
    * lt
    * gt
- end_time (datetime field)
    * exact
    * lt
    * gt
- state (Submitted, Scheduling, Scheduled, Running, Canceling, Finished)
    * exact
- health (Unknown, Complete, Incomplete, Canceled)
    * exact
- priority (integer field)
    * exact
    * in
    * lt
    * gt
    * lte
    * gte
- definition (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- original_definition (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- multinode_definition (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- failure_tags (can be traversed to :ref:`jobfailuretag-rest-object`)
- failure_comment (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
    * isnull

.. _devicetype-rest-object:

DeviceType object
~~~~~~~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- architecture (can traverse to :ref:`architecture-rest-object`)
- processor (can traverse to :ref:`processorfamily-rest-object`)
- cpu_model (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- aliases (can traverse to :ref:`alias-rest-object`)
- bits (can traverse to :ref:`bitwidth-rest-object`)
- cores (can traverse to :ref:`core-rest-object`)
- core_count (integer field)
    * exact
    * in
- description (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- health_frequency (integer field)
    * exact
    * in
- disable_health_check (bool field)
    * exact
    * in
- health_denominator (hours, jobs)
    * exact
- display (bool field)
    * exact
    * in

.. _architecture-rest-object:

Architecture object
~~~~~~~~~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _processorfamily-rest-object:

ProcessorFamily object
~~~~~~~~~~~~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _alias-rest-object:

Alias object
~~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _core-rest-object:

Core object
~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _bitwidth-rest-object:

BitWidth object
~~~~~~~~~~~~~~~

- width (integer field)
    * exact
    * in

.. _device-rest-object:

Device object
~~~~~~~~~~~~~

- hostname (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- device_type (can traverse to :ref:`devicetype-rest-object`)
- device_version (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- physical_owner (can traverse to :ref:`user-rest-object`)
- physical_group (can traverse to :ref:`group-rest-object`)
- description (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- tags (can traverse to :ref:`tag-rest-object`)
- state (Idle, Reserved, Running)
    * exact
- health (GOOD, UNKNOWN, LOOPING, BAD, MAINTENANCE, RETIRED)
    * exact
- worker_host (can traverse to :ref:`worker-rest-object`)

.. _worker-rest-object:

Worker object
~~~~~~~~~~~~~

- hostname (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- description (text field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- last_ping (datetime field)
    * exact
    * lt
    * gt
- state (Online, Offline)
    * exact
- health (Active, Maintenance, Retired)
    * exact

.. _tag-rest-object:

Tag object
~~~~~~~~~~

- description (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _jobfailuretag-rest-object:

JobFailureTag object
~~~~~~~~~~~~~~~~~~~~

- description (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _user-rest-object:

User object
~~~~~~~~~~~

- group (can traverse to :ref:`group-rest-object`)
- username (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith
- email (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith

.. _group-rest-object:

Group object
~~~~~~~~~~~~

- name (char field)
    * exact
    * in
    * contains
    * icontains
    * startswith
    * endswith


Nested filtering
----------------

It is possible to filter objects using their relations. This is achieved
by using double underscore notation from django. Example:

.. code-block:: shell

    https://validation.linaro.org/restapi/v0.1/jobs/?requested_device_type__cores__name=kirin

In the example above *requested_device_type* comes from TestJob object.
It's a related field of DeviceType. DeviceType contains *cores* field which
is of type Core. Core object contains *name* field. So the example above
queries database for all TestJob objects which requested device type that
use cores with name *kirin*.

Sorting
-------

.. code-block:: shell

    https://validation.linaro.org/restapi/v0.1/jobs/?ordering=start_time

Sorting fields for TestJob object:
 - id
 - start_time
 - end_time
 - submit_time

Sorting fields for Device object:
 - hostname
 - device_type
 - device_version
 - physical_owner
 - physical_group
 - description
 - tags
 - state
 - health
 - worker_host

Sorting fields for Worker object:
 - hostname
 - description
 - last_ping
 - state
 - health

Sorting fields for Alias object:
 - name

Sorting fields for Tag object:
 - name
 - description

Creating and modifying objects
==============================

DjangoRestFramework allows to create and modify objects using POST and PUT http
requests respectively. These operations require successful authentication and
sufficient permissions. LAVA utilizes Token authentication using http request
header. Example:

.. code-block:: shell

    $ curl -H "Authorization: Token xxxxxx" 

Same token is used for REST API and XML-RPC API authentication.

Submitting a test job
---------------------

Sending a POST request on a /jobs/ endpoint will result in attempted job
submission in LAVA. There is only one argument which needs to be passed in the
request and that is ``definition``. Example:

.. code-block:: shell

    $ curl -H "Authorization: Token xxxxxx" -d '{"definition": "your-testjob-definition-here"}' -H "Content-Type: application/json" -X POST "https://your.lava.hostname/api/v0.2/jobs/"

Other database object can be created in a similar way. Different objects require
different fields.

Deprecated semi-REST API for retrieving results
===============================================

LAVA makes the test results available directly from the instance,
without needing to go through ``lavacli``. The results for any test
job which the user can view can be downloaded in :abbr:`CSV
(comma-separated value)` or YAML format.

For example, the results for test job number 123 are available in CSV
format using: ``https://validation.linaro.org/results/123/csv``. The
same results for job number 123 are available in YAML format using:
``https://validation.linaro.org/results/123/yaml``

If you know the test definition name, you can also download the
results for that specific test definition only in a similar way:
``https://validation.linaro.org/results/123/0_singlenode-advanced/csv``
for the data in CSV format and
``https://validation.linaro.org/results/123/0_singlenode-advanced/yaml``
for the YAML format.

Test definition names use a prefix to show the sequence within the
test job, for each namespace. The list of test definitions within a
test job can be retrieved using the Summary:
``https://validation.linaro.org/results/123/yaml_summary``

Some test jobs can be restricted to particular users or groups of
users. The results of these test jobs will be restricted in the same
way. To download these results, you will need to specify your username
and one of your :ref:`authentication_tokens` when using the REST
API. Remember to quote the URL if using it on the command line, to
avoid the & character being mis-interpreted by your shell, for
example:

.. code-block:: shell

    $ curl 'https://validation.linaro.org/results/123/0_singlenode-advanced/yaml?user=user.name&token=yourtokentextgoeshereononeverylongline'

Use the **Username** as specified in `your Profile </me>`_ - this may
differ from the username you use if logging in via LDAP.

.. caution:: Take care of your tokens - avoid using personal tokens in
   scripts and test definitions, or any other files that end up in
   public git repositories. Wherever supported, use ``https://`` when
   using a token to avoid it being sent in plain-text.

.. index:: results - pagination

.. _test_suite_result_pagination:

Chunking test suite results
===========================

When jobs have a large number of test results in a particular test suite,
it is advisable to use test case **chunking**, to provide pagination for
downloading test cases via the REST API.

Two special query string arguments are supported for allowing users to
**chunk** the test cases when downloading test suite results.

* **Limit** - determines how many results to read in this chunk

* **offset** - the number of results already received

Limit and offset are supported only for test suite exports (both csv
and yaml). Example:

.. code-block:: shell

    $ curl 'https://validation.linaro.org/results/123/0_singlenode-advanced/yaml?limit=100&offset=200'

    $ curl 'https://validation.linaro.org/results/123/0_singlenode-advanced/yaml?user=user.name&token=yourtokentextgoeshereononeverylongline&limit=100&offset=200'

To retrieve the full count of testcases in a single test suite (useful for
pagination), you can use the testcase-count REST API method, like so:

.. code-block:: shell

    $ curl 'https://validation.linaro.org/results/123/0_singlenode-advanced/+testcase-count?user=user.name&token=yourtokentextgoeshereononeverylongline'

.. note:: Test cases will be ordered by ID regardless of whether pagination is
   used or not. This applies to downloading test cases for a particular test
   suite only.

