ci/lava: Use python-fire in job submitter

Cleanup argparse to use dataclasses+python-fire to give easier
maintenance to job submitter.

Signed-off-by: Guilherme Gallo <guilherme.gallo@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/22500>
This commit is contained in:
Guilherme Gallo
2023-04-04 07:48:44 -03:00
committed by Marge Bot
parent c03f7233ca
commit cfe644a9e5
5 changed files with 86 additions and 75 deletions

View File

@@ -3,7 +3,7 @@ variables:
DEBIAN_BASE_TAG: "2023-04-16-kernel-6.3" DEBIAN_BASE_TAG: "2023-04-16-kernel-6.3"
DEBIAN_X86_BUILD_IMAGE_PATH: "debian/x86_build" DEBIAN_X86_BUILD_IMAGE_PATH: "debian/x86_build"
DEBIAN_BUILD_TAG: "2023-04-13-agility-610" DEBIAN_BUILD_TAG: "2023-04-14-pyfire"
DEBIAN_X86_BUILD_MINGW_IMAGE_PATH: "debian/x86_build-mingw" DEBIAN_X86_BUILD_MINGW_IMAGE_PATH: "debian/x86_build-mingw"
DEBIAN_BUILD_MINGW_TAG: "2023-01-03-ci-libva-2.17" DEBIAN_BUILD_MINGW_TAG: "2023-01-03-ci-libva-2.17"

View File

@@ -45,22 +45,23 @@ fi
touch results/lava.log touch results/lava.log
tail -f results/lava.log & tail -f results/lava.log &
PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \ PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \
submit \
--dump-yaml \ --dump-yaml \
--pipeline-info "$CI_JOB_NAME: $CI_PIPELINE_URL on $CI_COMMIT_REF_NAME ${CI_NODE_INDEX}/${CI_NODE_TOTAL}" \ --pipeline-info "$CI_JOB_NAME: $CI_PIPELINE_URL on $CI_COMMIT_REF_NAME ${CI_NODE_INDEX}/${CI_NODE_TOTAL}" \
--rootfs-url-prefix "https://${BASE_SYSTEM_HOST_PATH}" \ --rootfs-url-prefix "https://${BASE_SYSTEM_HOST_PATH}" \
--kernel-url-prefix "https://${BASE_SYSTEM_HOST_PATH}" \ --kernel-url-prefix "https://${BASE_SYSTEM_HOST_PATH}" \
--build-url "${ARTIFACT_URL}" \ --build-url "${ARTIFACT_URL}" \
--job-rootfs-overlay-url "${FDO_HTTP_CACHE_URI:-}https://${JOB_ROOTFS_OVERLAY_PATH}" \ --job-rootfs-overlay-url "${FDO_HTTP_CACHE_URI:-}https://${JOB_ROOTFS_OVERLAY_PATH}" \
--job-timeout ${JOB_TIMEOUT:-30} \ --job-timeout-min ${JOB_TIMEOUT:-30} \
--first-stage-init artifacts/ci-common/init-stage1.sh \ --first-stage-init artifacts/ci-common/init-stage1.sh \
--ci-project-dir ${CI_PROJECT_DIR} \ --ci-project-dir "${CI_PROJECT_DIR}" \
--device-type ${DEVICE_TYPE} \ --device-type "${DEVICE_TYPE}" \
--dtb ${DTB} \ --dtb-filename "${DTB}" \
--jwt-file "${CI_JOB_JWT_FILE}" \ --jwt-file "${CI_JOB_JWT_FILE}" \
--kernel-image-name ${KERNEL_IMAGE_NAME} \ --kernel-image-name "${KERNEL_IMAGE_NAME}" \
--kernel-image-type "${KERNEL_IMAGE_TYPE}" \ --kernel-image-type "${KERNEL_IMAGE_TYPE}" \
--boot-method ${BOOT_METHOD} \ --boot-method "${BOOT_METHOD}" \
--visibility-group ${VISIBILITY_GROUP} \ --visibility-group "${VISIBILITY_GROUP}" \
--lava-tags "${LAVA_TAGS}" \ --lava-tags "${LAVA_TAGS}" \
--mesa-job-name "$CI_JOB_NAME" \ --mesa-job-name "$CI_JOB_NAME" \
>> results/lava.log >> results/lava.log

View File

@@ -10,16 +10,17 @@
"""Send a job to LAVA, track it and collect log back""" """Send a job to LAVA, track it and collect log back"""
import argparse
import contextlib import contextlib
import pathlib import pathlib
import sys import sys
import time import time
from dataclasses import dataclass, fields
from datetime import datetime, timedelta from datetime import datetime, timedelta
from io import StringIO from io import StringIO
from os import getenv from os import getenv
from typing import Any, Optional from typing import Any, Optional
import fire
from lava.exceptions import ( from lava.exceptions import (
MesaCIException, MesaCIException,
MesaCIParseException, MesaCIParseException,
@@ -252,8 +253,6 @@ def print_job_final_status(job):
def execute_job_with_retries(proxy, job_definition, retry_count) -> Optional[LAVAJob]: def execute_job_with_retries(proxy, job_definition, retry_count) -> Optional[LAVAJob]:
for attempt_no in range(1, retry_count + 2): for attempt_no in range(1, retry_count + 2):
# Need to get the logger value from its object to enable autosave
# features, if AutoSaveDict is enabled from StructuredLogging module
job = LAVAJob(proxy, job_definition) job = LAVAJob(proxy, job_definition)
try: try:
@@ -294,71 +293,84 @@ def retriable_follow_job(proxy, job_definition) -> LAVAJob:
) )
def treat_mesa_job_name(args): @dataclass
# Remove mesa job names with spaces, which breaks the lava-test-case command class PathResolver:
args.mesa_job_name = args.mesa_job_name.split(" ")[0] def __post_init__(self):
for field in fields(self):
value = getattr(self, field.name)
if not value:
continue
if field.type == pathlib.Path:
value = pathlib.Path(value)
setattr(self, field.name, value.resolve())
def main(args): @dataclass
proxy = setup_lava_proxy() class LAVAJobSubmitter(PathResolver):
boot_method: str
ci_project_dir: str
device_type: str
job_timeout_min: int # The job timeout in minutes
build_url: str = None
dtb_filename: str = None
dump_yaml: bool = False # Whether to dump the YAML payload to stdout
first_stage_init: str = None
jwt_file: pathlib.Path = None
kernel_image_name: str = None
kernel_image_type: str = ""
kernel_url_prefix: str = None
lava_tags: str = "" # Comma-separated LAVA tags for the job
mesa_job_name: str = "mesa_ci_job"
pipeline_info: str = ""
rootfs_url_prefix: str = None
validate_only: bool = False # Whether to only validate the job, not execute it
visibility_group: str = None # Only affects LAVA farm maintainers
job_rootfs_overlay_url: str = None
# Overwrite the timeout for the testcases with the value offered by the def __post_init__(self) -> None:
# user. The testcase running time should be at least 4 times greater than super().__post_init__()
# the other sections (boot and setup), so we can safely ignore them. # Remove mesa job names with spaces, which breaks the lava-test-case command
# If LAVA fails to stop the job at this stage, it will fall back to the self.mesa_job_name = self.mesa_job_name.split(" ")[0]
# script section timeout with a reasonable delay.
GL_SECTION_TIMEOUTS[LogSectionType.TEST_CASE] = timedelta(minutes=args.job_timeout)
job_definition_stream = StringIO() def dump(self, job_definition):
lava_yaml.dump(generate_lava_yaml_payload(args), job_definition_stream) if self.dump_yaml:
job_definition = job_definition_stream.getvalue() with GitlabSection(
"yaml_dump",
"LAVA job definition (YAML)",
type=LogSectionType.LAVA_BOOT,
start_collapsed=True,
):
print(hide_sensitive_data(job_definition))
if args.dump_yaml: def submit(self):
with GitlabSection( proxy = setup_lava_proxy()
"yaml_dump",
"LAVA job definition (YAML)",
type=LogSectionType.LAVA_BOOT,
start_collapsed=True,
):
print(hide_sensitive_data(job_definition))
job = LAVAJob(proxy, job_definition)
if errors := job.validate(): # Overwrite the timeout for the testcases with the value offered by the
fatal_err(f"Error in LAVA job definition: {errors}") # user. The testcase running time should be at least 4 times greater than
print_log("LAVA job definition validated successfully") # the other sections (boot and setup), so we can safely ignore them.
# If LAVA fails to stop the job at this stage, it will fall back to the
# script section timeout with a reasonable delay.
GL_SECTION_TIMEOUTS[LogSectionType.TEST_CASE] = timedelta(
minutes=self.job_timeout_min
)
if args.validate_only: job_definition_stream = StringIO()
return lava_yaml.dump(generate_lava_yaml_payload(self), job_definition_stream)
job_definition = job_definition_stream.getvalue()
finished_job = retriable_follow_job(proxy, job_definition) self.dump(job_definition)
exit_code = 0 if finished_job.status == "pass" else 1
sys.exit(exit_code)
job = LAVAJob(proxy, job_definition)
if errors := job.validate():
fatal_err(f"Error in LAVA job definition: {errors}")
print_log("LAVA job definition validated successfully")
def create_parser(): if self.validate_only:
parser = argparse.ArgumentParser("LAVA job submitter") return
parser.add_argument("--pipeline-info") finished_job = retriable_follow_job(proxy, job_definition)
parser.add_argument("--rootfs-url-prefix") exit_code = 0 if finished_job.status == "pass" else 1
parser.add_argument("--kernel-url-prefix") sys.exit(exit_code)
parser.add_argument("--build-url")
parser.add_argument("--job-rootfs-overlay-url")
parser.add_argument("--job-timeout", type=int)
parser.add_argument("--first-stage-init")
parser.add_argument("--ci-project-dir")
parser.add_argument("--device-type")
parser.add_argument("--dtb", nargs='?', default="")
parser.add_argument("--kernel-image-name")
parser.add_argument("--kernel-image-type", nargs='?', default="")
parser.add_argument("--boot-method")
parser.add_argument("--lava-tags", nargs='?', default="")
parser.add_argument("--jwt-file", type=pathlib.Path)
parser.add_argument("--validate-only", action='store_true')
parser.add_argument("--dump-yaml", action='store_true')
parser.add_argument("--visibility-group")
parser.add_argument("--mesa-job-name")
return parser
if __name__ == "__main__": if __name__ == "__main__":
@@ -368,9 +380,4 @@ if __name__ == "__main__":
sys.stdout.reconfigure(line_buffering=True) sys.stdout.reconfigure(line_buffering=True)
sys.stderr.reconfigure(line_buffering=True) sys.stderr.reconfigure(line_buffering=True)
parser = create_parser() fire.Fire(LAVAJobSubmitter)
parser.set_defaults(func=main)
args = parser.parse_args()
treat_mesa_job_name(args)
args.func(args)

View File

@@ -1 +1,2 @@
lavacli==1.5.2 lavacli==1.5.2
fire==0.5.0

View File

@@ -21,7 +21,7 @@ def generate_lava_yaml_payload(args) -> dict[str, Any]:
"extra_nfsroot_args": " init=/init rootwait usbcore.quirks=0bda:8153:k" "extra_nfsroot_args": " init=/init rootwait usbcore.quirks=0bda:8153:k"
}, },
"timeouts": { "timeouts": {
"job": {"minutes": args.job_timeout}, "job": {"minutes": args.job_timeout_min},
"actions": { "actions": {
"depthcharge-retry": { "depthcharge-retry": {
# Could take between 1 and 1.5 min in slower boots # Could take between 1 and 1.5 min in slower boots
@@ -60,8 +60,10 @@ def generate_lava_yaml_payload(args) -> dict[str, Any]:
} }
if args.kernel_image_type: if args.kernel_image_type:
deploy["kernel"]["type"] = args.kernel_image_type deploy["kernel"]["type"] = args.kernel_image_type
if args.dtb: if args.dtb_filename:
deploy["dtb"] = {"url": "{}/{}.dtb".format(args.kernel_url_prefix, args.dtb)} deploy["dtb"] = {
"url": "{}/{}.dtb".format(args.kernel_url_prefix, args.dtb_filename)
}
# always boot over NFS # always boot over NFS
boot = { boot = {
@@ -75,7 +77,7 @@ def generate_lava_yaml_payload(args) -> dict[str, Any]:
# since LAVA's test parsing is not useful to us # since LAVA's test parsing is not useful to us
run_steps = [] run_steps = []
test = { test = {
"timeout": {"minutes": args.job_timeout}, "timeout": {"minutes": args.job_timeout_min},
"failure_retry": 1, "failure_retry": 1,
"definitions": [ "definitions": [
{ {