From ad951b9288e2a1703883a3d7b7f63c5b172a1423 Mon Sep 17 00:00:00 2001 From: Christophe Simon Date: Fri, 11 Mar 2022 15:55:07 +0100 Subject: [PATCH] Fixed HP raid parser when disks are in JBOD The function parsing the RAID logical volumes in the HP module did not manage the case where disks were set in JBOD mode, thus having no RAID array set. This patch fixes this by checking if arrays are defined on pdisks before parsing the logical disks. Also added returncode read when executing the RAID related commands to raise a more precise error. --- netbox_agent/raid/hp.py | 32 +++++++++++++++++++++++++------- netbox_agent/raid/omreport.py | 23 ++++++++++++++++++++--- netbox_agent/raid/storcli.py | 25 +++++++++++++++++++++---- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/netbox_agent/raid/hp.py b/netbox_agent/raid/hp.py index 7089918..82d3c06 100644 --- a/netbox_agent/raid/hp.py +++ b/netbox_agent/raid/hp.py @@ -7,10 +7,26 @@ import re REGEXP_CONTROLLER_HP = re.compile(r'Smart Array ([a-zA-Z0-9- ]+) in Slot ([0-9]+)') +class HPRaidControllerError(Exception): + pass -def ssacli(command): - output = subprocess.getoutput('ssacli {}'.format(command) ) - lines = output.split('\n') + +def ssacli(sub_command): + command = ["ssacli"] + command.extend(sub_command.split()) + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + p.wait() + stdout = p.stdout.read().decode("utf-8") + if p.returncode != 0: + mesg = "Failed to execute command '{}':\n{}".format( + " ".join(command), stdout + ) + raise HPRaidControllerError(mesg) + lines = stdout.split('\n') lines = list(filter(None, lines)) return lines @@ -92,8 +108,10 @@ class HPRaidController(RaidController): self.controller_name = controller_name self.data = data self.pdrives = self._get_physical_disks() - self.ldrives = self._get_logical_drives() - self._get_virtual_drives_map() + arrays = [d['Array'] for d in self.pdrives.values() if d.get('Array')] + if arrays: + self.ldrives = self._get_logical_drives() + self._get_virtual_drives_map() def get_product_name(self): return self.controller_name @@ -135,6 +153,7 @@ class HPRaidController(RaidController): 'Type': 'SSD' if attrs.get('Interface Type') == 'Solid State SATA' else 'HDD', '_src': self.__class__.__name__, + 'custom_fields': {'pd_identifier': name} } return ret @@ -164,8 +183,7 @@ class HPRaidController(RaidController): " Ignoring.".format(name) ) continue - attrs['custom_fields'] = ld - attrs['custom_fields']['pd_identifier'] = name + attrs['custom_fields'].update(ld) def get_physical_disks(self): return list(self.pdrives.values()) diff --git a/netbox_agent/raid/omreport.py b/netbox_agent/raid/omreport.py index 6b4f780..811761b 100644 --- a/netbox_agent/raid/omreport.py +++ b/netbox_agent/raid/omreport.py @@ -6,15 +6,32 @@ import logging import re +class OmreportControllerError(Exception): + pass + + def omreport(sub_command): - command = 'omreport {}'.format(sub_command) - output = subprocess.getoutput(command) + command = ["omreport"] + command.extend(sub_command.split()) + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + p.wait() + stdout = p.stdout.read().decode("utf-8") + if p.returncode != 0: + mesg = "Failed to execute command '{}':\n{}".format( + " ".join(command), stdout + ) + raise OmreportControllerError(mesg) + res = {} section_re = re.compile('^[A-Z]') current_section = None current_obj = None - for line in output.split('\n'): + for line in stdout.split('\n'): if ': ' in line: attr, value = line.split(': ', 1) attr = attr.strip() diff --git a/netbox_agent/raid/storcli.py b/netbox_agent/raid/storcli.py index ee15fcf..c80380c 100644 --- a/netbox_agent/raid/storcli.py +++ b/netbox_agent/raid/storcli.py @@ -8,10 +8,27 @@ import re import os +class StorcliControllerError(Exception): + pass + + def storecli(sub_command): - command = 'storcli {} J'.format(sub_command) - output = subprocess.getoutput(command) - data = json.loads(output) + command = ["storcli"] + command.extend(sub_command.split()) + command.append("J") + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + p.wait() + stdout = p.stdout.read().decode("utf-8") + if p.returncode != 0: + mesg = "Failed to execute command '{}':\n{}".format( + " ".join(command), stdout + ) + raise StorcliControllerError(mesg) + data = json.loads(stdout) controllers = dict([ ( c['Command Status']['Controller'], @@ -22,7 +39,7 @@ def storecli(sub_command): if not controllers: logging.error( "Failed to execute command '{}'. " - "Ignoring data.".format(command) + "Ignoring data.".format(" ".join(command)) ) return {} return controllers