From 5b9a4712e2fa47417df5e64edab8e30b80d1c4d5 Mon Sep 17 00:00:00 2001 From: Carsten Grohmann Date: Sat, 23 Oct 2021 14:37:02 +0200 Subject: [PATCH] Add support for newer kernels Suggested-by: Mikko Rantalainen --- OOMAnalyser.html | 12 ++- OOMAnalyser.py | 251 ++++++++++++++++++++++++++++++++++++++++++++--- test.py | 137 +++++++++++++++++++------- 3 files changed, 345 insertions(+), 55 deletions(-) diff --git a/OOMAnalyser.html b/OOMAnalyser.html index 80baac2..a519bf8 100644 --- a/OOMAnalyser.html +++ b/OOMAnalyser.html @@ -279,9 +279,14 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
- - - + + + +
@@ -884,6 +889,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
  • Report uncaught errors to the user
  • Add support for manually triggered OOM (suggested by Mikko Rantalainen)
  • Add support for systems w/o swap (suggested by Mikko Rantalainen)
  • +
  • Add support for newer kernels (suggested by Mikko Rantalainen)
  • ...
  • diff --git a/OOMAnalyser.py b/OOMAnalyser.py index c48c0a7..868492d 100644 --- a/OOMAnalyser.py +++ b/OOMAnalyser.py @@ -153,7 +153,17 @@ class BaseKernelConfig: name = 'Base configuration for all kernels' """Name/description of this kernel configuration""" - EXTRACT_PATTERN = { + EXTRACT_PATTERN = None + """ + Instance specific dictionary of RE pattern to analyse a OOM block for a specific kernel version + + This dict will be filled from EXTRACT_PATTERN_BASE and EXTRACT_PATTERN_OVERLAY during class constructor is executed. + + :type: None|Dict + :see: EXTRACT_PATTERN_BASE and EXTRACT_PATTERN_OVERLAY + """ + + EXTRACT_PATTERN_BASE = { 'invoked oom-killer': ( r'^(?P[\S ]+) invoked oom-killer: ' r'gfp_mask=(?P0x[a-z0-9]+)(\((?P[A-Z_|]+)\))?, ' @@ -255,7 +265,18 @@ class BaseKernelConfig: The first item is the RE pattern and the second is whether it is mandatory to find this pattern. + This dictionary will be copied to EXTRACT_PATTERN during class constructor is executed. + :type: dict(tuple(str, bool)) + :see: EXTRACT_PATTERN + """ + + EXTRACT_PATTERN_OVERLAY = {} + """ + To extend / overwrite parts of EXTRACT_PATTERN in kernel configuration. + + :type: dict(tuple(str, bool)) + :see: EXTRACT_PATTERN """ GFP_FLAGS = { @@ -325,22 +346,137 @@ class BaseKernelConfig: rec_version4kconfig = re.compile('.+') """RE to match kernel version to kernel configuration""" - rec_oom_begin = re.compile('invoked oom-killer:', re.MULTILINE) + rec_oom_begin = re.compile(r'invoked oom-killer:', re.MULTILINE) """RE to match the first line of an OOM block""" - rec_oom_end = re.compile('^Killed process \d+', re.MULTILINE) + rec_oom_end = re.compile(r'^Killed process \d+', re.MULTILINE) """RE to match the last line of an OOM block""" + def __init__(self): + super().__init__() + + if self.EXTRACT_PATTERN is None: + # Create a copy to prevent modifications on the class dictionary + # TODO replace with self.EXTRACT_PATTERN = self.EXTRACT_PATTERN.copy() after + # https://github.com/QQuick/Transcrypt/issues/716 "dict does not have a copy method" is fixed + self.EXTRACT_PATTERN = {} + self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_BASE) + + if self.EXTRACT_PATTERN_OVERLAY: + self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY) + + +class KernelConfig_4_6(BaseKernelConfig): + # Support changes: + # * "mm, oom_reaper: report success/failure" (bc448e897b6d24aae32701763b8a1fe15d29fa26) + + name = 'Configuration for Linux kernel 4.6 or later' + rec_version4kconfig = re.compile(r'^4\.([6-9]\.|[12][0-9]\.).+') + + # The "oom_reaper" line is optionally + rec_oom_end = re.compile(r'^((Out of memory.*|Memory cgroup out of memory): Killed process \d+|oom_reaper:)', + re.MULTILINE) + + def __init__(self): + super().__init__() + + +class KernelConfig_4_9(KernelConfig_4_6): + # Support changes: + # * "mm: oom: deduplicate victim selection code for memcg and global oom" (7c5f64f84483bd13886348edda8b3e7b799a7fdb) + + name = 'Configuration for Linux kernel 4.9 or later' + rec_version4kconfig = re.compile(r'^4\.([9]\.|[12][0-9]\.).+') + + EXTRACT_PATTERN_OVERLAY_49 = { + 'Details of process killed by OOM': ( + r'^(Out of memory.*|Memory cgroup out of memory): Killed process \d+ \(.*\)' + r'(, UID \d+,)?' + r' total-vm:(?P\d+)kB, anon-rss:(?P\d+)kB, ' + r'file-rss:(?P\d+)kB, shmem-rss:(?P\d+)kB.*', + True, + ), + } + + def __init__(self): + super().__init__() + self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_49) + + +class KernelConfig_5_0(KernelConfig_4_9): + # Support changes: + # * "mm, oom: reorganize the oom report in dump_header" (ef8444ea01d7442652f8e1b8a8b94278cb57eafd) + + name = 'Configuration for Linux kernel 5.0 or later' + rec_version4kconfig = re.compile(r'^[5-9]\..+') + + EXTRACT_PATTERN_OVERLAY_50 = { + # third last line - not integrated yet + # oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/,task=sed,pid=29481,uid=12345 + + 'Process killed by OOM': ( + r'^Out of memory: Killed process (?P\d+) \((?P[\S ]+)\) ' + r'total-vm:(?P\d+)kB, anon-rss:(?P\d+)kB, ' + r'file-rss:(?P\d+)kB, shmem-rss:(?P\d+)kB, ' + r'UID:\d+ pgtables:(?P\d+)kB oom_score_adj:(?P\d+)', + True, + ), + } + + def __init__(self): + super().__init__() + self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_50) + + +class KernelConfig_5_8(KernelConfig_5_0): + # Support changes: + # * "mm/writeback: discard NR_UNSTABLE_NFS, use NR_WRITEBACK instead" (8d92890bd6b8502d6aee4b37430ae6444ade7a8c) + + name = 'Configuration for Linux kernel 5.8 or later' + + rec_version4kconfig = re.compile(r'^(5\.[8-9]\.|5\.[1-9][0-9]\.|[6-9]\.).+') + + EXTRACT_PATTERN_OVERLAY_58 = { + 'Mem-Info (part 1)': ( + r'^Mem-Info:.*' + r'(?:\n)' + + # first line (starting w/o a space) + r'^active_anon:(?P\d+) inactive_anon:(?P\d+) ' + r'isolated_anon:(?P\d+)' + r'(?:\n)' + + # remaining lines (w/ leading space) + r'^ active_file:(?P\d+) inactive_file:(?P\d+) ' + r'isolated_file:(?P\d+)' + r'(?:\n)' + + r'^ unevictable:(?P\d+) dirty:(?P\d+) writeback:(?P\d+)', + True, + ), + } + + def __init__(self): + super().__init__() + self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_58) + class KernelConfigRhel7(BaseKernelConfig): """RHEL7 / CentOS7 specific configuration""" - name = 'RHEL7 / CentOS7 specific kernel configuration' + name = 'Configuration for RHEL7 / CentOS7 specific Linux kernel (3.10)' - rec_version4kconfig = re.compile('^3\..+') + rec_version4kconfig = re.compile(r'^3\..+') + + def __init__(self): + super().__init__() AllKernelConfigs = [ + KernelConfig_5_8(), + KernelConfig_5_0(), + KernelConfig_4_9(), + KernelConfig_4_6(), KernelConfigRhel7(), BaseKernelConfig(), ] @@ -426,6 +562,7 @@ class OOMEntity: """Remove all lines before and after OOM message block""" cleaned_lines = [] in_oom_lines = False + killed_process = False for line in oom_lines: # first line of the oom message block @@ -435,9 +572,21 @@ class OOMEntity: if in_oom_lines: cleaned_lines.append(line) - # next line will not be part of the oom anymore + # OOM blocks ends with the second last only or both lines + # Out of memory: Killed process ... + # oom_reaper: reaped process ... if 'Killed process' in line: - break + killed_process = True + continue + + # next line after "Killed process \d+ ..." + if killed_process: + if 'oom_reaper' in line: + break + else: + # remove this line + del cleaned_lines[-1] + break return cleaned_lines @@ -666,8 +815,8 @@ class OOMAnalyser: if not self.oom_result.kconfig.rec_oom_end.search(self.oom_entity.text): self.state = OOMEntityState.started - self.oom_result.error_msg = 'The inserted OOM is incomplete! The initial pattern was found ' \ - 'but not the final. The result may be incomplete!' + self.oom_result.error_msg = 'The inserted OOM is incomplete! The initial pattern was found but not the '\ + 'final.' return False self.state = OOMEntityState.complete @@ -1026,7 +1175,7 @@ class OOMDisplay: @rtype: OOMResult """ - example = u'''\ + example_rhel7 = u'''\ sed invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 sed cpuset=/ mems_allowed=0-1 CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 @@ -1167,6 +1316,78 @@ Total swap = 8388604kB [29752] 12345 29752 7522 296 19 0 0 rotatelogs Out of memory: Kill process 6576 (mysqld) score 651 or sacrifice child Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0kB, shmem-rss:0kB +''' + + example_ubuntu2110 = u'''\ +kworker/0:2 invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=-1, oom_score_adj=0 +CPU: 0 PID: 735 Comm: kworker/0:2 Not tainted 5.13.0-19-generic #19-Ubuntu +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ArchLinux 1.14.0-1 04/01/2014 +Workqueue: events moom_callback +Call Trace: + show_stack+0x52/0x58 + dump_stack+0x7d/0x9c + dump_header+0x4f/0x1f9 + oom_kill_process.cold+0xb/0x10 + out_of_memory.part.0+0xce/0x270 + out_of_memory+0x41/0x80 + moom_callback+0x7a/0xb0 + process_one_work+0x220/0x3c0 + worker_thread+0x53/0x420 + kthread+0x11f/0x140 + ? process_one_work+0x3c0/0x3c0 + ? set_kthread_struct+0x50/0x50 + ret_from_fork+0x22/0x30 +Mem-Info: +active_anon:221 inactive_anon:14331 isolated_anon:0 + active_file:18099 inactive_file:22324 isolated_file:0 + unevictable:4785 dirty:633 writeback:0 + slab_reclaimable:6027 slab_unreclaimable:6546 + mapped:15338 shmem:231 pagetables:412 bounce:0 + free:427891 free_pcp:153 free_cma:0 +Node 0 active_anon:884kB inactive_anon:57324kB active_file:72396kB inactive_file:89296kB unevictable:19140kB isolated(anon):0kB isolated(file):0kB mapped:61352kB dirty:2532kB writeback:0kB shmem:924kB shmem_thp: 0kB shmem_pmdmapped: 0kB anon_thp: 0kB writeback_tmp:0kB kernel_stack:1856kB pagetables:1648kB all_unreclaimable? no +Node 0 DMA free:15036kB min:352kB low:440kB high:528kB reserved_highatomic:0KB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15992kB managed:15360kB mlocked:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB +lowmem_reserve[]: 0 1893 1893 1893 1893 +Node 0 DMA32 free:1696528kB min:44700kB low:55872kB high:67044kB reserved_highatomic:0KB active_anon:884kB inactive_anon:57324kB active_file:72396kB inactive_file:89296kB unevictable:19140kB writepending:2532kB present:2080640kB managed:2010036kB mlocked:19140kB bounce:0kB free_pcp:612kB local_pcp:612kB free_cma:0kB +lowmem_reserve[]: 0 0 0 0 0 +Node 0 DMA: 1*4kB (U) 1*8kB (U) 1*16kB (U) 1*32kB (U) 0*64kB 1*128kB (U) 0*256kB 1*512kB (U) 0*1024kB 1*2048kB (M) 3*4096kB (M) = 15036kB +Node 0 DMA32: 0*4kB 4*8kB (UM) 25*16kB (UME) 151*32kB (UM) 56*64kB (UM) 21*128kB (ME) 36*256kB (UME) 47*512kB (UM) 41*1024kB (UM) 32*2048kB (UM) 377*4096kB (UM) = 1696528kB +Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB +42845 total pagecache pages +0 pages in swap cache +Swap cache stats: add 0, delete 0, find 0/0 +Free swap = 0kB +Total swap = 0kB +524158 pages RAM +0 pages HighMem/MovableOnly +17809 pages reserved +0 pages hwpoisoned +Tasks state (memory values in pages): +[ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name +[ 323] 0 323 9458 2766 77824 0 -250 systemd-journal +[ 356] 0 356 5886 1346 69632 0 -1000 systemd-udevd +[ 507] 0 507 70208 4646 98304 0 -1000 multipathd +[ 542] 101 542 21915 1391 69632 0 0 systemd-timesyn +[ 587] 102 587 4635 1882 73728 0 0 systemd-network +[ 589] 103 589 5875 2951 86016 0 0 systemd-resolve +[ 602] 0 602 1720 322 53248 0 0 cron +[ 603] 104 603 2159 1168 53248 0 -900 dbus-daemon +[ 608] 0 608 7543 4677 94208 0 0 networkd-dispat +[ 609] 107 609 55313 1248 73728 0 0 rsyslogd +[ 611] 0 611 311571 8248 221184 0 -900 snapd +[ 613] 0 613 3404 1668 65536 0 0 systemd-logind +[ 615] 0 615 98223 3142 126976 0 0 udisksd +[ 620] 0 620 1443 278 45056 0 0 agetty +[ 623] 0 623 1947 1147 57344 0 0 login +[ 650] 0 650 3283 1683 65536 0 -1000 sshd +[ 651] 0 651 27005 5232 106496 0 0 unattended-upgr +[ 661] 0 661 58546 1812 90112 0 0 polkitd +[ 856] 1000 856 3789 2157 73728 0 0 systemd +[ 857] 1000 857 25433 835 86016 0 0 (sd-pam) +[ 862] 1000 862 2208 1373 53248 0 0 bash +[ 876] 1000 876 2870 1356 57344 0 0 sudo +[ 877] 0 877 1899 1052 53248 0 0 bash +oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/unattended-upgrades.service,task=unattended-upgr,pid=651,uid=0 +Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:8380kB, file-rss:12548kB, shmem-rss:0kB, UID:0 pgtables:104kB oom_score_adj:0 ''' sorted_column = None @@ -1481,8 +1702,11 @@ Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss: return svg - def copy_example_to_form(self): - document.getElementById('textarea_oom').value = self.example + def copy_example_rhel7_to_form(self): + document.getElementById('textarea_oom').value = self.example_rhel7 + + def copy_example_ubuntu_to_form(self): + document.getElementById('textarea_oom').value = self.example_ubuntu2110 def reset_form(self): document.getElementById('textarea_oom').value = "" @@ -1533,9 +1757,6 @@ Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss: """ Show all extracted details as well as additionally generated information """ - if DEBUG: - print(self.oom_result.details) - hide_element('input') show_element('analysis') if self.oom_result.oom_type == OOMEntityType.manual: diff --git a/test.py b/test.py index 9d3d128..34f49a3 100755 --- a/test.py +++ b/test.py @@ -52,10 +52,15 @@ class TestBase(unittest.TestCase): def get_lines(self, text, count): """ Return the number of lines specified by count from given text + @type text: str @type count: int """ - lines = text.splitlines()[:count] + lines = text.splitlines() + if count < 0: + lines.reverse() + count = count * -1 + lines = lines[:count] res = '\n'.join(lines) return res @@ -167,8 +172,8 @@ class TestInBrowser(TestBase): self.click_analyse() - def check_results(self): - """Check the results of the analysis of the default example""" + def check_results_rhel7(self): + """Check the results of the analysis of the RHEL7 example""" self.assert_on_warn_error() h3_summary = self.driver.find_element_by_xpath('//h3[text()="Summary"]') self.assertTrue(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be displayed") @@ -193,8 +198,34 @@ class TestInBrowser(TestBase): explanation = self.driver.find_element_by_id('explanation') self.assertTrue('OOM killer was automatically triggered' in explanation.text, 'Missing text "OOM killer was automatically triggered"') + + self.check_swap_active() + + def check_results_ubuntu2110(self): + """Check the results of the analysis of the Ubuntu example""" + dirty_pages = self.driver.find_element_by_class_name('dirty_pages') + self.assertEqual(dirty_pages.text, '633 pages', 'Unexpected number of dirty pages') + + ram_pages = self.driver.find_element_by_class_name('ram_pages') + self.assertEqual(ram_pages.text, '524158 pages', 'Unexpected number of RAM pages') + + explanation = self.driver.find_element_by_id('explanation') + self.assertTrue('OOM killer was manually triggered' in explanation.text, + 'Missing text "OOM killer was manually triggered"') + + self.check_swap_inactive() + + def check_swap_inactive(self): + explanation = self.driver.find_element_by_id('explanation') + self.assertTrue('physical memory and no swap space' in explanation.text, + 'Missing text "physical memory and no swap space"') + self.assertFalse('swap space are in use' in explanation.text, + 'No swap space but text "swap space are in use"') + + def check_swap_active(self): + explanation = self.driver.find_element_by_id('explanation') self.assertTrue('swap space are in use' in explanation.text, - 'Missing text "swap space are in use"') + 'Swap space active but no text "swap space are in use"') def test_010_load_page(self): """Test if the page is loading""" @@ -205,11 +236,11 @@ class TestInBrowser(TestBase): elem = self.driver.find_element_by_id("version") self.assertIsNotNone(elem.text, "Version statement not set - JS not loaded") - def test_030_insert_and_analyse_example(self): - """Test loading and analysing example""" + def test_030_insert_and_analyse_rhel7_example(self): + """Test loading and analysing RHEL7 example""" textarea = self.driver.find_element_by_id('textarea_oom') self.assertEqual(textarea.get_attribute('value'), '', 'Empty textarea expected') - insert_example = self.driver.find_element_by_xpath('//button[text()="Insert example"]') + insert_example = self.driver.find_element_by_xpath('//button[contains(text(), "RHEL7" )]') insert_example.click() self.assertNotEqual(textarea.get_attribute('value'), '', 'Missing OOM text in textarea') @@ -217,9 +248,23 @@ class TestInBrowser(TestBase): self.assertFalse(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be not displayed") self.click_analyse() - self.check_results() + self.check_results_rhel7() - def test_031_empty_textarea(self): + def test_031_insert_and_analyse_ubuntu_example(self): + """Test loading and analysing Ubuntu 21.10 example""" + textarea = self.driver.find_element_by_id('textarea_oom') + self.assertEqual(textarea.get_attribute('value'), '', 'Empty textarea expected') + insert_example = self.driver.find_element_by_xpath('//button[contains(text(), "Ubuntu" )]') + insert_example.click() + self.assertNotEqual(textarea.get_attribute('value'), '', 'Missing OOM text in textarea') + + h3_summary = self.driver.find_element_by_xpath('//h3[text()="Summary"]') + self.assertFalse(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be not displayed") + + self.click_analyse() + self.check_results_ubuntu2110() + + def test_032_empty_textarea(self): """Test "Analyse" with empty textarea""" textarea = self.driver.find_element_by_id('textarea_oom') self.assertEqual(textarea.get_attribute('value'), '', 'Empty textarea expected') @@ -234,7 +279,7 @@ class TestInBrowser(TestBase): self.assertEqual(self.get_error_text(), "ERROR: Empty OOM text. Please insert an OOM message block.") self.click_reset() - def test_032_begin_but_no_end(self): + def test_033_begin_but_no_end(self): """Test incomplete OOM text - just the beginning""" example = """\ sed invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 @@ -243,10 +288,10 @@ CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 """ self.analyse_oom(example) self.assertEqual(self.get_error_text(), "ERROR: The inserted OOM is incomplete! The initial pattern was " - "found but not the final. The result may be incomplete!") + "found but not the final.") self.click_reset() - def test_033_no_begin_but_end(self): + def test_034_no_begin_but_end(self): """Test incomplete OOM text - just the end""" example = """\ Out of memory: Kill process 6576 (java) score 651 or sacrifice child @@ -258,7 +303,7 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k def test_040_trigger_proc_space(self): """Test trigger process name contains a space""" - example = OOMAnalyser.OOMDisplay.example + example = OOMAnalyser.OOMDisplay.example_rhel7 example = example.replace('sed', 'VM Monitoring Task') self.analyse_oom(example) @@ -268,7 +313,7 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k def test_050_kill_proc_space(self): """Test killed process name contains a space""" - example = OOMAnalyser.OOMDisplay.example + example = OOMAnalyser.OOMDisplay.example_rhel7 example = example.replace('mysqld', 'VM Monitoring Task') self.analyse_oom(example) @@ -278,8 +323,8 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k def test_060_removal_of_leading_but_useless_columns(self): """Test removal of leading but useless columns""" - self.analyse_oom(OOMAnalyser.OOMDisplay.example) - self.check_results() + self.analyse_oom(OOMAnalyser.OOMDisplay.example_rhel7) + self.check_results_rhel7() self.click_reset() for prefix in ["[11686.888109] ", "Apr 01 14:13:32 mysrv: ", @@ -289,17 +334,17 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k "kernel:", "Apr 01 14:13:32 mysrv kernel:", ]: - lines = OOMAnalyser.OOMDisplay.example.split('\n') + lines = OOMAnalyser.OOMDisplay.example_rhel7.split('\n') lines = ["{}{}".format(prefix, line) for line in lines] oom_text = "\n".join(lines) self.analyse_oom(oom_text) - self.check_results() + self.check_results_rhel7() self.click_reset() def test_070_manually_triggered_OOM(self): """Test for manually triggered OOM""" - example = OOMAnalyser.OOMDisplay.example + example = OOMAnalyser.OOMDisplay.example_rhel7 example = example.replace('order=0', 'order=-1') self.analyse_oom(example) self.assert_on_warn_error() @@ -310,18 +355,15 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k def test_080_swap_deactivated(self): """Test w/o swap or with deactivated swap""" - example = OOMAnalyser.OOMDisplay.example + example = OOMAnalyser.OOMDisplay.example_rhel7 example = example.replace('Total swap = 8388604kB', 'Total swap = 0kB') self.analyse_oom(example) self.assert_on_warn_error() - explanation = self.driver.find_element_by_id('explanation') - self.assertFalse('swap space are in use' in explanation.text, - 'No swap space but text "swap space are in use"') - + self.check_swap_inactive() self.click_reset() - example = OOMAnalyser.OOMDisplay.example + example = OOMAnalyser.OOMDisplay.example_rhel7 example = re.sub(r'\d+ pages in swap cac.*\n*', '', example, re.MULTILINE) example = re.sub(r'Swap cache stats.*\n*', '', example) example = re.sub(r'Free swap.*\n*', '', example) @@ -329,17 +371,14 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k self.analyse_oom(example) self.assert_on_warn_error() - - explanation = self.driver.find_element_by_id('explanation') - self.assertFalse('swap space are in use' in explanation.text, - 'No swap space but text "swap space are in use"') + self.check_swap_inactive() class TestPython(TestBase): def test_001_trigger_proc_space(self): """Test RE to find name of trigger process""" - first = self.get_first_line(OOMAnalyser.OOMDisplay.example) + first = self.get_first_line(OOMAnalyser.OOMDisplay.example_rhel7) pattern = OOMAnalyser.OOMAnalyser.oom_result.kconfig.EXTRACT_PATTERN['invoked oom-killer'][0] rec = re.compile(pattern, re.MULTILINE) match = rec.search(first) @@ -351,19 +390,19 @@ class TestPython(TestBase): def test_002_killed_proc_space(self): """Test RE to find name of killed process""" - last = self.get_last_line(OOMAnalyser.OOMDisplay.example) + text = self.get_lines(OOMAnalyser.OOMDisplay.example_rhel7, -2) pattern = OOMAnalyser.OOMAnalyser.oom_result.kconfig.EXTRACT_PATTERN['Process killed by OOM'][0] rec = re.compile(pattern, re.MULTILINE) - match = rec.search(last) + match = rec.search(text) self.assertTrue(match, "Error: re.search('Process killed by OOM') failed for simple process name") - last = last.replace('sed', 'VM Monitoring Task') - match = rec.search(last) + text = text.replace('sed', 'VM Monitoring Task') + match = rec.search(text) self.assertTrue(match, "Error: re.search('Process killed by OOM') failed for process name with space") def test_003_OOMEntity_number_of_columns_to_strip(self): """Test stripping useless / leading columns""" - oom_entity = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example) + oom_entity = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example_rhel7) for pos, line in [ (1, '[11686.888109] CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1'), (5, 'Apr 01 14:13:32 mysrv kernel: CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1'), @@ -375,7 +414,7 @@ class TestPython(TestBase): def test_004_extract_block_from_next_pos(self): """Test extracting a single block (all lines till the next line with a colon)""" - oom = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example) + oom = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example_rhel7) analyser = OOMAnalyser.OOMAnalyser(oom) text = analyser._extract_block_from_next_pos('Hardware name:') expected = '''\ @@ -388,7 +427,7 @@ Hardware name: HP ProLiant DL385 G7, BIOS A18 12/08/2012 def test_005_extract_kernel_version(self): """Test extracting kernel version""" - oom = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example) + oom = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example_rhel7) analyser = OOMAnalyser.OOMAnalyser(oom) for text, kversion in [ ('CPU: 0 PID: 19163 Comm: kworker/0:0 Tainted: G OE 5.4.0-80-lowlatency #90~18.04.1-Ubuntu', '5.4.0-80-lowlatency'), @@ -399,6 +438,30 @@ Hardware name: HP ProLiant DL385 G7, BIOS A18 12/08/2012 self.assertTrue(analyser._identify_kernel_version(), analyser.oom_result.error_msg) self.assertEqual(analyser.oom_result.kversion, kversion) + def test_006_choosing_kernel_config(self): + """Test choosing the right kernel configuration""" + for kcfg, kversion in [ + (OOMAnalyser.KernelConfig_5_8(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 5.13.0-514 #1'), + (OOMAnalyser.KernelConfig_5_8(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 5.8.0-514 #1'), + (OOMAnalyser.KernelConfig_4_6(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 4.6.0-514 #1'), + (OOMAnalyser.KernelConfigRhel7(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-1062.9.1.el7.x86_64 #1'), + (OOMAnalyser.KernelConfig_5_0(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 5.5.1 #1'), + (OOMAnalyser.KernelConfig_5_8(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 5.23.0 #1'), + (OOMAnalyser.KernelConfig_5_8(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 6.12.0 #1'), + (OOMAnalyser.BaseKernelConfig(), 'CPU: 4 PID: 29481 Comm: sed Not tainted 2.33.0 #1'), + ]: + oom = OOMAnalyser.OOMEntity(kversion) + analyser = OOMAnalyser.OOMAnalyser(oom) + analyser._identify_kernel_version() + analyser._choose_kernel_config() + result = analyser.oom_result.kconfig + self.assertEqual( + type(result), type(kcfg), + 'Mismatch between expected kernel config "%s" and chosen config "%s" for kernel version "%s"' % ( + type(kcfg), type(result), kversion + ) + ) + if __name__ == "__main__": unittest.main(verbosity=2)