Introduction to the Red Hat Distributed CI Python API
Back to all posts

How to interact with DCI infrastructure

DCI provides multiple ways to interact:

In this article, we will be focusing on Python API. The Python API is a convenient light wrapper to the REST API. It will enable you to create reporting tools, utilities or even new agents.

DCI HMAC-based authentication

To provide secure and robust authentication, DCI has implemented AWS Signature v4. It works by using signed headers using your credentials and SHA-256 hash function, through HMAC. The dciauth makes it easy to generate headers and validate requests in Python.

You absolutely need to be authenticated and have proper permissions to access

DCI Python API

Please refer to the introductory blog post to DCI for higher level overview of our infrastructure as it is necessary to understand the following concepts.

Before we get started, let's dive into some important API resources:

Products

A product is merely a Red Hat product that is distributed by DCI. (e.g: RHEL, RHOSP, OpenShift)

Topics

A topic is a version/variant of a product. (e.g: RHEL8.7, RHEL8.1, RHEL7.9)

Components

A component is an artifact (e.g: file, url) attached to a topic. Artifacts can be downloaded through the dci-downloader utility by specifying the topic, some filters are available.

Jobs

A job represent a run of an agent through a remoteci.

A Job goes through various states:

Job states

Hands-on

First, you'll need your credentials in a dcirc.sh file which will look like this:

DCI_CLIENT_ID='remoteci/XXXX'
DCI_API_SECRET='XXXXXXX'
DCI_CS_URL='https://api.distributed-ci.io/'
export DCI_CLIENT_ID
export DCI_API_SECRET
export DCI_CS_URL

If needed, you can read the next paragraph to learn how to retrieve them, otherwise, you can skip it.

Retrieving your credentials

You need to connect to the DCI web board, go to the remotecis tab on the left side. Then, look for your remoteci and click on the blue button in the Authentication column (see image below). A popup will display the cont

screen capture showing the output.

Installing Python DCI libraries

Recommended way to install Python DCI libraries either by RPM package or Python native package (Pypi).

RPM installation:

# On RHEL7
$ yum -y install https://packages.distributed-ci.io/dci-release.el7.noarch.rpm
$ yum install python-dciclient
# On RHEL8
$ yum -y install https://packages.distributed-ci.io/dci-release.el8.noarch.rpm
$ yum install python-dciclient

Python native package installation:

$ pip install dciclient

Authentication

The first thing, you will need to do is to create an authentication context. Context are required in all API calls to authenticate your requests. Authentication context objects will authenticate your request, there are three types of context:

For this hands-on, we will be using signature types context.

import os
from dciclient.v1.api import context

# You need to source your dcirc.sh credentials file
# before execution:
# $ source dcirc.sh
dci_client_id = os.getenv("DCI_CLIENT_ID")
dci_api_secret = os.getenv("DCI_API_SECRET")
ctx = context.build_signature_context(dci_client_id, dci_api_secret)

In the next paragraph, we will skip this part to keep examples shorts and readable.

Our first API Calls

Let's to try list available topics to your remoteci. For the sake of simplicity, we will use json module to parse the server REST API response.

import json
from dciclient.v1.api import topic

response = topic.list(ctx)
status_code = response.status_code # value will be 200
content = json.loads(response.content)

Two interesting values from the response object:

But requests can also accepts filters:

response = topic.list(ctx, where="name:RHEL-8.6")
status_code = response.status_code # 200
content = json.loads(response.content)

Then, if we try to look what's inside the content:

{'_meta': {'count': 1},
 'topics': [{'component_types': ['compose'],
   'component_types_optional': [],
   'created_at': '2021-10-11T11:00:11.692662',
   'data': {},
   'etag': '649dfc2cde7a45fbb7398ad6fcb9ae11',
   'export_control': True,
   'id': '3b8d7315-a941-467f-95ff-b789230bd50b',
   'name': 'RHEL-8.6',
   'next_topic': None,
   'next_topic_id': None,
   'product': {'created_at': '2017-08-21T13:22:41.891867',
    'description': 'RHEL is a Linux distribution developed by Red Hat and targeted toward the commercial market',
    'etag': '2a32426c22e893e988c5905f24d2bb69',
    'id': 'b3d8d6f3-8223-4553-bf08-9b2cecb869f2',
    'label': 'RHEL',
    'name': 'RHEL',
    'state': 'active',
    'updated_at': '2018-02-20T12:36:33.595643'},
   'product_id': 'b3d8d6f3-8223-4553-bf08-9b2cecb869f2',
   'state': 'active',
   'updated_at': '2022-02-28T17:12:53.874986'}]}

We can notice few fields:

Note that every resource in DCI that is available through the REST API has an unique identifier (id), and that every response will include an Entity Tag Header (ETag) that identify a specific version of that resource. It saves bandwith by leveraging client-side cache, and prevent mid-air collisions during concurrent write access.

For the next part, we will need to list components to create our first job.

id = response_topics['topics']['id']
response = topic.list_components(ctx, '3b8d7315-a941-467f-95ff-b789230bd50b')
content = json.loads(response.content)

Since the output is a tad long, we will truncate it

{'_meta': {'count': 112},
 'components': [{'canonical_project_name': 'RHEL-8.6.0-20220404.2',
   'created_at': '2022-04-05T09:59:37.236451',
   'data': {'arches': ['x86_64', 's390x', 'ppc64', 'ppc64le'],
    'channel': 'nightly',
    'major': '8',
    'minor': '6',
    'name': 'RHEL-8.6.0-20220404.2',
    'patch': '0',
    'product': 'RHEL',
    'tags': ['nightly'],
    'type': 'compose',
    'url': 'http://download.eng.bos.redhat.com/rhel-8/nightly/RHEL-8/RHEL-8.6.0-20220404.2/',
    'url_hostname': 'download.eng.bos.redhat.com',
    'url_path': '/rhel-8/nightly/RHEL-8/RHEL-8.6.0-20220404.2/',
    'version': '8.6.0'},
   'etag': '6795c72e610d5399ba0ed7a1dc5cbfcd',
   'id': '17144fb6-7f4f-4f1e-86ae-b5c95b5675ef',
   'message': None,
   'name': 'RHEL-8.6.0-20220404.2',
   'released_at': '2022-04-05T09:59:37.237918',
   'state': 'active',
   'tags': ['nightly', 'kernel:4.18.0-372.5.1.el8'],
   'team_id': None,
   'title': None,
   'topic_id': '3b8d7315-a941-467f-95ff-b789230bd50b',
   'type': 'compose',
   'updated_at': '2022-04-05T14:56:37.696052',
   'url': 'http://download.eng.bos.redhat.com/rhel-8/nightly/RHEL-8/RHEL-8.6.0-20220404.2'},
 (..)
]}

Some relevant information:

And now we're ready to create our first job !

Our first DCI job

Let's a create a job from the API:

response = job.create(ctx, topic_id='a35675ca-e749-4e89-9b86-61191fc9fed4', components=["bc3766e0-35e9-4ab9-9e7a-b309c9e85270"])
status_code = response.status_code # 201 (a resource was created)
content = json.loads(response.content)

{'job': {'client_version': 'python-dciclient_2.2.1',
  'comment': None,
  'configuration': None,
  'created_at': '2022-04-06T09:52:09.313737',
  'data': {},
  'duration': 0,
  'etag': 'a86cb94d3e7eeaf1996c67972a2427d7',
  'id': 'f0956ebf-5bdb-47b2-96c0-0f07a6ed4e02',
  'name': None,
  'previous_job_id': None,
  'product_id': 'b3d8d6f3-8223-4553-bf08-9b2cecb869f2',
  'remoteci_id': '334ddc4d-e350-4b9c-a07f-45a9044117fe',
  'state': 'active',
  'status': 'new',
  'status_reason': None,
  'tags': [],
  'team_id': '47513c50-d5a5-4d93-a63f-f6545a3313fc',
  'topic_id': 'a35675ca-e749-4e89-9b86-61191fc9fed4',
  'update_previous_job_id': None,
  'updated_at': '2022-04-06T09:52:09.313737',
  'url': None,
  'user_agent': 'python-dciclient_2.2.1'}}

Now we can delete our job using

response = job.delete(ctx, 'f0956ebf-5bdb-47b2-96c0-0f07a6ed4e02', 'a86cb94d3e7eeaf1996c67972a2427d7')
status_code = response.status_code # 204 resource delete
content = json.loads(response.content) # empty string :)

Conclusion

It's just the tip of the iceberg, the DCI Python API is quite rich, and allows you to do a lot more including administration tasks. With this API, you can create new agents, dedicated dashboards, manipulate jobs, uploading test files etc.

For more insight on the Python, do not hesitate to ping us.

Stay tuned !

Distributed CI documentation