Merge ~aieri/charm-hw-health:lp1832906 into charm-hw-health:master

Proposed by Andrea Ieri
Status: Merged
Approved by: Paul Goins
Approved revision: 7ae7e1f45d2f4507fa4f5f8168fa73a4e32b8eaa
Merged at revision: 7ae7e1f45d2f4507fa4f5f8168fa73a4e32b8eaa
Proposed branch: ~aieri/charm-hw-health:lp1832906
Merge into: charm-hw-health:master
Diff against target: 702 lines (+256/-86)
13 files modified
src/files/mdadm/cron_mdadm.py (+93/-31)
src/tests/hw-health-samples/mdadm.output (+4/-4)
src/tests/hw-health-samples/mdadm.output.critical.1 (+4/-4)
src/tests/hw-health-samples/mdadm.output.critical.2 (+33/-0)
src/tests/hw-health-samples/mdadm.output.warning (+33/-0)
src/tests/unit/lib/samples.py (+17/-0)
src/tests/unit/test_check_mdadm.py (+10/-18)
src/tests/unit/test_check_megacli.py (+7/-7)
src/tests/unit/test_check_nvme.py (+5/-3)
src/tests/unit/test_check_sas2ircu.py (+5/-4)
src/tests/unit/test_check_sas3ircu.py (+5/-3)
src/tests/unit/test_cron_mdadm.py (+35/-8)
src/tests/unit/test_hwdiscovery.py (+5/-4)
Reviewer Review Type Date Requested Status
Paul Goins Approve
Drew Freiberger (community) Approve
Stuart Bishop Pending
Review via email: mp+385839@code.launchpad.net

Commit message

Fix issue with cron_mdadm.py which causes degraded state to not be reported

Closes-Bug: 1832906

Description of the change

    Fix issue with cron_mdadm.py which causes degraded state to not be reported

    There was a formatting assumption which broke the detection of the degraded
    flag in the State section of each device report from mdadm --detail <devices>
    This merge adds code to split the State flags and check for both degraded and
    recovering states and sets alert status based on the combination of states.

    Also added is the direct detection of a "removed" member of the raid.

    Closes-Bug: 1832906

To post a comment you must log in.
Revision history for this message
Andrea Ieri (aieri) wrote :

This supersedes 385838. I had to recreate this from scratch to clean up the launchpad diff.

Revision history for this message
Andrea Ieri (aieri) wrote :

Which itself was an attempt at superseding 385837

Revision history for this message
Andrea Ieri (aieri) wrote :

Comments from 385837 should have now been addressed.

Revision history for this message
Drew Freiberger (afreiberger) wrote :

Please see inline comment echoing Stu's point about API oddity on get_device_stats. We moved the try-except of the parse_output function into get_device_stats, but didn't refactor this exception to be API clean. It reads a bit hackish.

Revision history for this message
Drew Freiberger (afreiberger) wrote :

lgtm, this addresses the bug, the comments prior, and includes all of the updates from the history of the other merge.

review: Approve
Revision history for this message
Paul Goins (vultaire) wrote :

I haven't fully reviewed this yet, but - it seems we're missing the samples.py module into which the get_sample() function was refactored?

review: Needs Fixing
Revision history for this message
Andrea Ieri (aieri) wrote :

Yes, I had done a git commit -a and missed committing hte samples.py file. The MR has now been updated

Revision history for this message
Paul Goins (vultaire) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/files/mdadm/cron_mdadm.py b/src/files/mdadm/cron_mdadm.py
2index 0b0b365..5aaafa4 100755
3--- a/src/files/mdadm/cron_mdadm.py
4+++ b/src/files/mdadm/cron_mdadm.py
5@@ -6,24 +6,28 @@ import shutil
6 import subprocess
7 import sys
8
9-OUTPUT_FILE = "/var/lib/nagios/mdadm.out"
10-TEMP_FILE = "/tmp/mdadm.out"
11+OUTPUT_FILE = '/var/lib/nagios/mdadm.out'
12+TEMP_FILE = '/tmp/mdadm.out'
13
14
15 def get_devices():
16- if os.path.exists("/sbin/mdadm"):
17+ if os.path.exists('/sbin/mdadm'):
18 try:
19- devices_raw = subprocess.check_output(["/sbin/mdadm", "--detail", "--scan"])
20- devices_re = re.compile(r"^ARRAY\s+([^ ]+) ")
21+ devices_raw = subprocess.check_output(
22+ ['/sbin/mdadm', '--detail', '--scan']
23+ )
24+ devices_re = re.compile(r'^ARRAY\s+([^ ]+) ')
25 devices = set()
26- for line in devices_raw.decode().split("\n"):
27+ for line in devices_raw.decode().split('\n'):
28 line = line.strip()
29 device_re = devices_re.match(line)
30 if device_re is not None:
31 devices.add(device_re.group(1))
32 return devices
33 except subprocess.CalledProcessError as error:
34- rc = generate_output("CRITICAL: get_devices error - {}".format(error))
35+ rc = generate_output(
36+ 'CRITICAL: get_devices error - {}'.format(error)
37+ )
38 if rc:
39 sys.exit(0)
40 return set()
41@@ -31,90 +35,148 @@ def get_devices():
42
43 def generate_output(msg):
44 try:
45- with open(TEMP_FILE, "w") as fd:
46+ with open(TEMP_FILE, 'w') as fd:
47 fd.write(msg)
48 shutil.move(TEMP_FILE, OUTPUT_FILE)
49 return True
50 except Exception as error:
51- print("Unable to generate output file:", error)
52+ print('Unable to generate output file:', error)
53 return False
54
55
56 def get_devices_stats(devices):
57- mdadm_detail = ["/sbin/mdadm", "--detail"]
58+ mdadm_detail = ['/sbin/mdadm', '--detail']
59 mdadm_detail.extend(sorted(devices))
60+
61 devices_details_raw = subprocess.check_output(mdadm_detail)
62
63- devices_re = r"^(/\S+):$"
64- state_re = r"^\s*State\s+:\s+(\S+)$"
65- status_re = r"^\s*(Active|Working|Failed|Spare) Devices\s+:\s+(\d+)$"
66+ devices_re = r'^(/\S+):$'
67+ state_re = r'^\s*State\s+:\s+(.+)\s*$'
68+ status_re = r'^\s*(Active|Working|Failed|Spare) Devices\s+:\s+(\d+)$'
69+ rebuild_status_re = r'^\s*Rebuild Status\s+:\s+(\d+%\s+\S+)$'
70+ removed_re = r'^\s*-\s+0\s+0\s+(\d+)\s+removed$'
71+ # 4 8 162 3 spare rebuilding /dev/sdk2
72+ rebuilding_re = r'^\s*\d+\s+\d+\s+\d+\s+\d+\s+\S+\s+rebuilding\s+(\S+)$'
73
74 devices_cre = re.compile(devices_re)
75 state_cre = re.compile(state_re)
76 status_cre = re.compile(status_re)
77+ rebuild_status_cre = re.compile(rebuild_status_re)
78+ removed_cre = re.compile(removed_re)
79+ rebuilding_cre = re.compile(rebuilding_re)
80
81 device = None
82 devices_stats = {}
83- for line in devices_details_raw.decode().split("\n"):
84+ for line in devices_details_raw.decode().split('\n'):
85 line = line.rstrip()
86 m = devices_cre.match(line)
87 if m:
88 device = m.group(1)
89 devices_stats[device] = {
90- "stats": {"Active": 0, "Working": 0, "Failed": 0, "Spare": 0},
91- "degraded": False,
92+ 'stats': {
93+ 'Active': 0,
94+ 'Working': 0,
95+ 'Failed': 0,
96+ 'Spare': 0,
97+ },
98+ 'rebuild_status': '',
99+ 'degraded': False,
100+ 'recovering': False,
101+ 'removed': [],
102+ 'rebuilding': [],
103 }
104 continue
105
106 m = state_cre.match(line)
107 if m:
108- if "degraded" in m.group(1) and device:
109- devices_stats[device]["degraded"] = True
110+ # format for State line can be "clean" or "clean, degraded" or "active, degraded, rebuilding", etc.
111+ states = m.group(1).split(", ")
112+ if 'degraded' in states and device:
113+ devices_stats[device]['degraded'] = True
114+ if 'recovering' in states and device:
115+ devices_stats[device]['recovering'] = True
116 continue
117
118 m = status_cre.match(line)
119 if m and device:
120- devices_stats[device]["stats"][m.group(1)] = int(m.group(2))
121+ devices_stats[device]['stats'][m.group(1)] = int(m.group(2))
122+ continue
123+
124+ m = removed_cre.match(line)
125+ if m and device:
126+ devices_stats[device]['removed'].append(m.group(1))
127+ continue
128+
129+ m = rebuild_status_cre.match(line)
130+ if m and device:
131+ devices_stats[device]['rebuild_status'] = m.group(1)
132+ continue
133+
134+ m = rebuilding_cre.match(line)
135+ if m and device:
136+ devices_stats[device]['rebuilding'].append(m.group(1))
137+ continue
138+
139 return devices_stats
140
141
142 def parse_output():
143 devices = get_devices()
144 if len(devices) == 0:
145- return generate_output("WARNING: unexpectedly checked no devices")
146+ return generate_output('WARNING: unexpectedly checked no devices')
147
148 try:
149 devices_stats = get_devices_stats(devices)
150 except subprocess.CalledProcessError as error:
151- return generate_output("WARNING: error executing mdadm: {}".format(error))
152+ return generate_output(
153+ 'WARNING: error executing mdadm: {}'.format(error)
154+ )
155
156 msg = []
157 critical = False
158+ warning = False
159 for device in devices_stats:
160+ parts = []
161 # Is device degraded?
162- if devices_stats[device]["degraded"]:
163+ if devices_stats[device]['degraded'] and devices_stats[device]['recovering']:
164+ warning = True
165+ parts = ['{} recovering'.format(device)]
166+ elif devices_stats[device]['degraded']:
167 critical = True
168- parts = ["{} degraded".format(device)]
169+ parts = ['{} degraded'.format(device)]
170 else:
171- parts = ["{} ok".format(device)]
172+ parts = ['{} ok'.format(device)]
173
174 # If Failed drives are found, list counters (how many?)
175- failed_cnt = devices_stats[device]["stats"].get("Failed", 0)
176+ failed_cnt = devices_stats[device]['stats'].get('Failed', 0)
177 if failed_cnt > 0:
178 critical = True
179 dev_stats = [
180- "{}[{}]".format(status, devices_stats[device]["stats"][status])
181- for status in sorted(devices_stats[device]["stats"])
182+ '{}[{}]'.format(status, devices_stats[device]['stats'][status])
183+ for status in sorted(devices_stats[device]['stats'])
184 ]
185 parts.extend(dev_stats)
186- msg.append(", ".join(parts))
187+
188+ if len(devices_stats[device]['removed']) != 0:
189+ critical = True
190+ members = " and ".join(devices_stats[device]['removed'])
191+ parts.append('RaidDevice(s) {} marked removed'.format(members))
192+
193+ if len(devices_stats[device]['rebuilding']) != 0:
194+ rebuilding_members = " ".join(devices_stats[device]['rebuilding'])
195+ rebuild_status = devices_stats[device]['rebuild_status']
196+ parts.append('{} rebuilding ({})'.format(rebuilding_members, rebuild_status))
197+
198+ msg.append(', '.join(parts))
199
200 if critical:
201- msg = "CRITICAL: {}".format("; ".join(msg))
202+ msg = 'CRITICAL: {}'.format('; '.join(msg))
203+ elif warning:
204+ msg = 'WARNING: {}'.format('; '.join(msg))
205 else:
206- msg = "OK: {}".format("; ".join(msg))
207+ msg = 'OK: {}'.format('; '.join(msg))
208 return generate_output(msg)
209
210
211-if __name__ == "__main__":
212+if __name__ == '__main__':
213 parse_output()
214diff --git a/src/tests/hw-health-samples/mdadm.output b/src/tests/hw-health-samples/mdadm.output
215index 9356757..c5d498d 100644
216--- a/src/tests/hw-health-samples/mdadm.output
217+++ b/src/tests/hw-health-samples/mdadm.output
218@@ -15,7 +15,7 @@ Working Devices : 2
219 Failed Devices : 0
220 Spare Devices : 0
221
222- Name : fnos-nvme05:0 (local to host fnos-nvme05)
223+ Name : some-host:0 (local to host some-host)
224 UUID : dfaf7413:7551b000:56dd7442:5b020adb
225 Events : 51
226
227@@ -39,7 +39,7 @@ Working Devices : 2
228 Failed Devices : 0
229 Spare Devices : 0
230
231- Name : fnos-nvme05:1 (local to host fnos-nvme05)
232+ Name : some-host:1 (local to host some-host)
233 UUID : 8946258c:a6246ca7:8d3dc20a:33bcabcb
234 Events : 51
235
236@@ -65,7 +65,7 @@ Working Devices : 2
237 Failed Devices : 0
238 Spare Devices : 0
239
240- Name : fnos-nvme05:3 (local to host fnos-nvme05)
241+ Name : some-host:3 (local to host some-host)
242 UUID : ae461b25:13219daa:75846e9e:2e5a52f1
243 Events : 1418
244
245@@ -91,7 +91,7 @@ Working Devices : 2
246 Failed Devices : 0
247 Spare Devices : 0
248
249- Name : fnos-nvme05:2 (local to host fnos-nvme05)
250+ Name : some-host:2 (local to host some-host)
251 UUID : a83fda21:56cf85d4:99c802ed:c07c4f76
252 Events : 5715
253
254diff --git a/src/tests/hw-health-samples/mdadm.output.critical b/src/tests/hw-health-samples/mdadm.output.critical.1
255similarity index 95%
256rename from src/tests/hw-health-samples/mdadm.output.critical
257rename to src/tests/hw-health-samples/mdadm.output.critical.1
258index 4bdab35..05ec12b 100644
259--- a/src/tests/hw-health-samples/mdadm.output.critical
260+++ b/src/tests/hw-health-samples/mdadm.output.critical.1
261@@ -15,7 +15,7 @@ Working Devices : 2
262 Failed Devices : 0
263 Spare Devices : 0
264
265- Name : fnos-nvme05:0 (local to host fnos-nvme05)
266+ Name : some-host:0 (local to host some-host)
267 UUID : dfaf7413:7551b000:56dd7442:5b020adb
268 Events : 51
269
270@@ -39,7 +39,7 @@ Working Devices : 1
271 Failed Devices : 1
272 Spare Devices : 0
273
274- Name : fnos-nvme05:1 (local to host fnos-nvme05)
275+ Name : some-host:1 (local to host some-host)
276 UUID : 8946258c:a6246ca7:8d3dc20a:33bcabcb
277 Events : 51
278
279@@ -65,7 +65,7 @@ Working Devices : 2
280 Failed Devices : 0
281 Spare Devices : 0
282
283- Name : fnos-nvme05:3 (local to host fnos-nvme05)
284+ Name : some-host:3 (local to host some-host)
285 UUID : ae461b25:13219daa:75846e9e:2e5a52f1
286 Events : 1418
287
288@@ -91,7 +91,7 @@ Working Devices : 2
289 Failed Devices : 0
290 Spare Devices : 0
291
292- Name : fnos-nvme05:2 (local to host fnos-nvme05)
293+ Name : some-host:2 (local to host some-host)
294 UUID : a83fda21:56cf85d4:99c802ed:c07c4f76
295 Events : 5715
296
297diff --git a/src/tests/hw-health-samples/mdadm.output.critical.2 b/src/tests/hw-health-samples/mdadm.output.critical.2
298new file mode 100644
299index 0000000..3b1d268
300--- /dev/null
301+++ b/src/tests/hw-health-samples/mdadm.output.critical.2
302@@ -0,0 +1,33 @@
303+/dev/md1:
304+ Version : 1.2
305+ Creation Time : Wed Jun 26 18:18:55 2019
306+ Raid Level : raid10
307+ Array Size : 7812235264 (7450.33 GiB 7999.73 GB)
308+ Used Dev Size : 3906117632 (3725.16 GiB 3999.86 GB)
309+ Raid Devices : 4
310+ Total Devices : 3
311+ Persistence : Superblock is persistent
312+
313+ Intent Bitmap : Internal
314+
315+ Update Time : Mon Oct 28 17:27:54 2019
316+ State : active, degraded
317+ Active Devices : 3
318+ Working Devices : 3
319+ Failed Devices : 0
320+ Spare Devices : 0
321+
322+ Layout : near=2
323+ Chunk Size : 512K
324+
325+Consistency Policy : bitmap
326+
327+ Name : some-host:1 (local to host some-host)
328+ UUID : 3887c84d:ebd28589:5a4b5d9e:3c83a25d
329+ Events : 491275
330+
331+ Number Major Minor RaidDevice State
332+ 0 8 98 0 active sync set-A /dev/sdg2
333+ 1 8 114 1 active sync set-B /dev/sdh2
334+ - 0 0 2 removed
335+ 3 8 162 3 active sync set-B /dev/sdk2
336diff --git a/src/tests/hw-health-samples/mdadm.output.warning b/src/tests/hw-health-samples/mdadm.output.warning
337new file mode 100644
338index 0000000..21f9090
339--- /dev/null
340+++ b/src/tests/hw-health-samples/mdadm.output.warning
341@@ -0,0 +1,33 @@
342+/dev/md1:
343+ Version : 1.2
344+ Creation Time : Thu Oct 18 17:41:01 2018
345+ Raid Level : raid10
346+ Array Size : 7812235264 (7450.33 GiB 7999.73 GB)
347+ Used Dev Size : 3906117632 (3725.16 GiB 3999.86 GB)
348+ Raid Devices : 4
349+ Total Devices : 4
350+ Persistence : Superblock is persistent
351+
352+ Intent Bitmap : Internal
353+
354+ Update Time : Mon Oct 28 18:46:34 2019
355+ State : active, degraded, recovering
356+ Active Devices : 3
357+Working Devices : 4
358+ Failed Devices : 0
359+ Spare Devices : 1
360+
361+ Layout : near=2
362+ Chunk Size : 512K
363+
364+ Rebuild Status : 13% complete
365+
366+ Name : some-host:1 (local to host some-host)
367+ UUID : fed3a645:1f742fd3:1685dda5:71794407
368+ Events : 750593
369+
370+ Number Major Minor RaidDevice State
371+ 0 8 98 0 active sync set-A /dev/sdg2
372+ 1 8 114 1 active sync set-B /dev/sdh2
373+ 2 8 146 2 active sync set-A /dev/sdj2
374+ 4 8 162 3 spare rebuilding /dev/sdk2
375diff --git a/src/tests/unit/lib/samples.py b/src/tests/unit/lib/samples.py
376new file mode 100644
377index 0000000..a0790ee
378--- /dev/null
379+++ b/src/tests/unit/lib/samples.py
380@@ -0,0 +1,17 @@
381+import os
382+import glob
383+
384+SAMPLES_DIR = os.path.join(
385+ os.path.dirname(__file__),
386+ '..',
387+ '..',
388+ 'hw-health-samples'
389+)
390+
391+
392+def get_sample(name):
393+ sample = os.path.join(SAMPLES_DIR, name)
394+ if glob.has_magic(sample):
395+ return glob.glob(sample)
396+ else:
397+ return sample
398diff --git a/src/tests/unit/test_check_mdadm.py b/src/tests/unit/test_check_mdadm.py
399index 231ca4f..0314032 100644
400--- a/src/tests/unit/test_check_mdadm.py
401+++ b/src/tests/unit/test_check_mdadm.py
402@@ -6,24 +6,23 @@ import unittest.mock as mock
403
404 import nagios_plugin3
405
406-sys.path.append('files/mdadm')
407+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
408+from samples import get_sample # noqa: E402
409+
410+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/mdadm'))
411 import check_mdadm # noqa: E402
412
413
414 class TestCheckMdadm(unittest.TestCase):
415 def test_parse_output_crit(self):
416- check_mdadm.INPUT_FILE = os.path.join(
417- os.getcwd(), 'tests', 'hw-health-samples',
418- 'mdadm.output.nrpe.critical')
419+ check_mdadm.INPUT_FILE = get_sample('mdadm.output.nrpe.critical')
420 expected = 'CRITICAL: critical msg'
421 with self.assertRaises(nagios_plugin3.CriticalError) as context:
422 check_mdadm.parse_output()
423 self.assertTrue(expected in str(context.exception))
424
425 def test_parse_output_warn(self):
426- check_mdadm.INPUT_FILE = os.path.join(
427- os.getcwd(), 'tests', 'hw-health-samples',
428- 'mdadm.output.nrpe.warning')
429+ check_mdadm.INPUT_FILE = get_sample('mdadm.output.nrpe.warning')
430 expected = 'WARNING: warning msg'
431 with self.assertRaises(nagios_plugin3.WarnError) as context:
432 check_mdadm.parse_output()
433@@ -31,8 +30,7 @@ class TestCheckMdadm(unittest.TestCase):
434
435 @mock.patch('sys.stdout', new_callable=io.StringIO)
436 def test_parse_output_ok(self, mock_print):
437- check_mdadm.INPUT_FILE = os.path.join(
438- os.getcwd(), 'tests', 'hw-health-samples', 'mdadm.output.nrpe.ok')
439+ check_mdadm.INPUT_FILE = get_sample('mdadm.output.nrpe.ok')
440 check_mdadm.parse_output()
441 self.assertEqual(
442 mock_print.getvalue(),
443@@ -41,9 +39,7 @@ class TestCheckMdadm(unittest.TestCase):
444
445 @mock.patch('sys.stdout', new_callable=io.StringIO)
446 def test_parse_output_unknown_filenotfound(self, mock_print):
447- check_mdadm.INPUT_FILE = os.path.join(
448- os.getcwd(), 'tests', 'hw-health-samples',
449- 'thisfiledoesnotexist')
450+ check_mdadm.INPUT_FILE = get_sample('thisfiledoesnotexist')
451 expected = 'UNKNOWN: file not found ({})'.format(
452 check_mdadm.INPUT_FILE)
453 with self.assertRaises(nagios_plugin3.UnknownError) as context:
454@@ -52,9 +48,7 @@ class TestCheckMdadm(unittest.TestCase):
455
456 @mock.patch('sys.stdout', new_callable=io.StringIO)
457 def test_parse_output_unknown1(self, mock_print):
458- check_mdadm.INPUT_FILE = os.path.join(
459- os.getcwd(), 'tests', 'hw-health-samples',
460- 'mdadm.output.nrpe.unknown.1')
461+ check_mdadm.INPUT_FILE = get_sample('mdadm.output.nrpe.unknown.1')
462 check_mdadm.parse_output()
463 self.assertEqual(
464 mock_print.getvalue(),
465@@ -63,9 +57,7 @@ class TestCheckMdadm(unittest.TestCase):
466
467 @mock.patch('sys.stdout', new_callable=io.StringIO)
468 def test_parse_output_unknown2(self, mock_print):
469- check_mdadm.INPUT_FILE = os.path.join(
470- os.getcwd(), 'tests', 'hw-health-samples',
471- 'mdadm.output.nrpe.unknown.2')
472+ check_mdadm.INPUT_FILE = get_sample('mdadm.output.nrpe.unknown.2')
473 check_mdadm.parse_output()
474 self.assertEqual(
475 mock_print.getvalue(),
476diff --git a/src/tests/unit/test_check_megacli.py b/src/tests/unit/test_check_megacli.py
477index 988075a..4207953 100644
478--- a/src/tests/unit/test_check_megacli.py
479+++ b/src/tests/unit/test_check_megacli.py
480@@ -6,15 +6,17 @@ import unittest.mock as mock
481
482 import nagios_plugin3
483
484-sys.path.append('files/megacli')
485+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
486+from samples import get_sample # noqa: E402
487+
488+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/megacli'))
489 import check_megacli # noqa: E402
490
491
492 class TestCheckMegaCLI(unittest.TestCase):
493 @mock.patch('sys.stdout', new_callable=io.StringIO)
494 def test_parse_output(self, mock_print):
495- check_megacli.INPUT_FILE = os.path.join(
496- os.getcwd(), 'tests', 'hw-health-samples', 'megacli.output.1')
497+ check_megacli.INPUT_FILE = get_sample('megacli.output.1')
498 check_megacli.parse_output()
499 actual = mock_print.getvalue()
500 expected = 'OK: Optimal, ldrives[1], pdrives[4]\n'
501@@ -22,8 +24,7 @@ class TestCheckMegaCLI(unittest.TestCase):
502
503 @mock.patch('sys.stdout', new_callable=io.StringIO)
504 def test_parse_output_critical_singledrive(self, mock_print):
505- check_megacli.INPUT_FILE = os.path.join(
506- os.getcwd(), 'tests', 'hw-health-samples', 'megacli.output.nrpe.critical.1')
507+ check_megacli.INPUT_FILE = get_sample('megacli.output.nrpe.critical.1')
508 expected = 'CRITICAL: adapter(0):ld(0):state(Degraded)'
509 with self.assertRaises(nagios_plugin3.CriticalError) as context:
510 check_megacli.parse_output()
511@@ -31,8 +32,7 @@ class TestCheckMegaCLI(unittest.TestCase):
512
513 @mock.patch('sys.stdout', new_callable=io.StringIO)
514 def test_parse_output_critical_multiple(self, mock_print):
515- check_megacli.INPUT_FILE = os.path.join(
516- os.getcwd(), 'tests', 'hw-health-samples', 'megacli.output.nrpe.critical.2')
517+ check_megacli.INPUT_FILE = get_sample('megacli.output.nrpe.critical.2')
518 expected = ('CRITICAL: adapter(0):ld(0):state(Degraded);'
519 ' adapter(0):ld(4):state(Degraded)')
520 with self.assertRaises(nagios_plugin3.CriticalError) as context:
521diff --git a/src/tests/unit/test_check_nvme.py b/src/tests/unit/test_check_nvme.py
522index 53466e4..097fd76 100644
523--- a/src/tests/unit/test_check_nvme.py
524+++ b/src/tests/unit/test_check_nvme.py
525@@ -4,7 +4,10 @@ import sys
526 import unittest
527 import unittest.mock as mock
528
529-sys.path.append('files/nvme')
530+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
531+from samples import get_sample # noqa: E402
532+
533+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/nvme'))
534 import check_nvme # noqa: E402
535
536
537@@ -14,8 +17,7 @@ class TestCheckNvme(unittest.TestCase):
538 @mock.patch('sys.stdout', new_callable=io.StringIO)
539 def test_parse_output(self, mock_print, mock_subprocess, mock_glob):
540 mock_glob.return_value = ['/dev/nvme0']
541- input_file = os.path.join(os.getcwd(), 'tests', 'hw-health-samples',
542- 'nvme.output.1')
543+ input_file = get_sample('nvme.output.1')
544 with open(input_file, 'r') as fd:
545 mock_subprocess.return_value = fd.read().encode()
546 check_nvme.parse_output()
547diff --git a/src/tests/unit/test_check_sas2ircu.py b/src/tests/unit/test_check_sas2ircu.py
548index 1d0dc80..5464889 100644
549--- a/src/tests/unit/test_check_sas2ircu.py
550+++ b/src/tests/unit/test_check_sas2ircu.py
551@@ -4,16 +4,17 @@ import sys
552 import unittest
553 import unittest.mock as mock
554
555-sys.path.append('files/sas2ircu')
556+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
557+from samples import get_sample # noqa: E402
558+
559+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/sas2ircu'))
560 import check_sas2ircu # noqa: E402
561
562
563 class TestCheckMegaCLI(unittest.TestCase):
564 @mock.patch('sys.stdout', new_callable=io.StringIO)
565 def test_parse_output(self, mock_print):
566- check_sas2ircu.INPUT_FILE = os.path.join(os.getcwd(), 'tests',
567- 'hw-health-samples',
568- 'sas2ircu.huawei.output.1')
569+ check_sas2ircu.INPUT_FILE = get_sample('sas2ircu.huawei.output.1')
570 check_sas2ircu.parse_output()
571 actual = mock_print.getvalue()
572 expected = 'OK: Ready[1:0,1:1,1:2,1:3,1:4,1:5,1:6,1:7]\n'
573diff --git a/src/tests/unit/test_check_sas3ircu.py b/src/tests/unit/test_check_sas3ircu.py
574index 5747de9..1379369 100644
575--- a/src/tests/unit/test_check_sas3ircu.py
576+++ b/src/tests/unit/test_check_sas3ircu.py
577@@ -4,15 +4,17 @@ import sys
578 import unittest
579 import unittest.mock as mock
580
581-sys.path.append('files/sas3ircu')
582+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
583+from samples import get_sample # noqa: E402
584+
585+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/sas3ircu'))
586 import check_sas3ircu # noqa: E402
587
588
589 class TestCheckMegaCLI(unittest.TestCase):
590 @mock.patch('sys.stdout', new_callable=io.StringIO)
591 def test_parse_output_ok(self, mock_print):
592- _filepath = os.path.join(os.getcwd(), 'tests', 'hw-health-samples',
593- 'sas3ircu.supermicro.ok.output.1')
594+ _filepath = get_sample('sas3ircu.supermicro.ok.output.1')
595 data = check_sas3ircu.parse_output(_filepath)
596 check_sas3ircu.eval_status(data)
597 actual = mock_print.getvalue()
598diff --git a/src/tests/unit/test_cron_mdadm.py b/src/tests/unit/test_cron_mdadm.py
599index d124931..f52ea9c 100644
600--- a/src/tests/unit/test_cron_mdadm.py
601+++ b/src/tests/unit/test_cron_mdadm.py
602@@ -5,7 +5,10 @@ import sys # noqa: F401
603 import unittest
604 import unittest.mock as mock
605
606-sys.path.append('files/mdadm')
607+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
608+from samples import get_sample # noqa: E402
609+
610+sys.path.append(os.path.join(os.path.dirname(__file__), '../../files/mdadm'))
611 import cron_mdadm # noqa: E402
612
613
614@@ -44,13 +47,11 @@ class TestCronMdadm(unittest.TestCase):
615 def test_parse_output_ok(self, mock_print, mdadm_details, devices, genout):
616 class Test_Popen(object):
617 def __init__(cls):
618- test_output = os.path.join(
619- os.getcwd(), 'tests', 'hw-health-samples', 'mdadm.output')
620+ test_output = get_sample('mdadm.output')
621 cls.stdout = io.FileIO(test_output)
622 cls.wait = lambda: 0
623
624- test_output = os.path.join(
625- os.getcwd(), 'tests', 'hw-health-samples', 'mdadm.output')
626+ test_output = get_sample('mdadm.output')
627 with open(test_output, 'r') as fd:
628 mdadm_details.return_value = ''.join(fd.readlines()).encode()
629 devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
630@@ -87,9 +88,7 @@ class TestCronMdadm(unittest.TestCase):
631 @mock.patch('cron_mdadm.get_devices')
632 @mock.patch('subprocess.check_output')
633 def test_parse_output_degraded(self, mdadm_details, devices, genout):
634- test_output = os.path.join(
635- os.getcwd(), 'tests', 'hw-health-samples',
636- 'mdadm.output.critical')
637+ test_output = get_sample('mdadm.output.critical.1')
638 with open(test_output, 'r') as fd:
639 mdadm_details.return_value = ''.join(fd.readlines()).encode()
640
641@@ -100,3 +99,31 @@ class TestCronMdadm(unittest.TestCase):
642 ' /dev/md2 ok')
643 self.assertTrue(cron_mdadm.parse_output())
644 genout.assert_called_once_with(expected)
645+
646+ @mock.patch('cron_mdadm.generate_output')
647+ @mock.patch('cron_mdadm.get_devices')
648+ @mock.patch('subprocess.check_output')
649+ def test_parse_output_removed(self, mdadm_details, devices, genout):
650+ test_output = get_sample('mdadm.output.critical.2')
651+ with open(test_output, 'r') as fd:
652+ mdadm_details.return_value = ''.join(fd.readlines()).encode()
653+
654+ devices.return_value = set(['/dev/md1'])
655+ genout.return_value = True
656+ expected = ('CRITICAL: /dev/md1 degraded, RaidDevice(s) 2 marked removed')
657+ self.assertTrue(cron_mdadm.parse_output())
658+ genout.assert_called_once_with(expected)
659+
660+ @mock.patch('cron_mdadm.generate_output')
661+ @mock.patch('cron_mdadm.get_devices')
662+ @mock.patch('subprocess.check_output')
663+ def test_parse_output_rebuilding(self, mdadm_details, devices, genout):
664+ test_output = get_sample('mdadm.output.warning')
665+ with open(test_output, 'r') as fd:
666+ mdadm_details.return_value = ''.join(fd.readlines()).encode()
667+
668+ devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
669+ genout.return_value = True
670+ expected = ('WARNING: /dev/md1 recovering, /dev/sdk2 rebuilding (13% complete)')
671+ self.assertTrue(cron_mdadm.parse_output())
672+ genout.assert_called_once_with(expected)
673diff --git a/src/tests/unit/test_hwdiscovery.py b/src/tests/unit/test_hwdiscovery.py
674index 6e5bf3d..43ea360 100644
675--- a/src/tests/unit/test_hwdiscovery.py
676+++ b/src/tests/unit/test_hwdiscovery.py
677@@ -1,11 +1,13 @@
678-import glob
679 import os # noqa: F401
680 import subprocess # noqa: F401
681 import sys
682 import unittest
683 import unittest.mock as mock
684
685-sys.path.append('lib/hwhealth')
686+sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
687+from samples import get_sample # noqa: E402
688+
689+sys.path.append(os.path.join(os.path.dirname(__file__), '../../lib/hwhealth'))
690 import hwdiscovery # noqa: E402
691 from discovery.lshw import Hardware # noqa: E402
692
693@@ -83,8 +85,7 @@ class TestGetTools(unittest.TestCase):
694 'lshw.supermicro.sas.02.json': set(['Nvme', 'Sas3Ircu']),
695 }
696
697- for filename in glob.glob(os.path.join(
698- os.getcwd(), 'tests/hw-health-samples/lshw.*.json')):
699+ for filename in get_sample('lshw.*.json'):
700 mock_hwinfo.return_value = Hardware(filename)
701 actual = hwdiscovery._get_tools()
702 if os.path.basename(filename) in TOOLS:

Subscribers

People subscribed via source and target branches

to all changes: