From a219e777f931d106d0d8787f106204600be8bba8 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:45:45 +0800 Subject: [PATCH 1/3] feat: initial impl of crash log check --- providers/base/bin/crash_log_check.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 providers/base/bin/crash_log_check.py diff --git a/providers/base/bin/crash_log_check.py b/providers/base/bin/crash_log_check.py new file mode 100644 index 0000000000..9cf9cc4923 --- /dev/null +++ b/providers/base/bin/crash_log_check.py @@ -0,0 +1,49 @@ +from datetime import datetime +import os +from subprocess import run, PIPE +import sys +import typing as T + + +def get_boot_time() -> datetime: + return datetime.strptime( + run(["uptime", "-s"], stdout=PIPE, encoding="utf-8").stdout.strip(), + "%Y-%m-%d %H:%M:%S", + ) + + +def get_crash_logs() -> T.List[str]: + boot_time = get_boot_time() + crash_files_of_this_boot = [] + + for crash_file in os.listdir("/var/crash"): + file_stats = os.stat("/var/crash/{}".format(crash_file)) + last_modified_time = max( + file_stats.st_atime, file_stats.st_mtime, file_stats.st_ctime + ) # whichever timestamp is the latest + + if datetime.fromtimestamp(last_modified_time) >= boot_time: + crash_files_of_this_boot.append(crash_file) + + return crash_files_of_this_boot + + +def main(): + crash_files = get_crash_logs() + + if len(crash_files) == 0: + print("[ OK ] No crash files found in /var/crash") + return 0 + + print( + "[ ERROR ] Found the following crash files of this boot", + file=sys.stderr, + ) + for file in crash_files: + print(file, file=sys.stderr) + + return 1 + + +if __name__ == "__main__": + exit(main()) From d3523878aa8fcc6caff2ee6c3a677b63e45d93f8 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:34:32 +0800 Subject: [PATCH 2/3] test: add basic unit tests --- providers/base/bin/crash_log_check.py | 3 +- providers/base/tests/test_crash_log_check.py | 51 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 providers/base/tests/test_crash_log_check.py diff --git a/providers/base/bin/crash_log_check.py b/providers/base/bin/crash_log_check.py index 9cf9cc4923..c238fe949c 100644 --- a/providers/base/bin/crash_log_check.py +++ b/providers/base/bin/crash_log_check.py @@ -17,10 +17,11 @@ def get_crash_logs() -> T.List[str]: crash_files_of_this_boot = [] for crash_file in os.listdir("/var/crash"): + print(crash_file) file_stats = os.stat("/var/crash/{}".format(crash_file)) last_modified_time = max( file_stats.st_atime, file_stats.st_mtime, file_stats.st_ctime - ) # whichever timestamp is the latest + ) # whichever timestamp is the latest if datetime.fromtimestamp(last_modified_time) >= boot_time: crash_files_of_this_boot.append(crash_file) diff --git a/providers/base/tests/test_crash_log_check.py b/providers/base/tests/test_crash_log_check.py new file mode 100644 index 0000000000..2035ed1cf6 --- /dev/null +++ b/providers/base/tests/test_crash_log_check.py @@ -0,0 +1,51 @@ +from datetime import datetime +import unittest +from unittest.mock import patch +import crash_log_check as CLC +from os import stat_result + + +class CrashLogCheckTests(unittest.TestCase): + def test_no_crash_file_path(self): + with patch("os.listdir") as mock_list: + mock_list.return_value = [] + self.assertEqual(CLC.main(), 0) + + def test_all_new_crash_files(self): + with patch("os.listdir") as mock_listdir, patch( + "os.stat" + ) as mock_stat, patch( + "crash_log_check.get_boot_time" + ) as mock_get_boot_time: + mock_listdir.return_value = ["crash1.crash", "crash2.crash"] + + def mock_stat_side_effect(filename: str) -> stat_result: + if filename == "crash1.crash": + t1 = datetime.timestamp(datetime(2024, 8, 1, 5, 30, 5)) + t2 = datetime.timestamp(datetime(2024, 8, 1, 5, 31, 2)) + t3 = datetime.timestamp(datetime(2024, 8, 1, 5, 31, 1)) + return stat_result( + # slightly obscure syntax, we fill the first 7 args + # with 0 because it's unused, then fill the last 3 spots + # with timestamps (atime, mtime, ctime) + # unwrap the 7-zeros array and flatten with * operator + [*[0] * 7, t1, t2, t3] + ) + if filename == "crash2.crash": + t1 = datetime.timestamp(datetime(2024, 8, 1, 5, 40, 5)) + t2 = datetime.timestamp(datetime(2024, 8, 1, 5, 40, 2)) + t3 = datetime.timestamp(datetime(2024, 8, 1, 5, 40, 1)) + return stat_result( + [*[0] * 7, 1723092286, 1722484746, 1722594746] + ) + raise Exception("Unexpected use of this mock") + + mock_stat.side_effect = mock_stat_side_effect + mock_get_boot_time.return_value = datetime(2024, 8, 1, 5, 30, 3) + + logs = CLC.get_crash_logs() + + self.assertListEqual(["crash1.crash", 'crash2.crash'], logs) + + # def test_mixed_timestamps(self): + # NotImplemented() From f1e08e53ebdad5ddec8ce8beabbe7ebaa3477e72 Mon Sep 17 00:00:00 2001 From: Zhongning Li <60045212+tomli380576@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:37:39 +0800 Subject: [PATCH 3/3] fix: missing shebang header --- providers/base/bin/crash_log_check.py | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 providers/base/bin/crash_log_check.py diff --git a/providers/base/bin/crash_log_check.py b/providers/base/bin/crash_log_check.py old mode 100644 new mode 100755 index c238fe949c..4cb436c5f9 --- a/providers/base/bin/crash_log_check.py +++ b/providers/base/bin/crash_log_check.py @@ -1,3 +1,5 @@ +#! /usr/bin/python3 + from datetime import datetime import os from subprocess import run, PIPE