Extends pci devices metrics

Collects PCI class, revision, and bus information for the pci-devices
collector, these metrics as well as vendor id and device id are
components which can be used to construct device information like
lspci output, which is how cyborg agent collects accelerator devices.

Accelerator device based scheduling is possible after ironic has such
information in place.

Change-Id: I6c37c554f37dd5f1d21c8fd4fad2a4f44a3c75d7
Story: 2007971
Task: 40474
This commit is contained in:
Kaifeng Wang 2020-08-03 14:13:33 +08:00
parent a9ed390f08
commit b424fbfa35
3 changed files with 53 additions and 17 deletions

View File

@ -315,17 +315,38 @@ def collect_pci_devices_info(data, failures):
with open(os.path.join(pci_devices_path, subdir,
'device')) as vendor_device:
device = vendor_device.read().strip().split('x')[1]
with open(os.path.join(pci_devices_path, subdir,
'class')) as vendor_device:
pci_class = vendor_device.read().strip().split('x')[1]
except IOError as exc:
LOG.warning('Failed to gather vendor id or product id '
LOG.warning('Failed to gather vendor id, product id or pci class '
'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)
LOG.warning('Wrong format of vendor id, product id or pci class '
'in PCI device %s: %s', subdir, exc)
continue
pci_revision = None
pci_revision_path = os.path.join(pci_devices_path, subdir,
'revision')
if os.path.isfile(pci_revision_path):
try:
with open(pci_revision_path) as revision_file:
pci_revision = revision_file.read().strip().split('x')[1]
except IOError as exc:
LOG.warning('Failed to gather PCI revision from PCI '
'device %s: %s', subdir, exc)
except IndexError as exc:
LOG.warning('Wrong format of PCI revision in PCI '
'device %s: %s', subdir, exc)
LOG.debug(
'Found a PCI device with vendor id %s and product id %s',
vendor, device)
'Found a PCI device with vendor id %s, product id %s, class %s '
'and revision %s', vendor, device, pci_class, pci_revision)
pci_devices_info.append({'vendor_id': vendor,
'product_id': device})
'product_id': device,
'class': pci_class,
'revision': pci_revision,
'bus': subdir})
data['pci_devices'] = pci_devices_info

View File

@ -361,14 +361,21 @@ class TestCollectPciDevicesInfo(base.IronicAgentTest):
self.data = {}
self.failures = utils.AccumulatedFailures()
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(os.path, 'isdir', autospec=True)
def test_success(self, mock_isdir, mock_listdir):
def test_success(self, mock_isdir, mock_isfile, mock_listdir):
subdirs = ['foo', 'bar']
mock_listdir.return_value = subdirs
mock_isfile.return_value = True
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'}]
reads = ['0x1234', '0x5678', '0x060000', '0x01',
'0x9876', '0x5432', '0x030000', '0x00']
expected_pci_devices = [{'vendor_id': '1234', 'product_id': '5678',
'class': '060000', 'revision': '01',
'bus': 'foo'},
{'vendor_id': '9876', 'product_id': '5432',
'class': '030000', 'revision': '00',
'bus': 'bar'}]
mock_open = mock.mock_open()
with mock.patch('builtins.open', mock_open):
@ -376,7 +383,7 @@ class TestCollectPciDevicesInfo(base.IronicAgentTest):
mock_read.side_effect = reads
inspector.collect_pci_devices_info(self.data, self.failures)
self.assertEqual(2 * len(subdirs), mock_open.call_count)
self.assertEqual(4 * len(subdirs), mock_open.call_count)
self.assertListEqual(expected_pci_devices, self.data['pci_devices'])
def test_wrong_path(self, mock_listdir):
@ -387,14 +394,18 @@ class TestCollectPciDevicesInfo(base.IronicAgentTest):
self.assertNotIn('pci_devices', self.data)
self.assertEqual(1, len(self.failures._failures))
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(os.path, 'isdir', autospec=True)
def test_bad_pci_device_info(self, mock_isdir, mock_listdir):
def test_bad_pci_device_info(self, mock_isdir, mock_isfile, mock_listdir):
subdirs = ['foo', 'bar', 'baz']
mock_listdir.return_value = subdirs
mock_isfile.return_value = False
mock_isdir.return_value = True
reads = ['0x1234', '0x5678', '0x9876', IOError, IndexError,
'0x5432']
expected_pci_devices = [{'vendor_id': '1234', 'product_id': '5678'}]
reads = ['0x1234', '0x5678', '0x060000', '0x9876',
IOError, IndexError]
expected_pci_devices = [{'vendor_id': '1234', 'product_id': '5678',
'class': '060000', 'revision': None,
'bus': 'foo'}]
mock_open = mock.mock_open()
with mock.patch('builtins.open', mock_open):
@ -403,8 +414,8 @@ class TestCollectPciDevicesInfo(base.IronicAgentTest):
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)
# will not be called, so there are 6 mock_open calls in total
self.assertEqual(6, mock_open.call_count)
self.assertListEqual(expected_pci_devices, self.data['pci_devices'])

View File

@ -0,0 +1,4 @@
---
features:
- |
Introspection of PCI devices now collects PCI class, revision and PCI bus.