Merge ~afreiberger/charm-hw-health:bug/1838457-refactor into charm-hw-health:master
- Git
- lp:~afreiberger/charm-hw-health
- bug/1838457-refactor
- Merge into master
Status: | Superseded |
---|---|
Proposed branch: | ~afreiberger/charm-hw-health:bug/1838457-refactor |
Merge into: | charm-hw-health:master |
Diff against target: |
24810 lines (+23252/-347) 56 files modified
.gitignore (+0/-2) Makefile (+18/-14) src/README.md (+1/-2) src/files/common/check_hw_health_cron_output.py (+78/-0) src/files/common/hw_health_lib.py (+263/-0) src/files/hplog/cron_hplog.py (+304/-0) src/files/ilorest/check_ilorest.py (+74/-0) src/files/ilorest/cron_ilorest.py (+146/-0) src/files/mdadm/check_mdadm.py (+31/-6) src/files/mdadm/cron_mdadm.py (+36/-10) src/files/megacli/check_megacli.py (+38/-8) src/files/megacli/cron_megacli.sh (+13/-2) src/files/nvme/check_nvme.py (+18/-1) src/files/sas2ircu/check_sas2ircu.py (+28/-4) src/files/sas2ircu/cron_sas2ircu.sh (+13/-2) src/files/sas3ircu/check_sas3ircu.py (+32/-7) src/files/sas3ircu/cron_sas3ircu.sh (+13/-2) src/files/ssacli/cron_ssacli.py (+178/-0) src/lib/hwhealth/discovery/lshw.py (+48/-29) src/lib/hwhealth/discovery/supported_vendors.py (+17/-5) src/lib/hwhealth/hwdiscovery.py (+55/-22) src/lib/hwhealth/tools.py (+317/-70) src/metadata.yaml (+1/-0) src/reactive/hw_health.py (+31/-3) src/tests/expected/lshw.py (+1917/-0) src/tests/functional/requirements.txt (+1/-0) src/tests/functional/test_hwhealth.py (+145/-60) src/tests/hw-health-samples/cron_outputs/hplog.out.ok (+1/-0) src/tests/hw-health-samples/cron_outputs/ipmi_sensors.out.critical (+1/-0) src/tests/hw-health-samples/cron_outputs/ssacli.out.ok (+1/-0) src/tests/hw-health-samples/hplog.f.ewah.out (+6/-0) src/tests/hw-health-samples/hplog.p.ewah.out (+6/-0) src/tests/hw-health-samples/hplog.t.ewah.out (+20/-0) src/tests/hw-health-samples/hplog.v.ewah.out (+60/-0) src/tests/hw-health-samples/ilorest.list.Chassis (+138/-0) src/tests/hw-health-samples/ilorest.list.HpeSmartStorage (+452/-0) src/tests/hw-health-samples/ilorest.list.Memory (+1376/-0) src/tests/hw-health-samples/ilorest.list.Power (+7929/-0) src/tests/hw-health-samples/ilorest.list.Processor (+286/-0) src/tests/hw-health-samples/ilorest.list.Thermal (+1513/-0) src/tests/hw-health-samples/lshw.hpe.nonvme.json (+7359/-0) src/tests/hw-health-samples/ssacli.array.status.faileddrive.out (+7/-0) src/tests/hw-health-samples/ssacli.array.status.ok.out (+6/-0) src/tests/hw-health-samples/ssacli.ctrl.status.failedbattery.out (+7/-0) src/tests/unit/requirements.txt (+4/-0) src/tests/unit/test_actions.py (+3/-1) src/tests/unit/test_check_mdadm.py (+14/-7) src/tests/unit/test_check_megacli.py (+10/-3) src/tests/unit/test_check_sas2ircu.py (+1/-1) src/tests/unit/test_cron_hplog.py (+37/-0) src/tests/unit/test_cron_ilorest.py (+24/-0) src/tests/unit/test_cron_mdadm.py (+10/-5) src/tests/unit/test_cron_ssacli.py (+33/-0) src/tests/unit/test_hwdiscovery.py (+64/-75) src/tests/unit/test_lshw.py (+61/-0) src/tox.ini (+7/-6) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Drew Freiberger (community) | Needs Resubmitting | ||
Xav Paice | Pending | ||
Review via email:
|
This proposal supersedes a proposal from 2020-07-21.
This proposal has been superseded by a proposal from 2020-08-07.
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : Posted in a previous version of this proposal | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
This is a substantial size change, and could do with some improved test coverage.
Tests all pass - however there's a bunch of rebase errors in the change that should have been picked up by lint, let alone unit tests.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
also needs a rebase, there's conflicts that will cause issues
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : | # |
I've run through and rebased on top of master. turns out a lot of the test coverage was already in master for these bits.
----------- coverage: platform linux, python 3.8.2-final-0 -----------
Name Stmts Miss Cover
-------
actions/__init__.py 0 0 100%
actions/actions.py 40 8 80%
files/common/
files/hplog/
files/ilorest/
files/mdadm/
files/mdadm/
files/megacli/
files/nvme/
files/sas2ircu/
files/sas3ircu/
files/ssacli/
lib/hwhealth/
lib/hwhealth/
lib/hwhealth/
lib/hwhealth/
lib/hwhealth/
-------
TOTAL 1489 571 62%
lint/unit tests are passing, running functional now.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : | # |
I've fleshed out a couple of functional test bugs...this is still WIP.
I'm trying to find a way to remove the test input data from this MP and submit a dependent merge with just those sample files so this review can be code oriented.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : | # |
running functional tests after resolving what appear to be the issues with the hp repos and lack of focal support. Focal deploy is now succeeding. Will have to see if the resolution for the new GPG key on the hp mcp repo fixes bionic.
- a60b332... by Drew Freiberger
-
Added older megacli checksum
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : | # |
test results not good on functional testing. Needs more review. Blocking for MR waiting for HP tools for focal to be released. Or refactor based on tools that are series-specific.
- 25cb015... by Drew Freiberger
-
Update hp-health ssacli repository to current-gen9
- d98a133... by Drew Freiberger
-
Revert "Add supported_series no-op code to AptVendorTool class"
This reverts commit 760d646c6fcb0cb
5ea40fbd2eebc13 e83250e025. - 26aabf1... by Drew Freiberger
-
Update tools discovery code and tests to be series-aware
Also remove known-bad checksum (zero byte file checksum e3b...)
from approved list of megacli checksums.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Drew Freiberger (afreiberger) wrote : | # |
focal repositories do not have the same gen9 hp tooling, so are not enabled.
Gen10 hp tooling is a future TODO and will then support focal for HP tools.
Unmerged commits
- 26aabf1... by Drew Freiberger
-
Update tools discovery code and tests to be series-aware
Also remove known-bad checksum (zero byte file checksum e3b...)
from approved list of megacli checksums. - d98a133... by Drew Freiberger
-
Revert "Add supported_series no-op code to AptVendorTool class"
This reverts commit 760d646c6fcb0cb
5ea40fbd2eebc13 e83250e025. - 25cb015... by Drew Freiberger
-
Update hp-health ssacli repository to current-gen9
- a60b332... by Drew Freiberger
-
Added older megacli checksum
- 760d646... by Drew Freiberger
-
Add supported_series no-op code to AptVendorTool class
- 9f33298... by Drew Freiberger
-
Update HPE tool repositories and keys
- 686d4b1... by Drew Freiberger
-
Skip apt-add-repo for undefined AptVendorTool repos
Update cron script paths for cron_$tool.sh tools
Fixed functional test errors and cleaned nits from black
- b851dc8... by Drew Freiberger
-
Fixing up tests from merge conflict resolution errors for nagios check tools
- 8232cd1... by Drew Freiberger
-
Fixed lint and focal test environment issues
Updated checksum for new version of sas3ircu tool
- 85988a1... by Andrea Ieri
-
[test_hwhealth] pep8 compliance
Preview Diff
1 | diff --git a/.gitignore b/.gitignore |
2 | index e808ce9..06bf3e8 100644 |
3 | --- a/.gitignore |
4 | +++ b/.gitignore |
5 | @@ -23,5 +23,3 @@ builds |
6 | |
7 | # tools bundle (needed for functional testing) |
8 | tools.zip |
9 | -tools-checksum.zip |
10 | -tools-missing.zip |
11 | diff --git a/Makefile b/Makefile |
12 | index 70f7666..d314eb8 100644 |
13 | --- a/Makefile |
14 | +++ b/Makefile |
15 | @@ -27,11 +27,11 @@ lint: |
16 | |
17 | test: lint unittest functional |
18 | |
19 | -unittest: |
20 | - @cd src && tox -e unit |
21 | +unittest: build |
22 | + @cd $(JUJU_REPOSITORY)/builds/hw-health && tox -e unit |
23 | |
24 | -functional: resource_check build |
25 | - @cd src && tox -e functional |
26 | +functional: resource_check build buildresources |
27 | + @cd $(JUJU_REPOSITORY)/builds/hw-health && tox -e functional |
28 | |
29 | resource_check: |
30 | ifeq ("$(wildcard $(JUJU_REPOSITORY)/tools.zip)","") |
31 | @@ -39,17 +39,21 @@ ifeq ("$(wildcard $(JUJU_REPOSITORY)/tools.zip)","") |
32 | before running functional tests. \ |
33 | Check the README for instructions on how to do so.) |
34 | endif |
35 | -ifeq ("$(wildcard $(JUJU_REPOSITORY)/tools-checksum.zip)","") |
36 | - $(error ERROR: You must save the tools resource in $(JUJU_REPOSITORY)/tools-checksum.zip \ |
37 | - before running functional tests. \ |
38 | - megacli tool needs to be replaced by an empty file within the zip archive.) |
39 | -endif |
40 | -ifeq ("$(wildcard $(JUJU_REPOSITORY)/tools-missing.zip)","") |
41 | - $(error ERROR: You must save the tools resource in $(JUJU_REPOSITORY)/tools-missing.zip \ |
42 | - before running functional tests. \ |
43 | - megacli tool needs to be removed from the zip archive.) |
44 | -endif |
45 | |
46 | +buildresources: |
47 | + @echo "Copying tools.zip to the builds directory" |
48 | + @cp $(JUJU_REPOSITORY)/tools.zip $(JUJU_REPOSITORY)/builds/tools.zip |
49 | + @echo "Building tools-missing.zip" |
50 | + @zip $(JUJU_REPOSITORY)/tools.zip --copy \ |
51 | + --out $(JUJU_REPOSITORY)/builds/tools-missing.zip \ |
52 | + --exclude megacli |
53 | + @echo "Building tools-checksum.zip" |
54 | + @zip $(JUJU_REPOSITORY)/tools.zip --copy \ |
55 | + --out $(JUJU_REPOSITORY)/builds/tools-checksum.zip \ |
56 | + --exclude megacli |
57 | + @touch $(JUJU_REPOSITORY)/builds/megacli |
58 | + @zip --move --junk-path $(JUJU_REPOSITORY)/builds/tools-checksum.zip \ |
59 | + $(JUJU_REPOSITORY)/builds/megacli |
60 | |
61 | build: |
62 | @echo "Building charm to base directory $(JUJU_REPOSITORY)" |
63 | diff --git a/src/README.md b/src/README.md |
64 | index 45b48ed..0cb9007 100644 |
65 | --- a/src/README.md |
66 | +++ b/src/README.md |
67 | @@ -20,8 +20,7 @@ Hardware-independent tools: |
68 | default) |
69 | |
70 | In the backlog, hp-health logic still needs to be backported to support |
71 | -Hewlett-Packard equipment (HP Smart Array Controllers and MSA Controllers with |
72 | -hpacucli, hpssacli, ssacli). |
73 | +Hewlett-Packard gen8 and older equipment (HP Controllers with hpacucli) |
74 | |
75 | Furthermore, other hardware in the roadmap is: |
76 | |
77 | diff --git a/src/files/common/check_hw_health_cron_output.py b/src/files/common/check_hw_health_cron_output.py |
78 | new file mode 100755 |
79 | index 0000000..ebb63e1 |
80 | --- /dev/null |
81 | +++ b/src/files/common/check_hw_health_cron_output.py |
82 | @@ -0,0 +1,78 @@ |
83 | +#!/usr/bin/env python3 |
84 | +# ------------------------------------------------ |
85 | +# This file is juju managed |
86 | +# ------------------------------------------------ |
87 | + |
88 | +# Copyright (C) 2008, 2009, 2012 James Troup <james.troup@canonical.com> |
89 | +# Copyright (C) 2009 LaMont Jones <lamont.jones@canonical.com> |
90 | + |
91 | +############################################################################### |
92 | + |
93 | +from optparse import OptionParser |
94 | + |
95 | +from nagios_plugin3 import (check_file_freshness, |
96 | + try_check, |
97 | + WarnError, |
98 | + UnknownError, |
99 | + CriticalError) |
100 | + |
101 | + |
102 | +############################################################################### |
103 | + |
104 | + |
105 | +def check_and_open(filename, time_limit): |
106 | + check_file_freshness(filename, time_limit) |
107 | + return open(filename) |
108 | + |
109 | + |
110 | +############################################################################### |
111 | + |
112 | + |
113 | +def check_file_contents(input_filename, time_limit): |
114 | + input_file = check_and_open(input_filename, time_limit) |
115 | + for line in input_file.readlines(): |
116 | + line = line.rstrip() |
117 | + if not line: |
118 | + continue |
119 | + if line.startswith("WARN"): |
120 | + raise WarnError(line) |
121 | + if line.startswith("UNKNOWN"): |
122 | + raise UnknownError(line) |
123 | + elif line.startswith("OK"): |
124 | + print(line) |
125 | + else: |
126 | + raise CriticalError(line) |
127 | + |
128 | + |
129 | +############################################################################### |
130 | + |
131 | + |
132 | +def main(): |
133 | + parser = OptionParser() |
134 | + parser.add_option( |
135 | + "--time", |
136 | + dest="time_limit", |
137 | + help="freshness time limit [default=%default]", |
138 | + metavar="SECONDS", |
139 | + default=1200, |
140 | + type=int |
141 | + ) |
142 | + parser.add_option( |
143 | + "-f", "--filename", |
144 | + dest="input_file", |
145 | + help=('file containing the output of ' |
146 | + 'cron_ilorest.py [default=%default]'), |
147 | + metavar="FILE", |
148 | + nargs=1, |
149 | + # default="/var/lib/nagios/UNSETOUTPUTFILE.out", |
150 | + type=str |
151 | + ) |
152 | + |
153 | + (opts, args) = parser.parse_args() |
154 | + |
155 | + try_check(check_file_contents, opts.input_file, opts.time_limit) |
156 | + print("All OK") |
157 | + |
158 | + |
159 | +if __name__ == "__main__": |
160 | + main() |
161 | diff --git a/src/files/common/hw_health_lib.py b/src/files/common/hw_health_lib.py |
162 | new file mode 100644 |
163 | index 0000000..c75a4e5 |
164 | --- /dev/null |
165 | +++ b/src/files/common/hw_health_lib.py |
166 | @@ -0,0 +1,263 @@ |
167 | +# ----------------------------------------------- |
168 | +# This file is juju managed |
169 | +# ----------------------------------------------- |
170 | + |
171 | +# Copyright (C) 2008-2013 James Troup <james.troup@canonical.com> |
172 | +# Copyright (c) 2020 Drew Freiberger <drew.freiberger@canonical.com> |
173 | + |
174 | +import argparse |
175 | +import os |
176 | +import re |
177 | +import subprocess |
178 | +import tempfile |
179 | +import yaml |
180 | + |
181 | +from datetime import datetime, timedelta |
182 | +from binascii import crc32 |
183 | +from socket import gethostname |
184 | + |
185 | + |
186 | +ignoring_output = [] |
187 | + |
188 | + |
189 | +def read_ignore_file(ignore_file): # noqa C901 |
190 | + """ |
191 | + Reads a file to create a list of ignore dicts to use with |
192 | + the ignore function. |
193 | + |
194 | + The directory /etc/nagios/ignores/ must be owned by nagios user, |
195 | + so that it can create and re-write the ignores files. |
196 | + Use the following to create it with correct perms: |
197 | + D=/etc/nagios/ignores; sudo mkdir $D; sudo chown nagios $D; |
198 | + MODULE=power # or array, controller, fans |
199 | + sudo -u nagios vim /etc/nagios/ignores/ignores-cat-hp-$MODULE.txt |
200 | + |
201 | + The following is useful to paste at the top of an ignores file: |
202 | + # This file used by /usr/lib/nagios/plugins/check_hp_hardware.py |
203 | + # so that specific hardware errors can be ignored |
204 | + # and the nagios alert NOT downtimed or acknowledged, |
205 | + # thus allowing other hardware failures to alert. |
206 | + # ---------------------------------------------------------------- |
207 | + # Ignore lines are an expiry date and a substring to match, like: |
208 | + # [YYYY-mm-dd HH:MM] nagios error string |
209 | + # Lines will be disabled when not matched so they can alert again, |
210 | + # but starting a line with '*' will persist a line when not matched |
211 | + # and should be used for flapping alerts/hardware only. |
212 | + # Comment lines (starting with '#') and blank lines are ignored. |
213 | + # Example ignore lines: |
214 | + # # do not alert on fan 1 until 2016-11-17 11:00 |
215 | + # [2016-11-17 11:00] System Board (1): fans are not redundant |
216 | + # # ignore power supply 1 until 2016-11-17 0:00, even if unmatched |
217 | + # * [2016-11-17] Pwr. Supply Bay (1): power supply is 'Failed' |
218 | + |
219 | + # example ignore dict |
220 | + # {'matched': False, # set to True if it matches, check before write |
221 | + # 'expired': False, # has ignore date passed |
222 | + # 'line': "[2016-11-01 23:00] substring to match" |
223 | + # 'ignore': "substring to match OR None", # None if expired |
224 | + # } |
225 | + """ |
226 | + ignores = [] |
227 | + if os.path.isfile(ignore_file): |
228 | + for line in open(ignore_file).readlines(): |
229 | + d = {'matched': False, 'expired': False, 'line': line.rstrip(), 'ignore': None} |
230 | + line = line.strip() |
231 | + # special case lines starting with '*', do not disable if unmatched |
232 | + persist = False |
233 | + if line.startswith('*'): |
234 | + persist = True |
235 | + line = line.lstrip('*').strip() |
236 | + # parse date lines |
237 | + if line.startswith('['): |
238 | + parts = re.split('\\[|\\]', line, maxsplit=2) |
239 | + date = parts[1].strip() |
240 | + ignore = parts[2].strip() |
241 | + try: |
242 | + date = datetime.strptime(date, '%Y-%m-%d %H:%M') |
243 | + except ValueError: |
244 | + try: |
245 | + date = datetime.strptime(date, '%Y-%m-%d') |
246 | + except ValueError: |
247 | + print("Failed to parse ignore date: {}".format(d['line'])) |
248 | + date = None |
249 | + if date: |
250 | + # Do not alert directly at midnight UTC |
251 | + host_crc = crc32(gethostname()) & 0xFFFFFFFF |
252 | + date += timedelta(seconds=(host_crc % 86400)) |
253 | + # check if date valid and is in the future |
254 | + if date: |
255 | + if datetime.now().weekday() in (5, 6): |
256 | + # Ignore Saturday/Sunday to not annoy on-call |
257 | + d['ignore'] = ignore |
258 | + elif date > datetime.now(): |
259 | + d['ignore'] = ignore |
260 | + else: |
261 | + d['expired'] = True |
262 | + if persist and not d['expired']: |
263 | + # set matched True so will not get disabled on non-match |
264 | + d['matched'] = True |
265 | + # comment lines and empty lines are just added |
266 | + elif line.startswith('#') or not line: |
267 | + # add with matched True so does not trigger a file rewrite |
268 | + d['matched'] = True |
269 | + # unrecognized lines left matched False so rewritten |
270 | + ignores.append(d) |
271 | + return ignores |
272 | + |
273 | + |
274 | +def write_ignore_file(ignores, ignore_file): |
275 | + # if any ignores are not matched then write out file lines again |
276 | + if any([not i['matched'] for i in ignores]): |
277 | + dirname, basename = os.path.split(ignore_file) |
278 | + date = datetime.now() |
279 | + try: |
280 | + f = tempfile.NamedTemporaryFile(dir=dirname, prefix=basename, delete=False) |
281 | + for ignore in ignores: |
282 | + if not ignore['matched'] and ignore['ignore']: |
283 | + ignore['line'] = "# not matched at {} #{}".format( |
284 | + date.strftime("%Y-%m-%dT%H:%M:%S"), ignore['line'] |
285 | + ) |
286 | + elif ignore['expired']: |
287 | + # this won't get updated unless the alert has cleared |
288 | + ignore['line'] = "# expired #{}".format(ignore['line']) |
289 | + elif not ignore['matched']: |
290 | + ignore['line'] = "# unknown or bad format #{}".format(ignore['line']) |
291 | + f.write(ignore['line'] + '\n') |
292 | + f.flush() |
293 | + os.fsync(f.fileno()) |
294 | + f.close() |
295 | + # NamedTemporaryFile has restricted permissions, open them up a bit |
296 | + os.chmod(f.name, os.stat(f.name).st_mode | 0o044) |
297 | + os.rename(f.name, ignore_file) |
298 | + except OSError: |
299 | + print("Failed to write {}".format(ignore_file)) |
300 | + pass |
301 | + |
302 | + |
303 | +def ignore(line, ignores): |
304 | + # check if each ignore is in line |
305 | + for ignore in ignores: |
306 | + if ignore['ignore'] and ignore['ignore'] in line: |
307 | + ignoring_output.append("Ignoring: {}".format(line)) |
308 | + # note: ignores can be updated since it is passed by reference |
309 | + # matched=True to keep using this ignore (see write_ignore_file) |
310 | + ignore['matched'] = True |
311 | + return True |
312 | + return False |
313 | + |
314 | + |
315 | +def get_hp_controller_slots(): |
316 | + """ |
317 | + TODO search for the controller slot |
318 | + |
319 | + Use the utility to determine the current controller slot(s) available for probing |
320 | + """ |
321 | + slots = [] |
322 | + cmd = ['/usr/sbin/hpssacli', 'ctrl', 'all', 'show'] |
323 | + try: |
324 | + results = subprocess.check_output(cmd).decode('UTF-8') |
325 | + except subprocess.CalledProcessError: |
326 | + return slots |
327 | + for line in results.splitlines(): |
328 | + if 'in Slot' in line: |
329 | + slots.append(line.split()[5]) |
330 | + return slots |
331 | + |
332 | + |
333 | +class HWCronArgumentParser(argparse.ArgumentParser): |
334 | + def __init__( |
335 | + self, |
336 | + def_write_file=None, |
337 | + *args, |
338 | + **kwargs |
339 | + ): |
340 | + super().__init__( |
341 | + formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
342 | + *args, |
343 | + **kwargs |
344 | + ) |
345 | + # self.prog is populated by ArgumentParser |
346 | + self._def_write_file = \ |
347 | + def_write_file or '/var/lib/nagios/{}.out'.format(self.prog) |
348 | + |
349 | + def parse_args(self, *args, **kwargs): |
350 | + self.add_argument( |
351 | + '-w', '--write', dest='write', type=str, |
352 | + default=self._def_write_file, |
353 | + help='cache tool output in this file', |
354 | + ) |
355 | + super().parse_args(*args, **kwargs) |
356 | + |
357 | + |
358 | +class HWCheckArgumentParser(argparse.ArgumentParser): |
359 | + def __init__( |
360 | + self, |
361 | + def_input_file=None, |
362 | + *args, |
363 | + **kwargs |
364 | + ): |
365 | + super().__init__( |
366 | + formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
367 | + *args, |
368 | + **kwargs |
369 | + ) |
370 | + # self.prog is populated by ArgumentParser |
371 | + self._def_input_file = \ |
372 | + def_input_file or '/var/lib/nagios/{}.out'.format(self.prog) |
373 | + |
374 | + def parse_args(self, *args, **kwargs): |
375 | + self.add_argument( |
376 | + '-i', '--input', dest='input_file', type=str, |
377 | + default=self._def_input_file, |
378 | + help='read cached tool output from this file', |
379 | + ) |
380 | + super().parse_args(*args, **kwargs) |
381 | + |
382 | + |
383 | +class HPArgumentParser(HWCronArgumentParser): |
384 | + def __init__( |
385 | + self, |
386 | + def_exclude_file=None, |
387 | + *args, |
388 | + **kwargs |
389 | + ): |
390 | + super().__init__(*args, **kwargs) |
391 | + self._def_exclude_file = \ |
392 | + def_exclude_file or '/etc/nagios/{}.exclude.yaml'.format(self.prog) |
393 | + |
394 | + def _expired(self, exclusion): |
395 | + return 'expires' in exclusion and exclusion['expires'] < datetime.now() |
396 | + |
397 | + def parse_args(self, *args, **kwargs): |
398 | + self.add_argument( |
399 | + '--debug', dest='debug', action='store_true', |
400 | + help='Extra debugging', |
401 | + ) |
402 | + |
403 | + self.add_argument( |
404 | + '--exclude', dest='exclude', type=str, action='append', |
405 | + help='Errors to ignore (multiple)', |
406 | + ) |
407 | + |
408 | + self.add_argument( |
409 | + '--exclude-file', dest='exclude_file', type=str, |
410 | + default=self._def_exclude_file, |
411 | + help='YAML file with errors to ignore', |
412 | + ) |
413 | + |
414 | + # Ensure we initialize a namespace if needed, |
415 | + # and have a reference to it |
416 | + namespace = kwargs.get('namespace') or argparse.Namespace() |
417 | + kwargs['namespace'] = namespace |
418 | + # now parse args and put them in the namespace |
419 | + super().parse_args(*args, **kwargs) |
420 | + |
421 | + if namespace.exclude is None: |
422 | + namespace.exclude = [] |
423 | + if os.path.exists(namespace.exclude_file): |
424 | + with open(namespace.exclude_file) as f: |
425 | + for i in yaml.safe_load(f): |
426 | + if not self._expired(i): |
427 | + namespace.exclude.append(i['error']) |
428 | + |
429 | + return namespace |
430 | diff --git a/src/files/hplog/cron_hplog.py b/src/files/hplog/cron_hplog.py |
431 | new file mode 100755 |
432 | index 0000000..d2b21cf |
433 | --- /dev/null |
434 | +++ b/src/files/hplog/cron_hplog.py |
435 | @@ -0,0 +1,304 @@ |
436 | +#!/usr/bin/env python3 |
437 | +# ------------------------------------------------ |
438 | +# This file is juju managed |
439 | +# ----------------------------------------------- |
440 | + |
441 | +""" |
442 | +Copyright (C) 2008-2013 James Troup <james.troup@canonical.com> |
443 | +Copyright (c) 2020 Rewrite: Drew Freiberger <drew.freiberger@canonical.com> |
444 | + |
445 | +hplog lives in either /usr/sbin or /sbin depending on version of the |
446 | +hp-health package and cron jobs don't get a sane $PATH |
447 | +""" |
448 | +import os |
449 | +import subprocess |
450 | +import sys |
451 | + |
452 | +try: |
453 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
454 | + from hw_health_lib import ( |
455 | + read_ignore_file, |
456 | + write_ignore_file, |
457 | + ignore, |
458 | + HPArgumentParser, |
459 | + ) |
460 | +except ImportError: |
461 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
462 | + common_libs_dir = os.path.abspath( |
463 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
464 | + ) |
465 | + if common_libs_dir not in sys.path: |
466 | + sys.path.append(common_libs_dir) |
467 | + from hw_health_lib import ( |
468 | + read_ignore_file, |
469 | + write_ignore_file, |
470 | + ignore, |
471 | + HPArgumentParser, |
472 | + ) |
473 | + |
474 | +FLAG_PROCESSOR = { |
475 | + 't': 'parse_temperature', |
476 | + 'f': 'parse_fans', |
477 | + 'p': 'parse_power', |
478 | + 'v': 'save_log', |
479 | +} |
480 | +OUTPUT_FILE = '/var/lib/nagios/hplog.out' |
481 | +EXCLUDE_FILE = '/etc/nagios/hplog.exclude.yaml' |
482 | +DEBUG_FILE = '/var/lib/nagios/hplog.debug' |
483 | + |
484 | + |
485 | +def parse_args(argv=None): |
486 | + parser = HPArgumentParser( |
487 | + prog='cron_hplog', |
488 | + def_write_file=OUTPUT_FILE, |
489 | + def_exclude_file=EXCLUDE_FILE |
490 | + ) |
491 | + parser.add_argument( |
492 | + '-f', '--hplog_flags', dest='hplog_flags', type=str, |
493 | + default=','.join(FLAG_PROCESSOR.keys()), |
494 | + help='Flags to call hplog with', |
495 | + ) |
496 | + parser.add_argument( |
497 | + '-l', '--log_path', dest='log_path', type=str, |
498 | + default=DEBUG_FILE, |
499 | + help='Where to write hplog -v output for troubleshooting', |
500 | + ) |
501 | + parser.add_argument( |
502 | + '-s', '--single_psu', dest='single_psu', action='store_true', |
503 | + help='Do not alert on lack of PSU redundancy', default=False, |
504 | + ) |
505 | + return parser.parse_args(args=argv) |
506 | + |
507 | + |
508 | +def call_hplog(flag): |
509 | + """ |
510 | + Grab hplogs from hpasm if available |
511 | + |
512 | + The output of this cron script will be checked by |
513 | + nagios via check_hplog.py. The |
514 | + """ |
515 | + env = 'export PATH=$PATH:/usr/sbin:/sbin' |
516 | + |
517 | + cmd = 'hplog -{}'.format(flag) |
518 | + try: |
519 | + cmdline = '{}; {}'.format(env, cmd) |
520 | + output = subprocess.check_output(cmdline, shell=True).decode('UTF-8') |
521 | + except subprocess.CalledProcessError as e: |
522 | + return ('Failed running command "{}" Return Code {}: {}' |
523 | + ''.format(cmd, e.returncode, e.output)) |
524 | + |
525 | + funcname = FLAG_PROCESSOR[flag] |
526 | + return globals()[funcname](output) |
527 | + |
528 | + |
529 | +def parse_temperature(result): |
530 | + """ |
531 | + Temperature [hplog -t] |
532 | + 10 20 30 40 50 60 70 |
533 | + 01234567890123456789012345678901234567890123456789012345678901234567890 |
534 | + ID TYPE LOCATION STATUS CURRENT THRESHOLD |
535 | + 1 Basic Sensor I/O Zone Normal 102F/ 39C 158F/ 70C |
536 | + 2 Basic Sensor Ambient Normal 73F/ 23C 102F/ 39C |
537 | + 3 Basic Sensor CPU (1) Normal 95F/ 35C 260F/127C |
538 | + 4 Basic Sensor CPU (1) Normal 95F/ 35C 260F/127C |
539 | + 5 Basic Sensor Pwr. Supply Bay Normal 111F/ 44C 170F/ 77C |
540 | + 6 Basic Sensor CPU (2) Normal ---F/---C 260F/127C |
541 | + 7 Basic Sensor CPU (2) Normal ---F/---C 260F/127C |
542 | + """ |
543 | + input_file = result.splitlines() |
544 | + if os.path.isfile('/etc/nagios/skip-cat-hp-temperature.txt'): |
545 | + return |
546 | + header_line = input_file.pop(0).strip() |
547 | + if header_line != "ID TYPE LOCATION STATUS CURRENT THRESHOLD": # noqa E501 |
548 | + return "UNKNOWN Unrecognised header line in 'hplog -t' output" |
549 | + for line in input_file: |
550 | + line = line.rstrip() |
551 | + if not line: |
552 | + continue |
553 | + temp_id = line[0:3].strip() |
554 | + # temp_type = line[4:16].strip() |
555 | + temp_location = line[17:32].strip() |
556 | + temp_location = "%s (%s)" % (temp_location, temp_id) |
557 | + temp_status = line[33:41].strip() |
558 | + temp_current = line[42:51].split("/")[1].strip() |
559 | + temp_threshold = line[52:].split("/")[1].strip() |
560 | + if temp_status not in ["Normal", "Nominal", "Absent"]: |
561 | + return ( |
562 | + "%s: temperature is '%s' (%s / %s)" % (temp_location, |
563 | + temp_status, |
564 | + temp_current, |
565 | + temp_threshold) |
566 | + ) |
567 | + |
568 | + return |
569 | + |
570 | + |
571 | +def parse_fans(result): |
572 | + """ |
573 | + Fans [hplog -f] |
574 | + 10 20 30 40 50 60 70 |
575 | + 01234567890123456789012345678901234567890123456789012345678901234567890 |
576 | + ID TYPE LOCATION STATUS REDUNDANT FAN SPEED |
577 | + 1 Var. Speed I/O Zone Normal Yes Medium ( 45) |
578 | + 2 Var. Speed I/O Zone Normal Yes Medium ( 45) |
579 | + 3 Var. Speed Processor Zone Normal Yes Medium ( 41) |
580 | + 4 Var. Speed Processor Zone Normal Yes Low ( 36) |
581 | + 5 Var. Speed Processor Zone Normal Yes Low ( 36) |
582 | + 6 Var. Speed Processor Zone Normal Yes Low ( 36) |
583 | + """ |
584 | + input_file = result.splitlines() |
585 | + if os.path.isfile('/etc/nagios/skip-cat-hp-fans.txt'): |
586 | + return |
587 | + header_line = input_file.pop(0).strip() |
588 | + if header_line != "ID TYPE LOCATION STATUS REDUNDANT FAN SPEED": # noqa E501 |
589 | + return "UNKNOWN Unrecognised header line in 'hplog -f' output" |
590 | + |
591 | + ignore_file = '/etc/nagios/ignores/ignores-cat-hp-fans.txt' |
592 | + ignores = read_ignore_file(ignore_file) |
593 | + for line in input_file: |
594 | + line = line.rstrip() |
595 | + if not line: |
596 | + continue |
597 | + fan_id = line[0:3].strip() |
598 | + fan_type = line[4:16].strip() |
599 | + fan_location = line[17:32].strip() |
600 | + fan_location = "%s (%s)" % (fan_location, fan_id) |
601 | + fan_status = line[33:40].strip() |
602 | + fan_redundant = line[41:50].strip() |
603 | + fan_speed = line[51:].strip() |
604 | + |
605 | + (return_now, msg) = process_fan_line( |
606 | + fan_type, |
607 | + fan_location, |
608 | + fan_status, |
609 | + fan_speed, |
610 | + fan_redundant, |
611 | + ignores |
612 | + ) |
613 | + if return_now: |
614 | + return msg |
615 | + |
616 | + write_ignore_file(ignores, ignore_file) |
617 | + |
618 | + |
619 | +def process_fan_line( |
620 | + fan_type, |
621 | + fan_location, |
622 | + fan_status, |
623 | + fan_speed, |
624 | + fan_redundant, |
625 | + ignores |
626 | +): |
627 | + if fan_type == "Basic Fan": |
628 | + return(False, None) |
629 | + |
630 | + if fan_type not in ["Var. Speed", "Pwr. Supply", "Auto. Speed"]: |
631 | + return(True, "UNKNOWN %s: Unrecognised fan type '%s'" % (fan_location, |
632 | + fan_type)) |
633 | + |
634 | + if fan_status not in ["Normal", "Nominal"]: |
635 | + err = "%s: fans are '%s' (%s / Redundant: %s)" % (fan_location, |
636 | + fan_status, |
637 | + fan_speed, |
638 | + fan_redundant) |
639 | + if not ignore(err, ignores): |
640 | + return(True, err) |
641 | + |
642 | + if fan_redundant not in ["Yes", "N/A"] and fan_type == "Var. Speed": |
643 | + err = "%s: fans are not redundant (%s / Status: %s)" % (fan_location, |
644 | + fan_speed, |
645 | + fan_redundant) |
646 | + if not ignore(err, ignores): |
647 | + return(True, err) |
648 | + |
649 | + return(False, None) |
650 | + |
651 | + |
652 | +def parse_power(result): |
653 | + """ |
654 | + Power supplies [hplog -p] |
655 | + 10 20 30 40 50 60 70 |
656 | + 01234567890123456789012345678901234567890123456789012345678901234567890 |
657 | + ID TYPE LOCATION STATUS REDUNDANT |
658 | + 1 Standard Pwr. Supply Bay Normal Yes |
659 | + 2 Standard Pwr. Supply Bay Normal Yes |
660 | + """ |
661 | + input_file = result.splitlines() |
662 | + if os.path.isfile('/etc/nagios/skip-cat-hp-power.txt'): |
663 | + return |
664 | + header_line = input_file.pop(0).strip() |
665 | + if header_line != "ID TYPE LOCATION STATUS REDUNDANT": |
666 | + return "UNKNOWN Unrecognised header line in 'hplog -p' output" |
667 | + |
668 | + ignore_file = '/etc/nagios/ignores/ignores-cat-hp-power.txt' |
669 | + ignores = read_ignore_file(ignore_file) |
670 | + |
671 | + for line in input_file: |
672 | + line = line.rstrip() |
673 | + if not line: |
674 | + continue |
675 | + power_id = line[0:3].strip() |
676 | + power_type = line[4:16].strip() |
677 | + power_location = line[17:32].strip() |
678 | + power_location = "%s (%s)" % (power_location, power_id) |
679 | + power_status = line[33:40].strip() |
680 | + # power_redundant = line[41:50].strip() |
681 | + if power_type != "Standard": |
682 | + err = "%s: Unrecognised power type '%s'" % (power_location, |
683 | + power_type) |
684 | + if not ignore(err, ignores): |
685 | + return 'UNKNOWN {}'.format(err) |
686 | + if not ARGS.single_psu and power_status not in ["Normal", "Nominal"]: |
687 | + err = "%s: power supply is '%s'" % (power_location, power_status) |
688 | + if not ignore(err, ignores): |
689 | + return err |
690 | + write_ignore_file(ignores, ignore_file) |
691 | + |
692 | + |
693 | +def save_log(result): |
694 | + """ |
695 | + Save full hplog -v output for troubleshooting after alert |
696 | + """ |
697 | + with open(ARGS.log_path, 'w') as f: |
698 | + f.write(result) |
699 | + return |
700 | + |
701 | + |
702 | +def main(): |
703 | + global ARGS |
704 | + ARGS = parse_args() |
705 | + |
706 | + try: |
707 | + # This matches hpasmlited on latest packages for bionic on <= gen9 |
708 | + subprocess.check_call('ps -ef | grep -q hp[a]sm', shell=True) |
709 | + except subprocess.CalledProcessError as e: |
710 | + msg = ( |
711 | + 'UNKNOWN hp[a]sm daemon not found running, cannot run hplog: ' |
712 | + '{}'.format(e.output) |
713 | + ) |
714 | + exit = 3 |
715 | + else: |
716 | + errors = [] |
717 | + for flag in ARGS.hplog_flags.split(','): |
718 | + log_output = call_hplog(flag) |
719 | + if log_output: |
720 | + errors.append(log_output) |
721 | + |
722 | + if len(errors) > 0: |
723 | + msg = ('CRIT {} error(s): {}' |
724 | + ''.format(len(errors), ' - '.join(errors))) |
725 | + exit = 2 |
726 | + else: |
727 | + msg = 'OK No errors found' |
728 | + exit = 0 |
729 | + |
730 | + if ARGS.write: |
731 | + with open(ARGS.write, 'w') as f: |
732 | + f.write(msg) |
733 | + else: |
734 | + print(msg) |
735 | + sys.exit(exit) |
736 | + |
737 | + |
738 | +if __name__ == '__main__': |
739 | + main() |
740 | diff --git a/src/files/ilorest/check_ilorest.py b/src/files/ilorest/check_ilorest.py |
741 | new file mode 100755 |
742 | index 0000000..430c632 |
743 | --- /dev/null |
744 | +++ b/src/files/ilorest/check_ilorest.py |
745 | @@ -0,0 +1,74 @@ |
746 | +#!/usr/bin/env python3 |
747 | +# ------------------------------------------------ |
748 | +# This file is juju managed |
749 | +# ------------------------------------------------ |
750 | + |
751 | +# Copyright (C) 2008, 2009, 2012 James Troup <james.troup@canonical.com> |
752 | +# Copyright (C) 2009 LaMont Jones <lamont.jones@canonical.com> |
753 | + |
754 | +############################################################################### |
755 | + |
756 | +from optparse import OptionParser |
757 | + |
758 | +from nagios_plugin3 import (check_file_freshness, |
759 | + try_check, |
760 | + WarnError, |
761 | + CriticalError) |
762 | + |
763 | + |
764 | +############################################################################### |
765 | + |
766 | + |
767 | +def check_and_open(filename, time_limit): |
768 | + check_file_freshness(filename, time_limit) |
769 | + return open(filename) |
770 | + |
771 | + |
772 | +############################################################################### |
773 | + |
774 | + |
775 | +def check_file_contents(input_filename, time_limit): |
776 | + input_file = check_and_open(input_filename, time_limit) |
777 | + for line in input_file.readlines(): |
778 | + line = line.rstrip() |
779 | + if not line: |
780 | + continue |
781 | + if line.startswith("WARN"): |
782 | + raise WarnError(line) |
783 | + elif line.startswith("OK"): |
784 | + print(line) |
785 | + else: |
786 | + raise CriticalError(line) |
787 | + |
788 | + |
789 | +############################################################################### |
790 | + |
791 | + |
792 | +def main(): |
793 | + parser = OptionParser() |
794 | + parser.add_option( |
795 | + "--time", |
796 | + dest="time_limit", |
797 | + help="freshness time limit [default=%default]", |
798 | + metavar="SECONDS", |
799 | + default=1200, |
800 | + type=int |
801 | + ) |
802 | + parser.add_option( |
803 | + "-f", "--filename", |
804 | + dest="input_file", |
805 | + help=('file containing the output of ' |
806 | + 'cron_ilorest.py [default=%default]'), |
807 | + metavar="FILE", |
808 | + default="/var/lib/nagios/ilorest.nagios", |
809 | + type=str |
810 | + ) |
811 | + |
812 | + (opts, args) = parser.parse_args() |
813 | + |
814 | + try_check(check_file_contents, opts.input_file, opts.time_limit) |
815 | + print("All OK") |
816 | + |
817 | + |
818 | +if __name__ == "__main__": |
819 | + main() |
820 | diff --git a/src/files/ilorest/cron_ilorest.py b/src/files/ilorest/cron_ilorest.py |
821 | new file mode 100755 |
822 | index 0000000..739438d |
823 | --- /dev/null |
824 | +++ b/src/files/ilorest/cron_ilorest.py |
825 | @@ -0,0 +1,146 @@ |
826 | +#!/usr/bin/env python3 |
827 | +# ------------------------------------------------ |
828 | +# This file is juju managed |
829 | +# ------------------------------------------------ |
830 | + |
831 | +# ilorest interface for Gen10 HPE machines |
832 | +# Original author: Ryan Finnie <ryan.finnie@canonical.com> |
833 | +# Porting for the HW health charm: Andrea Ieri <andrea.ieri@canonical.com> |
834 | + |
835 | +import json |
836 | +import sys |
837 | +import os |
838 | +from subprocess import check_output |
839 | + |
840 | +try: |
841 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
842 | + from hw_health_lib import HPArgumentParser |
843 | +except ImportError: |
844 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
845 | + common_libs_dir = os.path.abspath( |
846 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
847 | + ) |
848 | + if common_libs_dir not in sys.path: |
849 | + sys.path.append(common_libs_dir) |
850 | + from hw_health_lib import HPArgumentParser |
851 | + |
852 | + |
853 | +DEFAULT_SELECTORS = [ |
854 | + 'Chassis', |
855 | + 'HpeSmartStorage', |
856 | + 'Memory', |
857 | + 'Power', |
858 | + 'Processor', |
859 | + 'Thermal', |
860 | +] |
861 | + |
862 | +EXCLUDE_FILE = '/etc/nagios/cron_ilorest.exclude.yaml' |
863 | +OUTPUT_FILE = '/var/lib/nagios/ilorest.nagios' |
864 | + |
865 | + |
866 | +class CronILOrest: |
867 | + def __init__(self, argv=[]): |
868 | + self.args = self.parse_args(argv) |
869 | + |
870 | + def parse_args(self, argv=None): |
871 | + parser = HPArgumentParser( |
872 | + prog='cron_ilorest', |
873 | + description=('Convert the output of ilorest into an appropriate ' |
874 | + 'Nagios status line'), |
875 | + def_write_file=OUTPUT_FILE, |
876 | + def_exclude_file=EXCLUDE_FILE |
877 | + ) |
878 | + |
879 | + parser.add_argument( |
880 | + '--selectors', dest='selectors', type=str, nargs='+', |
881 | + default=DEFAULT_SELECTORS, |
882 | + help='iLO selectors to run', |
883 | + ) |
884 | + |
885 | + return parser.parse_args(args=argv) |
886 | + |
887 | + def check_selector(self, selector): |
888 | + if self.args.debug: |
889 | + print('Checking selector {}'.format(selector), file=sys.stderr) |
890 | + ilorest_output = self._get_json_ilorest_output(selector) |
891 | + |
892 | + errors = [] |
893 | + jsonidx = -1 |
894 | + # Disregard the first chunk of data, it's banner/debug/etc |
895 | + for jsondata in ilorest_output.split('\n{\n')[1:]: |
896 | + # The output will be one or more JSON defs |
897 | + jsonidx += 1 |
898 | + j = json.loads('{' + jsondata) |
899 | + errors += self._walk_selector(j, [selector, str(jsonidx)]) |
900 | + return errors |
901 | + |
902 | + def _get_json_ilorest_output(self, selector): |
903 | + cmd = ['ilorest', 'list', '-j', '--selector={}'.format(selector)] |
904 | + return check_output(cmd).decode('UTF-8') |
905 | + |
906 | + def _get_health_status_message(self, j, crumb_trail=[]): |
907 | + desc = j['Name'] |
908 | + if 'SerialNumber' in j: |
909 | + desc += ' ({})'.format(j['SerialNumber']) |
910 | + state = j.get('Status', 'null').get('State', 'unknown') |
911 | + health = j.get('Status', 'null').get('Health', 'unknown') |
912 | + msg = '{} ({}): {} health {}'.format(' '.join(crumb_trail), |
913 | + desc, |
914 | + state, |
915 | + health) |
916 | + if self.args.debug: |
917 | + print(msg, file=sys.stderr) |
918 | + |
919 | + if msg in self.args.exclude and self.args.debug: |
920 | + print('Ignoring excluded error: {}'.format(msg), file=sys.stderr) |
921 | + return [] |
922 | + else: |
923 | + return [msg] |
924 | + |
925 | + def _walk_selector(self, j, crumb_trail=[]): |
926 | + errors = [] |
927 | + if j.get('Status') and j.get('Status').get('Health') != 'OK': |
928 | + errors.extend(self._get_health_status_message(j, crumb_trail)) |
929 | + |
930 | + for keyname in j.keys(): |
931 | + # If the key value is a list, it might be a list of |
932 | + # dicts with Status data |
933 | + if type(j[keyname]) != list: |
934 | + continue |
935 | + for i in range(len(j[keyname])): |
936 | + if type(j[keyname][i]) != dict: |
937 | + continue |
938 | + if 'Status' not in j[keyname][i]: |
939 | + continue |
940 | + self._walk_selector(j[keyname][i], |
941 | + (crumb_trail + [keyname, str(i)])) |
942 | + return errors |
943 | + |
944 | + |
945 | +def main(argv=None): |
946 | + cronilorest = CronILOrest(argv) |
947 | + |
948 | + errors = [cronilorest.check_selector(selector) |
949 | + for selector in cronilorest.args.selectors] |
950 | + |
951 | + if len(errors) > 0: |
952 | + msg = 'CRIT {} error(s): {}'.format(len(errors), ' - '.join(errors)) |
953 | + exit = 2 |
954 | + else: |
955 | + msg = 'OK No errors found' |
956 | + exit = 0 |
957 | + |
958 | + if cronilorest.args.write: |
959 | + if cronilorest.args.write == '-': |
960 | + print(msg) |
961 | + else: |
962 | + with open(cronilorest.args.write, 'w') as f: |
963 | + f.write(msg) |
964 | + else: |
965 | + # This should never happen since 'write' has a default value |
966 | + print(msg) |
967 | + sys.exit(exit) |
968 | + |
969 | + |
970 | +if __name__ == '__main__': |
971 | + main(sys.argv[1:]) |
972 | diff --git a/src/files/mdadm/check_mdadm.py b/src/files/mdadm/check_mdadm.py |
973 | index 56e37a8..29a82c6 100755 |
974 | --- a/src/files/mdadm/check_mdadm.py |
975 | +++ b/src/files/mdadm/check_mdadm.py |
976 | @@ -2,17 +2,33 @@ |
977 | # -*- coding: us-ascii -*- |
978 | |
979 | import os |
980 | - |
981 | +import sys |
982 | +import argparse |
983 | from nagios_plugin3 import CriticalError, WarnError, UnknownError, try_check |
984 | |
985 | +try: |
986 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
987 | + from hw_health_lib import HWCheckArgumentParser |
988 | +except ImportError: |
989 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
990 | + common_libs_dir = os.path.abspath( |
991 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
992 | + ) |
993 | + if common_libs_dir not in sys.path: |
994 | + sys.path.append(common_libs_dir) |
995 | + from hw_health_lib import HWCheckArgumentParser |
996 | + |
997 | INPUT_FILE = '/var/lib/nagios/mdadm.out' |
998 | +ARGS = argparse.Namespace() |
999 | |
1000 | |
1001 | def parse_output(): |
1002 | - if not os.path.exists(INPUT_FILE): |
1003 | - raise UnknownError('UNKNOWN: file not found ({})'.format(INPUT_FILE)) |
1004 | + if not os.path.exists(ARGS.input_file): |
1005 | + raise UnknownError( |
1006 | + 'UNKNOWN: file not found ({})'.format(ARGS.input_file) |
1007 | + ) |
1008 | |
1009 | - with open(INPUT_FILE, 'r') as fd: |
1010 | + with open(ARGS.input_file, 'r') as fd: |
1011 | for line in fd.readlines(): |
1012 | line = line.strip() |
1013 | if line.startswith('CRITICAL: '): |
1014 | @@ -23,9 +39,18 @@ def parse_output(): |
1015 | print(line) |
1016 | |
1017 | |
1018 | -def main(): |
1019 | +def parse_args(argv=None): |
1020 | + parser = HWCheckArgumentParser( |
1021 | + prog='check_mdadm', |
1022 | + def_input_file=INPUT_FILE, |
1023 | + ) |
1024 | + return parser.parse_args(args=argv, namespace=ARGS) |
1025 | + |
1026 | + |
1027 | +def main(argv): |
1028 | + parse_args(argv) |
1029 | try_check(parse_output) |
1030 | |
1031 | |
1032 | if __name__ == '__main__': |
1033 | - main() |
1034 | + main(sys.argv[1:]) |
1035 | diff --git a/src/files/mdadm/cron_mdadm.py b/src/files/mdadm/cron_mdadm.py |
1036 | index 5aaafa4..ddf5d5e 100755 |
1037 | --- a/src/files/mdadm/cron_mdadm.py |
1038 | +++ b/src/files/mdadm/cron_mdadm.py |
1039 | @@ -5,18 +5,31 @@ import re |
1040 | import shutil |
1041 | import subprocess |
1042 | import sys |
1043 | +import argparse |
1044 | |
1045 | -OUTPUT_FILE = '/var/lib/nagios/mdadm.out' |
1046 | -TEMP_FILE = '/tmp/mdadm.out' |
1047 | +try: |
1048 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
1049 | + from hw_health_lib import HWCronArgumentParser |
1050 | +except ImportError: |
1051 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
1052 | + common_libs_dir = os.path.abspath( |
1053 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
1054 | + ) |
1055 | + if common_libs_dir not in sys.path: |
1056 | + sys.path.append(common_libs_dir) |
1057 | + from hw_health_lib import HWCronArgumentParser |
1058 | + |
1059 | +OUTPUT_FILE = "/var/lib/nagios/mdadm.out" |
1060 | +TEMP_FILE = "/tmp/mdadm.out" |
1061 | +ARGS = argparse.Namespace() |
1062 | |
1063 | |
1064 | def get_devices(): |
1065 | if os.path.exists('/sbin/mdadm'): |
1066 | try: |
1067 | - devices_raw = subprocess.check_output( |
1068 | - ['/sbin/mdadm', '--detail', '--scan'] |
1069 | - ) |
1070 | - devices_re = re.compile(r'^ARRAY\s+([^ ]+) ') |
1071 | + cmd = ["/sbin/mdadm", "--detail", "--scan"] |
1072 | + devices_raw = subprocess.check_output(cmd) |
1073 | + devices_re = re.compile(r"^ARRAY\s+([^ ]+) ") |
1074 | devices = set() |
1075 | for line in devices_raw.decode().split('\n'): |
1076 | line = line.strip() |
1077 | @@ -26,7 +39,7 @@ def get_devices(): |
1078 | return devices |
1079 | except subprocess.CalledProcessError as error: |
1080 | rc = generate_output( |
1081 | - 'CRITICAL: get_devices error - {}'.format(error) |
1082 | + "CRITICAL: get_devices error - {}".format(error) |
1083 | ) |
1084 | if rc: |
1085 | sys.exit(0) |
1086 | @@ -37,7 +50,7 @@ def generate_output(msg): |
1087 | try: |
1088 | with open(TEMP_FILE, 'w') as fd: |
1089 | fd.write(msg) |
1090 | - shutil.move(TEMP_FILE, OUTPUT_FILE) |
1091 | + shutil.move(TEMP_FILE, ARGS.write) |
1092 | return True |
1093 | except Exception as error: |
1094 | print('Unable to generate output file:', error) |
1095 | @@ -129,7 +142,7 @@ def parse_output(): |
1096 | devices_stats = get_devices_stats(devices) |
1097 | except subprocess.CalledProcessError as error: |
1098 | return generate_output( |
1099 | - 'WARNING: error executing mdadm: {}'.format(error) |
1100 | + "WARNING: error executing mdadm: {}".format(error) |
1101 | ) |
1102 | |
1103 | msg = [] |
1104 | @@ -178,5 +191,18 @@ def parse_output(): |
1105 | return generate_output(msg) |
1106 | |
1107 | |
1108 | -if __name__ == '__main__': |
1109 | +def parse_args(argv=None): |
1110 | + parser = HWCronArgumentParser( |
1111 | + prog='cron_mdadm', |
1112 | + def_write_file=OUTPUT_FILE, |
1113 | + ) |
1114 | + return parser.parse_args(args=argv, namespace=ARGS) |
1115 | + |
1116 | + |
1117 | +def main(argv): |
1118 | + parse_args(argv) |
1119 | parse_output() |
1120 | + |
1121 | + |
1122 | +if __name__ == "__main__": |
1123 | + main(sys.argv[1:]) |
1124 | diff --git a/src/files/megacli/check_megacli.py b/src/files/megacli/check_megacli.py |
1125 | index ea28462..ed1e8d3 100755 |
1126 | --- a/src/files/megacli/check_megacli.py |
1127 | +++ b/src/files/megacli/check_megacli.py |
1128 | @@ -1,14 +1,31 @@ |
1129 | #!/usr/bin/env python3 |
1130 | # -*- coding: us-ascii -*- |
1131 | |
1132 | +import argparse |
1133 | +import os |
1134 | import re |
1135 | - |
1136 | +import sys |
1137 | from nagios_plugin3 import CriticalError, WarnError, try_check |
1138 | |
1139 | +try: |
1140 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
1141 | + from hw_health_lib import HWCheckArgumentParser |
1142 | +except ImportError: |
1143 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
1144 | + common_libs_dir = os.path.abspath( |
1145 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
1146 | + ) |
1147 | + if common_libs_dir not in sys.path: |
1148 | + sys.path.append(common_libs_dir) |
1149 | + from hw_health_lib import HWCheckArgumentParser |
1150 | + |
1151 | INPUT_FILE = '/var/lib/nagios/megacli.out' |
1152 | +ARGS = argparse.Namespace() |
1153 | |
1154 | |
1155 | -def handle_results(nlines, match, critical, errors, num_ldrive, num_pdrive, policy): |
1156 | +def handle_results( |
1157 | + nlines, match, critical, errors, num_ldrive, num_pdrive, policy |
1158 | +): |
1159 | if nlines == 0: |
1160 | raise WarnError('WARNING: controller not found') |
1161 | elif not match: |
1162 | @@ -17,7 +34,8 @@ def handle_results(nlines, match, critical, errors, num_ldrive, num_pdrive, poli |
1163 | if len(errors) > 0: |
1164 | msg = ', '.join([ |
1165 | '{}({})'.format(cnt, vars()[cnt]) |
1166 | - for cnt in ('failed_ld', 'wrg_policy_ld') if vars().get(cnt, 0) > 0 |
1167 | + for cnt in ('failed_ld', 'wrg_policy_ld') |
1168 | + if vars().get(cnt, 0) > 0 |
1169 | ]) |
1170 | msg += '; '.join(errors) |
1171 | else: |
1172 | @@ -30,7 +48,8 @@ def handle_results(nlines, match, critical, errors, num_ldrive, num_pdrive, poli |
1173 | if num_ldrive == 0: |
1174 | msg = 'OK: no disks configured for RAID' |
1175 | else: |
1176 | - msg = 'OK: Optimal, ldrives[{}], pdrives[{}]'.format(num_ldrive, num_pdrive) |
1177 | + msg = ('OK: Optimal, ldrives[{}], pdrives[{}]' |
1178 | + ''.format(num_ldrive, num_pdrive)) |
1179 | if policy: |
1180 | msg += ', policy[{}]'.format(policy) |
1181 | print(msg) |
1182 | @@ -57,7 +76,7 @@ def parse_output(policy=False): |
1183 | match = critical = False |
1184 | adapter_id = ldrive_id = None |
1185 | |
1186 | - with open(INPUT_FILE) as devices_raw: |
1187 | + with open(ARGS.input_file) as devices_raw: |
1188 | for line in devices_raw.readlines(): |
1189 | if len(line.strip()) and not line.startswith('Exit Code'): |
1190 | nlines += 1 |
1191 | @@ -107,12 +126,23 @@ def parse_output(policy=False): |
1192 | critical = True |
1193 | continue |
1194 | |
1195 | - handle_results(nlines, match, critical, errors, num_ldrive, num_pdrive, policy) |
1196 | + handle_results( |
1197 | + nlines, match, critical, errors, num_ldrive, num_pdrive, policy |
1198 | + ) |
1199 | + |
1200 | + |
1201 | +def parse_args(argv=None): |
1202 | + parser = HWCheckArgumentParser( |
1203 | + prog='check_megacli', |
1204 | + def_input_file=INPUT_FILE, |
1205 | + ) |
1206 | + return parser.parse_args(args=argv, namespace=ARGS) |
1207 | |
1208 | |
1209 | -def main(policy=False): |
1210 | +def main(argv, policy=False): |
1211 | + parse_args(argv) |
1212 | try_check(parse_output, policy) |
1213 | |
1214 | |
1215 | if __name__ == '__main__': |
1216 | - main() |
1217 | + main(sys.argv[1:]) |
1218 | diff --git a/src/files/megacli/check_megacli.sh b/src/files/megacli/cron_megacli.sh |
1219 | similarity index 50% |
1220 | rename from src/files/megacli/check_megacli.sh |
1221 | rename to src/files/megacli/cron_megacli.sh |
1222 | index cca1f9f..313cb96 100755 |
1223 | --- a/src/files/megacli/check_megacli.sh |
1224 | +++ b/src/files/megacli/cron_megacli.sh |
1225 | @@ -3,9 +3,20 @@ |
1226 | PATH="/snap/bin:/usr/local/bin:$PATH" |
1227 | FILE=/var/lib/nagios/megacli.out |
1228 | TMP_FILE=/tmp/megacli.out |
1229 | +RC_FILE=/var/lib/nagios/megacli.rc |
1230 | +CMD="megacli -LDInfo -LALL -aALL -NoLog" |
1231 | |
1232 | -megacli -LDInfo -LALL -aALL -NoLog 2>&1 > $TMP_FILE |
1233 | +if [ $# -gt 0 ]; then |
1234 | + echo "This program will cache the output of '$CMD' as follows" |
1235 | + echo " stdout + stderr -> $FILE" |
1236 | + echo " return code -> $RC_FILE" |
1237 | + echo |
1238 | + echo "It does not accept any option or argument" |
1239 | + exit 0 |
1240 | +fi |
1241 | + |
1242 | +$CMD 2>&1 > $TMP_FILE |
1243 | RC=$? |
1244 | -echo $RC > /var/lib/nagios/megacli.rc |
1245 | +echo $RC > $RC_FILE |
1246 | mv $TMP_FILE $FILE |
1247 | exit 0 |
1248 | diff --git a/src/files/nvme/check_nvme.py b/src/files/nvme/check_nvme.py |
1249 | index 7009c90..d4117c9 100755 |
1250 | --- a/src/files/nvme/check_nvme.py |
1251 | +++ b/src/files/nvme/check_nvme.py |
1252 | @@ -1,6 +1,8 @@ |
1253 | #!/usr/bin/env python3 |
1254 | # -*- coding: us-ascii -*- |
1255 | |
1256 | +import argparse |
1257 | +import sys |
1258 | import glob |
1259 | import re |
1260 | import subprocess |
1261 | @@ -50,5 +52,20 @@ def parse_output(): |
1262 | print('\n'.join(alloutputs)) |
1263 | |
1264 | |
1265 | -if __name__ == '__main__': |
1266 | +def parse_args(argv=None): |
1267 | + parser = argparse.ArgumentParser( |
1268 | + prog='check_nvme', |
1269 | + description=('this program reads the nvme smart-log and outputs an ' |
1270 | + 'appropriate Nagios status line'), |
1271 | + formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
1272 | + ) |
1273 | + return parser.parse_args(argv) |
1274 | + |
1275 | + |
1276 | +def main(argv): |
1277 | + parse_args(argv) |
1278 | try_check(parse_output) |
1279 | + |
1280 | + |
1281 | +if __name__ == '__main__': |
1282 | + main(sys.argv[1:]) |
1283 | diff --git a/src/files/sas2ircu/check_sas2ircu.py b/src/files/sas2ircu/check_sas2ircu.py |
1284 | index 7a4c861..b38e131 100755 |
1285 | --- a/src/files/sas2ircu/check_sas2ircu.py |
1286 | +++ b/src/files/sas2ircu/check_sas2ircu.py |
1287 | @@ -1,11 +1,26 @@ |
1288 | #!/usr/bin/env python3 |
1289 | # -*- coding: us-ascii -*- |
1290 | |
1291 | +import argparse |
1292 | +import os |
1293 | import re |
1294 | - |
1295 | +import sys |
1296 | from nagios_plugin3 import CriticalError, WarnError, try_check |
1297 | |
1298 | +try: |
1299 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
1300 | + from hw_health_lib import HWCheckArgumentParser |
1301 | +except ImportError: |
1302 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
1303 | + common_libs_dir = os.path.abspath( |
1304 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
1305 | + ) |
1306 | + if common_libs_dir not in sys.path: |
1307 | + sys.path.append(common_libs_dir) |
1308 | + from hw_health_lib import HWCheckArgumentParser |
1309 | + |
1310 | INPUT_FILE = '/var/lib/nagios/sas2ircu.out' |
1311 | +ARGS = argparse.Namespace() |
1312 | |
1313 | |
1314 | def parse_output(): |
1315 | @@ -22,7 +37,7 @@ def parse_output(): |
1316 | devices = {} |
1317 | device = [] |
1318 | critical = False |
1319 | - with open(INPUT_FILE) as devices_raw: |
1320 | + with open(ARGS.input_file) as devices_raw: |
1321 | for line in devices_raw.readlines(): |
1322 | line = line.rstrip() |
1323 | for cre in encl_slot_state_cre: |
1324 | @@ -49,9 +64,18 @@ def parse_output(): |
1325 | print('OK: {}'.format(msg)) |
1326 | |
1327 | |
1328 | -def main(): |
1329 | +def parse_args(argv=None): |
1330 | + parser = HWCheckArgumentParser( |
1331 | + prog='check_sas2ircu', |
1332 | + def_input_file=INPUT_FILE, |
1333 | + ) |
1334 | + return parser.parse_args(args=argv, namespace=ARGS) |
1335 | + |
1336 | + |
1337 | +def main(argv): |
1338 | + parse_args(argv) |
1339 | try_check(parse_output) |
1340 | |
1341 | |
1342 | if __name__ == '__main__': |
1343 | - main() |
1344 | + main(sys.argv[1:]) |
1345 | diff --git a/src/files/sas2ircu/check_sas2ircu.sh b/src/files/sas2ircu/cron_sas2ircu.sh |
1346 | similarity index 50% |
1347 | rename from src/files/sas2ircu/check_sas2ircu.sh |
1348 | rename to src/files/sas2ircu/cron_sas2ircu.sh |
1349 | index 23594a0..544df4d 100755 |
1350 | --- a/src/files/sas2ircu/check_sas2ircu.sh |
1351 | +++ b/src/files/sas2ircu/cron_sas2ircu.sh |
1352 | @@ -3,9 +3,20 @@ |
1353 | PATH="/snap/bin:/usr/local/bin:$PATH" |
1354 | FILE=/var/lib/nagios/sas2ircu.out |
1355 | TMP_FILE=/tmp/sas2ircu.out |
1356 | +RC_FILE=/var/lib/nagios/sas2ircu.rc |
1357 | +CMD="sas2ircu 0 DISPLAY" |
1358 | |
1359 | -sas2ircu 0 DISPLAY 2>&1 > $TMP_FILE |
1360 | +if [ $# -gt 0 ]; then |
1361 | + echo "This program will cache the output of 'sas2ircu 0 DISPLAY' as follows" |
1362 | + echo " stdout + stderr -> $FILE" |
1363 | + echo " return code -> $RC_FILE" |
1364 | + echo |
1365 | + echo "It does not accept any option or argument" |
1366 | + exit 0 |
1367 | +fi |
1368 | + |
1369 | +$CMD 2>&1 > $TMP_FILE |
1370 | RC=$? |
1371 | -echo $RC > /var/lib/nagios/sas2ircu.rc |
1372 | +echo $RC > $RC_FILE |
1373 | mv $TMP_FILE $FILE |
1374 | exit 0 |
1375 | diff --git a/src/files/sas3ircu/check_sas3ircu.py b/src/files/sas3ircu/check_sas3ircu.py |
1376 | index 3e49d84..d62a90f 100755 |
1377 | --- a/src/files/sas3ircu/check_sas3ircu.py |
1378 | +++ b/src/files/sas3ircu/check_sas3ircu.py |
1379 | @@ -1,11 +1,27 @@ |
1380 | #!/usr/bin/env python3 |
1381 | # -*- coding: us-ascii -*- |
1382 | |
1383 | +import argparse |
1384 | +import os |
1385 | import re |
1386 | +import sys |
1387 | from collections import defaultdict |
1388 | from nagios_plugin3 import CriticalError, WarnError, UnknownError, try_check |
1389 | |
1390 | +try: |
1391 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
1392 | + from hw_health_lib import HWCheckArgumentParser |
1393 | +except ImportError: |
1394 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
1395 | + common_libs_dir = os.path.abspath( |
1396 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
1397 | + ) |
1398 | + if common_libs_dir not in sys.path: |
1399 | + sys.path.append(common_libs_dir) |
1400 | + from hw_health_lib import HWCheckArgumentParser |
1401 | + |
1402 | INPUT_FILE = '/var/lib/nagios/sas3ircu.out' |
1403 | +ARGS = argparse.Namespace() |
1404 | |
1405 | |
1406 | def parse_output(input_file): |
1407 | @@ -51,10 +67,10 @@ def parse_output(input_file): |
1408 | enclosure = _kv_parse(sections['encl']) |
1409 | |
1410 | return { |
1411 | - 'controller': controller, |
1412 | - 'volumes': volumes, |
1413 | - 'disks': topology, |
1414 | - 'enclosure': enclosure |
1415 | + 'controller': controller, |
1416 | + 'volumes': volumes, |
1417 | + 'disks': topology, |
1418 | + 'enclosure': enclosure, |
1419 | } |
1420 | |
1421 | |
1422 | @@ -183,10 +199,19 @@ class Status: |
1423 | print(self._status) |
1424 | |
1425 | |
1426 | -def main(): |
1427 | - data = parse_output(INPUT_FILE) |
1428 | +def parse_args(argv=None): |
1429 | + parser = HWCheckArgumentParser( |
1430 | + prog='check_sas3ircu', |
1431 | + def_input_file=INPUT_FILE, |
1432 | + ) |
1433 | + return parser.parse_args(args=argv, namespace=ARGS) |
1434 | + |
1435 | + |
1436 | +def main(argv=None): |
1437 | + parse_args(argv) |
1438 | + data = parse_output(ARGS.input_file) |
1439 | try_check(eval_status, data) |
1440 | |
1441 | |
1442 | if __name__ == '__main__': |
1443 | - main() |
1444 | + main(sys.argv[1:]) |
1445 | diff --git a/src/files/sas3ircu/check_sas3ircu.sh b/src/files/sas3ircu/cron_sas3ircu.sh |
1446 | similarity index 50% |
1447 | rename from src/files/sas3ircu/check_sas3ircu.sh |
1448 | rename to src/files/sas3ircu/cron_sas3ircu.sh |
1449 | index 0b26e96..a73c0f1 100755 |
1450 | --- a/src/files/sas3ircu/check_sas3ircu.sh |
1451 | +++ b/src/files/sas3ircu/cron_sas3ircu.sh |
1452 | @@ -3,9 +3,20 @@ |
1453 | PATH="/snap/bin:/usr/local/bin:$PATH" |
1454 | FILE=/var/lib/nagios/sas3ircu.out |
1455 | TMP_FILE=/tmp/sas3ircu.out |
1456 | +RC_FILE=/var/lib/nagios/sas3ircu.rc |
1457 | +CMD="sas3ircu 0 DISPLAY" |
1458 | |
1459 | -sas3ircu 0 DISPLAY 2>&1 > $TMP_FILE |
1460 | +if [ $# -gt 0 ]; then |
1461 | + echo "This program will cache the output of 'sas3ircu 0 DISPLAY' as follows" |
1462 | + echo " stdout + stderr -> $FILE" |
1463 | + echo " return code -> $RC_FILE" |
1464 | + echo |
1465 | + echo "It does not accept any option or argument" |
1466 | + exit 0 |
1467 | +fi |
1468 | + |
1469 | +$CMD 2>&1 > $TMP_FILE |
1470 | RC=$? |
1471 | -echo $RC > /var/lib/nagios/sas3ircu.rc |
1472 | +echo $RC > $RC_FILE |
1473 | mv $TMP_FILE $FILE |
1474 | exit 0 |
1475 | diff --git a/src/files/ssacli/cron_ssacli.py b/src/files/ssacli/cron_ssacli.py |
1476 | new file mode 100755 |
1477 | index 0000000..94c7ef7 |
1478 | --- /dev/null |
1479 | +++ b/src/files/ssacli/cron_ssacli.py |
1480 | @@ -0,0 +1,178 @@ |
1481 | +#!/usr/bin/env python3 |
1482 | +# ----------------------------------------------- |
1483 | +# This file is juju managed |
1484 | +# ----------------------------------------------- |
1485 | + |
1486 | +# Copyright (C) 2008-2013 James Troup <james.troup@canonical.com> |
1487 | +# Copyright (c) 2020 Rewrite: Drew Freiberger <drew.freiberger@canonical.com> |
1488 | + |
1489 | +# ssacli lives in either /usr/sbin or /sbin depending on version of the |
1490 | +# package and cron jobs don't get a sane $PATH |
1491 | +import os |
1492 | +import re |
1493 | +import subprocess |
1494 | +import sys |
1495 | + |
1496 | +try: |
1497 | + # shared lib will be under $NAGIOS_PLUGIN_DIR at runtime |
1498 | + from hw_health_lib import ( |
1499 | + read_ignore_file, |
1500 | + write_ignore_file, |
1501 | + ignore, |
1502 | + get_hp_controller_slots, |
1503 | + HPArgumentParser, |
1504 | + ) |
1505 | +except ImportError: |
1506 | + # shared lib will be under $CHARM_SOURCE_DIR/files/common during unit tests |
1507 | + common_libs_dir = os.path.abspath( |
1508 | + os.path.join(os.path.dirname(__file__), '..', 'common') |
1509 | + ) |
1510 | + if common_libs_dir not in sys.path: |
1511 | + sys.path.append(common_libs_dir) |
1512 | + from hw_health_lib import ( |
1513 | + read_ignore_file, |
1514 | + write_ignore_file, |
1515 | + ignore, |
1516 | + get_hp_controller_slots, |
1517 | + HPArgumentParser, |
1518 | + ) |
1519 | + |
1520 | +SSACLI_BIN = '/opt/smartstorageadmin/ssacli/bin/ssacli' |
1521 | +OUTPUT_FILE = '/var/lib/nagios/ssacli.out' |
1522 | +EXCLUDE_FILE = '/etc/nagios/ssacli.exclude.yaml' |
1523 | + |
1524 | + |
1525 | +def parse_args(argv=None): |
1526 | + parser = HPArgumentParser( |
1527 | + prog='cron_ssacli', |
1528 | + def_write_file=OUTPUT_FILE, |
1529 | + def_exclude_file=EXCLUDE_FILE |
1530 | + ) |
1531 | + return parser.parse_args(args=argv) |
1532 | + |
1533 | + |
1534 | +def check_array(slot): |
1535 | + """ |
1536 | + RAID arrays [hpacucli] |
1537 | + |
1538 | + logicaldrive 1 (68.3 GB, RAID RAID 1+0): OK |
1539 | + logicaldrive 1 (2.8 TB, RAID RAID 6 (ADG)): OK |
1540 | + physicaldrive 1:1 (box 1:bay 1, 72 GB): OK |
1541 | + physicaldrive 1:2 (box 1:bay 2, 72 GB): OK |
1542 | + """ |
1543 | + if os.path.isfile('/etc/nagios/skip-cat-hp-array.txt'): |
1544 | + return |
1545 | + |
1546 | + cmd = ( |
1547 | + '{ssacli} ctrl slot={slot} ld all show status; ' |
1548 | + '{ssacli} ctrl slot={slot} pd all show status'.format(ssacli=SSACLI_BIN, slot=slot) |
1549 | + ) |
1550 | + try: |
1551 | + result = subprocess.check_output(cmd, shell=True).decode('UTF-8') |
1552 | + return _parse_array_output(result) |
1553 | + except subprocess.CalledProcessError as e: |
1554 | + return ( |
1555 | + 'UNKNOWN Call to ssacli to show ld/pd info failed. ' |
1556 | + 'Array Slot {} - Return Code {} - {}' |
1557 | + ''.format(slot, e.returncode, e.output) |
1558 | + ) |
1559 | + |
1560 | + |
1561 | +def _parse_array_output(output): |
1562 | + innocuous_errors = re.compile(r'^Error: The specified (device|controller) ' |
1563 | + 'does not have any (logical|physical)') |
1564 | + drive_status_line = re.compile(r'^\s*(logicaldrive|physicaldrive)') |
1565 | + ignore_file = '/etc/nagios/ignores/ignores-cat-hp-array.txt' |
1566 | + ignores = read_ignore_file(ignore_file) |
1567 | + |
1568 | + for line in output.splitlines(): |
1569 | + line = line.strip() |
1570 | + if not line or innocuous_errors.search(line) or not drive_status_line.search(line): |
1571 | + continue |
1572 | + (drivetype, number) = line.split()[:2] |
1573 | + status = line.split('):')[1].lstrip().upper() |
1574 | + if status != 'OK': |
1575 | + err = '{} {} is "{}"'.format(drivetype, number, status) |
1576 | + if not ignore(err, ignores): |
1577 | + return err |
1578 | + write_ignore_file(ignores, ignore_file) |
1579 | + |
1580 | + |
1581 | +def check_controller(slot): |
1582 | + """ |
1583 | + RAID controllers [hpacucli] |
1584 | + |
1585 | + Smart Array 6400 in Slot 1 |
1586 | + Controller Status: OK |
1587 | + Cache Status: OK |
1588 | + Battery Status: Failed (Replace Batteries) |
1589 | + """ |
1590 | + if os.path.isfile('/etc/nagios/skip-cat-hp-controller.txt'): |
1591 | + return |
1592 | + |
1593 | + cmd = '{ssacli} ctrl slot={slot} show status'.format(ssacli=SSACLI_BIN, slot=slot) |
1594 | + try: |
1595 | + result = subprocess.check_output(cmd, shell=True).decode('UTF-8') |
1596 | + return _parse_controller_output(result) |
1597 | + except subprocess.CalledProcessError as e: |
1598 | + return ( |
1599 | + 'UNKNOWN Call to ssacli to show ld/pd info failed. ' |
1600 | + 'Array Slot {} - Return Code {} - {}' |
1601 | + ''.format(slot, e.returncode, e.output) |
1602 | + ) |
1603 | + |
1604 | + |
1605 | +def _parse_controller_output(output): |
1606 | + controller = "Unknown" |
1607 | + ignore_file = '/etc/nagios/ignores/ignores-cat-hp-controller.txt' |
1608 | + ignores = read_ignore_file(ignore_file) |
1609 | + for line in output.splitlines(): |
1610 | + line = line.strip() |
1611 | + if not line: |
1612 | + continue |
1613 | + if line.startswith("Smart Array") or line.startswith("Smart HBA"): |
1614 | + controller = line |
1615 | + else: |
1616 | + if ":" not in line: |
1617 | + err = "UNKNOWN Unrecognised line for controller '%s'" % (line) |
1618 | + return err |
1619 | + (part, status) = line.split(":") |
1620 | + status = status.strip().upper() |
1621 | + if status != "OK": |
1622 | + err = "%s on %s is '%s'" % (part, controller, status) |
1623 | + if not ignore(err, ignores): |
1624 | + return err |
1625 | + write_ignore_file(ignores, ignore_file) |
1626 | + |
1627 | + |
1628 | +def main(): |
1629 | + global ARGS |
1630 | + ARGS = parse_args() |
1631 | + |
1632 | + slots = get_hp_controller_slots() |
1633 | + if not slots: |
1634 | + msg = 'OK: no controller/array found to check' |
1635 | + exit = 0 |
1636 | + |
1637 | + errors = [] |
1638 | + for slot in slots: |
1639 | + errors += check_controller(slot) |
1640 | + errors += check_array(slot) |
1641 | + |
1642 | + if len(errors) > 0: |
1643 | + msg = 'CRIT {} error(s): {}'.format(len(errors), ' - '.join(errors)) |
1644 | + exit = 2 |
1645 | + else: |
1646 | + msg = 'OK No errors found' |
1647 | + exit = 0 |
1648 | + |
1649 | + if ARGS.write: |
1650 | + with open(ARGS.write, 'w') as f: |
1651 | + f.write(msg) |
1652 | + else: |
1653 | + print(msg) |
1654 | + sys.exit(exit) |
1655 | + |
1656 | + |
1657 | +if __name__ == '__main__': |
1658 | + main() |
1659 | diff --git a/src/lib/hwhealth/discovery/lshw.py b/src/lib/hwhealth/discovery/lshw.py |
1660 | index 7a2af8f..c2f6653 100644 |
1661 | --- a/src/lib/hwhealth/discovery/lshw.py |
1662 | +++ b/src/lib/hwhealth/discovery/lshw.py |
1663 | @@ -18,7 +18,8 @@ class Hardware(object): |
1664 | hwinfo = json.load(fd) |
1665 | else: |
1666 | output = subprocess.check_output(['lshw', '-json']) |
1667 | - # Note(aluria): py35 does not support extra args on subprocess.check_output |
1668 | + # Note(aluria): py35 does not support extra args on |
1669 | + # subprocess.check_output |
1670 | output_str = output.decode(errors='ignore') |
1671 | hwinfo = json.loads(output_str) |
1672 | with open(self.__filename, 'w') as fd: |
1673 | @@ -26,13 +27,16 @@ class Hardware(object): |
1674 | |
1675 | return hwinfo |
1676 | except PermissionError as error: |
1677 | - hookenv.log('lshw io error: {}'.format(error), hookenv.ERROR) |
1678 | + hookenv.log('lshw io error: {}'.format(error), |
1679 | + hookenv.ERROR) |
1680 | return {} |
1681 | except subprocess.CalledProcessError as error: |
1682 | - hookenv.log('lshw subprocess error: {}'.format(error), hookenv.ERROR) |
1683 | + hookenv.log('lshw subprocess error: {}'.format(error), |
1684 | + hookenv.ERROR) |
1685 | return {} |
1686 | except json.JSONDecodeError as error: |
1687 | - hookenv.log('lshw json error: {}'.format(error), hookenv.ERROR) |
1688 | + hookenv.log('lshw json error: {}'.format(error), |
1689 | + hookenv.ERROR) |
1690 | return {} |
1691 | |
1692 | @property |
1693 | @@ -64,7 +68,9 @@ class Hardware(object): |
1694 | bridge_class_items = [] |
1695 | for item in bridge_item.get('children', [{}]): |
1696 | if item.get('class', '') == 'bridge': |
1697 | - bridge_class_items.extend(self._get_inspect_bridges(item, bridge_class)) |
1698 | + bridge_class_items.extend( |
1699 | + self._get_inspect_bridges(item, bridge_class) |
1700 | + ) |
1701 | elif item.get('class', '') == bridge_class: |
1702 | bridge_class_items.append(item) |
1703 | return bridge_class_items |
1704 | @@ -74,8 +80,8 @@ class Hardware(object): |
1705 | """returns all the storage classes. |
1706 | |
1707 | Function is used by get_storage_class_info and get_disk_class_info. |
1708 | - The aim of this function is to easily parse products to detect which tool(s) |
1709 | - need to be used. |
1710 | + The aim of this function is to easily parse products to detect which |
1711 | + tool(s) need to be used. |
1712 | """ |
1713 | storage = [] |
1714 | # system -> bus -> bridge -> storage |
1715 | @@ -87,7 +93,9 @@ class Hardware(object): |
1716 | continue |
1717 | for item in bridge.get('children', [{}]): |
1718 | if item.get('class', '') == 'bridge': |
1719 | - storage.extend(self._get_inspect_bridges(item, 'storage')) |
1720 | + storage.extend( |
1721 | + self._get_inspect_bridges(item, 'storage') |
1722 | + ) |
1723 | elif item.get('class', '') == 'storage': |
1724 | storage.append(item) |
1725 | return storage |
1726 | @@ -96,16 +104,18 @@ class Hardware(object): |
1727 | def get_storage_class_info(self): |
1728 | """returns a list of storage controllers |
1729 | |
1730 | - Storage controllers have the following keys: vendor, product name, businfo and linux driver |
1731 | - The aim of the function is to easily parse products to detect which tool(s) |
1732 | - need to be used. |
1733 | + Storage controllers have the following keys: vendor, product name, |
1734 | + businfo and linux driver The aim of the function is to easily parse |
1735 | + products to detect which tool(s) need to be used. |
1736 | """ |
1737 | keys = 'vendor product businfo'.split() |
1738 | config_keys = ['driver'] |
1739 | storage = [] |
1740 | for item in self._get_storage_class: |
1741 | storage_item = dict([(k, item.get(k)) for k in keys]) |
1742 | - storage_item.update(dict([(k, item.get('configuration', {}).get(k)) for k in config_keys])) |
1743 | + storage_item.update( |
1744 | + dict([(k, item.get('configuration', {}).get(k)) |
1745 | + for k in config_keys])) |
1746 | storage_item.update({'has_children': 'children' in item}) |
1747 | storage.append(storage_item) |
1748 | |
1749 | @@ -115,12 +125,13 @@ class Hardware(object): |
1750 | def get_disk_class_info(self): |
1751 | """returns a list of storage devices |
1752 | |
1753 | - Storage devices have the following keys: product name, serial number, PCI bus info, |
1754 | - physical device and ID, size (capacity), logicalname (/dev/sdX) and storage parent |
1755 | + Storage devices have the following keys: product name, serial number, |
1756 | + PCI bus info, physical device and ID, size (capacity), logicalname |
1757 | + (/dev/sdX) and storage parent |
1758 | (ie. RAID controller) |
1759 | |
1760 | - The aim of the function is to easily parse products to detect which tool(s) |
1761 | - need to be used. |
1762 | + The aim of the function is to easily parse products to detect which |
1763 | + tool(s) need to be used. |
1764 | """ |
1765 | keys = 'product serial businfo physid dev size logicalname'.split() |
1766 | disks = [] |
1767 | @@ -139,8 +150,8 @@ class Hardware(object): |
1768 | vendor, product name, businfo, logicalname (eno1...), serial number, |
1769 | linux driver and driver version, NIC firmware version and speed |
1770 | |
1771 | - The aim of the function is to easily parse products to detect which tool(s) |
1772 | - need to be used. |
1773 | + The aim of the function is to easily parse products to detect which |
1774 | + tool(s) need to be used. |
1775 | """ |
1776 | keys = 'vendor product businfo logicalname serial'.split() |
1777 | config_keys = 'driver driverversion firmware speed'.split() |
1778 | @@ -161,7 +172,9 @@ class Hardware(object): |
1779 | nics_filtered = [] |
1780 | for nic in nics: |
1781 | nic_item = dict([(k, nic.get(k)) for k in keys]) |
1782 | - nic_item.update(dict([(k, nic.get('configuration', {}).get(k)) for k in config_keys])) |
1783 | + nic_item.update( |
1784 | + dict([(k, nic.get('configuration', {}).get(k)) |
1785 | + for k in config_keys])) |
1786 | nics_filtered.append(nic_item) |
1787 | return nics_filtered |
1788 | |
1789 | @@ -169,22 +182,26 @@ class Hardware(object): |
1790 | def formatted_system_info(self): |
1791 | ctxt = self.get_system |
1792 | return ( |
1793 | - '{description}: vendor[{vendor}], product_name[{product}], version[{version}]' |
1794 | - ', serial[{serial}], hostname[{hostname}]').format(**ctxt) |
1795 | + '{description}: vendor[{vendor}], product_name[{product}], ' |
1796 | + 'version[{version}], serial[{serial}], hostname[{hostname}]' |
1797 | + ).format(**ctxt) |
1798 | |
1799 | @property |
1800 | def formatted_motherboard_info(self): |
1801 | return '\n'.join([ |
1802 | - '{description}: vendor[{vendor}], product_name[{product}], version[{version}]' |
1803 | - ', serial[{serial}]'.format(**ctxt) for ctxt in self.get_motherboard]) |
1804 | + '{description}: vendor[{vendor}], product_name[{product}], ' |
1805 | + 'version[{version}], serial[{serial}]'.format(**ctxt) |
1806 | + for ctxt in self.get_motherboard]) |
1807 | |
1808 | @property |
1809 | def formatted_storage_class_info(self): |
1810 | - LINE = 'driver[{driver}], businfo[{businfo}], has_children[{has_children}]' |
1811 | + LINE = ('driver[{driver}], businfo[{businfo}], ' |
1812 | + 'has_children[{has_children}]') |
1813 | ctxts = [] |
1814 | for ctxt in self.get_storage_class_info: |
1815 | if ctxt.get('vendor') and ctxt.get('product'): |
1816 | - tmpl = 'Storage class: vendor[{vendor}], product_name[{product}], ' + LINE |
1817 | + tmpl = ('Storage class: vendor[{vendor}],' |
1818 | + 'product_name[{product}], ') + LINE |
1819 | else: |
1820 | tmpl = 'Storage class: {}'.format(LINE) |
1821 | ctxts.append(tmpl.format(**ctxt)) |
1822 | @@ -195,9 +212,10 @@ class Hardware(object): |
1823 | @property |
1824 | def formatted_disk_class_info(self): |
1825 | return '\n'.join([ |
1826 | - 'Disk class: ld[{logicalname}], dev[{dev}], physid[{physid}], businfo[{businfo}]' |
1827 | - ', product_name[{product}], serial[{serial}], size[{size}]' |
1828 | - ', storage_parent[{storage_parent}]'.format(**ctxt) for ctxt in self.get_disk_class_info |
1829 | + 'Disk class: ld[{logicalname}], dev[{dev}], physid[{physid}], ' |
1830 | + 'businfo[{businfo}], product_name[{product}], serial[{serial}], ' |
1831 | + 'size[{size}], storage_parent[{storage_parent}]'.format(**ctxt) |
1832 | + for ctxt in self.get_disk_class_info |
1833 | ]) |
1834 | |
1835 | @property |
1836 | @@ -206,7 +224,8 @@ class Hardware(object): |
1837 | 'NIC: iface[{logicalname}], businfo[{businfo}], vendor[{vendor}]' |
1838 | ', product_name[{product}], firmware[{firmware}], driver[{driver}' |
1839 | ', {driverversion}], serial[{serial}]' |
1840 | - ', speed[{speed}]'.format(**ctxt) for ctxt in self.get_network_class_info |
1841 | + ', speed[{speed}]'.format(**ctxt) |
1842 | + for ctxt in self.get_network_class_info |
1843 | ]) |
1844 | |
1845 | |
1846 | diff --git a/src/lib/hwhealth/discovery/supported_vendors.py b/src/lib/hwhealth/discovery/supported_vendors.py |
1847 | index 8dc588e..ef37a5a 100644 |
1848 | --- a/src/lib/hwhealth/discovery/supported_vendors.py |
1849 | +++ b/src/lib/hwhealth/discovery/supported_vendors.py |
1850 | @@ -1,12 +1,12 @@ |
1851 | # -*- coding: us-ascii -*- |
1852 | from hwhealth import tools |
1853 | |
1854 | -SUPPORTED_VENDORS = { |
1855 | +SUPPORTED_STORAGE = { |
1856 | 'LSI Logic / Symbios Logic': { |
1857 | 'SAS2308 PCI-Express Fusion-MPT SAS-2': tools.Sas2Ircu, |
1858 | 'SAS3008 PCI-Express Fusion-MPT SAS-3': tools.Sas3Ircu, |
1859 | 'MegaRAID SAS-3 3108 [Invader]': tools.MegaCLI, |
1860 | - }, |
1861 | + }, |
1862 | # 'Mellanox Technologies': { |
1863 | # 'MT27710 Family [ConnectX-4 Lx]': lambda: 'mlxconfig', |
1864 | # 'MT27700 Family [ConnectX-4]': lambda: 'mlxconfig', |
1865 | @@ -18,9 +18,21 @@ SUPPORTED_VENDORS = { |
1866 | 'NVMe SSD Controller SM961/PM961': tools.Nvme, |
1867 | 'NVMe SSD Controller 172Xa/172Xb': tools.Nvme, |
1868 | }, |
1869 | - # 'Hewlett-Packard Company': { |
1870 | - # 'Smart Array Gen9 Controllers': tools.HP, |
1871 | - # } |
1872 | + 'Hewlett-Packard Company': { |
1873 | + 'Smart Array Gen9 Controllers': tools.SsaCli, |
1874 | + 'Smart Storage PQI 12G SAS/PCIe 3': tools.ILOrest, |
1875 | + }, |
1876 | +} |
1877 | + |
1878 | +SUPPORTED_SYSTEMS = { |
1879 | + 'HPE': { |
1880 | + # tools.HpLog, # not sure if this works on gen10+ |
1881 | + tools.ILOrest, |
1882 | + }, |
1883 | + 'HP': { |
1884 | + tools.HpLog, |
1885 | + tools.SsaCli, |
1886 | + } |
1887 | } |
1888 | |
1889 | SUPPORTED_DRIVERS = { |
1890 | diff --git a/src/lib/hwhealth/hwdiscovery.py b/src/lib/hwhealth/hwdiscovery.py |
1891 | index f63a025..c23ee13 100644 |
1892 | --- a/src/lib/hwhealth/hwdiscovery.py |
1893 | +++ b/src/lib/hwhealth/hwdiscovery.py |
1894 | @@ -1,28 +1,40 @@ |
1895 | # -*- coding: us-ascii -*- |
1896 | +"""Discover hardware and determine tools selection.""" |
1897 | import os |
1898 | import re |
1899 | import subprocess |
1900 | +from itertools import chain |
1901 | |
1902 | from hwhealth import tools |
1903 | from hwhealth.discovery.lshw import Hardware |
1904 | -from hwhealth.discovery.supported_vendors import SUPPORTED_VENDORS, SUPPORTED_DRIVERS |
1905 | +from hwhealth.discovery.supported_vendors import ( |
1906 | + SUPPORTED_STORAGE, |
1907 | + SUPPORTED_DRIVERS, |
1908 | + SUPPORTED_SYSTEMS |
1909 | +) |
1910 | |
1911 | from charmhelpers.core import hookenv |
1912 | |
1913 | |
1914 | def get_tools(manufacturer='auto'): |
1915 | + """Return list of tool classes relevent for the current hardware. |
1916 | + |
1917 | + In testing, we set manufacturer = test in order to test all tools classes. |
1918 | + Filtering added for known bad tools that don't work on all series combinations. |
1919 | + """ |
1920 | if manufacturer == 'test': |
1921 | # Return all possible tools to aid testing |
1922 | - return [ |
1923 | - # tools.HPE(), # NotImplementedYet |
1924 | - # tools.HP(), # NotImplementedYet |
1925 | - tools.Mdadm(), |
1926 | - tools.MegaCLI(), |
1927 | - tools.Nvme(), |
1928 | - tools.Ipmi(), |
1929 | - tools.Sas2Ircu(), |
1930 | - tools.Sas3Ircu(), |
1931 | - ] |
1932 | + storage_tools = {tool |
1933 | + for vendor in SUPPORTED_STORAGE.values() |
1934 | + for tool in vendor.values()} |
1935 | + # Some system vendors have multiple tools, have to iterate sets |
1936 | + system_tools = set(chain.from_iterable(SUPPORTED_SYSTEMS.values())) |
1937 | + driver_tools = set(SUPPORTED_DRIVERS.values()) |
1938 | + all_tools = storage_tools | system_tools | driver_tools |
1939 | + series_filtered_tools = set( |
1940 | + tool for tool in all_tools if tool.is_series_supported() |
1941 | + ) |
1942 | + return series_filtered_tools |
1943 | elif manufacturer == 'auto': |
1944 | return _get_tools() |
1945 | else: |
1946 | @@ -30,22 +42,28 @@ def get_tools(manufacturer='auto'): |
1947 | |
1948 | |
1949 | def _get_tools(): |
1950 | + """Return list of tool classes relevent for the current hardware.""" |
1951 | hwinfo = Hardware() |
1952 | toolset = set() |
1953 | for storage in hwinfo.get_storage_class_info: |
1954 | - if storage.get('vendor'): |
1955 | - if storage.get('product') in SUPPORTED_VENDORS.get(storage['vendor'], []): |
1956 | - toolset.add(SUPPORTED_VENDORS[storage['vendor']][storage['product']]) |
1957 | - else: |
1958 | - hookenv.log('Product not supported: [{vendor}][{product}]'.format(**storage), |
1959 | - hookenv.DEBUG) |
1960 | + vendor = storage.get('vendor') |
1961 | + product = storage.get('product') |
1962 | + tool = SUPPORTED_STORAGE.get(vendor, {}).get(product) |
1963 | + if isinstance(tool, list) or isinstance(tool, set): |
1964 | + toolset.update(tool) |
1965 | + elif tool: |
1966 | + toolset.add(tool) |
1967 | + else: |
1968 | + hookenv.log('Product not supported: [{}][{}]' |
1969 | + ''.format(vendor, product), hookenv.DEBUG) |
1970 | |
1971 | driver = storage.get('driver') |
1972 | if driver: |
1973 | if driver in SUPPORTED_DRIVERS: |
1974 | toolset.add(SUPPORTED_DRIVERS[driver]) |
1975 | continue |
1976 | - hookenv.log('Driver not supported: {}'.format(driver), hookenv.DEBUG) |
1977 | + hookenv.log('Driver not supported: {}'.format(driver), |
1978 | + hookenv.DEBUG) |
1979 | |
1980 | # SW RAID? |
1981 | if _supports_mdadm(): |
1982 | @@ -54,22 +72,37 @@ def _get_tools(): |
1983 | if hookenv.config('enable_ipmi'): |
1984 | toolset.add(tools.Ipmi) |
1985 | |
1986 | - executed_toolset = set([tool() for tool in toolset]) |
1987 | + system_vendor = hwinfo.get_system.get('vendor') |
1988 | + tool = SUPPORTED_SYSTEMS.get(system_vendor) |
1989 | + if isinstance(tool, list) or isinstance(tool, set): |
1990 | + toolset.update(tool) |
1991 | + elif tool: |
1992 | + toolset.add(tool) |
1993 | + else: |
1994 | + hookenv.log('System vendor not supported: {}'.format(system_vendor), |
1995 | + hookenv.DEBUG) |
1996 | + |
1997 | + executed_toolset = set([tool() for tool in toolset if tool.is_series_supported]) |
1998 | return executed_toolset |
1999 | |
2000 | |
2001 | def _supports_mdadm(): |
2002 | - """scans for mdadm devices and returns True when the first one is found (otherwise, it returns False) |
2003 | + """Detect known mdadm supported storage devices. |
2004 | + |
2005 | + Returns True when the first one is found; otherwise, it returns False) |
2006 | """ |
2007 | if os.path.exists('/sbin/mdadm'): |
2008 | try: |
2009 | - devices_raw = subprocess.check_output(['/sbin/mdadm', '--detail', '--scan']) |
2010 | + devices_raw = subprocess.check_output( |
2011 | + ['/sbin/mdadm', '--detail', '--scan'] |
2012 | + ) |
2013 | devices_re = re.compile(r'^ARRAY\s+(\S+) ') |
2014 | for line in devices_raw.splitlines(): |
2015 | line = line.decode().strip() |
2016 | raid_dev = devices_re.search(line) |
2017 | if raid_dev: |
2018 | - hookenv.log("Found md raid array {}".format(raid_dev.group(1))) |
2019 | + hookenv.log("Found md raid array {}" |
2020 | + "".format(raid_dev.group(1))) |
2021 | return True |
2022 | except Exception as e: |
2023 | hookenv.log("mdadm scan failed with {}".format(e)) |
2024 | diff --git a/src/lib/hwhealth/tools.py b/src/lib/hwhealth/tools.py |
2025 | index 970ac7d..e90cb6b 100644 |
2026 | --- a/src/lib/hwhealth/tools.py |
2027 | +++ b/src/lib/hwhealth/tools.py |
2028 | @@ -16,7 +16,9 @@ from tempfile import TemporaryDirectory |
2029 | from charmhelpers import fetch |
2030 | from charmhelpers.contrib.charmsupport import nrpe |
2031 | from charmhelpers.core import hookenv |
2032 | +from charmhelpers.core.host import lsb_release |
2033 | from charmhelpers.core.templating import render |
2034 | +from charms import apt |
2035 | |
2036 | |
2037 | class JujuResourceNotFound(Exception): |
2038 | @@ -61,6 +63,7 @@ class Tool(): |
2039 | CRONJOB_SCRIPT_MODE = 0o100755 |
2040 | CRONJOB_SCRIPT_UID = 0 |
2041 | CRONJOB_SCRIPT_GID = 0 |
2042 | + CRONJOB_OUTPUT_DIR = '/var/lib/nagios' |
2043 | NRPE_PLUGINS_DIR = '/usr/local/lib/nagios/plugins' |
2044 | NRPE_PLUGINS_MODE = 0o100755 |
2045 | NRPE_PLUGINS_UID = 0 |
2046 | @@ -69,27 +72,47 @@ class Tool(): |
2047 | SUDOERS_MODE = 0o100440 |
2048 | SUDOERS_UID = 0 |
2049 | SUDOERS_GID = 0 |
2050 | + SUPPORTED_SERIES = ['xenial', 'bionic', 'focal'] |
2051 | TOOLS_DIR = '/usr/local/bin' |
2052 | TOOLS_MODE = 0o100755 |
2053 | TOOLS_UID = 0 |
2054 | TOOLS_GID = 0 |
2055 | |
2056 | - def __init__(self, shortname, nrpe_opts=''): |
2057 | + def __init__( |
2058 | + self, |
2059 | + shortname=None, |
2060 | + nrpe_opts='', |
2061 | + nrpe_script=None, |
2062 | + nrpe_script_dir=None, |
2063 | + cron_script=None, |
2064 | + cron_script_dir=None, |
2065 | + cron_script_args=None, |
2066 | + ): |
2067 | self._nagios_hostname = nrpe.get_nagios_hostname() |
2068 | self._nrpe_opts = nrpe_opts |
2069 | - self._shortname = shortname |
2070 | + self._shortname = (shortname if shortname |
2071 | + else self.__class__.__name__.lower()) |
2072 | self._files_dir = os.path.join(hookenv.charm_dir(), |
2073 | 'files', |
2074 | self._shortname) |
2075 | + self._nrpe_script = (nrpe_script if nrpe_script |
2076 | + else 'check_{}.py'.format(self._shortname)) |
2077 | + self._nrpe_script_dir = (nrpe_script_dir if nrpe_script_dir |
2078 | + else self._files_dir) |
2079 | + self._cron_script = (cron_script if cron_script |
2080 | + else 'cron_{}.py'.format(self._shortname)) |
2081 | + self._cron_script_dir = (cron_script_dir if cron_script_dir |
2082 | + else self._files_dir) |
2083 | self._templates_dir = os.path.join(hookenv.charm_dir(), |
2084 | 'templates', |
2085 | self._shortname) |
2086 | - self._nrpe_script = 'check_{}'.format(shortname) |
2087 | - self._cronjob_script = None |
2088 | - self._cronjob_script_args = None |
2089 | + self._common_libs_dir = os.path.join(hookenv.charm_dir(), |
2090 | + 'files/common') |
2091 | + self._common_libs = ['hw_health_lib.py'] |
2092 | + self._cron_script_args = cron_script_args |
2093 | |
2094 | def _install_nrpe_plugin(self): |
2095 | - src = os.path.join(self._files_dir, self._nrpe_script) |
2096 | + src = os.path.join(self._nrpe_script_dir, self._nrpe_script) |
2097 | dst = shutil.copy(src, self.NRPE_PLUGINS_DIR) |
2098 | os.chmod(dst, self.NRPE_PLUGINS_MODE) |
2099 | os.chown(dst, uid=self.NRPE_PLUGINS_UID, gid=self.NRPE_PLUGINS_GID) |
2100 | @@ -100,6 +123,21 @@ class Tool(): |
2101 | ) |
2102 | return dst |
2103 | |
2104 | + def _install_common_libs(self): |
2105 | + dsts = [] |
2106 | + for lib in self._common_libs: |
2107 | + src = os.path.join(self._common_libs_dir, lib) |
2108 | + dst = shutil.copy(src, self.NRPE_PLUGINS_DIR) |
2109 | + os.chmod(dst, self.NRPE_PLUGINS_MODE) |
2110 | + os.chown(dst, uid=self.NRPE_PLUGINS_UID, gid=self.NRPE_PLUGINS_GID) |
2111 | + hookenv.log( |
2112 | + 'Common Library {} for tool [{}] installed at as {}' |
2113 | + ''.format(lib, self._shortname, dst), |
2114 | + hookenv.DEBUG |
2115 | + ) |
2116 | + dsts.append(dst) |
2117 | + return dsts |
2118 | + |
2119 | def _remove_nrpe_plugin(self): |
2120 | plugin_path = os.path.join(self.NRPE_PLUGINS_DIR, self._nrpe_script) |
2121 | if not os.path.exists(plugin_path): |
2122 | @@ -111,10 +149,10 @@ class Tool(): |
2123 | ) |
2124 | |
2125 | def configure_nrpe_check(self, nrpe_setup): |
2126 | - cmd = ' '.join([self._nrpe_script, self._nrpe_opts]) |
2127 | + cmd = ' '.join([os.path.basename(self._nrpe_script), self._nrpe_opts]) |
2128 | nrpe_setup.add_check( |
2129 | shortname=self._shortname, |
2130 | - description='{} Hardware Health', |
2131 | + description='{} Hardware Health'.format(self._shortname), |
2132 | check_cmd=cmd, |
2133 | ) |
2134 | hookenv.log( |
2135 | @@ -136,6 +174,7 @@ class Tool(): |
2136 | ) |
2137 | |
2138 | def install(self): |
2139 | + self._install_common_libs() |
2140 | self._install_nrpe_plugin() |
2141 | hookenv.log('Installed tool [{}]'.format(self._shortname)) |
2142 | |
2143 | @@ -144,30 +183,45 @@ class Tool(): |
2144 | self._remove_nrpe_plugin() |
2145 | hookenv.log('Removed tool [{}]'.format(self._shortname)) |
2146 | |
2147 | + @classmethod |
2148 | + def is_series_supported(cls): |
2149 | + series = lsb_release()['DISTRIB_CODENAME'] |
2150 | + |
2151 | + # BUG(lp#1890652) The following works around xenial layer-apt bug during test |
2152 | + if ( |
2153 | + hookenv.config("manufacturer") == "test" |
2154 | + and series == "xenial" |
2155 | + and isinstance(cls, AptVendorTool) |
2156 | + ): |
2157 | + return False |
2158 | + |
2159 | + return series in cls.SUPPORTED_SERIES |
2160 | + |
2161 | def _install_cronjob(self, cron_user='root'): |
2162 | - assert self._cronjob_script is not None |
2163 | + assert self._cron_script is not None |
2164 | |
2165 | # Copy the cronjob script to the nagios plugins directory |
2166 | - src = os.path.join(self._files_dir, self._cronjob_script) |
2167 | + src = os.path.join(self._cron_script_dir, self._cron_script) |
2168 | dst = shutil.copy(src, self.NRPE_PLUGINS_DIR) |
2169 | os.chmod(dst, self.CRONJOB_SCRIPT_MODE) |
2170 | os.chown(dst, uid=self.CRONJOB_SCRIPT_UID, gid=self.CRONJOB_SCRIPT_GID) |
2171 | hookenv.log( |
2172 | 'Cronjob script [{}] copied to {}' |
2173 | - ''.format(self._cronjob_script, self.NRPE_PLUGINS_DIR), |
2174 | + ''.format(self._cron_script, self.NRPE_PLUGINS_DIR), |
2175 | hookenv.DEBUG |
2176 | ) |
2177 | |
2178 | cmdline = [dst] |
2179 | - if self._cronjob_script_args \ |
2180 | - and isinstance(self._cronjob_script_args, str): |
2181 | + if self._cron_script_args \ |
2182 | + and isinstance(self._cron_script_args, str): |
2183 | cmdline.extend([shlex.quote(arg) |
2184 | - for arg in self._cronjob_script_args.split()]) |
2185 | - else: |
2186 | - # Run it once to generate the temp file, otherwise the nrpe check |
2187 | - # might fail at first. For security reasons, cronjobs that allow |
2188 | - # parameters shared by the user don't run the script as root |
2189 | - # (check_ipmi allows a missing output file) |
2190 | + for arg in self._cron_script_args.split()]) |
2191 | + elif hookenv.config('manufacturer') != 'test': |
2192 | + # Run it once to generate the temp file unless we're on a test |
2193 | + # container, otherwise the nrpe check # might fail at first. |
2194 | + # For security reasons, cronjobs that allow parameters shared |
2195 | + # by the user don't run the script as root |
2196 | + # e.g. check_ipmi allows a missing output file |
2197 | subprocess.call(cmdline) |
2198 | |
2199 | # Generate random cronjob execution (internal in minutes) |
2200 | @@ -193,19 +247,18 @@ class Tool(): |
2201 | return dst |
2202 | |
2203 | def _remove_cronjob(self): |
2204 | - assert self._cronjob_script is not None |
2205 | + assert self._cron_script is not None |
2206 | |
2207 | crond_file = os.path.join(self.CROND_DIR, |
2208 | 'hwhealth_{}'.format(self._shortname)) |
2209 | - cronjob_script = os.path.join(self.NRPE_PLUGINS_DIR, |
2210 | - self._cronjob_script) |
2211 | - for filename in (crond_file, cronjob_script): |
2212 | + cron_script = os.path.join(self.NRPE_PLUGINS_DIR, self._cron_script) |
2213 | + for filename in (crond_file, cron_script): |
2214 | if not os.path.exists(filename): |
2215 | continue |
2216 | os.remove(filename) |
2217 | hookenv.log( |
2218 | 'Removed cronjob files [{}, {}]' |
2219 | - ''.format(crond_file, cronjob_script), |
2220 | + ''.format(crond_file, cron_script), |
2221 | hookenv.DEBUG |
2222 | ) |
2223 | |
2224 | @@ -214,7 +267,8 @@ class Tool(): |
2225 | if not sudoer_path.exists(): |
2226 | return |
2227 | sudoer_path.unlink() |
2228 | - hookenv.log('deleted sudoer file: {}'.format(sudoer_path), hookenv.DEBUG) |
2229 | + hookenv.log('deleted sudoer file: {}'.format(sudoer_path), |
2230 | + hookenv.DEBUG) |
2231 | |
2232 | |
2233 | class VendorTool(Tool): |
2234 | @@ -225,10 +279,8 @@ class VendorTool(Tool): |
2235 | cronjob that runs as root and saves the tool output in a temporary file |
2236 | that nrpe can read (as nagios user). |
2237 | """ |
2238 | - def __init__(self, shortname): |
2239 | - super().__init__(shortname=shortname) |
2240 | - self._cronjob_script = 'check_{}.sh'.format(shortname) |
2241 | - self._nrpe_script = 'check_{}.py'.format(shortname) |
2242 | + def __init__(self, *args, **kwargs): |
2243 | + super().__init__(*args, **kwargs) |
2244 | self.checksums = [] |
2245 | |
2246 | def install(self): |
2247 | @@ -260,10 +312,12 @@ class VendorTool(Tool): |
2248 | with open(tmpfile, 'rb') as fd: |
2249 | checksum.update(fd.read()) |
2250 | if checksum.hexdigest() not in self.checksums: |
2251 | - raise ToolChecksumError({'shortname': self._shortname, |
2252 | - 'checksum': checksum.hexdigest(), |
2253 | - 'expected_checksums': ', '.join(self.checksums), |
2254 | - }) |
2255 | + checksums_string = ', '.join(self.checksums) |
2256 | + raise ToolChecksumError({ |
2257 | + 'shortname': self._shortname, |
2258 | + 'checksum': checksum.hexdigest(), |
2259 | + 'expected_checksums': checksums_string |
2260 | + }) |
2261 | # We could just use self.TOOLS_DIR as a destination |
2262 | # here, but shutil.move refuses to overwrite the |
2263 | # destination file unless it receives a full path |
2264 | @@ -277,7 +331,8 @@ class VendorTool(Tool): |
2265 | |
2266 | except PermissionError as error: |
2267 | hookenv.log( |
2268 | - 'Unable to unzip tool {} from the provided resource: {}' |
2269 | + 'Unable to unzip tool {} ' |
2270 | + 'from the provided resource: {}' |
2271 | ''.format(self._shortname, error), |
2272 | hookenv.ERROR |
2273 | ) |
2274 | @@ -296,13 +351,194 @@ class VendorTool(Tool): |
2275 | ) |
2276 | |
2277 | |
2278 | +class AptVendorTool(Tool): |
2279 | + """A class representing a vendor tool binary package available via apt |
2280 | + |
2281 | + An AptVendorTool is a statically linked binary or set of binaries, |
2282 | + config files, and libraries available via apt repository from Ubuntu |
2283 | + or Hardware Vendor package Archives. Since it requires root privileges |
2284 | + to run locally, we also deploy a cronjob that runs as root and saves |
2285 | + the tool output in a temporary file that nrpe can read (as nagios user). |
2286 | + |
2287 | + """ |
2288 | + |
2289 | + HPE_ILOREST_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- |
2290 | + Version: GnuPG v1.4.12 (GNU/Linux) |
2291 | + |
2292 | + mQENBFZp0LkBCACXajRw3b4x7G7dulNYj0hUID4BtVFq/MjEb6PHckTxGxZDoQRX |
2293 | + RK54tiTFA9wq3b4P3yEFnOjbjRoI0d7Ls67FADugFO+cDCtsV9yuDlaYP/U/h2nX |
2294 | + N0R4AdYbsVd5yr6xr+GAy66Hmx5jFH3kbC+zJpOcI0tU9hcyU7gjbxu6KQ1ypI2Q |
2295 | + VRKf8sRBJXgmkOlbYx35ZUMFcmVxrLJXvUuxmAVXgT9f5M3Z3rsGt/ab+/+1TFSb |
2296 | + RsaqHsIPE0QH8ikqW4IeDQAo1T99pCdf7FWr45KFFTo7O4AZdLMWVgqeFHaSoZxJ |
2297 | + 307VIINsWiwQoPp0tfU5NOOOwB1Sv3x9QgFtABEBAAG0P0hld2xldHQgUGFja2Fy |
2298 | + ZCBFbnRlcnByaXNlIENvbXBhbnkgUlNBLTIwNDgtMjUgPHNpZ25ocEBocGUuY29t |
2299 | + PokBPQQTAQIAJwUCVmnQuQIbLwUJEswDAAYLCQgHAwIGFQgCCQoLAxYCAQIeAQIX |
2300 | + gAAKCRDCCK3eJsK3l9G+B/0ekblsBeN+xHIJ28pvo2aGb2KtWBwbT1ugI+aIS17K |
2301 | + UQyHZJUQH+ZeRLvosuoiQEdcGIqmOxi2hVhSCQAOV1LAonY16ACveA5DFAEBz1+a |
2302 | + WQyx6sOLLEAVX1VqGlBXxh3XLEUWOhlAf1gZPNtHsmURTUy2h1Lv/Yoj8KLyuK2n |
2303 | + DmrLOS3Ro+RqWocaJfvAgXKgt6Fq/ChDUHOnar7lGswzMsbE/yzLJ7He4y89ImK+ |
2304 | + 2ktR5HhDuxqgCe9CWH6Q/1WGhUa0hZ3nbluq7maa+kPe2g7JcRzPH/nJuDCAOZ7U |
2305 | + 6mHE8j0kMQMYjgaYEx2wc02aQRmPyxhbDLjSbtjomXRr |
2306 | + =voON |
2307 | + -----END PGP PUBLIC KEY BLOCK-----""" |
2308 | + HPE_MCP_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- |
2309 | + Version: GnuPG v1.4.12 (MingW32) |
2310 | + |
2311 | + mQENBFRtGAgBCADlSku65P14hVdx9E/W0n6MwuB3WGqmsyKNoa3HezFdMjWERldI |
2312 | + NNUdi8O28cZ6j2+Hi9L1HeQIQ9+7FHpR3JyQePBJtRX8WSEusfRtML98opDhJxKm |
2313 | + 8Jyxb7aTvCwdNHz3yxADINkMtOj5oRm7VCr8XHkG7YU27ELs8B+BXWvjO21oSosi |
2314 | + FurnhT+H3hQsYXfYA55aa21q0qX+L5dFJSNdzZVo7m9ybioVv2R5+PfBvdaSxCnm |
2315 | + OpcGXFaKAsqVHeTW0pd3sdkin1rkbhOBaU5lFBt2ZiMtKpKHpT8TZnqHpFHFbgi8 |
2316 | + j2ARJj4IDct2OGILddUIZSFyue6WE2hpV5c/ABEBAAG0OEhld2xldHQtUGFja2Fy |
2317 | + ZCBDb21wYW55IFJTQSAoSFAgQ29kZXNpZ25pbmcgU2VydmljZSkgLSAxiQE+BBMB |
2318 | + AgAoBQJUbRgIAhsDBQkSzAMABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRD6 |
2319 | + 3Y1ksSdeo6BJCADOfIPPLPpIOnFK9jH4t8lLUd+RyMc+alA3uTDPUJa/ZHa6DHfh |
2320 | + 42iaPYVEV8OG0tnbMlHmwvsZ5c1/MRMw1UbxCvD88P2qM4SUrUjQUlSCms2GLGvF |
2321 | + ftFXBiOJQ7/yBc9o+yoSvwPrrTxSCk4+Sqm0IfVXVzChDM9dM9YPY2Vzjd+LUaYC |
2322 | + 3X+eSuggUDO0TmJLJd7tZdF9fVXq3lr63BZ5PY98MTCuOoeSMDa9FIUQf6vn6UUJ |
2323 | + MDSRZ9OzhpNJOKR+ShVRwDK6My8gtVIW1EAW2w3VQWI2UNF07aLeO8UG6nTNWA23 |
2324 | + +OuZkUdgQovjcq01caSefgOkmiQOx6d74CAk |
2325 | + =X+eo |
2326 | + -----END PGP PUBLIC KEY BLOCK-----""" |
2327 | + |
2328 | + HPE_MCP_REPO_TMPL = ( |
2329 | + 'deb http://downloads.linux.hpe.com/SDR/repo/mcp {series}/current-gen9 non-free' |
2330 | + ) |
2331 | + HPE_ILOREST_REPO_TMPL = ( |
2332 | + 'deb http://downloads.linux.hpe.com/SDR/repo/ilorest {series}/current non-free' |
2333 | + ) |
2334 | + |
2335 | + # HP doesn't have focal APT sources as of yet |
2336 | + APT_SOURCES = { |
2337 | + 'ssacli': { |
2338 | + 'xenial': HPE_MCP_REPO_TMPL.format(series='xenial'), |
2339 | + 'bionic': HPE_MCP_REPO_TMPL.format(series='bionic'), |
2340 | + }, |
2341 | + 'ilorest': { |
2342 | + 'xenial': HPE_ILOREST_REPO_TMPL.format(series='xenial'), |
2343 | + 'bionic': HPE_ILOREST_REPO_TMPL.format(series='bionic'), |
2344 | + }, |
2345 | + 'hplog': { |
2346 | + 'xenial': HPE_MCP_REPO_TMPL.format(series='xenial'), |
2347 | + 'bionic': HPE_MCP_REPO_TMPL.format(series='bionic'), |
2348 | + }, |
2349 | + } |
2350 | + APT_KEYS = { |
2351 | + 'ssacli': { |
2352 | + 'xenial': HPE_MCP_KEY, |
2353 | + 'bionic': HPE_MCP_KEY, |
2354 | + }, |
2355 | + 'ilorest': { |
2356 | + 'xenial': HPE_ILOREST_KEY, |
2357 | + 'bionic': HPE_ILOREST_KEY, |
2358 | + }, |
2359 | + 'hplog': { |
2360 | + 'xenial': HPE_MCP_KEY, |
2361 | + 'bionic': HPE_MCP_KEY, |
2362 | + }, |
2363 | + } |
2364 | + |
2365 | + def __init__(self, shortname=None, apt_packages=[]): |
2366 | + super().__init__( |
2367 | + shortname=shortname, |
2368 | + nrpe_script='check_hw_health_cron_output.py', |
2369 | + nrpe_opts='--filename {}/{}.out'.format(self.CRONJOB_OUTPUT_DIR, |
2370 | + shortname) |
2371 | + ) |
2372 | + self.apt_packages = (apt_packages if apt_packages |
2373 | + else [self.__class__.__name__.lower()]) |
2374 | + self._nrpe_script_dir = self._common_libs_dir |
2375 | + |
2376 | + def install(self): |
2377 | + self._install_packages() |
2378 | + self.install_cronjob() |
2379 | + super().install() |
2380 | + |
2381 | + def remove(self): |
2382 | + self._remove_cronjob() |
2383 | + self._remove_packages() |
2384 | + super().remove() |
2385 | + |
2386 | + def _install_packages(self): |
2387 | + """ |
2388 | + Install the apt packages via layer-apt |
2389 | + |
2390 | + As deb packages manage their own checksums and each cloud can point to |
2391 | + trusted repositories via install_sources and install_keys config, no |
2392 | + checksumming is necessary as in resource-shipped VendorTool. |
2393 | + |
2394 | + Each instantiation should configure it's apt_packages based on the |
2395 | + hardware present on the system. |
2396 | + """ |
2397 | + self._add_apt_source() |
2398 | + if hookenv.config('manufacturer') == 'test': |
2399 | + # If we are forcing install on a container for functional tests, |
2400 | + # we should only download and not install the packages, as some |
2401 | + # vendor tools depend on hardware to be preset to complete postinst |
2402 | + # need one option added per package |
2403 | + apt.queue_install(self.apt_packages, options=["--download-only" for _ in self.apt_packages]) |
2404 | + else: |
2405 | + apt.queue_install(self.apt_packages) |
2406 | + |
2407 | + def _add_apt_source(self): |
2408 | + series = lsb_release()['DISTRIB_CODENAME'] |
2409 | + if self._shortname not in self.APT_SOURCES and self._shortname not in self.APT_KEYS: |
2410 | + return |
2411 | + if series in self.APT_SOURCES[self._shortname]: |
2412 | + apt.add_source(self.APT_SOURCES[self._shortname][series], |
2413 | + key=self.APT_KEYS[self._shortname][series]) |
2414 | + |
2415 | + def _remove_packages(self): |
2416 | + apt.purge(self.apt_packages) |
2417 | + |
2418 | + def install_cronjob(self): |
2419 | + hookenv.log( |
2420 | + 'Attempting AptVendorTool cronjob script install [{}]' |
2421 | + ''.format(self._cron_script), |
2422 | + hookenv.DEBUG |
2423 | + ) |
2424 | + # Don't install a cronjob until the tools are installed |
2425 | + if self.is_apt_installed(): |
2426 | + hookenv.log( |
2427 | + 'calling _install_cronjob for {}' |
2428 | + ''.format(self._cron_script), |
2429 | + hookenv.DEBUG |
2430 | + ) |
2431 | + self._install_cronjob() |
2432 | + |
2433 | + def configure_nrpe_check(self, nrpe_setup): |
2434 | + # Don't drop nrpe checks until the APT package is successfully |
2435 | + # installed or we'll get red warnings due to Apt errors which |
2436 | + # should just be reported through juju status |
2437 | + if self.is_apt_installed(): |
2438 | + super().configure_nrpe_check(nrpe_setup) |
2439 | + |
2440 | + def is_apt_installed(self): |
2441 | + """ |
2442 | + Check if packaged queued for install are installed |
2443 | + |
2444 | + The apt layer will install packages in the background, this method |
2445 | + allows the reactive layer to know when the apt packages have finished |
2446 | + installing so that it may go on to configure the nrpe layer of tools |
2447 | + """ |
2448 | + if hookenv.config('manufacturer') == 'test': |
2449 | + # it is okay to skip this part in testing as layer-apt will block |
2450 | + # if the package is not downloadable from sources |
2451 | + return True |
2452 | + apt_installed_packages = apt.installed() |
2453 | + for package in self.apt_packages: |
2454 | + if package not in apt_installed_packages: |
2455 | + return False |
2456 | + return True |
2457 | + |
2458 | + |
2459 | class Sas3Ircu(VendorTool): |
2460 | """A class representing the sas3ircu tool |
2461 | |
2462 | This is a tool supporting the LSI SAS 12Gb/s controllers |
2463 | """ |
2464 | def __init__(self): |
2465 | - super().__init__(shortname='sas3ircu') |
2466 | + super().__init__(cron_script='cron_sas3ircu.sh') |
2467 | self.checksums = [ |
2468 | 'f150eb37bb332668949a3eccf9636e0e03f874aecd17a39d586082c6be1386bd', |
2469 | 'd69967057992134df1b136f83bc775a641e32c4efc741def3ef6f6a25a9a14b5', |
2470 | @@ -315,8 +551,8 @@ class Sas2Ircu(VendorTool): |
2471 | This is a tool supporting the LSI SAS 6Gb/s controllers |
2472 | """ |
2473 | def __init__(self): |
2474 | - super().__init__(shortname='sas2ircu') |
2475 | - self.checksums = ['37467826d0b22aad47287efe70bb34e47f475d70e9b1b64cbd63f57607701e73'] |
2476 | + super().__init__(cron_script='cron_sas2ircu.sh') |
2477 | + self.checksums = ['37467826d0b22aad47287efe70bb34e47f475d70e9b1b64cbd63f57607701e73'] # noqa: E501 |
2478 | |
2479 | |
2480 | class MegaCLI(VendorTool): |
2481 | @@ -325,7 +561,7 @@ class MegaCLI(VendorTool): |
2482 | This is a tool supporting the LSI MegaRAID SAS controllers |
2483 | """ |
2484 | def __init__(self): |
2485 | - super().__init__(shortname='megacli') |
2486 | + super().__init__(cron_script='cron_megacli.sh') |
2487 | self.checksums = [ |
2488 | '34f1a235543662615ee35f458317380b3f89fac0e415dee755e0dbc7c4cf6f92', |
2489 | '1c4effe33ee5db82227e05925dd629771fd49c7d2be2382d48c48a864452cdec', |
2490 | @@ -333,6 +569,40 @@ class MegaCLI(VendorTool): |
2491 | ] |
2492 | |
2493 | |
2494 | +class HpLog(AptVendorTool): |
2495 | + """A class representing the hplog tool |
2496 | + |
2497 | + This is a tool supporting the LSI MegaRAID SAS controllers |
2498 | + """ |
2499 | + |
2500 | + SUPPORTED_SERIES = ['xenial', 'bionic'] |
2501 | + |
2502 | + def __init__(self): |
2503 | + super().__init__(apt_packages=['hp-health']) |
2504 | + |
2505 | + |
2506 | +class SsaCli(AptVendorTool): |
2507 | + """A class representing the ssacli tool |
2508 | + |
2509 | + This is a tool supporting the HP Smart Array controllers |
2510 | + """ |
2511 | + |
2512 | + SUPPORTED_SERIES = ['xenial', 'bionic'] |
2513 | + |
2514 | + def __init__(self): |
2515 | + super().__init__() |
2516 | + |
2517 | + |
2518 | +class ILOrest(AptVendorTool): |
2519 | + """A class representing the ILOrest vendor tool (HPE hardware (Gen 10+) |
2520 | + """ |
2521 | + |
2522 | + SUPPORTED_SERIES = ['xenial', 'bionic'] |
2523 | + |
2524 | + def __init__(self): |
2525 | + super().__init__() |
2526 | + |
2527 | + |
2528 | class Mdadm(VendorTool): |
2529 | """A class representing the mdadm tool (software RAID) |
2530 | |
2531 | @@ -340,10 +610,7 @@ class Mdadm(VendorTool): |
2532 | installation as it has a cronjob + check script |
2533 | """ |
2534 | def __init__(self): |
2535 | - shortname = 'mdadm' |
2536 | - super().__init__(shortname=shortname) |
2537 | - self._cronjob_script = 'cron_{}.py'.format(shortname) |
2538 | - self._nrpe_script = 'check_{}.py'.format(shortname) |
2539 | + super().__init__() |
2540 | |
2541 | def install(self): |
2542 | # mdadm should already be installed, but let's check |
2543 | @@ -368,11 +635,11 @@ class Ipmi(Tool): |
2544 | actual nrpe check, which is imported as a git submodule |
2545 | """ |
2546 | def __init__(self, nrpe_opts=''): |
2547 | - super().__init__(shortname='ipmi') |
2548 | - self._nrpe_script = 'check_ipmi.py' |
2549 | + super().__init__( |
2550 | + cron_script='cron_ipmi_sensors.py', |
2551 | + cron_script_args=hookenv.config('ipmi_check_options') |
2552 | + ) |
2553 | self._sudoer_file = '99-check_ipmi_sensor' |
2554 | - self._cronjob_script = 'cron_ipmi_sensors.py' |
2555 | - self._cronjob_script_args = hookenv.config('ipmi_check_options') |
2556 | |
2557 | def configure_nrpe_check(self, nrpe_setup): |
2558 | # extra options for check_ipmi_sensors Perl script are configured in |
2559 | @@ -423,37 +690,17 @@ class Ipmi(Tool): |
2560 | return dst |
2561 | |
2562 | |
2563 | -class HP(VendorTool): |
2564 | - """A class representing tools needed to monitor HP hardware |
2565 | - |
2566 | - This class will need to install tools such as hponcfg, hp-health, and |
2567 | - hpssacli |
2568 | - """ |
2569 | - def __init__(self): |
2570 | - raise NotImplementedError |
2571 | - |
2572 | - |
2573 | -class HPE(VendorTool): |
2574 | - """A class representing tools needed to monitor HPE hardware |
2575 | - |
2576 | - This class will need to install tools such as hponcfg and ilorest |
2577 | - """ |
2578 | - def __init__(self): |
2579 | - raise NotImplementedError |
2580 | - |
2581 | - |
2582 | class Nvme(Tool): |
2583 | """A class representing the nvme tool (userspace tooling to control NVMe) |
2584 | |
2585 | - Our nvme check kind of behaves like a VendorTool for the purpose of |
2586 | - installation as it has a cronjob + check script |
2587 | + This is a direct subclass of Tool because unlike a VendorTool we are not |
2588 | + using a cronjob script |
2589 | """ |
2590 | def __init__(self): |
2591 | - shortname = 'nvme' |
2592 | - super().__init__(shortname=shortname) |
2593 | - self._nrpe_script = 'check_{}.py'.format(shortname) |
2594 | + super().__init__() |
2595 | self._sudoer_template = '99-check_nvme.tmpl' |
2596 | self._sudoer_file = '99-check_nvme' |
2597 | + self._cron_script = None |
2598 | |
2599 | def install(self): |
2600 | # mdadm should already be installed, but let's check |
2601 | diff --git a/src/metadata.yaml b/src/metadata.yaml |
2602 | index 58c709a..a06914e 100644 |
2603 | --- a/src/metadata.yaml |
2604 | +++ b/src/metadata.yaml |
2605 | @@ -18,6 +18,7 @@ description: | |
2606 | nvme |
2607 | sas2ircu |
2608 | sas3ircu |
2609 | + ilorest |
2610 | |
2611 | tags: |
2612 | - monitoring |
2613 | diff --git a/src/reactive/hw_health.py b/src/reactive/hw_health.py |
2614 | index 6619dd4..a04f790 100644 |
2615 | --- a/src/reactive/hw_health.py |
2616 | +++ b/src/reactive/hw_health.py |
2617 | @@ -10,7 +10,16 @@ from hwhealth.hwdiscovery import get_tools |
2618 | from hwhealth import tools |
2619 | |
2620 | |
2621 | +def _set_install_status(tool): |
2622 | + if isinstance(tool, tools.VendorTool) and not isinstance(tool, tools.Mdadm): |
2623 | + status.maintenance('Installing from attached resource') |
2624 | + elif isinstance(tool, tools.AptVendorTool): |
2625 | + status.maintenance('Installing vendor tools via apt') |
2626 | + set_flag('hw-health.wait-for-vendor-apt') |
2627 | + |
2628 | + |
2629 | @when_none('hw-health.installed', 'hw-health.unsupported') |
2630 | +@when('nrpe-external-master.available') |
2631 | @when('general-info.connected') |
2632 | def install(): |
2633 | manufacturer = hookenv.config('manufacturer') |
2634 | @@ -32,9 +41,10 @@ def install(): |
2635 | else: |
2636 | try: |
2637 | tool_list = list() |
2638 | - for tool in toolset: |
2639 | - if isinstance(tool, tools.VendorTool) and not isinstance(tool, tools.Mdadm): |
2640 | - status.maintenance('Installing from attached resource') |
2641 | + for toolClass in toolset: |
2642 | + tool = toolClass() |
2643 | + _set_install_status(tool) |
2644 | + status.maintenance('Installing tool {}'.format(type(tool).__name__)) |
2645 | tool.install() |
2646 | # Save the class name in the unit kv db. This will be reused when |
2647 | # reconfiguring or removing the checks |
2648 | @@ -84,6 +94,24 @@ def remove_tools(): |
2649 | clear_flag('hw-health.configured') |
2650 | |
2651 | |
2652 | +@when('hw-health.wait-for-vendor-apt') |
2653 | +def wait_for_vendor_apt(): |
2654 | + # cycle through any vendor tools that are of type AptVendorTool and |
2655 | + # check if all packages needed are installed. If not, eject and wait |
2656 | + unitdb = unitdata.kv() |
2657 | + for tool_class_name in unitdb.get('toolset', set()): |
2658 | + # Re-instantiate the tool from the saved class name |
2659 | + tool_class = getattr(tools, tool_class_name) |
2660 | + if isinstance(tool_class, tools.AptVendorTool): |
2661 | + if tool_class.is_apt_installed(): |
2662 | + tool_class.install_cronjob() |
2663 | + else: |
2664 | + status.maintenance('Waiting for vendor tools to install via apt') |
2665 | + return |
2666 | + clear_flag('hw-health.wait-for-vendor-apt') |
2667 | + clear_flag('hw-health.configured') |
2668 | + |
2669 | + |
2670 | @when('config.changed') |
2671 | @when_not('config.changed.manufacturer') |
2672 | def config_changed(): |
2673 | diff --git a/src/tests/expected/lshw.py b/src/tests/expected/lshw.py |
2674 | new file mode 100644 |
2675 | index 0000000..d11909f |
2676 | --- /dev/null |
2677 | +++ b/src/tests/expected/lshw.py |
2678 | @@ -0,0 +1,1917 @@ |
2679 | +EXPECTED = { |
2680 | + "lshw.dell.01.json": { |
2681 | + "system": { |
2682 | + "description": "Rack Mount Chassis", |
2683 | + "hostname": "compute-4", |
2684 | + "product": "PowerEdge R730 (SKU=NotProvided;ModelName=PowerEdge R730, DBE)", # noqa E501 |
2685 | + "serial": "3YMGWL2", |
2686 | + "vendor": "Dell Inc.", |
2687 | + "version": None, |
2688 | + }, |
2689 | + "motherboard": [ |
2690 | + { |
2691 | + "description": "Motherboard", |
2692 | + "product": "04N3DF", |
2693 | + "serial": ".3YMGWL2.CNFCP0077F0059.", |
2694 | + "vendor": "Dell Inc.", |
2695 | + "version": "A10", |
2696 | + } |
2697 | + ], |
2698 | + "storage_class_info": [ |
2699 | + { |
2700 | + "businfo": "pci@0000:03:00.0", |
2701 | + "driver": "megaraid_sas", |
2702 | + "has_children": True, |
2703 | + "product": "MegaRAID SAS-3 3108 [Invader]", |
2704 | + "vendor": "LSI Logic / Symbios Logic", |
2705 | + }, |
2706 | + { |
2707 | + "businfo": "pci@0000:00:11.4", |
2708 | + "driver": "ahci", |
2709 | + "has_children": False, |
2710 | + "product": "C610/X99 series chipset sSATA Controller [AHCI mode]", # noqa E501 |
2711 | + "vendor": "Intel Corporation", |
2712 | + }, |
2713 | + { |
2714 | + "businfo": "pci@0000:00:1f.2", |
2715 | + "driver": "ahci", |
2716 | + "has_children": False, |
2717 | + "product": "C610/X99 series chipset 6-Port SATA Controller [AHCI mode]", # noqa E501 |
2718 | + "vendor": "Intel Corporation", |
2719 | + }, |
2720 | + { |
2721 | + "businfo": "pci@0000:82:00.0", |
2722 | + "driver": "nvme", |
2723 | + "has_children": False, |
2724 | + "product": "PCIe Data Center SSD", |
2725 | + "vendor": "Intel Corporation", |
2726 | + }, |
2727 | + ], |
2728 | + "disk_class_info": [ |
2729 | + { |
2730 | + "businfo": "scsi@0:2.0.0", |
2731 | + "dev": "8:0", |
2732 | + "logicalname": "/dev/sda", |
2733 | + "physid": "2.0.0", |
2734 | + "product": "PERC H730P Mini", |
2735 | + "serial": "00174c7006cbabeb2100aa7515604609", |
2736 | + "size": 4000225165312, |
2737 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2738 | + }, |
2739 | + { |
2740 | + "businfo": "scsi@0:2.1.0", |
2741 | + "dev": "8:16", |
2742 | + "logicalname": "/dev/sdb", |
2743 | + "physid": "2.1.0", |
2744 | + "product": "PERC H730P Mini", |
2745 | + "serial": "00f7e28406ccabeb2100aa7515604609", |
2746 | + "size": 4000225165312, |
2747 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2748 | + }, |
2749 | + { |
2750 | + "businfo": "scsi@0:2.2.0", |
2751 | + "dev": "8:32", |
2752 | + "logicalname": "/dev/sdc", |
2753 | + "physid": "2.2.0", |
2754 | + "product": "PERC H730P Mini", |
2755 | + "serial": "0024b29406cdabeb2100aa7515604609", |
2756 | + "size": 4000225165312, |
2757 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2758 | + }, |
2759 | + { |
2760 | + "businfo": "scsi@0:2.3.0", |
2761 | + "dev": "8:48", |
2762 | + "logicalname": "/dev/sdd", |
2763 | + "physid": "2.3.0", |
2764 | + "product": "PERC H730P Mini", |
2765 | + "serial": "007a79a506ceabeb2100aa7515604609", |
2766 | + "size": 4000225165312, |
2767 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2768 | + }, |
2769 | + { |
2770 | + "businfo": "scsi@0:2.4.0", |
2771 | + "dev": "8:64", |
2772 | + "logicalname": ["/dev/sde", "/srv/node/sde"], |
2773 | + "physid": "2.4.0", |
2774 | + "product": "PERC H730P Mini", |
2775 | + "serial": "0005ebba06cfabeb2100aa7515604609", |
2776 | + "size": 4000225165312, |
2777 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2778 | + }, |
2779 | + { |
2780 | + "businfo": "scsi@0:2.5.0", |
2781 | + "dev": "8:80", |
2782 | + "logicalname": ["/dev/sdf", "/srv/node/sdf"], |
2783 | + "physid": "2.5.0", |
2784 | + "product": "PERC H730P Mini", |
2785 | + "serial": "005c35cc06d1abeb2100aa7515604609", |
2786 | + "size": 4000225165312, |
2787 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2788 | + }, |
2789 | + { |
2790 | + "businfo": "scsi@0:2.6.0", |
2791 | + "dev": "8:96", |
2792 | + "logicalname": ["/dev/sdg", "/srv/node/sdg"], |
2793 | + "physid": "2.6.0", |
2794 | + "product": "PERC H730P Mini", |
2795 | + "serial": "00e046dd06d2abeb2100aa7515604609", |
2796 | + "size": 4000225165312, |
2797 | + "storage_parent": "MegaRAID SAS-3 3108 [Invader]", |
2798 | + }, |
2799 | + ], |
2800 | + "network_class_info": [ |
2801 | + { |
2802 | + "businfo": "pci@0000:01:00.0", |
2803 | + "driver": "mlx5_core", |
2804 | + "driverversion": "5.0-0", |
2805 | + "firmware": "14.21.3012 (DEL2810000034)", |
2806 | + "logicalname": "eno1", |
2807 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
2808 | + "serial": "de:ad:be:ef:00:ef", |
2809 | + "speed": None, |
2810 | + "vendor": "Mellanox Technologies", |
2811 | + }, |
2812 | + { |
2813 | + "businfo": "pci@0000:01:00.1", |
2814 | + "driver": "mlx5_core", |
2815 | + "driverversion": "5.0-0", |
2816 | + "firmware": "14.21.3012 (DEL2810000034)", |
2817 | + "logicalname": "eno2", |
2818 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
2819 | + "serial": "de:ad:be:ef:00:ef", |
2820 | + "speed": None, |
2821 | + "vendor": "Mellanox Technologies", |
2822 | + }, |
2823 | + { |
2824 | + "businfo": "pci@0000:01:02.6", |
2825 | + "driver": "mlx5_core", |
2826 | + "driverversion": "5.0-0", |
2827 | + "firmware": "14.21.3012 (DEL2810000034)", |
2828 | + "logicalname": "enp1s2f6", |
2829 | + "product": "Illegal Vendor ID", |
2830 | + "serial": "de:ad:be:ef:00:ef", |
2831 | + "speed": None, |
2832 | + "vendor": "Illegal Vendor ID", |
2833 | + }, |
2834 | + { |
2835 | + "businfo": "pci@0000:01:02.7", |
2836 | + "driver": "mlx5_core", |
2837 | + "driverversion": "5.0-0", |
2838 | + "firmware": "14.21.3012 (DEL2810000034)", |
2839 | + "logicalname": "enp1s2f7", |
2840 | + "product": "Illegal Vendor ID", |
2841 | + "serial": "de:ad:be:ef:00:ef", |
2842 | + "speed": None, |
2843 | + "vendor": "Illegal Vendor ID", |
2844 | + }, |
2845 | + { |
2846 | + "businfo": "pci@0000:01:03.0", |
2847 | + "driver": "mlx5_core", |
2848 | + "driverversion": "5.0-0", |
2849 | + "firmware": "14.21.3012 (DEL2810000034)", |
2850 | + "logicalname": "enp1s3", |
2851 | + "product": "Illegal Vendor ID", |
2852 | + "serial": "de:ad:be:ef:00:ef", |
2853 | + "speed": None, |
2854 | + "vendor": "Illegal Vendor ID", |
2855 | + }, |
2856 | + { |
2857 | + "businfo": "pci@0000:01:03.1", |
2858 | + "driver": "mlx5_core", |
2859 | + "driverversion": "5.0-0", |
2860 | + "firmware": "14.21.3012 (DEL2810000034)", |
2861 | + "logicalname": "enp1s3f1", |
2862 | + "product": "Illegal Vendor ID", |
2863 | + "serial": "de:ad:be:ef:00:ef", |
2864 | + "speed": None, |
2865 | + "vendor": "Illegal Vendor ID", |
2866 | + }, |
2867 | + { |
2868 | + "businfo": "pci@0000:01:03.2", |
2869 | + "driver": "mlx5_core", |
2870 | + "driverversion": "5.0-0", |
2871 | + "firmware": "14.21.3012 (DEL2810000034)", |
2872 | + "logicalname": "enp1s3f2", |
2873 | + "product": "Illegal Vendor ID", |
2874 | + "serial": "de:ad:be:ef:00:ef", |
2875 | + "speed": None, |
2876 | + "vendor": "Illegal Vendor ID", |
2877 | + }, |
2878 | + { |
2879 | + "businfo": "pci@0000:01:03.3", |
2880 | + "driver": "mlx5_core", |
2881 | + "driverversion": "5.0-0", |
2882 | + "firmware": "14.21.3012 (DEL2810000034)", |
2883 | + "logicalname": "enp1s3f3", |
2884 | + "product": "Illegal Vendor ID", |
2885 | + "serial": "de:ad:be:ef:00:ef", |
2886 | + "speed": None, |
2887 | + "vendor": "Illegal Vendor ID", |
2888 | + }, |
2889 | + { |
2890 | + "businfo": "pci@0000:01:03.4", |
2891 | + "driver": "mlx5_core", |
2892 | + "driverversion": "5.0-0", |
2893 | + "firmware": "14.21.3012 (DEL2810000034)", |
2894 | + "logicalname": "enp1s3f4", |
2895 | + "product": "Illegal Vendor ID", |
2896 | + "serial": "de:ad:be:ef:00:ef", |
2897 | + "speed": None, |
2898 | + "vendor": "Illegal Vendor ID", |
2899 | + }, |
2900 | + { |
2901 | + "businfo": "pci@0000:01:03.5", |
2902 | + "driver": "mlx5_core", |
2903 | + "driverversion": "5.0-0", |
2904 | + "firmware": "14.21.3012 (DEL2810000034)", |
2905 | + "logicalname": "enp1s3f5", |
2906 | + "product": "Illegal Vendor ID", |
2907 | + "serial": "de:ad:be:ef:00:ef", |
2908 | + "speed": None, |
2909 | + "vendor": "Illegal Vendor ID", |
2910 | + }, |
2911 | + { |
2912 | + "businfo": "pci@0000:01:03.6", |
2913 | + "driver": "mlx5_core", |
2914 | + "driverversion": "5.0-0", |
2915 | + "firmware": "14.21.3012 (DEL2810000034)", |
2916 | + "logicalname": "enp1s3f6", |
2917 | + "product": "Illegal Vendor ID", |
2918 | + "serial": "de:ad:be:ef:00:ef", |
2919 | + "speed": None, |
2920 | + "vendor": "Illegal Vendor ID", |
2921 | + }, |
2922 | + { |
2923 | + "businfo": "pci@0000:01:03.7", |
2924 | + "driver": "mlx5_core", |
2925 | + "driverversion": "5.0-0", |
2926 | + "firmware": "14.21.3012 (DEL2810000034)", |
2927 | + "logicalname": "enp1s3f7", |
2928 | + "product": "Illegal Vendor ID", |
2929 | + "serial": "de:ad:be:ef:00:ef", |
2930 | + "speed": None, |
2931 | + "vendor": "Illegal Vendor ID", |
2932 | + }, |
2933 | + { |
2934 | + "businfo": "pci@0000:01:04.0", |
2935 | + "driver": "mlx5_core", |
2936 | + "driverversion": "5.0-0", |
2937 | + "firmware": "14.21.3012 (DEL2810000034)", |
2938 | + "logicalname": "enp1s4", |
2939 | + "product": "Illegal Vendor ID", |
2940 | + "serial": "de:ad:be:ef:00:ef", |
2941 | + "speed": None, |
2942 | + "vendor": "Illegal Vendor ID", |
2943 | + }, |
2944 | + { |
2945 | + "businfo": "pci@0000:01:04.1", |
2946 | + "driver": "mlx5_core", |
2947 | + "driverversion": "5.0-0", |
2948 | + "firmware": "14.21.3012 (DEL2810000034)", |
2949 | + "logicalname": "enp1s4f1", |
2950 | + "product": "Illegal Vendor ID", |
2951 | + "serial": "de:ad:be:ef:00:ef", |
2952 | + "speed": None, |
2953 | + "vendor": "Illegal Vendor ID", |
2954 | + }, |
2955 | + { |
2956 | + "businfo": "pci@0000:01:04.2", |
2957 | + "driver": "mlx5_core", |
2958 | + "driverversion": "5.0-0", |
2959 | + "firmware": "14.21.3012 (DEL2810000034)", |
2960 | + "logicalname": "enp1s4f2", |
2961 | + "product": "Illegal Vendor ID", |
2962 | + "serial": "de:ad:be:ef:00:ef", |
2963 | + "speed": None, |
2964 | + "vendor": "Illegal Vendor ID", |
2965 | + }, |
2966 | + { |
2967 | + "businfo": "pci@0000:01:04.3", |
2968 | + "driver": "mlx5_core", |
2969 | + "driverversion": "5.0-0", |
2970 | + "firmware": "14.21.3012 (DEL2810000034)", |
2971 | + "logicalname": "enp1s4f3", |
2972 | + "product": "Illegal Vendor ID", |
2973 | + "serial": "de:ad:be:ef:00:ef", |
2974 | + "speed": None, |
2975 | + "vendor": "Illegal Vendor ID", |
2976 | + }, |
2977 | + { |
2978 | + "businfo": "pci@0000:01:04.4", |
2979 | + "driver": "mlx5_core", |
2980 | + "driverversion": "5.0-0", |
2981 | + "firmware": "14.21.3012 (DEL2810000034)", |
2982 | + "logicalname": "enp1s4f4", |
2983 | + "product": "Illegal Vendor ID", |
2984 | + "serial": "de:ad:be:ef:00:ef", |
2985 | + "speed": None, |
2986 | + "vendor": "Illegal Vendor ID", |
2987 | + }, |
2988 | + { |
2989 | + "businfo": "pci@0000:01:04.5", |
2990 | + "driver": "mlx5_core", |
2991 | + "driverversion": "5.0-0", |
2992 | + "firmware": "14.21.3012 (DEL2810000034)", |
2993 | + "logicalname": "enp1s4f5", |
2994 | + "product": "Illegal Vendor ID", |
2995 | + "serial": "de:ad:be:ef:00:ef", |
2996 | + "speed": None, |
2997 | + "vendor": "Illegal Vendor ID", |
2998 | + }, |
2999 | + { |
3000 | + "businfo": "pci@0000:01:04.6", |
3001 | + "driver": "mlx5_core", |
3002 | + "driverversion": "5.0-0", |
3003 | + "firmware": "14.21.3012 (DEL2810000034)", |
3004 | + "logicalname": "enp1s4f6", |
3005 | + "product": "Illegal Vendor ID", |
3006 | + "serial": "de:ad:be:ef:00:ef", |
3007 | + "speed": None, |
3008 | + "vendor": "Illegal Vendor ID", |
3009 | + }, |
3010 | + { |
3011 | + "businfo": "pci@0000:01:04.7", |
3012 | + "driver": "mlx5_core", |
3013 | + "driverversion": "5.0-0", |
3014 | + "firmware": "14.21.3012 (DEL2810000034)", |
3015 | + "logicalname": "enp1s4f7", |
3016 | + "product": "Illegal Vendor ID", |
3017 | + "serial": "de:ad:be:ef:00:ef", |
3018 | + "speed": None, |
3019 | + "vendor": "Illegal Vendor ID", |
3020 | + }, |
3021 | + { |
3022 | + "businfo": "pci@0000:01:05.0", |
3023 | + "driver": "mlx5_core", |
3024 | + "driverversion": "5.0-0", |
3025 | + "firmware": "14.21.3012 (DEL2810000034)", |
3026 | + "logicalname": "enp1s5", |
3027 | + "product": "Illegal Vendor ID", |
3028 | + "serial": "de:ad:be:ef:00:ef", |
3029 | + "speed": None, |
3030 | + "vendor": "Illegal Vendor ID", |
3031 | + }, |
3032 | + { |
3033 | + "businfo": "pci@0000:01:05.1", |
3034 | + "driver": "mlx5_core", |
3035 | + "driverversion": "5.0-0", |
3036 | + "firmware": "14.21.3012 (DEL2810000034)", |
3037 | + "logicalname": "enp1s5f1", |
3038 | + "product": "Illegal Vendor ID", |
3039 | + "serial": "de:ad:be:ef:00:ef", |
3040 | + "speed": None, |
3041 | + "vendor": "Illegal Vendor ID", |
3042 | + }, |
3043 | + { |
3044 | + "businfo": "pci@0000:83:00.0", |
3045 | + "driver": "mlx5_core", |
3046 | + "driverversion": "5.0-0", |
3047 | + "firmware": "14.21.3012 (DEL2420110034)", |
3048 | + "logicalname": "enp131s0f0", |
3049 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3050 | + "serial": "de:ad:be:ef:00:ef", |
3051 | + "speed": None, |
3052 | + "vendor": "Mellanox Technologies", |
3053 | + }, |
3054 | + { |
3055 | + "businfo": "pci@0000:83:00.1", |
3056 | + "driver": "mlx5_core", |
3057 | + "driverversion": "5.0-0", |
3058 | + "firmware": "14.21.3012 (DEL2420110034)", |
3059 | + "logicalname": "enp131s0f1", |
3060 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3061 | + "serial": "de:ad:be:ef:00:ef", |
3062 | + "speed": None, |
3063 | + "vendor": "Mellanox Technologies", |
3064 | + }, |
3065 | + { |
3066 | + "businfo": "pci@0000:83:02.6", |
3067 | + "driver": "mlx5_core", |
3068 | + "driverversion": "5.0-0", |
3069 | + "firmware": "14.21.3012 (DEL2420110034)", |
3070 | + "logicalname": "enp131s2f6", |
3071 | + "product": "Illegal Vendor ID", |
3072 | + "serial": "de:ad:be:ef:00:ef", |
3073 | + "speed": None, |
3074 | + "vendor": "Illegal Vendor ID", |
3075 | + }, |
3076 | + { |
3077 | + "businfo": "pci@0000:83:02.7", |
3078 | + "driver": "mlx5_core", |
3079 | + "driverversion": "5.0-0", |
3080 | + "firmware": "14.21.3012 (DEL2420110034)", |
3081 | + "logicalname": "enp131s2f7", |
3082 | + "product": "Illegal Vendor ID", |
3083 | + "serial": "de:ad:be:ef:00:ef", |
3084 | + "speed": None, |
3085 | + "vendor": "Illegal Vendor ID", |
3086 | + }, |
3087 | + { |
3088 | + "businfo": "pci@0000:83:03.0", |
3089 | + "driver": "mlx5_core", |
3090 | + "driverversion": "5.0-0", |
3091 | + "firmware": "14.21.3012 (DEL2420110034)", |
3092 | + "logicalname": "enp131s3", |
3093 | + "product": "Illegal Vendor ID", |
3094 | + "serial": "de:ad:be:ef:00:ef", |
3095 | + "speed": None, |
3096 | + "vendor": "Illegal Vendor ID", |
3097 | + }, |
3098 | + { |
3099 | + "businfo": "pci@0000:83:03.1", |
3100 | + "driver": "mlx5_core", |
3101 | + "driverversion": "5.0-0", |
3102 | + "firmware": "14.21.3012 (DEL2420110034)", |
3103 | + "logicalname": "enp131s3f1", |
3104 | + "product": "Illegal Vendor ID", |
3105 | + "serial": "de:ad:be:ef:00:ef", |
3106 | + "speed": None, |
3107 | + "vendor": "Illegal Vendor ID", |
3108 | + }, |
3109 | + { |
3110 | + "businfo": "pci@0000:83:03.2", |
3111 | + "driver": "mlx5_core", |
3112 | + "driverversion": "5.0-0", |
3113 | + "firmware": "14.21.3012 (DEL2420110034)", |
3114 | + "logicalname": "enp131s3f2", |
3115 | + "product": "Illegal Vendor ID", |
3116 | + "serial": "de:ad:be:ef:00:ef", |
3117 | + "speed": None, |
3118 | + "vendor": "Illegal Vendor ID", |
3119 | + }, |
3120 | + { |
3121 | + "businfo": "pci@0000:83:03.3", |
3122 | + "driver": "mlx5_core", |
3123 | + "driverversion": "5.0-0", |
3124 | + "firmware": "14.21.3012 (DEL2420110034)", |
3125 | + "logicalname": "enp131s3f3", |
3126 | + "product": "Illegal Vendor ID", |
3127 | + "serial": "de:ad:be:ef:00:ef", |
3128 | + "speed": None, |
3129 | + "vendor": "Illegal Vendor ID", |
3130 | + }, |
3131 | + { |
3132 | + "businfo": "pci@0000:83:03.4", |
3133 | + "driver": "mlx5_core", |
3134 | + "driverversion": "5.0-0", |
3135 | + "firmware": "14.21.3012 (DEL2420110034)", |
3136 | + "logicalname": "enp131s3f4", |
3137 | + "product": "Illegal Vendor ID", |
3138 | + "serial": "de:ad:be:ef:00:ef", |
3139 | + "speed": None, |
3140 | + "vendor": "Illegal Vendor ID", |
3141 | + }, |
3142 | + { |
3143 | + "businfo": "pci@0000:83:03.5", |
3144 | + "driver": "mlx5_core", |
3145 | + "driverversion": "5.0-0", |
3146 | + "firmware": "14.21.3012 (DEL2420110034)", |
3147 | + "logicalname": "enp131s3f5", |
3148 | + "product": "Illegal Vendor ID", |
3149 | + "serial": "de:ad:be:ef:00:ef", |
3150 | + "speed": None, |
3151 | + "vendor": "Illegal Vendor ID", |
3152 | + }, |
3153 | + { |
3154 | + "businfo": "pci@0000:83:03.6", |
3155 | + "driver": "mlx5_core", |
3156 | + "driverversion": "5.0-0", |
3157 | + "firmware": "14.21.3012 (DEL2420110034)", |
3158 | + "logicalname": "enp131s3f6", |
3159 | + "product": "Illegal Vendor ID", |
3160 | + "serial": "de:ad:be:ef:00:ef", |
3161 | + "speed": None, |
3162 | + "vendor": "Illegal Vendor ID", |
3163 | + }, |
3164 | + { |
3165 | + "businfo": "pci@0000:83:03.7", |
3166 | + "driver": "mlx5_core", |
3167 | + "driverversion": "5.0-0", |
3168 | + "firmware": "14.21.3012 (DEL2420110034)", |
3169 | + "logicalname": "enp131s3f7", |
3170 | + "product": "Illegal Vendor ID", |
3171 | + "serial": "de:ad:be:ef:00:ef", |
3172 | + "speed": None, |
3173 | + "vendor": "Illegal Vendor ID", |
3174 | + }, |
3175 | + { |
3176 | + "businfo": "pci@0000:83:04.0", |
3177 | + "driver": "mlx5_core", |
3178 | + "driverversion": "5.0-0", |
3179 | + "firmware": "14.21.3012 (DEL2420110034)", |
3180 | + "logicalname": "enp131s4", |
3181 | + "product": "Illegal Vendor ID", |
3182 | + "serial": "de:ad:be:ef:00:ef", |
3183 | + "speed": None, |
3184 | + "vendor": "Illegal Vendor ID", |
3185 | + }, |
3186 | + { |
3187 | + "businfo": "pci@0000:83:04.1", |
3188 | + "driver": "mlx5_core", |
3189 | + "driverversion": "5.0-0", |
3190 | + "firmware": "14.21.3012 (DEL2420110034)", |
3191 | + "logicalname": "enp131s4f1", |
3192 | + "product": "Illegal Vendor ID", |
3193 | + "serial": "de:ad:be:ef:00:ef", |
3194 | + "speed": None, |
3195 | + "vendor": "Illegal Vendor ID", |
3196 | + }, |
3197 | + { |
3198 | + "businfo": "pci@0000:83:04.2", |
3199 | + "driver": "mlx5_core", |
3200 | + "driverversion": "5.0-0", |
3201 | + "firmware": "14.21.3012 (DEL2420110034)", |
3202 | + "logicalname": "enp131s4f2", |
3203 | + "product": "Illegal Vendor ID", |
3204 | + "serial": "de:ad:be:ef:00:ef", |
3205 | + "speed": None, |
3206 | + "vendor": "Illegal Vendor ID", |
3207 | + }, |
3208 | + { |
3209 | + "businfo": "pci@0000:83:04.3", |
3210 | + "driver": "mlx5_core", |
3211 | + "driverversion": "5.0-0", |
3212 | + "firmware": "14.21.3012 (DEL2420110034)", |
3213 | + "logicalname": "enp131s4f3", |
3214 | + "product": "Illegal Vendor ID", |
3215 | + "serial": "de:ad:be:ef:00:ef", |
3216 | + "speed": None, |
3217 | + "vendor": "Illegal Vendor ID", |
3218 | + }, |
3219 | + { |
3220 | + "businfo": "pci@0000:83:04.4", |
3221 | + "driver": "mlx5_core", |
3222 | + "driverversion": "5.0-0", |
3223 | + "firmware": "14.21.3012 (DEL2420110034)", |
3224 | + "logicalname": "enp131s4f4", |
3225 | + "product": "Illegal Vendor ID", |
3226 | + "serial": "de:ad:be:ef:00:ef", |
3227 | + "speed": None, |
3228 | + "vendor": "Illegal Vendor ID", |
3229 | + }, |
3230 | + { |
3231 | + "businfo": "pci@0000:83:04.5", |
3232 | + "driver": "mlx5_core", |
3233 | + "driverversion": "5.0-0", |
3234 | + "firmware": "14.21.3012 (DEL2420110034)", |
3235 | + "logicalname": "enp131s4f5", |
3236 | + "product": "Illegal Vendor ID", |
3237 | + "serial": "de:ad:be:ef:00:ef", |
3238 | + "speed": None, |
3239 | + "vendor": "Illegal Vendor ID", |
3240 | + }, |
3241 | + { |
3242 | + "businfo": "pci@0000:83:04.6", |
3243 | + "driver": "mlx5_core", |
3244 | + "driverversion": "5.0-0", |
3245 | + "firmware": "14.21.3012 (DEL2420110034)", |
3246 | + "logicalname": "enp131s4f6", |
3247 | + "product": "Illegal Vendor ID", |
3248 | + "serial": "de:ad:be:ef:00:ef", |
3249 | + "speed": None, |
3250 | + "vendor": "Illegal Vendor ID", |
3251 | + }, |
3252 | + { |
3253 | + "businfo": "pci@0000:83:04.7", |
3254 | + "driver": "mlx5_core", |
3255 | + "driverversion": "5.0-0", |
3256 | + "firmware": "14.21.3012 (DEL2420110034)", |
3257 | + "logicalname": "enp131s4f7", |
3258 | + "product": "Illegal Vendor ID", |
3259 | + "serial": "de:ad:be:ef:00:ef", |
3260 | + "speed": None, |
3261 | + "vendor": "Illegal Vendor ID", |
3262 | + }, |
3263 | + { |
3264 | + "businfo": "pci@0000:83:05.0", |
3265 | + "driver": "mlx5_core", |
3266 | + "driverversion": "5.0-0", |
3267 | + "firmware": "14.21.3012 (DEL2420110034)", |
3268 | + "logicalname": "enp131s5", |
3269 | + "product": "Illegal Vendor ID", |
3270 | + "serial": "de:ad:be:ef:00:ef", |
3271 | + "speed": None, |
3272 | + "vendor": "Illegal Vendor ID", |
3273 | + }, |
3274 | + { |
3275 | + "businfo": "pci@0000:83:05.1", |
3276 | + "driver": "mlx5_core", |
3277 | + "driverversion": "5.0-0", |
3278 | + "firmware": "14.21.3012 (DEL2420110034)", |
3279 | + "logicalname": "enp131s5f1", |
3280 | + "product": "Illegal Vendor ID", |
3281 | + "serial": "de:ad:be:ef:00:ef", |
3282 | + "speed": None, |
3283 | + "vendor": "Illegal Vendor ID", |
3284 | + }, |
3285 | + ], |
3286 | + }, |
3287 | + "lshw.dell.02.json": { |
3288 | + "system": { |
3289 | + "description": "Notebook", |
3290 | + "hostname": "pluto", |
3291 | + "product": "Precision 7520 (07B0)", |
3292 | + "serial": "1N79PH2", |
3293 | + "vendor": "Dell Inc.", |
3294 | + "version": None, |
3295 | + }, |
3296 | + "motherboard": [ |
3297 | + { |
3298 | + "description": "Motherboard", |
3299 | + "product": "04FCWT", |
3300 | + "serial": "/1N79PH2/CN129637BG002D/", |
3301 | + "vendor": "Dell Inc.", |
3302 | + "version": "A00", |
3303 | + } |
3304 | + ], |
3305 | + "storage_class_info": [ |
3306 | + { |
3307 | + "businfo": "pci@0000:00:17.0", |
3308 | + "driver": "ahci", |
3309 | + "has_children": False, |
3310 | + "product": "Q170/Q150/B150/H170/H110/Z170/CM236 Chipset SATA Controller [AHCI Mode]", # noqa E501 |
3311 | + "vendor": "Intel Corporation", |
3312 | + }, |
3313 | + { |
3314 | + "businfo": "pci@0000:3c:00.0", |
3315 | + "driver": "nvme", |
3316 | + "has_children": False, |
3317 | + "product": "NVMe SSD Controller SM961/PM961", |
3318 | + "vendor": "Samsung Electronics Co Ltd", |
3319 | + }, |
3320 | + ], |
3321 | + "disk_class_info": [], |
3322 | + "network_class_info": [ |
3323 | + { |
3324 | + "businfo": "pci@0000:01:00.0", |
3325 | + "driver": "iwlwifi", |
3326 | + "driverversion": "4.15.0-46-generic", |
3327 | + "firmware": "34.0.1", |
3328 | + "logicalname": "wlp1s0", |
3329 | + "product": "Wireless 8265 / 8275", |
3330 | + "serial": "de:ad:be:ef:00:ef", |
3331 | + "speed": None, |
3332 | + "vendor": "Intel Corporation", |
3333 | + }, |
3334 | + { |
3335 | + "businfo": "pci@0000:00:1f.6", |
3336 | + "driver": "e1000e", |
3337 | + "driverversion": "3.2.6-k", |
3338 | + "firmware": "0.1-3", |
3339 | + "logicalname": "enp0s31f6", |
3340 | + "product": "Ethernet Connection (5) I219-LM", |
3341 | + "serial": "de:ad:be:ef:00:ef", |
3342 | + "speed": "1Gbit/s", |
3343 | + "vendor": "Intel Corporation", |
3344 | + }, |
3345 | + ], |
3346 | + }, |
3347 | + "lshw.hp.json": { |
3348 | + "system": { |
3349 | + "description": "Rack Mount Chassis", |
3350 | + "hostname": "hpmachine", |
3351 | + "product": "ProLiant DL380 Gen9 (719061-B21)", |
3352 | + "serial": "MXQ53804X4", |
3353 | + "vendor": "HP", |
3354 | + "version": None, |
3355 | + }, |
3356 | + "motherboard": [ |
3357 | + { |
3358 | + "description": "Motherboard", |
3359 | + "product": None, |
3360 | + "serial": None, |
3361 | + "vendor": None, |
3362 | + "version": None, |
3363 | + } |
3364 | + ], |
3365 | + "storage_class_info": [ |
3366 | + { |
3367 | + "businfo": "pci@0000:08:00.0", |
3368 | + "driver": "hpsa", |
3369 | + "has_children": True, |
3370 | + "product": "Smart Array Gen9 Controllers", |
3371 | + "vendor": "Hewlett-Packard Company", |
3372 | + }, |
3373 | + { |
3374 | + "businfo": "pci@0000:0b:00.0", |
3375 | + "driver": "nvme", |
3376 | + "has_children": False, |
3377 | + "product": "PCIe Data Center SSD", |
3378 | + "vendor": "Intel Corporation", |
3379 | + }, |
3380 | + ], |
3381 | + "disk_class_info": [], |
3382 | + "network_class_info": [ |
3383 | + { |
3384 | + "businfo": "pci@0000:05:00.0", |
3385 | + "driver": "ixgbe", |
3386 | + "driverversion": "4.2.1-k", |
3387 | + "firmware": "0x800005ab", |
3388 | + "logicalname": "ens1f0", |
3389 | + "product": "Ethernet Controller 10-Gigabit X540-AT2", |
3390 | + "serial": "de:ad:be:ef:00:ef", |
3391 | + "speed": "10Gbit/s", |
3392 | + "vendor": "Intel Corporation", |
3393 | + }, |
3394 | + { |
3395 | + "businfo": "pci@0000:05:00.1", |
3396 | + "driver": "ixgbe", |
3397 | + "driverversion": "4.2.1-k", |
3398 | + "firmware": "0x800005ab", |
3399 | + "logicalname": "ens1f1", |
3400 | + "product": "Ethernet Controller 10-Gigabit X540-AT2", |
3401 | + "serial": "de:ad:be:ef:00:ef", |
3402 | + "speed": "10Gbit/s", |
3403 | + "vendor": "Intel Corporation", |
3404 | + }, |
3405 | + { |
3406 | + "businfo": "pci@0000:04:00.0", |
3407 | + "driver": "ixgbe", |
3408 | + "driverversion": "4.2.1-k", |
3409 | + "firmware": "0x800005ac", |
3410 | + "logicalname": "eno49", |
3411 | + "product": "Ethernet Controller 10-Gigabit X540-AT2", |
3412 | + "serial": "de:ad:be:ef:00:ef", |
3413 | + "speed": "10Gbit/s", |
3414 | + "vendor": "Intel Corporation", |
3415 | + }, |
3416 | + { |
3417 | + "businfo": "pci@0000:04:00.1", |
3418 | + "driver": "ixgbe", |
3419 | + "driverversion": "4.2.1-k", |
3420 | + "firmware": "0x800005ac", |
3421 | + "logicalname": "eno50", |
3422 | + "product": "Ethernet Controller 10-Gigabit X540-AT2", |
3423 | + "serial": "de:ad:be:ef:00:ef", |
3424 | + "speed": "10Gbit/s", |
3425 | + "vendor": "Intel Corporation", |
3426 | + }, |
3427 | + { |
3428 | + "businfo": "pci@0000:02:00.0", |
3429 | + "driver": "tg3", |
3430 | + "driverversion": "3.137", |
3431 | + "firmware": "5719-v1.41 NCSI v1.3.7.0", |
3432 | + "logicalname": "eno1", |
3433 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3434 | + "serial": "de:ad:be:ef:00:ef", |
3435 | + "speed": None, |
3436 | + "vendor": "Broadcom Corporation", |
3437 | + }, |
3438 | + { |
3439 | + "businfo": "pci@0000:02:00.1", |
3440 | + "driver": "tg3", |
3441 | + "driverversion": "3.137", |
3442 | + "firmware": "5719-v1.41 NCSI v1.3.7.0", |
3443 | + "logicalname": "eno2", |
3444 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3445 | + "serial": "de:ad:be:ef:00:ef", |
3446 | + "speed": None, |
3447 | + "vendor": "Broadcom Corporation", |
3448 | + }, |
3449 | + { |
3450 | + "businfo": "pci@0000:02:00.2", |
3451 | + "driver": "tg3", |
3452 | + "driverversion": "3.137", |
3453 | + "firmware": "5719-v1.41 NCSI v1.3.7.0", |
3454 | + "logicalname": "eno3", |
3455 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3456 | + "serial": "de:ad:be:ef:00:ef", |
3457 | + "speed": None, |
3458 | + "vendor": "Broadcom Corporation", |
3459 | + }, |
3460 | + { |
3461 | + "businfo": "pci@0000:02:00.3", |
3462 | + "driver": "tg3", |
3463 | + "driverversion": "3.137", |
3464 | + "firmware": "5719-v1.41 NCSI v1.3.7.0", |
3465 | + "logicalname": "eno4", |
3466 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3467 | + "serial": "de:ad:be:ef:00:ef", |
3468 | + "speed": None, |
3469 | + "vendor": "Broadcom Corporation", |
3470 | + }, |
3471 | + ], |
3472 | + }, |
3473 | + "lshw.hpe.nonvme.json": { |
3474 | + "system": { |
3475 | + "description": "Rack Mount Chassis", |
3476 | + "hostname": "lcpip-hcosmg01", |
3477 | + "product": "ProLiant DL380 Gen10 (868706-B21)", |
3478 | + "serial": "2M291303M6", |
3479 | + "vendor": "HPE", |
3480 | + "version": None, |
3481 | + }, |
3482 | + "motherboard": [ |
3483 | + { |
3484 | + "description": "Motherboard", |
3485 | + "product": "ProLiant DL380 Gen10", |
3486 | + "serial": "PFARA%%LMBR4ZT", |
3487 | + "vendor": "HPE", |
3488 | + "version": None, |
3489 | + } |
3490 | + ], |
3491 | + "storage_class_info": [ |
3492 | + { |
3493 | + "businfo": "pci@0000:5c:00.0", |
3494 | + "driver": "smartpqi", |
3495 | + "has_children": True, |
3496 | + "product": "Smart Storage PQI 12G SAS/PCIe 3", |
3497 | + "vendor": "Adaptec", |
3498 | + } |
3499 | + ], |
3500 | + "disk_class_info": [ |
3501 | + { |
3502 | + "businfo": "scsi@1:1.0.0", |
3503 | + "dev": "8:0", |
3504 | + "logicalname": "/dev/sda", |
3505 | + "physid": "1.0.0", |
3506 | + "product": "LOGICAL VOLUME", |
3507 | + "serial": "PEYHC0DRHBU40M", |
3508 | + "size": 8001529315328, |
3509 | + "storage_parent": "Smart Storage PQI 12G SAS/PCIe 3", |
3510 | + }, |
3511 | + { |
3512 | + "businfo": "scsi@1:1.0.1", |
3513 | + "dev": "8:16", |
3514 | + "logicalname": "/dev/sdb", |
3515 | + "physid": "1.0.1", |
3516 | + "product": "LOGICAL VOLUME", |
3517 | + "serial": "PEYHC0DRHBU40M", |
3518 | + "size": 800131645440, |
3519 | + "storage_parent": "Smart Storage PQI 12G SAS/PCIe 3", |
3520 | + }, |
3521 | + ], |
3522 | + "network_class_info": [ |
3523 | + { |
3524 | + "businfo": "pci@0000:02:00.0", |
3525 | + "driver": "tg3", |
3526 | + "driverversion": "3.137", |
3527 | + "firmware": "5719-v1.46 NCSI v1.5.1.0", |
3528 | + "logicalname": "eno1", |
3529 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3530 | + "serial": "2e:77:1a:56:c8:fb", |
3531 | + "speed": "1Gbit/s", |
3532 | + "vendor": "Broadcom Inc. and subsidiaries", |
3533 | + }, |
3534 | + { |
3535 | + "businfo": "pci@0000:02:00.1", |
3536 | + "driver": "tg3", |
3537 | + "driverversion": "3.137", |
3538 | + "firmware": "5719-v1.46 NCSI v1.5.1.0", |
3539 | + "logicalname": "eno2", |
3540 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3541 | + "serial": "2e:77:1a:56:c8:fb", |
3542 | + "speed": "1Gbit/s", |
3543 | + "vendor": "Broadcom Inc. and subsidiaries", |
3544 | + }, |
3545 | + { |
3546 | + "businfo": "pci@0000:02:00.2", |
3547 | + "driver": "tg3", |
3548 | + "driverversion": "3.137", |
3549 | + "firmware": "5719-v1.46 NCSI v1.5.1.0", |
3550 | + "logicalname": "eno3", |
3551 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3552 | + "serial": "b8:83:03:52:42:26", |
3553 | + "speed": None, |
3554 | + "vendor": "Broadcom Inc. and subsidiaries", |
3555 | + }, |
3556 | + { |
3557 | + "businfo": "pci@0000:02:00.3", |
3558 | + "driver": "tg3", |
3559 | + "driverversion": "3.137", |
3560 | + "firmware": "5719-v1.46 NCSI v1.5.1.0", |
3561 | + "logicalname": "eno4", |
3562 | + "product": "NetXtreme BCM5719 Gigabit Ethernet PCIe", |
3563 | + "serial": "b8:83:03:52:42:27", |
3564 | + "speed": None, |
3565 | + "vendor": "Broadcom Inc. and subsidiaries", |
3566 | + }, |
3567 | + { |
3568 | + "businfo": "pci@0000:5d:00.0", |
3569 | + "driver": "mlx5_core", |
3570 | + "driverversion": "5.0-0", |
3571 | + "firmware": "14.23.8052 (HP_2690110034)", |
3572 | + "logicalname": "eno5", |
3573 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3574 | + "serial": "a2:ce:45:45:2b:ea", |
3575 | + "speed": None, |
3576 | + "vendor": "Mellanox Technologies", |
3577 | + }, |
3578 | + { |
3579 | + "businfo": "pci@0000:5d:00.1", |
3580 | + "driver": "mlx5_core", |
3581 | + "driverversion": "5.0-0", |
3582 | + "firmware": "14.23.8052 (HP_2690110034)", |
3583 | + "logicalname": "eno6", |
3584 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3585 | + "serial": "a2:ce:45:45:2b:ea", |
3586 | + "speed": None, |
3587 | + "vendor": "Mellanox Technologies", |
3588 | + }, |
3589 | + ], |
3590 | + }, |
3591 | + "lshw.huawei.json": { |
3592 | + "system": { |
3593 | + "description": "System", |
3594 | + "hostname": "huaweimachine", |
3595 | + "product": "RH2288H V3 (Type1Sku0)", |
3596 | + "serial": "2102311GHG10G2000031", |
3597 | + "vendor": "Huawei", |
3598 | + "version": "V100R003", |
3599 | + }, |
3600 | + "motherboard": [ |
3601 | + { |
3602 | + "description": "Motherboard", |
3603 | + "product": "BC11HGSA0", |
3604 | + "serial": "022HLVCNFB004121", |
3605 | + "vendor": "Huawei", |
3606 | + "version": "V100R003", |
3607 | + } |
3608 | + ], |
3609 | + "storage_class_info": [ |
3610 | + { |
3611 | + "businfo": "pci@0000:01:00.0", |
3612 | + "driver": "mpt3sas", |
3613 | + "has_children": True, |
3614 | + "product": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3615 | + "vendor": "LSI Logic / Symbios Logic", |
3616 | + }, |
3617 | + { |
3618 | + "businfo": "pci@0000:03:00.0", |
3619 | + "driver": "hio", |
3620 | + "has_children": False, |
3621 | + "product": None, |
3622 | + "vendor": None, |
3623 | + }, |
3624 | + { |
3625 | + "businfo": "pci@0000:00:11.4", |
3626 | + "driver": "ahci", |
3627 | + "has_children": False, |
3628 | + "product": "C610/X99 series chipset sSATA Controller [AHCI mode]", # noqa E501 |
3629 | + "vendor": "Intel Corporation", |
3630 | + }, |
3631 | + { |
3632 | + "businfo": "pci@0000:00:1f.2", |
3633 | + "driver": "ahci", |
3634 | + "has_children": False, |
3635 | + "product": "C610/X99 series chipset 6-Port SATA Controller [AHCI mode]", # noqa E501 |
3636 | + "vendor": "Intel Corporation", |
3637 | + }, |
3638 | + ], |
3639 | + "disk_class_info": [ |
3640 | + { |
3641 | + "businfo": "scsi@0:0.0.0", |
3642 | + "dev": "8:0", |
3643 | + "logicalname": "/dev/sda", |
3644 | + "physid": "0.0.0", |
3645 | + "product": "HGST HUS724040AL", |
3646 | + "serial": "PN1334PEJ6T0XS", |
3647 | + "size": 4000787030016, |
3648 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3649 | + }, |
3650 | + { |
3651 | + "businfo": "scsi@0:0.1.0", |
3652 | + "dev": "8:16", |
3653 | + "logicalname": "/dev/sdb", |
3654 | + "physid": "0.1.0", |
3655 | + "product": "HGST HUS724040AL", |
3656 | + "serial": "PN1334PEJ8L6XS", |
3657 | + "size": 4000787030016, |
3658 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3659 | + }, |
3660 | + { |
3661 | + "businfo": "scsi@0:0.2.0", |
3662 | + "dev": "8:32", |
3663 | + "logicalname": "/dev/sdc", |
3664 | + "physid": "0.2.0", |
3665 | + "product": "HGST HUS724040AL", |
3666 | + "serial": "PN1334PEJ93NES", |
3667 | + "size": 4000787030016, |
3668 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3669 | + }, |
3670 | + { |
3671 | + "businfo": "scsi@0:0.3.0", |
3672 | + "dev": "8:48", |
3673 | + "logicalname": "/dev/sdd", |
3674 | + "physid": "0.3.0", |
3675 | + "product": "HGST HUS724040AL", |
3676 | + "serial": "PN1334PEJ93J3S", |
3677 | + "size": 4000787030016, |
3678 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3679 | + }, |
3680 | + { |
3681 | + "businfo": "scsi@0:0.4.0", |
3682 | + "dev": "8:64", |
3683 | + "logicalname": "/dev/sde", |
3684 | + "physid": "0.4.0", |
3685 | + "product": "HGST HUS724040AL", |
3686 | + "serial": "PN1334PEJ6SZYS", |
3687 | + "size": 4000787030016, |
3688 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3689 | + }, |
3690 | + { |
3691 | + "businfo": "scsi@0:0.5.0", |
3692 | + "dev": "8:80", |
3693 | + "logicalname": "/dev/sdf", |
3694 | + "physid": "0.5.0", |
3695 | + "product": "HGST HUS724040AL", |
3696 | + "serial": "PN1334PEJ93MKS", |
3697 | + "size": 4000787030016, |
3698 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3699 | + }, |
3700 | + { |
3701 | + "businfo": "scsi@0:0.6.0", |
3702 | + "dev": "8:96", |
3703 | + "logicalname": "/dev/sdg", |
3704 | + "physid": "0.6.0", |
3705 | + "product": "HGST HUS724040AL", |
3706 | + "serial": "PN1334PEJ8L7TS", |
3707 | + "size": 4000787030016, |
3708 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3709 | + }, |
3710 | + { |
3711 | + "businfo": "scsi@0:0.7.0", |
3712 | + "dev": "8:112", |
3713 | + "logicalname": "/dev/sdh", |
3714 | + "physid": "0.7.0", |
3715 | + "product": "HGST HUS724040AL", |
3716 | + "serial": "PN1334PEJ8L85S", |
3717 | + "size": 4000787030016, |
3718 | + "storage_parent": "SAS2308 PCI-Express Fusion-MPT SAS-2", |
3719 | + }, |
3720 | + ], |
3721 | + "network_class_info": [ |
3722 | + { |
3723 | + "businfo": "pci@0000:02:00.0", |
3724 | + "driver": "igb", |
3725 | + "driverversion": "5.4.0-k", |
3726 | + "firmware": "1.63, 0x800009fa", |
3727 | + "logicalname": "enp2s0f0", |
3728 | + "product": "I350 Gigabit Network Connection", |
3729 | + "serial": "de:ad:be:ef:00:ef", |
3730 | + "speed": "1Gbit/s", |
3731 | + "vendor": "Intel Corporation", |
3732 | + }, |
3733 | + { |
3734 | + "businfo": "pci@0000:02:00.1", |
3735 | + "driver": "igb", |
3736 | + "driverversion": "5.4.0-k", |
3737 | + "firmware": "1.63, 0x800009fa", |
3738 | + "logicalname": "enp2s0f1", |
3739 | + "product": "I350 Gigabit Network Connection", |
3740 | + "serial": "de:ad:be:ef:00:ef", |
3741 | + "speed": "1Gbit/s", |
3742 | + "vendor": "Intel Corporation", |
3743 | + }, |
3744 | + { |
3745 | + "businfo": "pci@0000:82:00.0", |
3746 | + "driver": "mlx5_core", |
3747 | + "driverversion": "5.0-0", |
3748 | + "firmware": "14.18.2000 (MT_2420110034)", |
3749 | + "logicalname": "enp130s0f0", |
3750 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3751 | + "serial": "de:ad:be:ef:00:ef", |
3752 | + "speed": "10Gbit/s", |
3753 | + "vendor": "Mellanox Technologies", |
3754 | + }, |
3755 | + { |
3756 | + "businfo": "pci@0000:82:00.1", |
3757 | + "driver": "mlx5_core", |
3758 | + "driverversion": "5.0-0", |
3759 | + "firmware": "14.18.2000 (MT_2420110034)", |
3760 | + "logicalname": "enp130s0f1", |
3761 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3762 | + "serial": "de:ad:be:ef:00:ef", |
3763 | + "speed": "10Gbit/s", |
3764 | + "vendor": "Mellanox Technologies", |
3765 | + }, |
3766 | + { |
3767 | + "businfo": "pci@0000:81:00.0", |
3768 | + "driver": "mlx5_core", |
3769 | + "driverversion": "5.0-0", |
3770 | + "firmware": "14.18.2000 (MT_2420110034)", |
3771 | + "logicalname": "enp129s0f0", |
3772 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3773 | + "serial": "de:ad:be:ef:00:ef", |
3774 | + "speed": "10Gbit/s", |
3775 | + "vendor": "Mellanox Technologies", |
3776 | + }, |
3777 | + { |
3778 | + "businfo": "pci@0000:81:00.1", |
3779 | + "driver": "mlx5_core", |
3780 | + "driverversion": "5.0-0", |
3781 | + "firmware": "14.18.2000 (MT_2420110034)", |
3782 | + "logicalname": "enp129s0f1", |
3783 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3784 | + "serial": "de:ad:be:ef:00:ef", |
3785 | + "speed": "10Gbit/s", |
3786 | + "vendor": "Mellanox Technologies", |
3787 | + }, |
3788 | + ], |
3789 | + }, |
3790 | + "lshw.supermicro.notools.json": { |
3791 | + "system": { |
3792 | + "description": "Computer", |
3793 | + "hostname": "supermicro01", |
3794 | + "product": "SYS-6029U-TR4T (To be filled by O.E.M.)", |
3795 | + "serial": "E263684X8302310", |
3796 | + "vendor": "Supermicro", |
3797 | + "version": "0123456789", |
3798 | + }, |
3799 | + "motherboard": [ |
3800 | + { |
3801 | + "description": "Motherboard", |
3802 | + "product": "X11DPU", |
3803 | + "serial": "OM181S021896", |
3804 | + "vendor": "Supermicro", |
3805 | + "version": "1.10", |
3806 | + } |
3807 | + ], |
3808 | + "storage_class_info": [ |
3809 | + { |
3810 | + "businfo": "pci@0000:00:11.5", |
3811 | + "driver": "ahci", |
3812 | + "has_children": False, |
3813 | + "product": "Lewisburg SSATA Controller [AHCI mode]", |
3814 | + "vendor": "Intel Corporation", |
3815 | + }, |
3816 | + { |
3817 | + "businfo": "pci@0000:00:17.0", |
3818 | + "driver": "ahci", |
3819 | + "has_children": False, |
3820 | + "product": "Lewisburg SATA Controller [AHCI mode]", |
3821 | + "vendor": "Intel Corporation", |
3822 | + }, |
3823 | + ], |
3824 | + "disk_class_info": [], |
3825 | + "network_class_info": [ |
3826 | + { |
3827 | + "businfo": "pci@0000:18:00.0", |
3828 | + "driver": "i40e", |
3829 | + "driverversion": "2.1.14-k", |
3830 | + "firmware": "6.01 0x800036f0 1.1861.0", |
3831 | + "logicalname": "eno1", |
3832 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
3833 | + "serial": "de:ad:be:ef:00:ef", |
3834 | + "speed": "10Gbit/s", |
3835 | + "vendor": "Intel Corporation", |
3836 | + }, |
3837 | + { |
3838 | + "businfo": "pci@0000:18:00.1", |
3839 | + "driver": "i40e", |
3840 | + "driverversion": "2.1.14-k", |
3841 | + "firmware": "6.01 0x800036f0 1.1861.0", |
3842 | + "logicalname": "eno2", |
3843 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
3844 | + "serial": "de:ad:be:ef:00:ef", |
3845 | + "speed": "10Gbit/s", |
3846 | + "vendor": "Intel Corporation", |
3847 | + }, |
3848 | + { |
3849 | + "businfo": "pci@0000:18:00.2", |
3850 | + "driver": "i40e", |
3851 | + "driverversion": "2.1.14-k", |
3852 | + "firmware": "6.01 0x800036f0 1.1861.0", |
3853 | + "logicalname": "eno3", |
3854 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
3855 | + "serial": "de:ad:be:ef:00:ef", |
3856 | + "speed": None, |
3857 | + "vendor": "Intel Corporation", |
3858 | + }, |
3859 | + { |
3860 | + "businfo": "pci@0000:18:00.3", |
3861 | + "driver": "i40e", |
3862 | + "driverversion": "2.1.14-k", |
3863 | + "firmware": "6.01 0x800036f0 1.1861.0", |
3864 | + "logicalname": "eno4", |
3865 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
3866 | + "serial": "de:ad:be:ef:00:ef", |
3867 | + "speed": None, |
3868 | + "vendor": "Intel Corporation", |
3869 | + }, |
3870 | + { |
3871 | + "businfo": "pci@0000:af:00.0", |
3872 | + "driver": "mlx5_core", |
3873 | + "driverversion": "5.0-0", |
3874 | + "firmware": "14.21.1000 (SM_2001000001034)", |
3875 | + "logicalname": "enp175s0f0", |
3876 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3877 | + "serial": "de:ad:be:ef:00:ef", |
3878 | + "speed": None, |
3879 | + "vendor": "Mellanox Technologies", |
3880 | + }, |
3881 | + { |
3882 | + "businfo": "pci@0000:af:00.1", |
3883 | + "driver": "mlx5_core", |
3884 | + "driverversion": "5.0-0", |
3885 | + "firmware": "14.21.1000 (SM_2001000001034)", |
3886 | + "logicalname": "enp175s0f1", |
3887 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3888 | + "serial": "de:ad:be:ef:00:ef", |
3889 | + "speed": None, |
3890 | + "vendor": "Mellanox Technologies", |
3891 | + }, |
3892 | + { |
3893 | + "businfo": "pci@0000:d8:00.0", |
3894 | + "driver": "mlx5_core", |
3895 | + "driverversion": "5.0-0", |
3896 | + "firmware": "14.21.1000 (SM_2001000001034)", |
3897 | + "logicalname": "enp216s0f0", |
3898 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3899 | + "serial": "de:ad:be:ef:00:ef", |
3900 | + "speed": None, |
3901 | + "vendor": "Mellanox Technologies", |
3902 | + }, |
3903 | + { |
3904 | + "businfo": "pci@0000:d8:00.1", |
3905 | + "driver": "mlx5_core", |
3906 | + "driverversion": "5.0-0", |
3907 | + "firmware": "14.21.1000 (SM_2001000001034)", |
3908 | + "logicalname": "enp216s0f1", |
3909 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
3910 | + "serial": "de:ad:be:ef:00:ef", |
3911 | + "speed": None, |
3912 | + "vendor": "Mellanox Technologies", |
3913 | + }, |
3914 | + ], |
3915 | + }, |
3916 | + "lshw.supermicro.nvme.json": { |
3917 | + "system": { |
3918 | + "description": "Computer", |
3919 | + "hostname": "supermicro-nvme", |
3920 | + "product": "SYS-2029U-TN24R4T (To be filled by O.E.M.)", |
3921 | + "serial": "E264025X8400712", |
3922 | + "vendor": "Supermicro", |
3923 | + "version": "0123456789", |
3924 | + }, |
3925 | + "motherboard": [ |
3926 | + { |
3927 | + "description": "Motherboard", |
3928 | + "product": "X11DPU", |
3929 | + "serial": "OM183S019050", |
3930 | + "vendor": "Supermicro", |
3931 | + "version": "1.10", |
3932 | + } |
3933 | + ], |
3934 | + "storage_class_info": [ |
3935 | + { |
3936 | + "businfo": "pci@0000:00:11.5", |
3937 | + "driver": "ahci", |
3938 | + "has_children": False, |
3939 | + "product": "Lewisburg SSATA Controller [AHCI mode]", |
3940 | + "vendor": "Intel Corporation", |
3941 | + }, |
3942 | + { |
3943 | + "businfo": "pci@0000:00:17.0", |
3944 | + "driver": "ahci", |
3945 | + "has_children": False, |
3946 | + "product": "Lewisburg SATA Controller [AHCI mode]", |
3947 | + "vendor": "Intel Corporation", |
3948 | + }, |
3949 | + { |
3950 | + "businfo": "pci@0000:60:00.0", |
3951 | + "driver": "nvme", |
3952 | + "has_children": False, |
3953 | + "product": "Intel Corporation", |
3954 | + "vendor": "Intel Corporation", |
3955 | + }, |
3956 | + { |
3957 | + "businfo": "pci@0000:61:00.0", |
3958 | + "driver": "nvme", |
3959 | + "has_children": False, |
3960 | + "product": "Intel Corporation", |
3961 | + "vendor": "Intel Corporation", |
3962 | + }, |
3963 | + { |
3964 | + "businfo": "pci@0000:62:00.0", |
3965 | + "driver": "nvme", |
3966 | + "has_children": False, |
3967 | + "product": "Intel Corporation", |
3968 | + "vendor": "Intel Corporation", |
3969 | + }, |
3970 | + { |
3971 | + "businfo": "pci@0000:63:00.0", |
3972 | + "driver": "nvme", |
3973 | + "has_children": False, |
3974 | + "product": "Intel Corporation", |
3975 | + "vendor": "Intel Corporation", |
3976 | + }, |
3977 | + { |
3978 | + "businfo": "pci@0000:64:00.0", |
3979 | + "driver": "nvme", |
3980 | + "has_children": False, |
3981 | + "product": "Intel Corporation", |
3982 | + "vendor": "Intel Corporation", |
3983 | + }, |
3984 | + { |
3985 | + "businfo": "pci@0000:65:00.0", |
3986 | + "driver": "nvme", |
3987 | + "has_children": False, |
3988 | + "product": "Intel Corporation", |
3989 | + "vendor": "Intel Corporation", |
3990 | + }, |
3991 | + { |
3992 | + "businfo": "pci@0000:66:00.0", |
3993 | + "driver": "nvme", |
3994 | + "has_children": False, |
3995 | + "product": "Intel Corporation", |
3996 | + "vendor": "Intel Corporation", |
3997 | + }, |
3998 | + { |
3999 | + "businfo": "pci@0000:67:00.0", |
4000 | + "driver": "nvme", |
4001 | + "has_children": False, |
4002 | + "product": "Intel Corporation", |
4003 | + "vendor": "Intel Corporation", |
4004 | + }, |
4005 | + { |
4006 | + "businfo": "pci@0000:68:00.0", |
4007 | + "driver": "nvme", |
4008 | + "has_children": False, |
4009 | + "product": "Intel Corporation", |
4010 | + "vendor": "Intel Corporation", |
4011 | + }, |
4012 | + { |
4013 | + "businfo": "pci@0000:69:00.0", |
4014 | + "driver": "nvme", |
4015 | + "has_children": False, |
4016 | + "product": "Intel Corporation", |
4017 | + "vendor": "Intel Corporation", |
4018 | + }, |
4019 | + { |
4020 | + "businfo": "pci@0000:6a:00.0", |
4021 | + "driver": "nvme", |
4022 | + "has_children": False, |
4023 | + "product": "Intel Corporation", |
4024 | + "vendor": "Intel Corporation", |
4025 | + }, |
4026 | + ], |
4027 | + "disk_class_info": [], |
4028 | + "network_class_info": [ |
4029 | + { |
4030 | + "businfo": "pci@0000:18:00.0", |
4031 | + "driver": "ixgbe", |
4032 | + "driverversion": "5.1.0-k", |
4033 | + "firmware": "0x800005f8", |
4034 | + "logicalname": "eno1", |
4035 | + "product": "Ethernet Controller 10G X550T", |
4036 | + "serial": "de:ad:be:ef:00:ef", |
4037 | + "speed": "10Gbit/s", |
4038 | + "vendor": "Intel Corporation", |
4039 | + }, |
4040 | + { |
4041 | + "businfo": "pci@0000:18:00.1", |
4042 | + "driver": "ixgbe", |
4043 | + "driverversion": "5.1.0-k", |
4044 | + "firmware": "0x800005f8", |
4045 | + "logicalname": "eno2", |
4046 | + "product": "Ethernet Controller 10G X550T", |
4047 | + "serial": "de:ad:be:ef:00:ef", |
4048 | + "speed": "10Gbit/s", |
4049 | + "vendor": "Intel Corporation", |
4050 | + }, |
4051 | + { |
4052 | + "businfo": "pci@0000:19:00.0", |
4053 | + "driver": "ixgbe", |
4054 | + "driverversion": "5.1.0-k", |
4055 | + "firmware": "0x800005f9", |
4056 | + "logicalname": "eno3", |
4057 | + "product": "Ethernet Controller 10G X550T", |
4058 | + "serial": "de:ad:be:ef:00:ef", |
4059 | + "speed": None, |
4060 | + "vendor": "Intel Corporation", |
4061 | + }, |
4062 | + { |
4063 | + "businfo": "pci@0000:19:00.1", |
4064 | + "driver": "ixgbe", |
4065 | + "driverversion": "5.1.0-k", |
4066 | + "firmware": "0x800005f9", |
4067 | + "logicalname": "eno4", |
4068 | + "product": "Ethernet Controller 10G X550T", |
4069 | + "serial": "de:ad:be:ef:00:ef", |
4070 | + "speed": None, |
4071 | + "vendor": "Intel Corporation", |
4072 | + }, |
4073 | + { |
4074 | + "businfo": "pci@0000:3b:00.0", |
4075 | + "driver": "mlx5_core", |
4076 | + "driverversion": "5.0-0", |
4077 | + "firmware": "12.17.2020 (SM_2001000001033)", |
4078 | + "logicalname": "enp59s0f0", |
4079 | + "product": "MT27700 Family [ConnectX-4]", |
4080 | + "serial": "de:ad:be:ef:00:ef", |
4081 | + "speed": None, |
4082 | + "vendor": "Mellanox Technologies", |
4083 | + }, |
4084 | + { |
4085 | + "businfo": "pci@0000:3b:00.1", |
4086 | + "driver": "mlx5_core", |
4087 | + "driverversion": "5.0-0", |
4088 | + "firmware": "12.17.2020 (SM_2001000001033)", |
4089 | + "logicalname": "enp59s0f1", |
4090 | + "product": "MT27700 Family [ConnectX-4]", |
4091 | + "serial": "de:ad:be:ef:00:ef", |
4092 | + "speed": None, |
4093 | + "vendor": "Mellanox Technologies", |
4094 | + }, |
4095 | + { |
4096 | + "businfo": "pci@0000:d8:00.0", |
4097 | + "driver": "mlx5_core", |
4098 | + "driverversion": "5.0-0", |
4099 | + "firmware": "14.21.1000 (SM_2001000001034)", |
4100 | + "logicalname": "enp216s0f0", |
4101 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
4102 | + "serial": "de:ad:be:ef:00:ef", |
4103 | + "speed": None, |
4104 | + "vendor": "Mellanox Technologies", |
4105 | + }, |
4106 | + { |
4107 | + "businfo": "pci@0000:d8:00.1", |
4108 | + "driver": "mlx5_core", |
4109 | + "driverversion": "5.0-0", |
4110 | + "firmware": "14.21.1000 (SM_2001000001034)", |
4111 | + "logicalname": "enp216s0f1", |
4112 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
4113 | + "serial": "de:ad:be:ef:00:ef", |
4114 | + "speed": None, |
4115 | + "vendor": "Mellanox Technologies", |
4116 | + }, |
4117 | + ], |
4118 | + }, |
4119 | + "lshw.supermicro.sas.01.json": { |
4120 | + "system": { |
4121 | + "description": "Computer", |
4122 | + "hostname": "supermicro-sas01", |
4123 | + "product": "SYS-6029U-TR4T (To be filled by O.E.M.)", |
4124 | + "serial": "E263684X8302334", |
4125 | + "vendor": "Supermicro", |
4126 | + "version": "0123456789", |
4127 | + }, |
4128 | + "motherboard": [ |
4129 | + { |
4130 | + "description": "Motherboard", |
4131 | + "product": "X11DPU", |
4132 | + "serial": "OM183S008126", |
4133 | + "vendor": "Supermicro", |
4134 | + "version": "1.10", |
4135 | + } |
4136 | + ], |
4137 | + "storage_class_info": [ |
4138 | + { |
4139 | + "businfo": "pci@0000:00:11.5", |
4140 | + "driver": "ahci", |
4141 | + "has_children": False, |
4142 | + "product": "Lewisburg SSATA Controller [AHCI mode]", |
4143 | + "vendor": "Intel Corporation", |
4144 | + }, |
4145 | + { |
4146 | + "businfo": "pci@0000:00:17.0", |
4147 | + "driver": "ahci", |
4148 | + "has_children": False, |
4149 | + "product": "Lewisburg SATA Controller [AHCI mode]", |
4150 | + "vendor": "Intel Corporation", |
4151 | + }, |
4152 | + { |
4153 | + "businfo": "pci@0000:1a:00.0", |
4154 | + "driver": "nvme", |
4155 | + "has_children": False, |
4156 | + "product": "Intel Corporation", |
4157 | + "vendor": "Intel Corporation", |
4158 | + }, |
4159 | + { |
4160 | + "businfo": "pci@0000:86:00.0", |
4161 | + "driver": "mpt3sas", |
4162 | + "has_children": True, |
4163 | + "product": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4164 | + "vendor": "LSI Logic / Symbios Logic", |
4165 | + }, |
4166 | + { |
4167 | + "businfo": "pci@0000:af:00.0", |
4168 | + "driver": "mpt3sas", |
4169 | + "has_children": True, |
4170 | + "product": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4171 | + "vendor": "LSI Logic / Symbios Logic", |
4172 | + }, |
4173 | + ], |
4174 | + "disk_class_info": [ |
4175 | + { |
4176 | + "businfo": "scsi@0:0.0.0", |
4177 | + "dev": "8:0", |
4178 | + "logicalname": "/dev/sda", |
4179 | + "physid": "0.0.0", |
4180 | + "product": "HUS726060AL5210", |
4181 | + "serial": "K1HX9AJD", |
4182 | + "size": 6001175126016, |
4183 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4184 | + }, |
4185 | + { |
4186 | + "businfo": "scsi@0:0.1.0", |
4187 | + "dev": "8:16", |
4188 | + "logicalname": "/dev/sdb", |
4189 | + "physid": "0.1.0", |
4190 | + "product": "HUS726060AL5210", |
4191 | + "serial": "K1HXJ44F", |
4192 | + "size": 6001175126016, |
4193 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4194 | + }, |
4195 | + { |
4196 | + "businfo": "scsi@0:0.2.0", |
4197 | + "dev": "8:32", |
4198 | + "logicalname": "/dev/sdc", |
4199 | + "physid": "0.2.0", |
4200 | + "product": "HUS726060AL5210", |
4201 | + "serial": "K1HWXPJD", |
4202 | + "size": 6001175126016, |
4203 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4204 | + }, |
4205 | + { |
4206 | + "businfo": "scsi@0:0.3.0", |
4207 | + "dev": "8:48", |
4208 | + "logicalname": "/dev/sdd", |
4209 | + "physid": "0.3.0", |
4210 | + "product": "HUS726060AL5210", |
4211 | + "serial": "K1HXJ6YF", |
4212 | + "size": 6001175126016, |
4213 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4214 | + }, |
4215 | + { |
4216 | + "businfo": "scsi@0:0.4.0", |
4217 | + "dev": "8:64", |
4218 | + "logicalname": "/dev/sde", |
4219 | + "physid": "0.4.0", |
4220 | + "product": "HUS726060AL5210", |
4221 | + "serial": "K1HWXPDD", |
4222 | + "size": 6001175126016, |
4223 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4224 | + }, |
4225 | + { |
4226 | + "businfo": "scsi@0:0.5.0", |
4227 | + "dev": "8:80", |
4228 | + "logicalname": "/dev/sdf", |
4229 | + "physid": "0.5.0", |
4230 | + "product": "HUS726060AL5210", |
4231 | + "serial": "K1HXJLUF", |
4232 | + "size": 6001175126016, |
4233 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4234 | + }, |
4235 | + { |
4236 | + "businfo": "scsi@0:0.6.0", |
4237 | + "dev": "8:96", |
4238 | + "logicalname": "/dev/sdg", |
4239 | + "physid": "0.6.0", |
4240 | + "product": "HUS726060AL5210", |
4241 | + "serial": "K1HWXPYD", |
4242 | + "size": 6001175126016, |
4243 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4244 | + }, |
4245 | + { |
4246 | + "businfo": "scsi@0:0.7.0", |
4247 | + "dev": "8:112", |
4248 | + "logicalname": "/dev/sdh", |
4249 | + "physid": "0.7.0", |
4250 | + "product": "HUS726060AL5210", |
4251 | + "serial": "K1HWXP4D", |
4252 | + "size": 6001175126016, |
4253 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4254 | + }, |
4255 | + { |
4256 | + "businfo": "scsi@1:0.2.0", |
4257 | + "dev": "8:160", |
4258 | + "logicalname": "/dev/sdk", |
4259 | + "physid": "0.2.0", |
4260 | + "product": "ST6000NM0095", |
4261 | + "serial": "ZAD4S0DW0000C843M2UC", |
4262 | + "size": 6001175126016, |
4263 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4264 | + }, |
4265 | + { |
4266 | + "businfo": "scsi@1:0.0.0", |
4267 | + "dev": "8:128", |
4268 | + "logicalname": "/dev/sdi", |
4269 | + "physid": "0.0.0", |
4270 | + "product": "HUS726060AL5210", |
4271 | + "serial": "K1HWGU9D", |
4272 | + "size": 6001175126016, |
4273 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4274 | + }, |
4275 | + { |
4276 | + "businfo": "scsi@1:0.1.0", |
4277 | + "dev": "8:144", |
4278 | + "logicalname": "/dev/sdj", |
4279 | + "physid": "0.1.0", |
4280 | + "product": "ST6000NM0095", |
4281 | + "serial": "ZAD4S0A50000C8432082", |
4282 | + "size": 6001175126016, |
4283 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4284 | + }, |
4285 | + ], |
4286 | + "network_class_info": [ |
4287 | + { |
4288 | + "businfo": "pci@0000:18:00.0", |
4289 | + "driver": "i40e", |
4290 | + "driverversion": "2.1.14-k", |
4291 | + "firmware": "6.01 0x800036f0 1.1861.0", |
4292 | + "logicalname": "eno1", |
4293 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
4294 | + "serial": "de:ad:be:ef:00:ef", |
4295 | + "speed": "10Gbit/s", |
4296 | + "vendor": "Intel Corporation", |
4297 | + }, |
4298 | + { |
4299 | + "businfo": "pci@0000:18:00.1", |
4300 | + "driver": "i40e", |
4301 | + "driverversion": "2.1.14-k", |
4302 | + "firmware": "6.01 0x800036f0 1.1861.0", |
4303 | + "logicalname": "eno2", |
4304 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
4305 | + "serial": "de:ad:be:ef:00:ef", |
4306 | + "speed": "10Gbit/s", |
4307 | + "vendor": "Intel Corporation", |
4308 | + }, |
4309 | + { |
4310 | + "businfo": "pci@0000:18:00.2", |
4311 | + "driver": "i40e", |
4312 | + "driverversion": "2.1.14-k", |
4313 | + "firmware": "6.01 0x800036f0 1.1861.0", |
4314 | + "logicalname": "eno3", |
4315 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
4316 | + "serial": "de:ad:be:ef:00:ef", |
4317 | + "speed": None, |
4318 | + "vendor": "Intel Corporation", |
4319 | + }, |
4320 | + { |
4321 | + "businfo": "pci@0000:18:00.3", |
4322 | + "driver": "i40e", |
4323 | + "driverversion": "2.1.14-k", |
4324 | + "firmware": "6.01 0x800036f0 1.1861.0", |
4325 | + "logicalname": "eno4", |
4326 | + "product": "Ethernet Controller X710/X557-AT 10GBASE-T", |
4327 | + "serial": "de:ad:be:ef:00:ef", |
4328 | + "speed": None, |
4329 | + "vendor": "Intel Corporation", |
4330 | + }, |
4331 | + { |
4332 | + "businfo": "pci@0000:3b:00.0", |
4333 | + "driver": "mlx5_core", |
4334 | + "driverversion": "5.0-0", |
4335 | + "firmware": "12.17.2020 (SM_2001000001033)", |
4336 | + "logicalname": "enp59s0f0", |
4337 | + "product": "MT27700 Family [ConnectX-4]", |
4338 | + "serial": "de:ad:be:ef:00:ef", |
4339 | + "speed": None, |
4340 | + "vendor": "Mellanox Technologies", |
4341 | + }, |
4342 | + { |
4343 | + "businfo": "pci@0000:3b:00.1", |
4344 | + "driver": "mlx5_core", |
4345 | + "driverversion": "5.0-0", |
4346 | + "firmware": "12.17.2020 (SM_2001000001033)", |
4347 | + "logicalname": "enp59s0f1", |
4348 | + "product": "MT27700 Family [ConnectX-4]", |
4349 | + "serial": "de:ad:be:ef:00:ef", |
4350 | + "speed": None, |
4351 | + "vendor": "Mellanox Technologies", |
4352 | + }, |
4353 | + { |
4354 | + "businfo": "pci@0000:d8:00.0", |
4355 | + "driver": "mlx5_core", |
4356 | + "driverversion": "5.0-0", |
4357 | + "firmware": "14.21.1000 (SM_2001000001034)", |
4358 | + "logicalname": "enp216s0f0", |
4359 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
4360 | + "serial": "de:ad:be:ef:00:ef", |
4361 | + "speed": None, |
4362 | + "vendor": "Mellanox Technologies", |
4363 | + }, |
4364 | + { |
4365 | + "businfo": "pci@0000:d8:00.1", |
4366 | + "driver": "mlx5_core", |
4367 | + "driverversion": "5.0-0", |
4368 | + "firmware": "14.21.1000 (SM_2001000001034)", |
4369 | + "logicalname": "enp216s0f1", |
4370 | + "product": "MT27710 Family [ConnectX-4 Lx]", |
4371 | + "serial": "de:ad:be:ef:00:ef", |
4372 | + "speed": None, |
4373 | + "vendor": "Mellanox Technologies", |
4374 | + }, |
4375 | + ], |
4376 | + }, |
4377 | + "lshw.supermicro.sas.02.json": { |
4378 | + "system": { |
4379 | + "description": "Computer", |
4380 | + "hostname": "supermicro-sas02", |
4381 | + "product": "SYS-6028U-TR4+ (Default string)", |
4382 | + "serial": "S16512428221502", |
4383 | + "vendor": "Supermicro", |
4384 | + "version": "0123456789", |
4385 | + }, |
4386 | + "motherboard": [ |
4387 | + { |
4388 | + "description": "Motherboard", |
4389 | + "product": "X10DRU-i+", |
4390 | + "serial": "OM17CS000182", |
4391 | + "vendor": "Supermicro", |
4392 | + "version": "1.02B", |
4393 | + } |
4394 | + ], |
4395 | + "storage_class_info": [ |
4396 | + { |
4397 | + "businfo": "pci@0000:03:00.0", |
4398 | + "driver": "mpt3sas", |
4399 | + "has_children": True, |
4400 | + "product": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4401 | + "vendor": "LSI Logic / Symbios Logic", |
4402 | + }, |
4403 | + { |
4404 | + "businfo": "pci@0000:00:11.4", |
4405 | + "driver": "ahci", |
4406 | + "has_children": False, |
4407 | + "product": "C610/X99 series chipset sSATA Controller [AHCI mode]", # noqa E501 |
4408 | + "vendor": "Intel Corporation", |
4409 | + }, |
4410 | + { |
4411 | + "businfo": "pci@0000:00:1f.2", |
4412 | + "driver": "ahci", |
4413 | + "has_children": False, |
4414 | + "product": "C610/X99 series chipset 6-Port SATA Controller [AHCI mode]", # noqa E501 |
4415 | + "vendor": "Intel Corporation", |
4416 | + }, |
4417 | + { |
4418 | + "businfo": "pci@0000:81:00.0", |
4419 | + "driver": "nvme", |
4420 | + "has_children": False, |
4421 | + "product": "NVMe SSD Controller SM961/PM961", |
4422 | + "vendor": "Samsung Electronics Co Ltd", |
4423 | + }, |
4424 | + { |
4425 | + "businfo": "pci@0000:82:00.0", |
4426 | + "driver": "nvme", |
4427 | + "has_children": False, |
4428 | + "product": "NVMe SSD Controller SM961/PM961", |
4429 | + "vendor": "Samsung Electronics Co Ltd", |
4430 | + }, |
4431 | + ], |
4432 | + "disk_class_info": [ |
4433 | + { |
4434 | + "businfo": "scsi@10:0.0.0", |
4435 | + "dev": None, |
4436 | + "logicalname": None, |
4437 | + "physid": "0.0.0", |
4438 | + "product": "ST4000NM115-1YZ1", |
4439 | + "serial": "ZC10Z0XQ", |
4440 | + "size": None, |
4441 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4442 | + }, |
4443 | + { |
4444 | + "businfo": "scsi@10:1.0.0", |
4445 | + "dev": "8:0", |
4446 | + "logicalname": "/dev/sda", |
4447 | + "physid": "1.0.0", |
4448 | + "product": "Logical Volume", |
4449 | + "serial": "3589195262245447776", |
4450 | + "size": 3999999721472, |
4451 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4452 | + }, |
4453 | + { |
4454 | + "businfo": "scsi@10:0.1.0", |
4455 | + "dev": None, |
4456 | + "logicalname": None, |
4457 | + "physid": "0.1.0", |
4458 | + "product": "ST4000NM115-1YZ1", |
4459 | + "serial": "ZC10Z0ZL", |
4460 | + "size": None, |
4461 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4462 | + }, |
4463 | + { |
4464 | + "businfo": "scsi@10:0.2.0", |
4465 | + "dev": "8:16", |
4466 | + "logicalname": "/dev/sdb", |
4467 | + "physid": "0.2.0", |
4468 | + "product": "ST4000NM115-1YZ1", |
4469 | + "serial": "ZC10YVHJ", |
4470 | + "size": 4000787030016, |
4471 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4472 | + }, |
4473 | + { |
4474 | + "businfo": "scsi@10:0.3.0", |
4475 | + "dev": "8:32", |
4476 | + "logicalname": "/dev/sdc", |
4477 | + "physid": "0.3.0", |
4478 | + "product": "ST4000NM115-1YZ1", |
4479 | + "serial": "ZC10XX97", |
4480 | + "size": 4000787030016, |
4481 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4482 | + }, |
4483 | + { |
4484 | + "businfo": "scsi@10:0.4.0", |
4485 | + "dev": "8:48", |
4486 | + "logicalname": "/dev/sdd", |
4487 | + "physid": "0.4.0", |
4488 | + "product": "ST4000NM115-1YZ1", |
4489 | + "serial": "ZC10SFX1", |
4490 | + "size": 4000787030016, |
4491 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4492 | + }, |
4493 | + { |
4494 | + "businfo": "scsi@10:0.5.0", |
4495 | + "dev": "8:64", |
4496 | + "logicalname": "/dev/sde", |
4497 | + "physid": "0.5.0", |
4498 | + "product": "ST4000NM115-1YZ1", |
4499 | + "serial": "ZC10YVK1", |
4500 | + "size": 4000787030016, |
4501 | + "storage_parent": "SAS3008 PCI-Express Fusion-MPT SAS-3", |
4502 | + }, |
4503 | + ], |
4504 | + "network_class_info": [ |
4505 | + { |
4506 | + "businfo": "pci@0000:01:00.0", |
4507 | + "driver": "igb", |
4508 | + "driverversion": "5.3.0-k", |
4509 | + "firmware": "1.63, 0x800009fa", |
4510 | + "logicalname": "enp1s0f0", |
4511 | + "product": "I350 Gigabit Network Connection", |
4512 | + "serial": "de:ad:be:ef:00:ef", |
4513 | + "speed": "1Gbit/s", |
4514 | + "vendor": "Intel Corporation", |
4515 | + }, |
4516 | + { |
4517 | + "businfo": "pci@0000:01:00.1", |
4518 | + "driver": "igb", |
4519 | + "driverversion": "5.3.0-k", |
4520 | + "firmware": "1.63, 0x800009fa", |
4521 | + "logicalname": "enp1s0f1", |
4522 | + "product": "I350 Gigabit Network Connection", |
4523 | + "serial": "de:ad:be:ef:00:ef", |
4524 | + "speed": None, |
4525 | + "vendor": "Intel Corporation", |
4526 | + }, |
4527 | + { |
4528 | + "businfo": "pci@0000:01:00.2", |
4529 | + "driver": "igb", |
4530 | + "driverversion": "5.3.0-k", |
4531 | + "firmware": "1.63, 0x800009fa", |
4532 | + "logicalname": "enp1s0f2", |
4533 | + "product": "I350 Gigabit Network Connection", |
4534 | + "serial": "de:ad:be:ef:00:ef", |
4535 | + "speed": "1Gbit/s", |
4536 | + "vendor": "Intel Corporation", |
4537 | + }, |
4538 | + { |
4539 | + "businfo": "pci@0000:01:00.3", |
4540 | + "driver": "igb", |
4541 | + "driverversion": "5.3.0-k", |
4542 | + "firmware": "1.63, 0x800009fa", |
4543 | + "logicalname": "enp1s0f3", |
4544 | + "product": "I350 Gigabit Network Connection", |
4545 | + "serial": "de:ad:be:ef:00:ef", |
4546 | + "speed": None, |
4547 | + "vendor": "Intel Corporation", |
4548 | + }, |
4549 | + { |
4550 | + "businfo": "pci@0000:83:00.0", |
4551 | + "driver": "ixgbe", |
4552 | + "driverversion": "4.2.1-k", |
4553 | + "firmware": "0x800006da", |
4554 | + "logicalname": "enp131s0f0", |
4555 | + "product": "82599ES 10-Gigabit SFI/SFP+ Network Connection", |
4556 | + "serial": "de:ad:be:ef:00:ef", |
4557 | + "speed": "10Gbit/s", |
4558 | + "vendor": "Intel Corporation", |
4559 | + }, |
4560 | + { |
4561 | + "businfo": "pci@0000:83:00.1", |
4562 | + "driver": "ixgbe", |
4563 | + "driverversion": "4.2.1-k", |
4564 | + "firmware": "0x800006da", |
4565 | + "logicalname": "enp131s0f1", |
4566 | + "product": "82599ES 10-Gigabit SFI/SFP+ Network Connection", |
4567 | + "serial": "de:ad:be:ef:00:ef", |
4568 | + "speed": "10Gbit/s", |
4569 | + "vendor": "Intel Corporation", |
4570 | + }, |
4571 | + { |
4572 | + "businfo": "pci@0000:84:00.0", |
4573 | + "driver": "ixgbe", |
4574 | + "driverversion": "4.2.1-k", |
4575 | + "firmware": "0x800006da", |
4576 | + "logicalname": "ens4f0", |
4577 | + "product": "82599ES 10-Gigabit SFI/SFP+ Network Connection", |
4578 | + "serial": "de:ad:be:ef:00:ef", |
4579 | + "speed": "10Gbit/s", |
4580 | + "vendor": "Intel Corporation", |
4581 | + }, |
4582 | + { |
4583 | + "businfo": "pci@0000:84:00.1", |
4584 | + "driver": "ixgbe", |
4585 | + "driverversion": "4.2.1-k", |
4586 | + "firmware": "0x800006da", |
4587 | + "logicalname": "ens4f1", |
4588 | + "product": "82599ES 10-Gigabit SFI/SFP+ Network Connection", |
4589 | + "serial": "de:ad:be:ef:00:ef", |
4590 | + "speed": "10Gbit/s", |
4591 | + "vendor": "Intel Corporation", |
4592 | + }, |
4593 | + ], |
4594 | + }, |
4595 | +} |
4596 | diff --git a/src/tests/functional/requirements.txt b/src/tests/functional/requirements.txt |
4597 | index 193a938..88925a4 100644 |
4598 | --- a/src/tests/functional/requirements.txt |
4599 | +++ b/src/tests/functional/requirements.txt |
4600 | @@ -5,3 +5,4 @@ pytest |
4601 | pytest-asyncio |
4602 | requests |
4603 | charmhelpers |
4604 | +charms.reactive |
4605 | diff --git a/src/tests/functional/test_hwhealth.py b/src/tests/functional/test_hwhealth.py |
4606 | index 51d407a..3e544ea 100644 |
4607 | --- a/src/tests/functional/test_hwhealth.py |
4608 | +++ b/src/tests/functional/test_hwhealth.py |
4609 | @@ -19,16 +19,18 @@ SERIES = [ |
4610 | ] |
4611 | JUJU_REPOSITORY = os.getenv('JUJU_REPOSITORY', '.') |
4612 | NRPECFG_DIR = '/etc/nagios/nrpe.d' |
4613 | +DEF_TIMEOUT = 600 |
4614 | +# These go along with the hpe repos for the hp* tools |
4615 | |
4616 | ################ |
4617 | # Helper funcs # |
4618 | ################ |
4619 | |
4620 | |
4621 | -async def deploy_hwhealth_res(model, app_name, tool_name): |
4622 | +async def deploy_hwhealth_res(model, app_name, res_filename): |
4623 | # Attaching resources is not implemented yet in libjuju |
4624 | # see https://github.com/juju/python-libjuju/issues/294 |
4625 | - tools_res_path = os.path.join(JUJU_REPOSITORY, tool_name) |
4626 | + tools_res_path = os.path.join(JUJU_REPOSITORY, 'builds', res_filename) |
4627 | subprocess.check_call([ |
4628 | 'juju', |
4629 | 'deploy', |
4630 | @@ -41,8 +43,8 @@ async def deploy_hwhealth_res(model, app_name, tool_name): |
4631 | ]) |
4632 | |
4633 | |
4634 | -async def update_hwhealth_res(model, app_name, tool_name): |
4635 | - tools_res_path = os.path.join(JUJU_REPOSITORY, tool_name) |
4636 | +async def update_hwhealth_res(model, app_name, res_filename): |
4637 | + tools_res_path = os.path.join(JUJU_REPOSITORY, 'builds', res_filename) |
4638 | subprocess.check_call([ |
4639 | 'juju', |
4640 | 'attach-resource', |
4641 | @@ -82,8 +84,12 @@ async def deploy_app(request, model): |
4642 | series=series, |
4643 | channel=channel, |
4644 | ) |
4645 | - await model.deploy('ubuntu', application_name='ubuntu-checksum-{}'.format(release), |
4646 | - series=release, channel=channel) |
4647 | + await model.deploy( |
4648 | + 'ubuntu', |
4649 | + application_name='ubuntu-checksum-{}'.format(release), |
4650 | + series=release, |
4651 | + channel=channel |
4652 | + ) |
4653 | nrpe_app = await model.deploy( |
4654 | 'nrpe', |
4655 | application_name='nrpe-{}'.format(release), |
4656 | @@ -104,33 +110,43 @@ async def deploy_app(request, model): |
4657 | # Attaching resources is not implemented yet in libjuju |
4658 | # see https://github.com/juju/python-libjuju/issues/294 |
4659 | await deploy_hwhealth_res(model, hw_health_app_name, 'tools.zip') |
4660 | - await deploy_hwhealth_res(model, hw_health_checksum_app_name, 'tools-checksum.zip') |
4661 | + await deploy_hwhealth_res(model, hw_health_checksum_app_name, |
4662 | + 'tools-checksum.zip') |
4663 | |
4664 | # This is pretty horrible, but we can't deploy via libjuju |
4665 | while True: |
4666 | try: |
4667 | hw_health_app = model.applications[hw_health_app_name] |
4668 | - hw_health_checksum_app = model.applications[hw_health_checksum_app_name] |
4669 | + hw_health_checksum_app = \ |
4670 | + model.applications[hw_health_checksum_app_name] |
4671 | break |
4672 | except KeyError: |
4673 | await asyncio.sleep(5) |
4674 | |
4675 | - await hw_health_app.add_relation('general-info', |
4676 | - 'ubuntu-{}:juju-info'.format(release)) |
4677 | - await hw_health_app.add_relation('nrpe-external-master', |
4678 | - '{}:nrpe-external-master'.format(nrpe_app.name)) |
4679 | + await hw_health_app.add_relation( |
4680 | + 'general-info', |
4681 | + 'ubuntu-{}:juju-info'.format(release) |
4682 | + ) |
4683 | + await hw_health_app.add_relation( |
4684 | + 'nrpe-external-master', |
4685 | + '{}:nrpe-external-master'.format(nrpe_app.name) |
4686 | + ) |
4687 | |
4688 | - await hw_health_checksum_app.add_relation('general-info', |
4689 | - 'ubuntu-checksum-{}:juju-info'.format(release)) |
4690 | - await hw_health_checksum_app.add_relation('nrpe-external-master', |
4691 | - '{}:nrpe-external-master'.format(nrpe_app.name)) |
4692 | + await hw_health_checksum_app.add_relation( |
4693 | + 'general-info', |
4694 | + 'ubuntu-checksum-{}:juju-info'.format(release) |
4695 | + ) |
4696 | + await hw_health_checksum_app.add_relation( |
4697 | + 'nrpe-external-master', |
4698 | + '{}:nrpe-external-master'.format(nrpe_app.name) |
4699 | + ) |
4700 | |
4701 | # The app will initially be in blocked state because it's running in a |
4702 | # container |
4703 | await model.block_until( |
4704 | - lambda: (hw_health_app.status == 'blocked' and |
4705 | + lambda: (hw_health_app.status == 'blocked' and # noqa:W504 |
4706 | hw_health_checksum_app.status == 'blocked'), |
4707 | - timeout=600 |
4708 | + timeout=DEF_TIMEOUT |
4709 | ) |
4710 | yield hw_health_app |
4711 | |
4712 | @@ -151,10 +167,10 @@ async def toolset(monkeypatch): |
4713 | m.setattr('charmhelpers.core.hookenv.charm_dir', |
4714 | lambda: JUJU_REPOSITORY) |
4715 | m.setattr('charmhelpers.core.hookenv.config', |
4716 | - lambda x=None: dict()) |
4717 | + lambda x=None: dict({'manufacturer': 'test'})) |
4718 | m.setattr('charmhelpers.contrib.charmsupport.nrpe.get_nagios_hostname', |
4719 | lambda: 'pytest') |
4720 | - return hwdiscovery.get_tools('test') |
4721 | + return [tool() for tool in hwdiscovery.get_tools('test')] |
4722 | |
4723 | ######### |
4724 | # Tests # |
4725 | @@ -172,14 +188,17 @@ async def test_forced_deploy(deploy_app, model, run_command): |
4726 | for unit in model.units.values(): |
4727 | if unit.entity_id.startswith('ubuntu-{}'.format(series)): |
4728 | ubuntu_unit = unit |
4729 | - await model.block_until(lambda: ubuntu_unit.workload_status == 'active', timeout=180) |
4730 | + await model.block_until( |
4731 | + lambda: ubuntu_unit.workload_status == 'active', |
4732 | + timeout=DEF_TIMEOUT |
4733 | + ) |
4734 | await run_command(CREATE_FAKE_NVME, ubuntu_unit) |
4735 | break |
4736 | |
4737 | await deploy_app.set_config({'manufacturer': 'test'}) |
4738 | await model.block_until( |
4739 | lambda: deploy_app.status == 'active', |
4740 | - timeout=600 |
4741 | + timeout=DEF_TIMEOUT |
4742 | ) |
4743 | assert deploy_app.status == 'active' |
4744 | |
4745 | @@ -188,64 +207,84 @@ async def test_checksum_forced_deploy(deploy_app, model, run_command): |
4746 | # Create a fake NVMe device for the cronjob to be configured |
4747 | CREATE_FAKE_NVME = "/bin/bash -c 'touch /dev/nvme0'" |
4748 | series = deploy_app.name.split('-')[-1] |
4749 | - hw_health_checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4750 | - hw_health_checksum_app = model.applications[hw_health_checksum_app_name] |
4751 | + checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4752 | + checksum_app = model.applications[checksum_app_name] |
4753 | for unit in model.units.values(): |
4754 | if unit.entity_id.startswith('ubuntu-checksum-{}'.format(series)): |
4755 | ubuntu_unit = unit |
4756 | - await model.block_until(lambda: ubuntu_unit.workload_status == 'active', |
4757 | - timeout=180) |
4758 | + await model.block_until( |
4759 | + lambda: ubuntu_unit.workload_status == 'active', |
4760 | + timeout=DEF_TIMEOUT |
4761 | + ) |
4762 | await run_command(CREATE_FAKE_NVME, ubuntu_unit) |
4763 | - elif unit.entity_id.startswith(hw_health_checksum_app_name): |
4764 | - hw_health_checksum_unit = unit |
4765 | - |
4766 | - await hw_health_checksum_app.set_config({'manufacturer': 'test'}) |
4767 | - await model.block_until( |
4768 | - lambda: (hw_health_checksum_app.status == 'blocked' and |
4769 | - hw_health_checksum_unit.workload_status_message == 'Tool megacli - checksum error'), |
4770 | - timeout=600) |
4771 | + elif unit.entity_id.startswith(checksum_app_name): |
4772 | + checksum_unit = unit |
4773 | + |
4774 | + await checksum_app.set_config({'manufacturer': 'test'}) |
4775 | + try: |
4776 | + await model.block_until( |
4777 | + lambda: ( |
4778 | + checksum_app.status == 'blocked' and checksum_unit.workload_status_message == 'Tool megacli - checksum error' # noqa E501 |
4779 | + ), |
4780 | + timeout=DEF_TIMEOUT) |
4781 | + except asyncio.exceptions.TimeoutError: |
4782 | + print( |
4783 | + "failed to get expected state 'blocked:Tool megacli - checksum error', " |
4784 | + "witnessed '{}:{}'".format(checksum_app.status, checksum_unit.workload_status_message) |
4785 | + ) |
4786 | + assert checksum_app.status == 'blocked' |
4787 | + assert checksum_unit.workload_status_message == 'Tool megacli - checksum error' |
4788 | |
4789 | |
4790 | async def test_checksum_updated_resource_missing(deploy_app, model): |
4791 | series = deploy_app.name.split('-')[-1] |
4792 | - hw_health_checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4793 | - hw_health_checksum_app = model.applications[hw_health_checksum_app_name] |
4794 | - await update_hwhealth_res(model, hw_health_checksum_app_name, 'tools-missing.zip') |
4795 | + checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4796 | + checksum_app = model.applications[checksum_app_name] |
4797 | + await update_hwhealth_res(model, checksum_app_name, 'tools-missing.zip') |
4798 | for unit in model.units.values(): |
4799 | - if unit.entity_id.startswith(hw_health_checksum_app_name): |
4800 | - hw_health_checksum_unit = unit |
4801 | + if unit.entity_id.startswith(checksum_app_name): |
4802 | + checksum_unit = unit |
4803 | break |
4804 | |
4805 | await model.block_until( |
4806 | - lambda: (hw_health_checksum_app.status == 'blocked' and |
4807 | - hw_health_checksum_unit.workload_status_message == 'Tool megacli not found'), |
4808 | - timeout=600 |
4809 | + lambda: ( |
4810 | + checksum_app.status == 'blocked' and checksum_unit.workload_status_message == 'Tool megacli not found' # noqa E501 |
4811 | + ), |
4812 | + timeout=DEF_TIMEOUT |
4813 | ) |
4814 | |
4815 | |
4816 | async def test_checksum_updated_resource_ok(deploy_app, model): |
4817 | series = deploy_app.name.split('-')[-1] |
4818 | - hw_health_checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4819 | - hw_health_checksum_app = model.applications[hw_health_checksum_app_name] |
4820 | - await update_hwhealth_res(model, hw_health_checksum_app_name, 'tools.zip') |
4821 | + checksum_app_name = 'hw-health-checksum-{}'.format(series) |
4822 | + checksum_app = model.applications[checksum_app_name] |
4823 | + await update_hwhealth_res(model, checksum_app_name, 'tools.zip') |
4824 | for unit in model.units.values(): |
4825 | - if unit.entity_id.startswith(hw_health_checksum_app_name): |
4826 | - hw_health_checksum_unit = unit |
4827 | + if unit.entity_id.startswith(checksum_app_name): |
4828 | + checksum_unit = unit |
4829 | break |
4830 | |
4831 | await model.block_until( |
4832 | - lambda: (hw_health_checksum_app.status == 'active' and |
4833 | - hw_health_checksum_unit.workload_status_message == 'ready'), |
4834 | - timeout=600 |
4835 | + lambda: (checksum_app.status == 'active' and # noqa:W504 |
4836 | + checksum_unit.workload_status_message == 'ready'), |
4837 | + timeout=DEF_TIMEOUT |
4838 | ) |
4839 | |
4840 | |
4841 | -async def test_stats(toolset, deployed_unit, file_stat): |
4842 | +async def test_deployed_file_stats(monkeypatch, toolset, deploy_app, deployed_unit, file_stat): |
4843 | # This should really be a parametrized test, but fixtures cannot be used as |
4844 | # params value as if they were iterators |
4845 | # It should also check for other installed files and differentiate between |
4846 | # tool types (e.g. tools.Ipmi does not use a vendor binary) |
4847 | + series = deploy_app.name.split('-')[-1] |
4848 | for tool in toolset: |
4849 | + # Skip tools that are out of series for the currently deployed application |
4850 | + with monkeypatch.context() as m: |
4851 | + m.setattr('hwhealth.tools.lsb_release', |
4852 | + lambda x=None: {'DISTRIB_CODENAME': series}) |
4853 | + if not tool.is_series_supported(): |
4854 | + print('Skipping tool {}. Distribution {} not supported.'.format(tool, series)) |
4855 | + continue |
4856 | # Have we rendered the nrpe check cfg? |
4857 | nrpecfg_path = os.path.join(NRPECFG_DIR, |
4858 | 'check_{}.cfg'.format(tool._shortname)) |
4859 | @@ -255,7 +294,7 @@ async def test_stats(toolset, deployed_unit, file_stat): |
4860 | |
4861 | # Have we installed the nrpe check script? |
4862 | nrpescript_path = os.path.join(tool.NRPE_PLUGINS_DIR, |
4863 | - tool._nrpe_script) |
4864 | + os.path.basename(tool._nrpe_script)) |
4865 | print('Checking {}'.format(nrpescript_path)) |
4866 | test_stat = await file_stat(nrpescript_path, deployed_unit) |
4867 | assert test_stat['size'] > 0 |
4868 | @@ -263,6 +302,17 @@ async def test_stats(toolset, deployed_unit, file_stat): |
4869 | assert test_stat['uid'] == tool.NRPE_PLUGINS_UID |
4870 | assert test_stat['mode'] == oct(tool.NRPE_PLUGINS_MODE) |
4871 | |
4872 | + # Have we installed any common libs? |
4873 | + for lib in tool._common_libs: |
4874 | + lib_path = os.path.join(tool.NRPE_PLUGINS_DIR, |
4875 | + os.path.basename(lib)) |
4876 | + print('Checking {}'.format(nrpescript_path)) |
4877 | + test_stat = await file_stat(lib_path, deployed_unit) |
4878 | + assert test_stat['size'] > 0 |
4879 | + assert test_stat['gid'] == tool.NRPE_PLUGINS_GID |
4880 | + assert test_stat['uid'] == tool.NRPE_PLUGINS_UID |
4881 | + assert test_stat['mode'] == oct(tool.NRPE_PLUGINS_MODE) |
4882 | + |
4883 | if isinstance(tool, tools.Ipmi) or isinstance(tool, tools.Nvme): |
4884 | # Have we added sudo rights for running freeipmi commands? |
4885 | sudoer_path = os.path.join(tool.SUDOERS_DIR, tool._sudoer_file) |
4886 | @@ -275,10 +325,10 @@ async def test_stats(toolset, deployed_unit, file_stat): |
4887 | |
4888 | if isinstance(tool, tools.Ipmi) or isinstance(tool, tools.VendorTool): |
4889 | # Have we installed the cronjob script helper? |
4890 | - cronjob_script_path = os.path.join(tool.NRPE_PLUGINS_DIR, |
4891 | - tool._cronjob_script) |
4892 | - print('Checking {}'.format(cronjob_script_path)) |
4893 | - test_stat = await file_stat(cronjob_script_path, deployed_unit) |
4894 | + cron_script_path = os.path.join(tool.NRPE_PLUGINS_DIR, |
4895 | + tool._cron_script) |
4896 | + print('Checking {}'.format(cron_script_path)) |
4897 | + test_stat = await file_stat(cron_script_path, deployed_unit) |
4898 | assert test_stat['size'] > 0 |
4899 | assert test_stat['gid'] == tool.CRONJOB_SCRIPT_GID |
4900 | assert test_stat['uid'] == tool.CRONJOB_SCRIPT_UID |
4901 | @@ -305,14 +355,42 @@ async def test_stats(toolset, deployed_unit, file_stat): |
4902 | assert test_stat['mode'] == oct(tool.TOOLS_MODE) |
4903 | |
4904 | |
4905 | -async def test_removal(toolset, model, deploy_app, file_stat): |
4906 | +@pytest.mark.parametrize('script_type', ['_nrpe_script', '_cron_script']) |
4907 | +async def test_imports(script_type, monkeypatch, toolset, deploy_app, deployed_unit, run_command): |
4908 | + '''Dry run all auxiliary files to ensure we have all needed dependecies''' |
4909 | + series = deploy_app.name.split('-')[-1] |
4910 | + for tool in toolset: |
4911 | + # Skip tools that are out of series for the currently deployed application |
4912 | + with monkeypatch.context() as m: |
4913 | + m.setattr('hwhealth.tools.lsb_release', |
4914 | + lambda x=None: {'DISTRIB_CODENAME': series}) |
4915 | + if not tool.is_series_supported(): |
4916 | + print('Skipping tool {}. Distribution {} not supported.'.format(tool, series)) |
4917 | + continue |
4918 | + script_name = getattr(tool, script_type) |
4919 | + tool_name = tool.__class__.__name__ |
4920 | + if not script_name: |
4921 | + # Cannot pytest.skip because it would break out of the loop |
4922 | + print('Skipping test as {} does not have a {}' |
4923 | + ''.format(tool_name, script_type)) |
4924 | + else: |
4925 | + print('Checking {}: {}'.format(tool_name, script_name)) |
4926 | + path = os.path.join(tool.NRPE_PLUGINS_DIR, script_name) |
4927 | + cmd = path + " --help" |
4928 | + results = await run_command(cmd, deployed_unit) |
4929 | + rc = results['Code'] |
4930 | + assert rc == '0', ('{}, {}. RC is non-zero. results={}' |
4931 | + ''.format(tool_name, script_name, results)) |
4932 | + |
4933 | + |
4934 | +async def test_removal(monkeypatch, toolset, model, deploy_app, file_stat): |
4935 | '''Remove the unit, test that all files have been cleaned up''' |
4936 | hw_health_app_name = deploy_app.name |
4937 | series = deploy_app.name.split('-')[-1] |
4938 | await deploy_app.remove() |
4939 | await model.block_until( |
4940 | lambda: hw_health_app_name not in model.applications, |
4941 | - timeout=600 |
4942 | + timeout=DEF_TIMEOUT |
4943 | ) |
4944 | # Since we've removed the hw-health app, we can't target it anymore, we |
4945 | # need to find the principal unit |
4946 | @@ -320,6 +398,13 @@ async def test_removal(toolset, model, deploy_app, file_stat): |
4947 | if unit.entity_id.startswith('ubuntu-{}'.format(series)): |
4948 | ubuntu_unit = unit |
4949 | for tool in toolset: |
4950 | + # Skip tools that are out of series for the currently deployed application |
4951 | + with monkeypatch.context() as m: |
4952 | + m.setattr('hwhealth.tools.lsb_release', |
4953 | + lambda x=None: {'DISTRIB_CODENAME': series}) |
4954 | + if not tool.is_series_supported(): |
4955 | + print('Skipping tool {}. Distribution {} not supported.'.format(tool, series)) |
4956 | + continue |
4957 | # Have we removed the nrpe check cfg? |
4958 | nrpecfg_path = os.path.join(NRPECFG_DIR, |
4959 | 'check_{}.cfg'.format(tool._shortname)) |
4960 | @@ -344,7 +429,7 @@ async def test_removal(toolset, model, deploy_app, file_stat): |
4961 | if isinstance(tool, tools.Ipmi) or isinstance(tool, tools.VendorTool): |
4962 | # Have we removed the cronjob script helper? |
4963 | cronjob_path = os.path.join(tool.NRPE_PLUGINS_DIR, |
4964 | - tool._cronjob_script) |
4965 | + tool._cron_script) |
4966 | print('Checking {}'.format(cronjob_path)) |
4967 | with pytest.raises(RuntimeError): |
4968 | await file_stat(cronjob_path, ubuntu_unit) |
4969 | @@ -356,7 +441,7 @@ async def test_removal(toolset, model, deploy_app, file_stat): |
4970 | with pytest.raises(RuntimeError): |
4971 | await file_stat(cronjob_path, ubuntu_unit) |
4972 | |
4973 | - if isinstance(tool, tools.VendorTool) and not isinstance(tool, tools.Mdadm): |
4974 | + if isinstance(tool, tools.VendorTool) and not isinstance(tool, tools.Mdadm): # noqa E501 |
4975 | # /sbin/mdadm will not be removed, but the vendor binaries |
4976 | # should have been |
4977 | bin_path = os.path.join(tool.TOOLS_DIR, tool._shortname) |
4978 | diff --git a/src/tests/hw-health-samples/cron_outputs/hplog.out.ok b/src/tests/hw-health-samples/cron_outputs/hplog.out.ok |
4979 | new file mode 100644 |
4980 | index 0000000..3ff5bcb |
4981 | --- /dev/null |
4982 | +++ b/src/tests/hw-health-samples/cron_outputs/hplog.out.ok |
4983 | @@ -0,0 +1 @@ |
4984 | +OK No errors found |
4985 | \ No newline at end of file |
4986 | diff --git a/src/tests/hw-health-samples/cron_outputs/ipmi_sensors.out.critical b/src/tests/hw-health-samples/cron_outputs/ipmi_sensors.out.critical |
4987 | new file mode 100644 |
4988 | index 0000000..4359fa2 |
4989 | --- /dev/null |
4990 | +++ b/src/tests/hw-health-samples/cron_outputs/ipmi_sensors.out.critical |
4991 | @@ -0,0 +1 @@ |
4992 | +IPMI Status: Critical [16 system event log (SEL) entries present] | 'Current Power'=116 'Temp 1'=28.00;;~:42.00 'Temp 2 (CPU 1)'=40.00;;~:81.00 'Temp 3 (CPU 2)'=40.00;;~:81.00 'Temp 4 (MemD1)'=44.00;;~:87.00 'Temp 5 (MemD2)'=37.00;;~:87.00 'Temp 7 (IIOH)'=60.00;;~:105.00 'Temp 8 (PCIR)'=37.00;;~:85.00 'Temp 9 (PCIR)'=43.00;;~:85.00 'Temp 10 (PCIR)'=32.00;;~:70.00 'Temp 11 (PCIR)'=31.00;;~:65.00 'Temp 12 (PCIR)'=40.00;;~:75.00 'Temp 13 (PCIR)'=38.00;;~:87.00 'Temp 14 (PCIR)'=32.00;;~:65.00 'Temp 15 (IOH2)'=0.00;;~:105.00 |
4993 | diff --git a/src/tests/hw-health-samples/cron_outputs/ssacli.out.ok b/src/tests/hw-health-samples/cron_outputs/ssacli.out.ok |
4994 | new file mode 100644 |
4995 | index 0000000..3ff5bcb |
4996 | --- /dev/null |
4997 | +++ b/src/tests/hw-health-samples/cron_outputs/ssacli.out.ok |
4998 | @@ -0,0 +1 @@ |
4999 | +OK No errors found |
5000 | \ No newline at end of file |
supersedes https:/ /code.launchpad .net/~aieri/ charm-hw- health/ +git/hw- health- charm/+ merge/381343 to address comments.