Skip to content

Commit 79acafc

Browse files
committed
feat(example_api_lab.py): Add example for labs how to submit result to Maestro
This example for labs with their own Maestro-API token how to submit their results directly to maestro. Maestro also will take care about sending them to KCIDB. Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
1 parent 8781263 commit 79acafc

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

tools/example_api_lab.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/env python3
2+
"""
3+
KernelCI API example
4+
3rd party lab submission, when lab is not integrated with KernelCI pipeline
5+
6+
"""
7+
8+
import requests
9+
import json
10+
import sys
11+
12+
# KernelCI API URL
13+
api_url = "https://staging.kernelci.org:9000/latest"
14+
15+
16+
def add_authorization_header(headers):
17+
# read api token from .api_token file
18+
try:
19+
with open(".api_token", "r") as f:
20+
api_token = f.read().strip()
21+
except FileNotFoundError:
22+
print("Error: API token file not found")
23+
sys.exit(1)
24+
# Add the authorization header to the request
25+
headers["Authorization"] = f"Bearer {api_token}"
26+
return headers
27+
28+
29+
def retrieve_node(node_id):
30+
url = api_url + "/node/" + node_id
31+
headers = {"Content-Type": "application/json"}
32+
response = requests.get(url, headers=headers)
33+
if response.status_code != 200:
34+
print("Failed to retrieve node:", response.text)
35+
return None
36+
return response.json()
37+
38+
39+
def create_node(node):
40+
# Create a new node in the database
41+
url = api_url + "/node"
42+
headers = {"Content-Type": "application/json"}
43+
headers = add_authorization_header(headers)
44+
response = requests.post(url, headers=headers, data=json.dumps(node))
45+
if response.status_code != 200:
46+
print("Failed to create node:", response.text)
47+
return None
48+
try:
49+
return response.json()
50+
except json.JSONDecodeError:
51+
print("Failed to create node:", response.text)
52+
return None
53+
54+
55+
def update_node_tree(node_id, treedata):
56+
# Update the node with a new tree
57+
url = api_url + "/nodes/" + node_id
58+
headers = {"Content-Type": "application/json"}
59+
headers = add_authorization_header(headers)
60+
print(treedata)
61+
response = requests.put(url, headers=headers, data=json.dumps(treedata))
62+
if response.status_code != 200:
63+
print("Failed to update node tree:", response.text)
64+
return None
65+
return response
66+
67+
68+
def create_job(jobname, parent_node):
69+
job_node = {
70+
"kind": "job",
71+
"name": jobname,
72+
"path": ["checkout", jobname],
73+
"group": jobname,
74+
"parent": parent_node["id"],
75+
"state": "running",
76+
"result": None,
77+
"artifacts": {},
78+
"data": {},
79+
"submitter": parent_node["submitter"],
80+
"treeid": parent_node["treeid"],
81+
}
82+
job_node["data"]["kernel_revision"] = parent_node["data"]["kernel_revision"]
83+
return create_node(job_node)
84+
85+
86+
def submit_job_results(job_node_id, tests, artifacts):
87+
"""
88+
tests supplied as a list of
89+
{
90+
"name": "testname",
91+
"result": "pass" or "fail" or "skip",
92+
"platform": "platformname",
93+
"runtime": "lab-abcde,
94+
95+
}
96+
WARNING: platform must be defined in kernelci-pipeline instance
97+
It is also preferable to use fixed name for your lab, set in runtime field.
98+
"""
99+
job_node = retrieve_node(job_node_id)
100+
child_nodes = []
101+
# iterate over tests and create child_nodes
102+
for test in tests:
103+
nodepath = job_node["path"]
104+
nodepath.append(test["name"])
105+
test_node = {
106+
"kind": "test",
107+
"name": test["name"],
108+
"path": nodepath,
109+
"group": test["name"],
110+
"parent": job_node["id"],
111+
"state": "done",
112+
"result": test["result"],
113+
"artifacts": {},
114+
"data": {},
115+
"submitter": job_node["submitter"],
116+
"treeid": job_node["treeid"],
117+
}
118+
test_node["data"]["kernel_revision"] = job_node["data"]["kernel_revision"]
119+
test_node["data"]["platform"] = test["platform"]
120+
test_node["data"]["runtime"] = test["runtime"]
121+
child_node = {"node": test_node, "child_nodes": []}
122+
child_nodes.append(child_node)
123+
124+
# update job_node state to done
125+
job_node["state"] = "done"
126+
# update also result (it might be dependent on test results)
127+
job_node["result"] = (
128+
"fail" if any(test["result"] == "fail" for test in tests) else "pass"
129+
)
130+
131+
treedata = {"node": job_node, "child_nodes": child_nodes}
132+
133+
return update_node_tree(job_node["id"], treedata)
134+
135+
136+
def main():
137+
# create sample job node and then update with sample test results
138+
job_name = "job-12346"
139+
tests = [
140+
{
141+
"name": "test1",
142+
"result": "pass",
143+
"platform": "qemu",
144+
"runtime": "lab-abcde",
145+
},
146+
{
147+
"name": "test2",
148+
"result": "fail",
149+
"platform": "qemu-x86",
150+
"runtime": "lab-abcde",
151+
},
152+
]
153+
# you need to place here log files, etc.
154+
# ex:
155+
# "artifacts": {
156+
# "lava_log": "http://mon.kernelci.org:3000/baseline-x86-679351bb4c0614bf480e125d/log.txt.gz",
157+
# "callback_data": "http://mon.kernelci.org:3000/baseline-x86-679351bb4c0614bf480e125d/lava_callback.json.gz"
158+
# },
159+
# kcidb bridge will search for lava_log or test_log
160+
artifacts = {}
161+
# build node id, kernel which we used for testing
162+
parent_node_id = "67934c4b4c0614bf480df84f"
163+
164+
# create job node that defines the lab job
165+
job_response = create_job(job_name, retrieve_node(parent_node_id))
166+
if job_response is None:
167+
print("Failed to create job node")
168+
return
169+
170+
job_node_id = job_response["id"]
171+
print("Created job node:", job_node_id)
172+
173+
# Here you retrieve results from your lab and fill tests
174+
175+
# update job node with test results and set it to completed
176+
submit_job_results(job_node_id, tests, artifacts)
177+
178+
179+
if __name__ == "__main__":
180+
main()

0 commit comments

Comments
 (0)