From f7e080c8bfeae354b4ca17e41663f8b84c4e7e1a Mon Sep 17 00:00:00 2001 From: Szymon Borkowski Date: Mon, 6 Jun 2016 15:16:11 +0200 Subject: [PATCH] Add PCI devices collector to inspector Adds a new collector, which gathers list of PCI devices. Each entry is a dictionary containing 2 keys: - vendor-id - product-id Such information can then be used by the inspector to distinguish appropriate PCI devices. Change-Id: Id7521d66410e7d408d7eada692b6123e769ce084 Partial-Bug: #1580893 --- ironic_python_agent/inspector.py | 50 +++++++++++++++++ .../tests/unit/test_inspector.py | 55 +++++++++++++++++++ ...add-pci-devices-info-3f86934a505d1b31.yaml | 9 +++ setup.cfg | 1 + 4 files changed, 115 insertions(+) create mode 100644 releasenotes/notes/add-pci-devices-info-3f86934a505d1b31.yaml diff --git a/ironic_python_agent/inspector.py b/ironic_python_agent/inspector.py index 715baf189..3c59f9e3c 100644 --- a/ironic_python_agent/inspector.py +++ b/ironic_python_agent/inspector.py @@ -16,6 +16,7 @@ import base64 import io import json +import os import tarfile import time @@ -381,3 +382,52 @@ def collect_extra_hardware(data, failures): except ValueError as exc: msg = 'JSON returned from hardware-detect cannot be decoded: %s' failures.add(msg, exc) + + +def collect_pci_devices_info(data, failures): + """Collect a list of PCI devices. + + Each PCI device entry in list is a dictionary containing vendor_id and + product_id keys, which will be then used by the ironic inspector to + distinguish various PCI devices. + + The data is gathered from /sys/bus/pci/devices directory. + + :param data: mutable data that we'll send to inspector + :param failures: AccumulatedFailures object + """ + pci_devices_path = '/sys/bus/pci/devices' + pci_devices_info = [] + try: + subdirs = os.listdir(pci_devices_path) + except OSError as exc: + msg = 'Failed to get list of PCI devices: %s' + failures.add(msg, exc) + return + for subdir in subdirs: + if not os.path.isdir(os.path.join(pci_devices_path, subdir)): + continue + try: + # note(sborkows): ids located in files inside PCI devices + # directory are stored in hex format (0x1234 for example) and + # we only need that part after 'x' delimiter + with open(os.path.join(pci_devices_path, subdir, + 'vendor')) as vendor_file: + vendor = vendor_file.read().strip().split('x')[1] + with open(os.path.join(pci_devices_path, subdir, + 'device')) as vendor_device: + device = vendor_device.read().strip().split('x')[1] + except IOError as exc: + LOG.warning('Failed to gather vendor id or product id ' + 'from PCI device %s: %s', subdir, exc) + continue + except IndexError as exc: + LOG.warning('Wrong format of vendor id or product id in PCI ' + 'device %s: %s', subdir, exc) + continue + LOG.debug( + 'Found a PCI device with vendor id %s and product id %s', + vendor, device) + pci_devices_info.append({'vendor_id': vendor, + 'product_id': device}) + data['pci_devices'] = pci_devices_info diff --git a/ironic_python_agent/tests/unit/test_inspector.py b/ironic_python_agent/tests/unit/test_inspector.py index cb75d7913..dfeb3a678 100644 --- a/ironic_python_agent/tests/unit/test_inspector.py +++ b/ironic_python_agent/tests/unit/test_inspector.py @@ -17,6 +17,7 @@ import base64 import collections import copy import io +import os import tarfile import time @@ -461,6 +462,60 @@ class TestCollectExtraHardware(test_base.BaseTestCase): mock_execute.assert_called_once_with('hardware-detect') +@mock.patch.object(os, 'listdir', autospec=True) +class TestCollectPciDevicesInfo(test_base.BaseTestCase): + def setUp(self): + super(TestCollectPciDevicesInfo, self).setUp() + self.data = {} + self.failures = utils.AccumulatedFailures() + + @mock.patch.object(os.path, 'isdir', autospec=True) + def test_success(self, mock_isdir, mock_listdir): + subdirs = ['foo', 'bar'] + mock_listdir.return_value = subdirs + mock_isdir.return_value = True + reads = ['0x1234', '0x5678', '0x9876', '0x5432'] + expected_pci_devices = [{'vendor_id': '1234', 'product_id': '5678'}, + {'vendor_id': '9876', 'product_id': '5432'}] + + mock_open = mock.mock_open() + with mock.patch('six.moves.builtins.open', mock_open): + mock_read = mock_open.return_value.read + mock_read.side_effect = reads + inspector.collect_pci_devices_info(self.data, self.failures) + + self.assertEqual(2 * len(subdirs), mock_open.call_count) + self.assertListEqual(expected_pci_devices, self.data['pci_devices']) + + def test_wrong_path(self, mock_listdir): + mock_listdir.side_effect = OSError() + + inspector.collect_pci_devices_info(self.data, self.failures) + + self.assertFalse('pci_devices' in self.data) + self.assertEqual(1, len(self.failures._failures)) + + @mock.patch.object(os.path, 'isdir', autospec=True) + def test_bad_pci_device_info(self, mock_isdir, mock_listdir): + subdirs = ['foo', 'bar', 'baz'] + mock_listdir.return_value = subdirs + mock_isdir.return_value = True + reads = ['0x1234', '0x5678', '0x9876', IOError, IndexError, + '0x5432'] + expected_pci_devices = [{'vendor_id': '1234', 'product_id': '5678'}] + + mock_open = mock.mock_open() + with mock.patch('six.moves.builtins.open', mock_open): + mock_read = mock_open.return_value.read + mock_read.side_effect = reads + inspector.collect_pci_devices_info(self.data, self.failures) + + # note(sborkows): due to throwing IOError, the corresponding mock_open + # will not be called, so there are 5 mock_open calls in total + self.assertEqual(5, mock_open.call_count) + self.assertListEqual(expected_pci_devices, self.data['pci_devices']) + + @mock.patch.object(utils, 'get_agent_params', lambda: {'BOOTIF': '01-cdef'}) @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True) class TestWaitForDhcp(test_base.BaseTestCase): diff --git a/releasenotes/notes/add-pci-devices-info-3f86934a505d1b31.yaml b/releasenotes/notes/add-pci-devices-info-3f86934a505d1b31.yaml new file mode 100644 index 000000000..0a5097e9f --- /dev/null +++ b/releasenotes/notes/add-pci-devices-info-3f86934a505d1b31.yaml @@ -0,0 +1,9 @@ +--- +features: + - Adds new PCI devices collector named "pci_devices" + to inspector module. + Data is gathered from /sys/bus/pci/devices directory + and is stored under "pci_devices" key as a dictionary + containing "vendor_id" and "product_id" keys, which + then will be used by the inspector to distinguish + various PCI devices. diff --git a/setup.cfg b/setup.cfg index 2d6ec623b..14a893e61 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ ironic_python_agent.inspector.collectors = default = ironic_python_agent.inspector:collect_default logs = ironic_python_agent.inspector:collect_logs extra-hardware = ironic_python_agent.inspector:collect_extra_hardware + pci-devices = ironic_python_agent.inspector:collect_pci_devices_info [pbr] autodoc_index_modules = True