|
1 | 1 | # |
2 | | -# Copyright(c) 2022 Intel Corporation |
| 2 | +# Copyright(c) 2020-2022 Intel Corporation |
| 3 | +# Copyright(c) 2026 Unvertical |
3 | 4 | # SPDX-License-Identifier: BSD-3-Clause |
4 | 5 | # |
5 | 6 |
|
6 | 7 | import re |
| 8 | +from time import sleep |
7 | 9 |
|
| 10 | +from connection.utils.output import CmdException |
8 | 11 | from core.test_run import TestRun |
| 12 | +from storage_devices.device import Device |
| 13 | +from test_tools.os_tools import ( |
| 14 | + get_syslog_path, |
| 15 | + is_kernel_module_loaded, |
| 16 | + load_kernel_module, |
| 17 | + unload_kernel_module, |
| 18 | +) |
9 | 19 |
|
10 | | -syslog_path = "/var/log/messages" |
| 20 | +MODULE_NAME = "scsi_debug" |
11 | 21 |
|
| 22 | +FLUSH = re.compile(r"scsi_debug:[\s\S]*cmd 35") |
| 23 | +FUA = re.compile(r"scsi_debug:[\s\S]*cmd 2a 08") |
12 | 24 |
|
13 | | -class Logs: |
14 | | - last_read_line = 1 |
15 | | - FLUSH = re.compile(r"scsi_debug:[\s\S]*cmd 35") |
16 | | - FUA = re.compile(r"scsi_debug:[\s\S]*cmd 2a 08") |
17 | 25 |
|
18 | | - @staticmethod |
19 | | - def check_syslog_for_signals(): |
20 | | - Logs.check_syslog_for_flush() |
21 | | - Logs.check_syslog_for_fua() |
| 26 | +class ScsiDebug: |
| 27 | + def __init__(self, params): |
| 28 | + self.params = params |
| 29 | + self.syslog_path = None |
| 30 | + self.last_read_line = 0 |
| 31 | + self.reload() |
22 | 32 |
|
23 | | - @staticmethod |
24 | | - def check_syslog_for_flush(): |
25 | | - """Check syslog for FLUSH logs""" |
26 | | - log_lines = Logs._read_syslog(Logs.last_read_line) |
27 | | - flush_logs_counter = Logs._count_logs(log_lines, Logs.FLUSH) |
28 | | - log_type = "FLUSH" |
29 | | - Logs._validate_logs_amount(flush_logs_counter, log_type) |
| 33 | + def reload(self): |
| 34 | + self.unload() |
| 35 | + sleep(1) |
| 36 | + load_output = load_kernel_module(MODULE_NAME, self.params) |
| 37 | + if load_output.exit_code != 0: |
| 38 | + raise CmdException(f"Failed to load {MODULE_NAME} module", load_output) |
| 39 | + TestRun.LOGGER.info(f"{MODULE_NAME} loaded successfully.") |
| 40 | + sleep(10) |
30 | 41 |
|
31 | 42 | @staticmethod |
32 | | - def check_syslog_for_fua(): |
33 | | - """Check syslog for FUA logs""" |
34 | | - log_lines = Logs._read_syslog(Logs.last_read_line) |
35 | | - fua_logs_counter = Logs._count_logs(log_lines, Logs.FUA) |
36 | | - log_type = "FUA" |
37 | | - Logs._validate_logs_amount(fua_logs_counter, log_type) |
| 43 | + def unload(): |
| 44 | + if is_kernel_module_loaded(MODULE_NAME): |
| 45 | + unload_kernel_module(MODULE_NAME) |
38 | 46 |
|
39 | | - @staticmethod |
40 | | - def _read_syslog(last_read_line: int): |
41 | | - """Read recent lines in syslog, mark last line and return read lines as list.""" |
42 | | - log_lines = TestRun.executor.run_expect_success( |
43 | | - f"tail -qn +{last_read_line} {syslog_path}" |
44 | | - ).stdout.splitlines() |
45 | | - # mark last read line to continue next reading from here |
46 | | - Logs.last_read_line += len(log_lines) |
| 47 | + def get_devices(self): |
| 48 | + scsi_debug_devices = TestRun.executor.run_expect_success( |
| 49 | + "lsscsi --scsi_id | grep scsi_debug").stdout |
| 50 | + return [Device(f'/dev/disk/by-id/scsi-{device.split()[-1]}') |
| 51 | + for device in scsi_debug_devices.splitlines()] |
47 | 52 |
|
48 | | - return log_lines |
| 53 | + def reset_stats(self): |
| 54 | + """Set syslog position to current end so subsequent reads only see new entries.""" |
| 55 | + if self.syslog_path is None: |
| 56 | + self.syslog_path = get_syslog_path() |
49 | 57 |
|
50 | | - @staticmethod |
51 | | - def _count_logs(log_lines: list, expected_log): |
52 | | - """Count specified log in list and return its amount.""" |
53 | | - logs_counter = 0 |
| 58 | + line_count = TestRun.executor.run_expect_success( |
| 59 | + f"wc -l < {self.syslog_path}" |
| 60 | + ).stdout.strip() |
| 61 | + self.last_read_line = int(line_count) |
54 | 62 |
|
55 | | - for line in log_lines: |
56 | | - is_log_in_line = expected_log.search(line) |
57 | | - if is_log_in_line is not None: |
58 | | - logs_counter += 1 |
| 63 | + def get_flush_count(self): |
| 64 | + log_lines = self._read_syslog() |
| 65 | + flush_count, _ = self._count_logs(log_lines) |
| 66 | + return flush_count |
59 | 67 |
|
60 | | - return logs_counter |
| 68 | + def get_fua_count(self): |
| 69 | + log_lines = self._read_syslog() |
| 70 | + _, fua_count = self._count_logs(log_lines) |
| 71 | + return fua_count |
61 | 72 |
|
62 | | - @staticmethod |
63 | | - def _validate_logs_amount(logs_counter: int, log_type: str): |
64 | | - """Validate amount of logs and return""" |
65 | | - if logs_counter == 0: |
66 | | - if Logs._is_flush(log_type): |
67 | | - TestRun.LOGGER.error(f"{log_type} log not occured") |
68 | | - else: |
69 | | - TestRun.LOGGER.warning(f"{log_type} log not occured") |
70 | | - elif logs_counter == 1: |
71 | | - TestRun.LOGGER.warning(f"{log_type} log occured only once.") |
72 | | - else: |
73 | | - TestRun.LOGGER.info(f"{log_type} log occured {logs_counter} times.") |
| 73 | + def _read_syslog(self): |
| 74 | + """Read syslog lines since last mark and advance the position.""" |
| 75 | + if self.syslog_path is None: |
| 76 | + self.syslog_path = get_syslog_path() |
| 77 | + |
| 78 | + start_line = self.last_read_line + 1 |
| 79 | + log_lines = TestRun.executor.run_expect_success( |
| 80 | + f"tail -qn +{start_line} {self.syslog_path}" |
| 81 | + ).stdout.splitlines() |
| 82 | + self.last_read_line += len(log_lines) |
| 83 | + return log_lines |
74 | 84 |
|
75 | 85 | @staticmethod |
76 | | - def _is_flush(log_type: str): |
77 | | - return log_type == "FLUSH" |
| 86 | + def _count_logs(log_lines): |
| 87 | + flush_count = 0 |
| 88 | + fua_count = 0 |
| 89 | + for line in log_lines: |
| 90 | + if FLUSH.search(line): |
| 91 | + flush_count += 1 |
| 92 | + if FUA.search(line): |
| 93 | + fua_count += 1 |
| 94 | + return flush_count, fua_count |
0 commit comments