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:

committed by
Marge Bot

parent
c03f7233ca
commit
cfe644a9e5
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
|
||||||
|
@@ -1 +1,2 @@
|
|||||||
lavacli==1.5.2
|
lavacli==1.5.2
|
||||||
|
fire==0.5.0
|
||||||
|
@@ -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": [
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user