From f3e95636932b415e4208846cd2ab16625873e494 Mon Sep 17 00:00:00 2001 From: Sudeep Bhandari Date: Thu, 21 Mar 2024 13:28:59 +0545 Subject: [PATCH] Add config option collect_timeout (#67) * Add config option collect_timeout --- prometheus_hardware_exporter/__main__.py | 8 + prometheus_hardware_exporter/collector.py | 38 +++-- .../collectors/sasircu.py | 6 +- prometheus_hardware_exporter/config.py | 2 + prometheus_hardware_exporter/utils.py | 11 +- tests/unit/test_collector.py | 145 ++++++++++-------- tests/unit/test_dmidecode.py | 7 +- tests/unit/test_ipmi_dcmi.py | 19 ++- tests/unit/test_ipmi_sel.py | 11 +- tests/unit/test_ipmimonitoring.py | 7 +- tests/unit/test_perccli.py | 43 ++++-- tests/unit/test_sas2ircu.py | 34 ++-- tests/unit/test_sas3ircu.py | 34 ++-- tests/unit/test_ssacli.py | 37 +++-- tests/unit/test_storcli.py | 25 ++- tests/unit/test_utils.py | 18 ++- 16 files changed, 293 insertions(+), 152 deletions(-) diff --git a/prometheus_hardware_exporter/__main__.py b/prometheus_hardware_exporter/__main__.py index e6006f2..67036e4 100644 --- a/prometheus_hardware_exporter/__main__.py +++ b/prometheus_hardware_exporter/__main__.py @@ -17,6 +17,7 @@ SsaCLICollector, ) from .config import ( + DEFAULT_COLLECT_TIMEOUT, DEFAULT_CONFIG, DEFAULT_IPMI_SEL_INTERVAL, DEFAULT_REDFISH_CLIENT_MAX_RETRY, @@ -74,6 +75,12 @@ def parse_command_line() -> argparse.Namespace: default=DEFAULT_IPMI_SEL_INTERVAL, type=int, ) + parser.add_argument( + "--collect-timeout", + help="The timeout duration when running the shell commands to get the hardware data", + default=DEFAULT_COLLECT_TIMEOUT, + type=int, + ) parser.add_argument( "--collector.hpe_ssa", help="Enable HPE Smart Array Controller collector (default: disabled)", @@ -197,6 +204,7 @@ def main() -> None: redfish_client_timeout=namespace.redfish_client_timeout, redfish_client_max_retry=namespace.redfish_client_max_retry, redfish_discover_cache_ttl=namespace.redfish_discover_cache_ttl, + collect_timeout=namespace.collect_timeout, ) # Start the exporter diff --git a/prometheus_hardware_exporter/collector.py b/prometheus_hardware_exporter/collector.py index 02f4231..500afb9 100644 --- a/prometheus_hardware_exporter/collector.py +++ b/prometheus_hardware_exporter/collector.py @@ -31,7 +31,10 @@ class PowerEdgeRAIDCollector(BlockingCollector): """Collector for PowerEdge RAID controller.""" - perccli = PercCLI() + def __init__(self, config: Config) -> None: + """Initialize the collector.""" + self.perccli = PercCLI(config) + super().__init__(config) @property def specifications(self) -> List[Specification]: @@ -177,8 +180,11 @@ def process(self, payloads: List[Payload], datastore: Dict[str, Payload]) -> Lis class MegaRAIDCollector(BlockingCollector): """Collector for MegaRAID controller.""" - storcli = StorCLI() - mega_raid_helper = MegaRAIDCollectorHelper() + def __init__(self, config: Config) -> None: + """Initialize the collector.""" + self.storcli = StorCLI(config) + self.mega_raid_helper = MegaRAIDCollectorHelper() + super().__init__(config) @property def specifications(self) -> List[Specification]: @@ -324,9 +330,12 @@ def process(self, payloads: List[Payload], datastore: Dict[str, Payload]) -> Lis class IpmiDcmiCollector(BlockingCollector): """Collector for ipmi dcmi metrics.""" - ipmi_dcmi = IpmiDcmi() - ipmi_tool = IpmiTool() - dmidecode = Dmidecode() + def __init__(self, config: Config) -> None: + """Initialze the collector.""" + self.ipmi_dcmi = IpmiDcmi(config) + self.ipmi_tool = IpmiTool(config) + self.dmidecode = Dmidecode(config) + super().__init__(config) @property def specifications(self) -> List[Specification]: @@ -410,7 +419,10 @@ def process(self, payloads: List[Payload], datastore: Dict[str, Payload]) -> Lis class IpmiSensorsCollector(BlockingCollector): """Collector for ipmi sensors data.""" - ipmimonitoring = IpmiMonitoring() + def __init__(self, config: Config) -> None: + """Initialize the collector.""" + self.ipmimonitoring = IpmiMonitoring(config) + super().__init__(config) @property def specifications(self) -> List[Specification]: @@ -560,7 +572,10 @@ def _get_sensor_value_from_reading(self, reading: str) -> float: class IpmiSelCollector(BlockingCollector): """Collector for IPMI SEL data.""" - ipmi_sel = IpmiSel() + def __init__(self, config: Config) -> None: + """Initialize the collector.""" + self.ipmi_sel = IpmiSel(config) + super().__init__(config) @property def specifications(self) -> List[Specification]: @@ -626,7 +641,7 @@ class LSISASControllerCollector(BlockingCollector): def __init__(self, version: int, config: Config) -> None: """Initialize the collector.""" self.version = version - self.sasircu = Sasircu(version) + self.sasircu = Sasircu(config, version) self.lsi_sas_helper = LSISASCollectorHelper() super().__init__(config) @@ -804,7 +819,10 @@ def process(self, payloads: List[Payload], datastore: Dict[str, Payload]) -> Lis class SsaCLICollector(BlockingCollector): """Collector for storage arrays that support ssacli.""" - ssacli = SsaCLI() + def __init__(self, config: Config) -> None: + """Initialize the collector.""" + super().__init__(config) + self.ssacli = SsaCLI(self.config) @property def specifications(self) -> List[Specification]: diff --git a/prometheus_hardware_exporter/collectors/sasircu.py b/prometheus_hardware_exporter/collectors/sasircu.py index 4a0110a..04143d2 100644 --- a/prometheus_hardware_exporter/collectors/sasircu.py +++ b/prometheus_hardware_exporter/collectors/sasircu.py @@ -5,6 +5,8 @@ from logging import getLogger from typing import Any, Dict, List, Set, Tuple +from prometheus_hardware_exporter.config import Config + from ..utils import Command logger = getLogger(__name__) @@ -53,11 +55,11 @@ class Sasircu(Command): prefix = "" command = "" - def __init__(self, version: int) -> None: + def __init__(self, config: Config, version: int) -> None: """Initialize the command line tool.""" self.version = version self.command = f"sas{version}ircu" - super().__init__() + super().__init__(config) def _parse_key_value(self, text: str) -> Dict[str, Any]: """Return a dictionary from a text with the format of "key : value". diff --git a/prometheus_hardware_exporter/config.py b/prometheus_hardware_exporter/config.py index c74fd03..cc1be60 100644 --- a/prometheus_hardware_exporter/config.py +++ b/prometheus_hardware_exporter/config.py @@ -11,6 +11,7 @@ DEFAULT_CONFIG = os.path.join(os.environ.get("SNAP_DATA", "./"), "config.yaml") +DEFAULT_COLLECT_TIMEOUT = 30 DEFAULT_IPMI_SEL_INTERVAL = 86400 DEFAULT_REDFISH_CLIENT_TIMEOUT = 15 DEFAULT_REDFISH_CLIENT_MAX_RETRY = 1 @@ -26,6 +27,7 @@ class Config(BaseModel): level: str = "DEBUG" enable_collectors: List[str] = [] + collect_timeout: int = DEFAULT_COLLECT_TIMEOUT ipmi_sel_interval: int = DEFAULT_IPMI_SEL_INTERVAL redfish_host: str = "127.0.0.1" diff --git a/prometheus_hardware_exporter/utils.py b/prometheus_hardware_exporter/utils.py index 5a45602..8db355c 100644 --- a/prometheus_hardware_exporter/utils.py +++ b/prometheus_hardware_exporter/utils.py @@ -6,6 +6,8 @@ from logging import getLogger from typing import Optional, Union +from .config import Config + logger = getLogger(__name__) @@ -23,9 +25,10 @@ class Command: prefix = "" command = "" - def __init__(self) -> None: + def __init__(self, config: Config) -> None: """Initialize the Command class.""" self.installed = False + self.config = config def __call__(self, args: Optional[str] = None) -> Result: """Run the command, and return the result and error. @@ -62,7 +65,11 @@ def check_output( try: logger.debug("Running command: %s", full_command) result.data = ( - subprocess.check_output(full_command, shell=True, timeout=30).decode().strip() + subprocess.check_output( + full_command, shell=True, timeout=self.config.collect_timeout + ) + .decode() + .strip() ) except subprocess.CalledProcessError as err: logger.error(err) diff --git a/tests/unit/test_collector.py b/tests/unit/test_collector.py index 4cfb1e4..551550d 100644 --- a/tests/unit/test_collector.py +++ b/tests/unit/test_collector.py @@ -30,8 +30,18 @@ class TestCustomCollector(unittest.TestCase): def test_mega_raid_collector_not_installed(self): """Test mega raid collector when storcli is not installed.""" mega_raid_collector = MegaRAIDCollector(Mock()) - mega_raid_collector.sasircu = Mock() - mega_raid_collector.sasircu.installed = False + mega_raid_collector.storcli = Mock() + mega_raid_collector.storcli.installed = False + payloads = mega_raid_collector.collect() + + self.assertEqual(len(list(payloads)), 1) + + def test_mega_raid_collector_no_controller(self): + """Test mega raid collector when no controllers are present.""" + mega_raid_collector = MegaRAIDCollector(Mock()) + mega_raid_collector.storcli = Mock() + mega_raid_collector.storcli.installed = True + mega_raid_collector.storcli.get_all_information.return_value = {} payloads = mega_raid_collector.collect() self.assertEqual(len(list(payloads)), 1) @@ -469,16 +479,17 @@ def test_ssacli_installed_and_okay(self): for payload in payloads: self.assertIn(payload.name, available_metrics) - def test_perccli_collector_command_success(self): - with patch.object(PowerEdgeRAIDCollector, "perccli") as mock_cli: - # 1 success, 1 fail - mock_cli.ctrl_exists.return_value = True - mock_cli.ctrl_successes.return_value = {0: False, 1: True} - mock_cli.get_controllers.return_value = {"count": 1} - mock_cli.get_virtual_drives.return_value = {} + @patch("prometheus_hardware_exporter.collector.PercCLI") + def test_perccli_collector_command_success(self, mock_perccli): + perccli = mock_perccli() + perccli.ctrl_exists.return_value = True + perccli.ctrl_successes.return_value = {0: False, 1: True} + perccli.get_controllers.return_value = {"count": 1} + perccli.get_virtual_drives.return_value = {} + + power_edge_collector = PowerEdgeRAIDCollector(Mock()) + payloads = list(power_edge_collector.collect()) - power_edge_collector = PowerEdgeRAIDCollector(Mock()) - payloads = list(power_edge_collector.collect()) assert len(payloads) >= 4 assert payloads[0].samples[0].value == 1.0 @@ -491,17 +502,18 @@ def test_perccli_collector_command_success(self): assert payloads[2].samples[0].labels["controller_id"] == "1" assert payloads[2].samples[0].name == "perccli_command_ctrl_success" - def test_perccli_virtual_device_command_success(self): - with patch.object(PowerEdgeRAIDCollector, "perccli") as mock_cli: - mock_cli.success.return_value = True - mock_cli.ctrl_successes.return_value = {0: False, 1: True} - mock_cli.get_controllers.return_value = {"count": 1} - mock_cli.get_virtual_drives.return_value = { - 0: [{"DG": "0", "VD": "0", "cache": "NRWTD", "state": "Optl"}] - } + @patch("prometheus_hardware_exporter.collector.PercCLI") + def test_perccli_virtual_device_command_success(self, mock_perccli): + perccli = mock_perccli() + perccli.success.return_value = True + perccli.ctrl_successes.return_value = {0: False, 1: True} + perccli.get_controllers.return_value = {"count": 1} + perccli.get_virtual_drives.return_value = { + 0: [{"DG": "0", "VD": "0", "cache": "NRWTD", "state": "Optl"}] + } - power_edge_collector = PowerEdgeRAIDCollector(Mock()) - payloads = list(power_edge_collector.collect()) + power_edge_collector = PowerEdgeRAIDCollector(Mock()) + payloads = list(power_edge_collector.collect()) get_payloads = [] @@ -526,51 +538,54 @@ def test_perccli_virtual_device_command_success(self): ]: assert name in get_payloads - def test_perccli_cmd_fail(self): - with patch.object(PowerEdgeRAIDCollector, "perccli") as mock_cli: - mock_cli.success.return_value = False - power_edge_collector = PowerEdgeRAIDCollector(Mock()) - payloads = list(power_edge_collector.collect()) - assert len(payloads) == 1 - assert payloads[0].samples[0].value == 0.0 - - def test_perccli_no_controller_exists(self): - with patch.object(PowerEdgeRAIDCollector, "perccli") as mock_cli: - mock_cli.success.return_value = True - mock_cli.ctrl_exists.return_value = False - power_edge_collector = PowerEdgeRAIDCollector(Mock()) - payloads = list(power_edge_collector.collect()) - assert len(payloads) == 2 - assert payloads[1].samples[0].value == 0.0 - - def test_perccli_physical_device_command_success(self): - with patch.object(PowerEdgeRAIDCollector, "perccli") as mock_cli: - mock_cli.success.return_value = True - mock_cli.ctrl_successes.return_value = {0: False, 1: True} - mock_cli.get_controllers.return_value = {"count": 1} - mock_cli.get_physical_devices.return_value = { - 0: [ - { - "eid": "69", - "slt": "0", - "state": "Onln", - "DG": 0, - "size": "558.375 GB", - "media_type": "HDD", - }, - { - "eid": "69", - "slt": "1", - "state": "Onln", - "DG": 0, - "size": "558.375 GB", - "media_type": "HDD", - }, - ] - } + @patch("prometheus_hardware_exporter.collector.PercCLI") + def test_perccli_cmd_fail(self, mock_perccli): + perccli = mock_perccli() + perccli.success.return_value = False + power_edge_collector = PowerEdgeRAIDCollector(Mock()) + payloads = list(power_edge_collector.collect()) + assert len(payloads) == 1 + assert payloads[0].samples[0].value == 0.0 + + @patch("prometheus_hardware_exporter.collector.PercCLI") + def test_perccli_no_controller_exists(self, mock_perccli): + perccli = mock_perccli() + perccli.success.return_value = True + perccli.ctrl_exists.return_value = False + power_edge_collector = PowerEdgeRAIDCollector(Mock()) + payloads = list(power_edge_collector.collect()) + assert len(payloads) == 2 + assert payloads[1].samples[0].value == 0.0 + + @patch("prometheus_hardware_exporter.collector.PercCLI") + def test_perccli_physical_device_command_success(self, mock_perccli): + perccli = mock_perccli() + perccli.success.return_value = True + perccli.ctrl_successes.return_value = {0: False, 1: True} + perccli.get_controllers.return_value = {"count": 1} + perccli.get_physical_devices.return_value = { + 0: [ + { + "eid": "69", + "slt": "0", + "state": "Onln", + "DG": 0, + "size": "558.375 GB", + "media_type": "HDD", + }, + { + "eid": "69", + "slt": "1", + "state": "Onln", + "DG": 0, + "size": "558.375 GB", + "media_type": "HDD", + }, + ] + } - power_edge_collector = PowerEdgeRAIDCollector(Mock()) - payloads = list(power_edge_collector.collect()) + power_edge_collector = PowerEdgeRAIDCollector(Mock()) + payloads = list(power_edge_collector.collect()) get_payloads = [] diff --git a/tests/unit/test_dmidecode.py b/tests/unit/test_dmidecode.py index 6128959..29da970 100644 --- a/tests/unit/test_dmidecode.py +++ b/tests/unit/test_dmidecode.py @@ -2,6 +2,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.dmidecode import Dmidecode +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result TYPE_39_OUTPUT = "tests/unit/test_resources/dmidecode/dmidecode_type_39_output.txt" @@ -14,7 +15,8 @@ class TestDmidecode(unittest.TestCase): def test_00_get_power_capacities_success(self, mock_call): with open(TYPE_39_OUTPUT, "r") as content: mock_call.return_value = Result(content.read(), None) - dmidecode = Dmidecode() + config = Config() + dmidecode = Dmidecode(config) power_capacities = dmidecode.get_power_capacities() self.assertEqual(power_capacities, [1400, 1400]) @@ -22,6 +24,7 @@ def test_00_get_power_capacities_success(self, mock_call): def test_01_get_power_capacities_error(self, mock_call): mock_call.return_value = Result("", True) - dmidecode = Dmidecode() + config = Config() + dmidecode = Dmidecode(config) power_capacities = dmidecode.get_power_capacities() self.assertEqual(power_capacities, []) diff --git a/tests/unit/test_ipmi_dcmi.py b/tests/unit/test_ipmi_dcmi.py index b577d24..be2ab1b 100644 --- a/tests/unit/test_ipmi_dcmi.py +++ b/tests/unit/test_ipmi_dcmi.py @@ -2,6 +2,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.ipmi_dcmi import IpmiDcmi, IpmiTool +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result DCMI_SAMPLE_OUTPUT = "tests/unit/test_resources/ipmi/ipmi_dcmi_sample_output.txt" @@ -15,21 +16,24 @@ class TestIpmiDcmi(unittest.TestCase): def test_00_get_current_power_success(self, mock_call): with open(DCMI_SAMPLE_OUTPUT, "r") as content: mock_call.return_value = Result(content.read(), None) - ipmi_dcmi = IpmiDcmi() + config = Config() + ipmi_dcmi = IpmiDcmi(config) payload = ipmi_dcmi.get_current_power() self.assertEqual(payload, {"current_power": 105}) @patch.object(Command, "__call__") def test_01_get_current_power_error(self, mock_call): mock_call.return_value = Result("", True) - ipmi_dcmi = IpmiDcmi() + config = Config() + ipmi_dcmi = IpmiDcmi(config) payload = ipmi_dcmi.get_current_power() self.assertEqual(payload, {}) @patch.object(Command, "__call__") def test_01_get_current_power_parse_failure(self, mock_call): mock_call.return_value = Result("", None) - ipmi_dcmi = IpmiDcmi() + config = Config() + ipmi_dcmi = IpmiDcmi(config) payload = ipmi_dcmi.get_current_power() self.assertEqual(payload, {}) @@ -41,14 +45,16 @@ class TestIpmiTool(unittest.TestCase): def test_00_get_ps_redundancy_success(self, mock_call): with open(IPMITOOL_SDR_PS_SAMPLE_OUTPUT, "r") as content: mock_call.return_value = Result(content.read(), None) - ipmitool = IpmiTool() + config = Config() + ipmitool = IpmiTool(config) ps_redundancy = ipmitool.get_ps_redundancy() self.assertEqual(ps_redundancy, (True, True)) @patch.object(Command, "__call__") def test_01_get_ps_redundancy_error(self, mock_call): mock_call.return_value = Result("", True) - ipmitool = IpmiTool() + config = Config() + ipmitool = IpmiTool(config) ps_redundancy = ipmitool.get_ps_redundancy() self.assertEqual(ps_redundancy, (False, False)) @@ -57,6 +63,7 @@ def test_02_get_ps_redundancy_success_redundancy_disable(self, mock_call): with open(IPMITOOL_SDR_PS_SAMPLE_OUTPUT, "r") as content: data = content.read().replace("Fully Redundant", "Not Fully Redundant") mock_call.return_value = Result(data, None) - ipmitool = IpmiTool() + config = Config() + ipmitool = IpmiTool(config) ps_redundancy = ipmitool.get_ps_redundancy() self.assertEqual(ps_redundancy, (True, False)) diff --git a/tests/unit/test_ipmi_sel.py b/tests/unit/test_ipmi_sel.py index c25e35d..3a09823 100644 --- a/tests/unit/test_ipmi_sel.py +++ b/tests/unit/test_ipmi_sel.py @@ -4,6 +4,7 @@ from freezegun import freeze_time from prometheus_hardware_exporter.collectors.ipmi_sel import IpmiSel +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result SEL_SAMPLE_OUTPUT = "tests/unit/test_resources/ipmi/ipmi_sel_sample_output.txt" @@ -46,22 +47,24 @@ class TestIpmiSel(unittest.TestCase): def test_00_get_sel_entries_success(self, mock_call): with open(SEL_SAMPLE_OUTPUT, "r") as content: mock_call.return_value = Result(content.read(), None) - ipmi_sel = IpmiSel() + config = Config() + ipmi_sel = IpmiSel(config) payloads = ipmi_sel.get_sel_entries(24 * 60 * 60) expected_sel_entries = SAMPLE_SEL_ENTRIES - print(payloads) self.assertEqual(payloads, expected_sel_entries) @patch.object(Command, "__call__") def test_01_get_sel_entries_zero_records(self, mock_call): mock_call.return_value = Result("", None) - ipmi_sel = IpmiSel() + config = Config() + ipmi_sel = IpmiSel(config) payloads = ipmi_sel.get_sel_entries(300) self.assertEqual(payloads, []) @patch.object(Command, "__call__") def test_02_get_sel_entries_error(self, mock_call): mock_call.return_value = Result("", Exception()) - ipmi_sel = IpmiSel() + config = Config() + ipmi_sel = IpmiSel(config) payloads = ipmi_sel.get_sel_entries(300) self.assertEqual(payloads, None) diff --git a/tests/unit/test_ipmimonitoring.py b/tests/unit/test_ipmimonitoring.py index af38d0d..8c46ec3 100644 --- a/tests/unit/test_ipmimonitoring.py +++ b/tests/unit/test_ipmimonitoring.py @@ -2,6 +2,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.ipmimonitoring import IpmiMonitoring +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result IPMIMONITORING_SAMPLE_OUTPUT = "tests/unit/test_resources/ipmi/ipmimonitoring_sample_output.txt" @@ -43,7 +44,8 @@ class TestIpmiMonitoring(unittest.TestCase): def test_00_get_sensor_data_success(self, mock_call): with open(IPMIMONITORING_SAMPLE_OUTPUT, "r") as content: mock_call.return_value = Result(content.read(), None) - ipmimonitoring = IpmiMonitoring() + config = Config() + ipmimonitoring = IpmiMonitoring(config) payloads = ipmimonitoring.get_sensor_data() expected_sensor_entries = SAMPLE_SENSOR_ENTRIES self.assertEqual(payloads, expected_sensor_entries) @@ -51,6 +53,7 @@ def test_00_get_sensor_data_success(self, mock_call): @patch.object(Command, "__call__") def test_00_get_sensor_data_error(self, mock_call): mock_call.return_value = Result("", True) - ipmimonitoring = IpmiMonitoring() + config = Config() + ipmimonitoring = IpmiMonitoring(config) payloads = ipmimonitoring.get_sensor_data() self.assertEqual(payloads, []) diff --git a/tests/unit/test_perccli.py b/tests/unit/test_perccli.py index 2b1fa4c..62fb32a 100644 --- a/tests/unit/test_perccli.py +++ b/tests/unit/test_perccli.py @@ -4,6 +4,7 @@ import pytest from prometheus_hardware_exporter.collectors.perccli import PercCLI +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result PERCCLI_NO_CONTROLLER = "tests/unit/test_resources/perccli/perccli_not_controller.json" @@ -45,38 +46,45 @@ def mock_get_ctrl(): class TestPercCLI: def test_00_no_controller(self, mock_get_ctrl_no_controller): """Command success but not controller exists.""" - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.ctrl_successes() == {} assert cli.success() is True assert cli.ctrl_exists() is False def test_01_no_success(self, mock_get_ctrl_error): """Command success but not controller exists.""" - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.success() is False def test_02_get_controllers(self, mock_get_ctrl): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.get_controllers() == {"count": 1} def test_03_get_controllers_error(self, mock_get_ctrl_error): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.ctrl_exists() is False assert cli.ctrl_successes() == {} assert cli.get_controllers() == {} def test_04_get_virtual_drives(self, mock_get_ctrl): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.get_virtual_drives() == { 0: [{"DG": "0", "VD": "0", "cache": "NRWTD", "state": "Optl"}] } def test_05_get_virtual_drives_error(self, mock_get_ctrl_error): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.get_virtual_drives() == {} def test_06_cmd_status(self, mock_get_ctrl): - cli = PercCLI() + config = Config() + cli = PercCLI(config) result = cli._get_controllers() for controller in result["Controllers"]: @@ -84,7 +92,8 @@ def test_06_cmd_status(self, mock_get_ctrl): assert cmd_status is True def test_07_cmd_status_fail(self, mock_get_ctrl_no_controller): - cli = PercCLI() + config = Config() + cli = PercCLI(config) result = cli._get_controllers() for controller in result["Controllers"]: @@ -96,7 +105,8 @@ def test_08__get_controllers(self, mock_call): with open(PERCCLI_OUTPUT, "r") as content: return_data = content.read() mock_call.return_value = Result(data=return_data) - cli = PercCLI() + config = Config() + cli = PercCLI(config) result = cli._get_controllers() assert not isinstance(result, Exception) @@ -104,7 +114,8 @@ def test_08__get_controllers(self, mock_call): def test_09__get_controllers_fail(self, mock_call): err_mock = Mock() mock_call.return_value = Result(data=None, error=err_mock) - cli = PercCLI() + config = Config() + cli = PercCLI(config) result = cli._get_controllers() assert result == err_mock @@ -118,18 +129,21 @@ def test_10__get_controllers_get_json_output_fail( mock_call.return_value = Result() err_mock = Mock() mock_get_json_output.return_value = Result(data=None, error=err_mock) - cli = PercCLI() + config = Config() + cli = PercCLI(config) result = cli._get_controllers() assert result.error == err_mock def test_11_ctrl_successes(self, mock_get_ctrl): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.ctrl_successes() == {0: True} assert cli.ctrl_exists() is True assert cli.success() is True def test_12_get_physical_devices(self, mock_get_ctrl): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.ctrl_successes() == {0: True} assert cli.ctrl_exists() is True @@ -155,5 +169,6 @@ def test_12_get_physical_devices(self, mock_get_ctrl): } def test_13_get_physical_devices_error(self, mock_get_ctrl_error): - cli = PercCLI() + config = Config() + cli = PercCLI(config) assert cli.get_physical_devices() == {} diff --git a/tests/unit/test_sas2ircu.py b/tests/unit/test_sas2ircu.py index dfd7a90..b378412 100644 --- a/tests/unit/test_sas2ircu.py +++ b/tests/unit/test_sas2ircu.py @@ -2,6 +2,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.sasircu import Sasircu +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result LIST = "tests/unit/test_resources/sas2ircu/list.txt" @@ -16,7 +17,8 @@ class TestSasircu(unittest.TestCase): def test_00_list_okay(self, mock_call): with open(LIST, "r") as content: mock_call.return_value = Result(content.read(), None) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) adapters = sas2ircu.get_adapters() expected_adapters = { "0": { @@ -43,7 +45,8 @@ def test_00_list_okay(self, mock_call): @patch.object(Command, "__call__") def test_01_list_failed(self, mock_call): mock_call.return_value = Result("", None) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) adapters = sas2ircu.get_adapters() self.assertEqual(adapters, {}) @@ -51,7 +54,8 @@ def test_01_list_failed(self, mock_call): def test_10_get_all_information_has_volume(self, mock_call): with open(DISPLAY_HAS_VOLUMES, "r") as content: mock_call.return_value = Result(content.read(), None) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) information = sas2ircu.get_all_information(0) expected_information = { "controller": { @@ -137,7 +141,8 @@ def test_10_get_all_information_has_volume(self, mock_call): def test_11_get_all_information_no_volumes(self, mock_call): with open(DISPLAY_NO_VOLUMES, "r") as content: mock_call.return_value = Result(content.read(), None) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) information = sas2ircu.get_all_information(0) expected_information = { "controller": { @@ -206,7 +211,8 @@ def test_11_get_all_information_no_volumes(self, mock_call): @patch.object(Command, "__call__") def test_20_get_all_information_failed(self, mock_call): mock_call.return_value = Result("some content", True) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) information = sas2ircu.get_all_information(0) expected_information = {} self.assertEqual(information, expected_information) @@ -214,7 +220,8 @@ def test_20_get_all_information_failed(self, mock_call): @patch.object(Command, "__call__") def test_21_get_all_information_failed(self, mock_call): mock_call.return_value = Result("", None) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) information = sas2ircu.get_all_information(0) expected_information = {} self.assertEqual(information, expected_information) @@ -222,31 +229,36 @@ def test_21_get_all_information_failed(self, mock_call): @patch.object(Command, "__call__") def test_30_get_adapters_failed(self, mock_call): mock_call.return_value = Result("", True) - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) adapters = sas2ircu.get_adapters() expected_adapters = {} self.assertEqual(adapters, expected_adapters) def test_40__parse_key_value_failed(self): - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) kv = sas2ircu._parse_key_value("") expected_kv = {} self.assertEqual(kv, expected_kv) def test_50__get_controller_failed(self): - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) controllers = sas2ircu._get_controller("") expected_controllers = {} self.assertEqual(controllers, expected_controllers) def test_60__get_physical_disks(self): - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) topology = sas2ircu._get_physical_disks("") expected_topology = {} self.assertEqual(topology, expected_topology) def test_70__get_enclosures_failed(self): - sas2ircu = Sasircu(2) + config = Config() + sas2ircu = Sasircu(config, 2) enclosures = sas2ircu._get_enclosures("") expected_enclosures = {} self.assertEqual(enclosures, expected_enclosures) diff --git a/tests/unit/test_sas3ircu.py b/tests/unit/test_sas3ircu.py index cabb3ff..ca641a6 100644 --- a/tests/unit/test_sas3ircu.py +++ b/tests/unit/test_sas3ircu.py @@ -2,6 +2,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.sasircu import Sasircu +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result LIST = "tests/unit/test_resources/sas3ircu/list.txt" @@ -16,7 +17,8 @@ class TestSasircu(unittest.TestCase): def test_00_list_okay(self, mock_call): with open(LIST, "r") as content: mock_call.return_value = Result(content.read(), None) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) adapters = sas3ircu.get_adapters() expected_adapters = { "0": { @@ -43,7 +45,8 @@ def test_00_list_okay(self, mock_call): @patch.object(Command, "__call__") def test_01_list_failed(self, mock_call): mock_call.return_value = Result("", None) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) adapters = sas3ircu.get_adapters() self.assertEqual(adapters, {}) @@ -51,7 +54,8 @@ def test_01_list_failed(self, mock_call): def test_10_get_all_information_has_volume(self, mock_call): with open(DISPLAY_HAS_VOLUMES, "r") as content: mock_call.return_value = Result(content.read(), None) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) information = sas3ircu.get_all_information(0) expected_information = { "controller": { @@ -139,7 +143,8 @@ def test_10_get_all_information_has_volume(self, mock_call): def test_11_get_all_information_no_volumes(self, mock_call): with open(DISPLAY_NO_VOLUMES, "r") as content: mock_call.return_value = Result(content.read(), None) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) information = sas3ircu.get_all_information(0) expected_information = { "controller": { @@ -204,7 +209,8 @@ def test_11_get_all_information_no_volumes(self, mock_call): @patch.object(Command, "__call__") def test_20_get_all_information_failed(self, mock_call): mock_call.return_value = Result("some content", True) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) information = sas3ircu.get_all_information(0) expected_information = {} self.assertEqual(information, expected_information) @@ -212,7 +218,8 @@ def test_20_get_all_information_failed(self, mock_call): @patch.object(Command, "__call__") def test_21_get_all_information_failed(self, mock_call): mock_call.return_value = Result("", None) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) information = sas3ircu.get_all_information(0) expected_information = {} self.assertEqual(information, expected_information) @@ -220,31 +227,36 @@ def test_21_get_all_information_failed(self, mock_call): @patch.object(Command, "__call__") def test_30_get_adapters_failed(self, mock_call): mock_call.return_value = Result("", True) - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) adapters = sas3ircu.get_adapters() expected_adapters = {} self.assertEqual(adapters, expected_adapters) def test_40__parse_key_value_failed(self): - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) kv = sas3ircu._parse_key_value("") expected_kv = {} self.assertEqual(kv, expected_kv) def test_50__get_controller_failed(self): - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) controllers = sas3ircu._get_controller("") expected_controllers = {} self.assertEqual(controllers, expected_controllers) def test_60__get_physical_disks(self): - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) topology = sas3ircu._get_physical_disks("") expected_topology = {} self.assertEqual(topology, expected_topology) def test_70__get_enclosures_failed(self): - sas3ircu = Sasircu(3) + config = Config() + sas3ircu = Sasircu(config, 3) enclosures = sas3ircu._get_enclosures("") expected_enclosures = {} self.assertEqual(enclosures, expected_enclosures) diff --git a/tests/unit/test_ssacli.py b/tests/unit/test_ssacli.py index fc6a9fe..f9cd566 100644 --- a/tests/unit/test_ssacli.py +++ b/tests/unit/test_ssacli.py @@ -12,6 +12,7 @@ ) from prometheus_hardware_exporter.collectors.ssacli import SsaCLI +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result @@ -21,21 +22,24 @@ class TestSsaCLI(unittest.TestCase): @patch.object(Command, "__call__") def test_00__get_controller_slots_success(self, mock_call): mock_call.return_value = Result(CTRL_ALL_SHOW, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) slots = ssacli._get_controller_slots() self.assertEqual(slots, ["2", "3", "12"]) @patch.object(Command, "__call__") def test_01__get_controller_slots_error(self, mock_call): mock_call.return_value = Result("", True) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) slots = ssacli._get_controller_slots() self.assertEqual(slots, []) @patch.object(Command, "__call__") def test_10__get_controller_status_success(self, mock_call): mock_call.return_value = Result(CTRL_SHOW_STATUS, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ctrl_status = ssacli._get_controller_status(1) expected_ctrl_status = { "Controller Status": "OK", @@ -47,7 +51,8 @@ def test_10__get_controller_status_success(self, mock_call): @patch.object(Command, "__call__") def test_11__get_controller_status_success_bad(self, mock_call): mock_call.return_value = Result(CTRL_SHOW_STATUS_BAD, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ctrl_status = ssacli._get_controller_status(1) expected_ctrl_status = { "Controller Status": "OK", @@ -59,14 +64,16 @@ def test_11__get_controller_status_success_bad(self, mock_call): @patch.object(Command, "__call__") def test_12__get_controller_status_error(self, mock_call): mock_call.return_value = Result("", True) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ctrl_status = ssacli._get_controller_status(1) self.assertEqual(ctrl_status, {}) @patch.object(Command, "__call__") def test_20__get_ld_status_success(self, mock_call): mock_call.return_value = Result(CTRL_LD_ALL_SHOW_STATUS, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ld_status = ssacli._get_ld_status(1) expected_ld_status = {"1": "OK"} self.assertEqual(ld_status, expected_ld_status) @@ -74,21 +81,24 @@ def test_20__get_ld_status_success(self, mock_call): @patch.object(Command, "__call__") def test_21__get_ld_status_error(self, mock_call): mock_call.return_value = Result("", True) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ld_status = ssacli._get_ld_status(1) self.assertEqual(ld_status, {}) @patch.object(Command, "__call__") def test_22__get_ld_status_absent(self, mock_call): mock_call.return_value = Result(CTRL_LD_ALL_SHOW_STATUS_ABSENT, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) ld_status = ssacli._get_ld_status(1) self.assertEqual(ld_status, {}) @patch.object(Command, "__call__") def test_30__get_pd_status_success(self, mock_call): mock_call.return_value = Result(CTRL_PD_ALL_SHOW_STATUS, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) pd_status = ssacli._get_pd_status(1) expected_pd_status = {"2I:0:1": "OK", "2I:0:2": "OK"} self.assertEqual(pd_status, expected_pd_status) @@ -96,14 +106,16 @@ def test_30__get_pd_status_success(self, mock_call): @patch.object(Command, "__call__") def test_31__get_pd_status_error(self, mock_call): mock_call.return_value = Result("", True) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) pd_status = ssacli._get_pd_status(1) self.assertEqual(pd_status, {}) @patch.object(Command, "__call__") def test_32__get_pd_status_absent(self, mock_call): mock_call.return_value = Result(CTRL_PD_ALL_SHOW_STATUS_ABSENT, None) - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) pd_status = ssacli._get_pd_status(1) self.assertEqual(pd_status, {}) @@ -122,7 +134,8 @@ def test_40_get_payload( } mock_ld_status.return_value = {"1": "OK"} mock_pd_status.return_value = {"2I:0:1": "OK", "2I:0:2": "OK"} - ssacli = SsaCLI() + config = Config() + ssacli = SsaCLI(config) payload = ssacli.get_payload() expected_payload = { "1": { diff --git a/tests/unit/test_storcli.py b/tests/unit/test_storcli.py index 21c0398..e9b8eab 100644 --- a/tests/unit/test_storcli.py +++ b/tests/unit/test_storcli.py @@ -3,6 +3,7 @@ from unittest.mock import patch from prometheus_hardware_exporter.collectors.storcli import StorCLI +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result CALL_SHOW_ALL = "tests/unit/test_resources/storcli/cALL_show_all.txt" @@ -13,19 +14,22 @@ class TestStorCLI(unittest.TestCase): @patch.object(Command, "__call__") def test_00__extract_enclosures_fail(self, mock_call): - storcli = StorCLI() + config = Config() + storcli = StorCLI(config) payload = storcli._extract_enclosures({}) self.assertEqual(payload, []) @patch.object(Command, "__call__") def test_01__extract_virtual_drives_fail(self, mock_call): - storcli = StorCLI() + config = Config() + storcli = StorCLI(config) payload = storcli._extract_virtual_drives({}) self.assertEqual(payload, []) @patch.object(Command, "__call__") def test_03__extract_physical_drives_fail(self, mock_call): - storcli = StorCLI() + config = Config() + storcli = StorCLI(config) payload = storcli._extract_physical_drives({}) self.assertEqual(payload, []) @@ -33,7 +37,8 @@ def test_03__extract_physical_drives_fail(self, mock_call): def test_10_get_all_information_okay(self, mock_call): with open(CALL_SHOW_ALL, "r") as content: mock_call.return_value = Result(content.read(), None) - storcli = StorCLI() + config = Config() + storcli = StorCLI(config) payload = storcli.get_all_information() expected_payload = { 0: { @@ -105,7 +110,17 @@ def test_10_get_all_information_okay(self, mock_call): def test_11_get_all_information_fail(self, mock_call): mock_controllers = json.dumps({"Controllers": [{}]}) mock_call.return_value = Result(mock_controllers, None) - storcli = StorCLI() + config = Config() + storcli = StorCLI(config) + payload = storcli.get_all_information() + expected_payload = {} + self.assertEqual(payload, expected_payload) + + @patch.object(Command, "__call__") + def test_12_get_all_information_error(self, mock_call): + mock_call.return_value = Result(error="error") + config = Config() + storcli = StorCLI(config) payload = storcli.get_all_information() expected_payload = {} self.assertEqual(payload, expected_payload) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index c137e46..bfe3806 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -3,6 +3,7 @@ from unittest.mock import patch from prometheus_hardware_exporter import utils +from prometheus_hardware_exporter.config import Config from prometheus_hardware_exporter.utils import Command, Result, get_json_output @@ -12,15 +13,17 @@ class TestCommand(unittest.TestCase): @patch.object(Command, "check_output") def test_command_installed(self, mock_check_output): mock_check_output.return_value = Result("", None) - command = Command() - result = command() + config = Config() + command = Command(config) + result = command(config) self.assertTrue(command.installed) self.assertEqual(result.error, None) @patch.object(Command, "check_output") def test_command_not_installed(self, mock_check_output): mock_check_output.return_value = Result("", Exception()) - command = Command() + config = Config() + command = Command(config) result = command() self.assertFalse(command.installed) self.assertEqual(type(result.error), ValueError) @@ -28,7 +31,8 @@ def test_command_not_installed(self, mock_check_output): @patch.object(utils.subprocess, "check_output") def test_check_output_okay(self, mock_subprocess_check_output): mock_subprocess_check_output.return_value = b"" - command = Command() + config = Config() + command = Command(config) command.installed = True result = command() self.assertEqual(result.data, "") @@ -37,7 +41,8 @@ def test_check_output_okay(self, mock_subprocess_check_output): @patch.object(utils.subprocess, "check_output") def test_check_output_failed(self, mock_subprocess_check_output): mock_subprocess_check_output.side_effect = subprocess.CalledProcessError(1, "cmd") - command = Command() + config = Config() + command = Command(config) command.installed = True result = command() self.assertEqual(result.data, "") @@ -46,7 +51,8 @@ def test_check_output_failed(self, mock_subprocess_check_output): @patch.object(utils.subprocess, "check_output") def test_check_output_timeout(self, mock_subprocess_check_output): - command = Command() + config = Config() + command = Command(config) command.installed = True command()