Merge ~aieri/charm-hw-health:bug/1838457-HPArgumentParser into ~aieri/charm-hw-health:bug/1838457

Proposed by Drew Freiberger
Status: Work in progress
Proposed branch: ~aieri/charm-hw-health:bug/1838457-HPArgumentParser
Merge into: ~aieri/charm-hw-health:bug/1838457
Diff against target: 395 lines (+224/-83) (has conflicts)
4 files modified
src/files/common/hw_health_lib.py (+6/-72)
src/files/hplog/cron_hplog.py (+14/-9)
src/files/hpssacli/cron_hpssacli.py (+202/-0)
src/files/ilorest/cron_ilorest.py (+2/-2)
Conflict in src/files/hpssacli/cron_hpssacli.py
Conflict in src/files/ssacli/cron_ssacli.py
Reviewer Review Type Date Requested Status
Andrea Ieri Pending
Review via email: mp+381195@code.launchpad.net
To post a comment you must log in.

Unmerged commits

5956c9b... by Andrea Ieri

[cron_ilorest] switch parse_args to use the default behavior of reading sys.argv[1:] when args=None

ef5c96d... by Andrea Ieri

Use the ArgumentDefaultsHelpFormatter formatter class by default

a8bc9e5... by Andrea Ieri

convert hp cron tools to use the HPArgumentParser subclass

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/files/common/hw_health_lib.py b/src/files/common/hw_health_lib.py
2index 3c48d8d..c3b45a3 100644
3--- a/src/files/common/hw_health_lib.py
4+++ b/src/files/common/hw_health_lib.py
5@@ -9,7 +9,6 @@ import argparse
6 import os
7 import re
8 import subprocess
9-import sys
10 import tempfile
11 import yaml
12
13@@ -165,76 +164,6 @@ def get_hp_controller_slots():
14 return slots
15
16
17-def parse_args_wrapper(desc=None, write_filepath=None, exclude_filepath=None):
18- """
19- decorator that wraps parse_args for hw-health tools in a common set of
20- arguments and allows each module to add additional command-line arguments
21- """
22- def decorator(func, *args, **kwargs):
23- def wrapped_func(*args, **kwargs):
24- if kwargs.get('argv') is None:
25- argv = sys.argv
26- kwargs['argv'] = argv
27- else:
28- argv = kwargs['argv']
29-
30- parser = argparse.ArgumentParser(
31- description=desc,
32- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
33- )
34-
35- parser.add_argument(
36- '--debug', dest='debug', action='store_true',
37- help='Extra debugging',
38- )
39-
40- parser.add_argument(
41- '--exclude', dest='exclude', type=str, action='append',
42- help='Errors to ignore (multiple)',
43- )
44-
45- exclude_file = '/etc/nagios/{}.exclude.yaml'.format(desc)
46- if exclude_filepath:
47- exclude_file = exclude_filepath
48-
49- parser.add_argument(
50- '--exclude-file', dest='exclude_file', type=str,
51- default=exclude_file,
52- help='YAML file with errors to ignore',
53- )
54-
55- default_file = '/var/lib/nagios/{}.out'.format(desc)
56- if write_filepath:
57- default_file = write_filepath
58-
59- parser.add_argument(
60- '-w', '--write', dest='write', type=str,
61- default=default_file,
62- help='Write nagios status lines to file',
63- )
64-
65- # allow the wrapped function to extend parser with add_argument calls
66- parser = func(parser, *args, **kwargs)
67-
68- parsed_args = parser.parse_args(args=argv[1:])
69-
70- if parsed_args.exclude is None:
71- parsed_args.exclude = []
72- if parsed_args.exclude_file and os.path.exists(parsed_args.exclude_file):
73- with open(parsed_args.exclude_file) as f:
74- y = yaml.safe_load(f)
75- for i in y:
76- if (
77- 'expires' in i and # noqa:W504
78- i['expires'] < datetime.datetime.now()
79- ):
80- continue
81- parsed_args.exclude.append(i['error'])
82- return parsed_args
83- return wrapped_func
84- return decorator
85-
86-
87 class HPArgumentParser(argparse.ArgumentParser):
88 def __init__(
89 self,
90@@ -243,7 +172,12 @@ class HPArgumentParser(argparse.ArgumentParser):
91 *args,
92 **kwargs
93 ):
94- super().__init__(*args, **kwargs)
95+ super().__init__(
96+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
97+ *args,
98+ **kwargs
99+ )
100+
101 # self.prog is populated by ArgumentParser
102 self._def_write_file = \
103 def_write_file or '/var/lib/nagios/{}.out'.format(self.prog)
104diff --git a/src/files/hplog/cron_hplog.py b/src/files/hplog/cron_hplog.py
105index 5e0cf73..d2b21cf 100755
106--- a/src/files/hplog/cron_hplog.py
107+++ b/src/files/hplog/cron_hplog.py
108@@ -20,7 +20,7 @@ try:
109 read_ignore_file,
110 write_ignore_file,
111 ignore,
112- parse_args_wrapper,
113+ HPArgumentParser,
114 )
115 except ImportError:
116 # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests
117@@ -33,7 +33,7 @@ except ImportError:
118 read_ignore_file,
119 write_ignore_file,
120 ignore,
121- parse_args_wrapper,
122+ HPArgumentParser,
123 )
124
125 FLAG_PROCESSOR = {
126@@ -42,27 +42,32 @@ FLAG_PROCESSOR = {
127 'p': 'parse_power',
128 'v': 'save_log',
129 }
130+OUTPUT_FILE = '/var/lib/nagios/hplog.out'
131+EXCLUDE_FILE = '/etc/nagios/hplog.exclude.yaml'
132+DEBUG_FILE = '/var/lib/nagios/hplog.debug'
133
134
135-@parse_args_wrapper(desc='cron_hplog',
136- write_filepath='/var/lib/nagios/hplog.out',
137- exclude_filepath='/etc/nagios/hplog.exclude.yaml')
138-def parse_args(parser, *args, **kwargs):
139+def parse_args(argv=None):
140+ parser = HPArgumentParser(
141+ prog='cron_hplog',
142+ def_write_file=OUTPUT_FILE,
143+ def_exclude_file=EXCLUDE_FILE
144+ )
145 parser.add_argument(
146 '-f', '--hplog_flags', dest='hplog_flags', type=str,
147- default='t,p,f,v',
148+ default=','.join(FLAG_PROCESSOR.keys()),
149 help='Flags to call hplog with',
150 )
151 parser.add_argument(
152 '-l', '--log_path', dest='log_path', type=str,
153- default='/var/lib/nagios/hplog.debug',
154+ default=DEBUG_FILE,
155 help='Where to write hplog -v output for troubleshooting',
156 )
157 parser.add_argument(
158 '-s', '--single_psu', dest='single_psu', action='store_true',
159 help='Do not alert on lack of PSU redundancy', default=False,
160 )
161- return parser
162+ return parser.parse_args(args=argv)
163
164
165 def call_hplog(flag):
166diff --git a/src/files/hpssacli/cron_hpssacli.py b/src/files/hpssacli/cron_hpssacli.py
167new file mode 100755
168index 0000000..74d7619
169--- /dev/null
170+++ b/src/files/hpssacli/cron_hpssacli.py
171@@ -0,0 +1,202 @@
172+<<<<<<< src/files/hpssacli/cron_hpssacli.py
173+=======
174+#!/usr/bin/env python3
175+# -----------------------------------------------
176+# This file is juju managed
177+# -----------------------------------------------
178+
179+# Copyright (C) 2008-2013 James Troup <james.troup@canonical.com>
180+# Copyright (c) 2020 Rewrite: Drew Freiberger <drew.freiberger@canonical.com>
181+
182+# hpssacli lives in either /usr/sbin or /sbin depending on version of the
183+# package and cron jobs don't get a sane $PATH
184+""" OLD SCRIPT THAT COLLECTED INFO TO PORT
185+export PATH=$PATH:/usr/sbin:/sbin
186+
187+# Not all HP's have CCISS and iLO (e.g. DL140 G1 (old), RX2600 (!x86), etc.)
188+cciss=`find /sys/block | grep -c /cciss`
189+array=`egrep -c "storage +Smart Array" /var/lib/nagios/cat-lshw.txt`
190+cd /var/lib/nagios/
191+
192+# Check storage status (assuming we have an array to check)
193+if [ -s /etc/cat-cciss-slots.txt ] && [ $cciss -gt 0 -o $array -gt 0 ]; then
194+ for slot in $(cat /etc/cat-cciss-slots.txt); do
195+ hpssacli ctrl slot=${slot} ld all show status >> cat-hp-array.txt.new ;
196+ hpssacli ctrl slot=${slot} pd all show status >> cat-hp-array.txt.new ;
197+ hpssacli ctrl slot=${slot} show status >> cat-hp-controller.txt.new ;
198+ done
199+else
200+ # Create a dummy OK line to keep nagios checks happy
201+ echo 'Smart Array IS NOT INSTALLED' > cat-hp-controller.txt.new
202+ echo ' Status: OK' >> cat-hp-controller.txt.new
203+ echo 'physicaldrive (NO ARRAY CONTROLLER INSTALLED): OK' > cat-hp-array.txt.new # noqa E501
204+fi
205+"""
206+import os
207+import re
208+import subprocess
209+import sys
210+
211+try:
212+ # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime
213+ from hw_health_lib import (
214+ read_ignore_file,
215+ write_ignore_file,
216+ ignore,
217+ get_hp_controller_slots,
218+ )
219+except ImportError:
220+ # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests
221+ common_libs_dir = os.path.abspath(
222+ os.path.join(os.path.dirname(__file__), '..', 'common')
223+ )
224+ if common_libs_dir not in sys.path:
225+ sys.path.append(common_libs_dir)
226+ from hw_health_lib import (
227+ read_ignore_file,
228+ write_ignore_file,
229+ ignore,
230+ get_hp_controller_slots,
231+ HPArgumentParser,
232+ )
233+
234+OUTPUT_FILE = '/var/lib/nagios/hpssacli.out'
235+EXCLUDE_FILE = '/etc/nagios/hpssacli.exclude.yaml'
236+
237+
238+def parse_args(argv=None):
239+ parser = HPArgumentParser(
240+ prog='cron_hpssacli',
241+ def_write_file=OUTPUT_FILE,
242+ def_exclude_file=EXCLUDE_FILE
243+ )
244+ return parser.parse_args(args=argv)
245+
246+
247+def check_array(slot):
248+ """
249+ RAID arrays [hpacucli]
250+
251+ logicaldrive 1 (68.3 GB, RAID RAID 1+0): OK
252+ logicaldrive 1 (2.8 TB, RAID RAID 6 (ADG)): OK
253+ physicaldrive 1:1 (box 1:bay 1, 72 GB): OK
254+ physicaldrive 1:2 (box 1:bay 2, 72 GB): OK
255+ """
256+ if os.path.isfile('/etc/nagios/skip-cat-hp-array.txt'):
257+ return
258+
259+ cmd = (
260+ 'hpssacli ctrl slot={} ld all show status; '
261+ 'hpssacli ctrl slot={} pd all show status'.format(slot, slot)
262+ )
263+ try:
264+ result = subprocess.check_output(cmd, shell=True).decode('UTF-8')
265+ return _parse_array_output(result)
266+ except subprocess.CalledProcessError as e:
267+ return (
268+ 'UNKNOWN Call to hpssacli to show ld/pd info failed. '
269+ 'Array Slot {} - Return Code {} - {}'
270+ ''.format(slot, e.returncode, e.output)
271+ )
272+
273+
274+def _parse_array_output(output):
275+ innocuous_errors = re.compile(r'^Error: The specified (device|controller) '
276+ 'does not have any (logical|physical)')
277+ drive_status_line = re.compile(r'^\s*(logicaldrive|physicaldrive)')
278+ ignore_file = '/etc/nagios/ignores/ignores-cat-hp-array.txt'
279+ ignores = read_ignore_file(ignore_file)
280+
281+ for line in output.splitlines():
282+ line = line.strip()
283+ if (not line or innocuous_errors.search(line)
284+ or not drive_status_line.search(line)):
285+ continue
286+ (drivetype, number) = line.split()[:2]
287+ status = line.split('):')[1].lstrip().upper()
288+ if status != 'OK':
289+ err = '{} {} is "{}"'.format(drivetype, number, status)
290+ if not ignore(err, ignores):
291+ return err
292+ write_ignore_file(ignores, ignore_file)
293+
294+
295+def check_controller(slot):
296+ """
297+ RAID controllers [hpacucli]
298+
299+ Smart Array 6400 in Slot 1
300+ Controller Status: OK
301+ Cache Status: OK
302+ Battery Status: Failed (Replace Batteries)
303+ """
304+ if os.path.isfile('/etc/nagios/skip-cat-hp-controller.txt'):
305+ return
306+
307+ cmd = ('hpssacli ctrl slot={} show status'.format(slot))
308+ try:
309+ result = subprocess.check_output(cmd, shell=True).decode('UTF-8')
310+ return _parse_controller_output(result)
311+ except subprocess.CalledProcessError as e:
312+ return (
313+ 'UNKNOWN Call to hpssacli to show ld/pd info failed. '
314+ 'Array Slot {} - Return Code {} - {}'
315+ ''.format(slot, e.returncode, e.output)
316+ )
317+
318+
319+def _parse_controller_output(output):
320+ controller = "Unknown"
321+ ignore_file = '/etc/nagios/ignores/ignores-cat-hp-controller.txt'
322+ ignores = read_ignore_file(ignore_file)
323+ for line in output.splitlines():
324+ line = line.strip()
325+ if not line:
326+ continue
327+ if line.startswith("Smart Array") or line.startswith("Smart HBA"):
328+ controller = line
329+ else:
330+ if ":" not in line:
331+ err = "UNKNOWN Unrecognised line for controller '%s'" % (line)
332+ return err
333+ (part, status) = line.split(":")
334+ status = status.strip().upper()
335+ if status != "OK":
336+ err = "%s on %s is '%s'" % (part, controller, status)
337+ if not ignore(err, ignores):
338+ return err
339+ write_ignore_file(ignores, ignore_file)
340+
341+
342+def main():
343+ global ARGS
344+ ARGS = parse_args()
345+
346+ slots = get_hp_controller_slots()
347+ if not slots:
348+ msg = 'OK: no controller/array found to check'
349+ exit = 0
350+
351+ errors = []
352+ for slot in slots:
353+ errors += check_controller(slot)
354+ errors += check_array(slot)
355+
356+ if len(errors) > 0:
357+ msg = 'CRIT {} error(s): {}'.format(len(errors), ' - '.join(errors))
358+ exit = 2
359+ else:
360+ msg = 'OK No errors found'
361+ exit = 0
362+
363+ if ARGS.write:
364+ with open(ARGS.write, 'w') as f:
365+ f.write(msg)
366+ else:
367+ print(msg)
368+ sys.exit(exit)
369+
370+
371+if __name__ == '__main__':
372+ main()
373+>>>>>>> src/files/hpssacli/cron_hpssacli.py
374diff --git a/src/files/ilorest/cron_ilorest.py b/src/files/ilorest/cron_ilorest.py
375index ce59256..739438d 100755
376--- a/src/files/ilorest/cron_ilorest.py
377+++ b/src/files/ilorest/cron_ilorest.py
378@@ -42,7 +42,7 @@ class CronILOrest:
379 def __init__(self, argv=[]):
380 self.args = self.parse_args(argv)
381
382- def parse_args(self, argv=[]):
383+ def parse_args(self, argv=None):
384 parser = HPArgumentParser(
385 prog='cron_ilorest',
386 description=('Convert the output of ilorest into an appropriate '
387@@ -117,7 +117,7 @@ class CronILOrest:
388 return errors
389
390
391-def main(argv):
392+def main(argv=None):
393 cronilorest = CronILOrest(argv)
394
395 errors = [cronilorest.check_selector(selector)

Subscribers

People subscribed via source and target branches

to all changes: