1import re 2from dataclasses import dataclass 3from datetime import timedelta 4from enum import Enum, auto 5from os import getenv 6from typing import Optional, Pattern, Union 7 8from lava.utils.gitlab_section import GitlabSection 9 10 11class LogSectionType(Enum): 12 UNKNOWN = auto() 13 LAVA_BOOT = auto() 14 TEST_DUT_SUITE = auto() 15 TEST_SUITE = auto() 16 TEST_CASE = auto() 17 LAVA_POST_PROCESSING = auto() 18 19 20# Empirically, successful device boot in LAVA time takes less than 3 21# minutes. 22# LAVA itself is configured to attempt thrice to boot the device, 23# summing up to 9 minutes. 24# It is better to retry the boot than cancel the job and re-submit to avoid 25# the enqueue delay. 26LAVA_BOOT_TIMEOUT = int(getenv("LAVA_BOOT_TIMEOUT", 9)) 27 28# Test DUT suite phase is where the initialization happens in DUT, not on docker. 29# The device will be listening to SSH session until the end of the job. 30LAVA_TEST_DUT_SUITE_TIMEOUT = int(getenv("JOB_TIMEOUT", 60)) 31 32# Test suite phase is where the initialization happens on docker. 33LAVA_TEST_SUITE_TIMEOUT = int(getenv("LAVA_TEST_SUITE_TIMEOUT", 5)) 34 35# Test cases may take a long time, this script has no right to interrupt 36# them. But if the test case takes almost 1h, it will never succeed due to 37# Gitlab job timeout. 38LAVA_TEST_CASE_TIMEOUT = int(getenv("JOB_TIMEOUT", 60)) 39 40# LAVA post processing may refer to a test suite teardown, or the 41# adjustments to start the next test_case 42LAVA_POST_PROCESSING_TIMEOUT = int(getenv("LAVA_POST_PROCESSING_TIMEOUT", 5)) 43 44FALLBACK_GITLAB_SECTION_TIMEOUT = timedelta(minutes=10) 45DEFAULT_GITLAB_SECTION_TIMEOUTS = { 46 LogSectionType.LAVA_BOOT: timedelta(minutes=LAVA_BOOT_TIMEOUT), 47 LogSectionType.TEST_DUT_SUITE: timedelta(minutes=LAVA_TEST_DUT_SUITE_TIMEOUT), 48 LogSectionType.TEST_SUITE: timedelta(minutes=LAVA_TEST_SUITE_TIMEOUT), 49 LogSectionType.TEST_CASE: timedelta(minutes=LAVA_TEST_CASE_TIMEOUT), 50 LogSectionType.LAVA_POST_PROCESSING: timedelta( 51 minutes=LAVA_POST_PROCESSING_TIMEOUT 52 ), 53} 54 55 56@dataclass(frozen=True) 57class LogSection: 58 regex: Union[Pattern, str] 59 levels: tuple[str] 60 section_id: str 61 section_header: str 62 section_type: LogSectionType 63 collapsed: bool = False 64 65 def from_log_line_to_section( 66 self, lava_log_line: dict[str, str] 67 ) -> Optional[GitlabSection]: 68 if lava_log_line["lvl"] not in self.levels: 69 return 70 71 if match := re.search(self.regex, lava_log_line["msg"]): 72 section_id = self.section_id.format(*match.groups()) 73 section_header = self.section_header.format(*match.groups()) 74 timeout = DEFAULT_GITLAB_SECTION_TIMEOUTS[self.section_type] 75 return GitlabSection( 76 id=section_id, 77 header=f"{section_header} - Timeout: {timeout}", 78 type=self.section_type, 79 start_collapsed=self.collapsed, 80 ) 81 82 83LOG_SECTIONS = ( 84 LogSection( 85 regex=re.compile(r"<?STARTTC>? ([^>]*)"), 86 levels=("target", "debug"), 87 section_id="{}", 88 section_header="test_case {}", 89 section_type=LogSectionType.TEST_CASE, 90 ), 91 LogSection( 92 regex=re.compile(r"<?STARTRUN>? ([^>]*ssh.*server.*)"), 93 levels=("debug"), 94 section_id="{}", 95 section_header="[dut] test_suite {}", 96 section_type=LogSectionType.TEST_DUT_SUITE, 97 ), 98 LogSection( 99 regex=re.compile(r"<?STARTRUN>? ([^>]*)"), 100 levels=("debug"), 101 section_id="{}", 102 section_header="[docker] test_suite {}", 103 section_type=LogSectionType.TEST_SUITE, 104 ), 105 LogSection( 106 regex=re.compile(r"ENDTC>? ([^>]+)"), 107 levels=("target", "debug"), 108 section_id="post-{}", 109 section_header="Post test_case {}", 110 collapsed=True, 111 section_type=LogSectionType.LAVA_POST_PROCESSING, 112 ), 113) 114