diff --git a/nova/exception.py b/nova/exception.py index b8f1d702e950..cbc7f816f665 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -1364,3 +1364,7 @@ class ImageDownloadModuleMetaDataError(ImageDownloadModuleError): class ImageDownloadModuleConfigurationError(ImageDownloadModuleError): msg_fmt = _("The module %(module)s is misconfigured: %(reason)s.") + + +class PciDeviceWrongAddressFormat(NovaException): + msg_fmt = _("The PCI address %(address)s has an incorrect format.") diff --git a/nova/pci/__init__.py b/nova/pci/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/nova/pci/pci_utils.py b/nova/pci/pci_utils.py new file mode 100644 index 000000000000..e5ce4a10bc43 --- /dev/null +++ b/nova/pci/pci_utils.py @@ -0,0 +1,57 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Intel, Inc. +# Copyright (c) 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import re + +from nova import exception + + +_PCI_ADDRESS_PATTERN = ("^(hex{4}):(hex{2}):(hex{2}).(oct{1})$". + replace("hex", "[\da-fA-F]"). + replace("oct", "[0-7]")) +_PCI_ADDRESS_REGEX = re.compile(_PCI_ADDRESS_PATTERN) + + +def pci_device_prop_match(pci_dev, specs): + """Check if the pci_dev meet spec requirement + + Specs is a list of PCI device property requirements. + An example of device requirement that the PCI should be either: + a) Device with vendor_id as 0x8086 and product_id as 0x8259, or + b) Device with vendor_id as 0x10de and product_id as 0x10d8: + + [{"vendor_id":"8086", "product_id":"8259"}, + {"vendor_id":"10de", "product_id":"10d8"}] + + """ + def _matching_devices(spec): + return all(pci_dev.get(k) == v for k, v in spec.iteritems()) + + return any(_matching_devices(spec) for spec in specs) + + +def parse_address(address): + """ + Returns (domain, bus, slot, function) from PCI address that is stored in + PciDevice DB table. + """ + m = _PCI_ADDRESS_REGEX.match(address) + if not m: + raise exception.PciDeviceWrongAddressFormat(address=address) + return m.groups() diff --git a/nova/tests/pci/__init__.py b/nova/tests/pci/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/nova/tests/pci/test_pci_utils.py b/nova/tests/pci/test_pci_utils.py new file mode 100644 index 000000000000..3d968290d7fc --- /dev/null +++ b/nova/tests/pci/test_pci_utils.py @@ -0,0 +1,63 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 Intel, Inc. +# Copyright (c) 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova import exception +from nova.pci import pci_utils +from nova import test + + +class PciDeviceMatchTestCase(test.TestCase): + def setUp(self): + super(PciDeviceMatchTestCase, self).setUp() + self.fake_pci_1 = {'vendor_id': 'v1', + 'device_id': 'd1'} + + def test_single_spec_match(self): + self.assertTrue(pci_utils.pci_device_prop_match( + self.fake_pci_1, [{'vendor_id': 'v1', 'device_id': 'd1'}])) + + def test_multiple_spec_match(self): + self.assertTrue(pci_utils.pci_device_prop_match( + self.fake_pci_1, + [{'vendor_id': 'v1', 'device_id': 'd1'}, + {'vendor_id': 'v3', 'device_id': 'd3'}])) + + def test_spec_dismatch(self): + self.assertFalse(pci_utils.pci_device_prop_match( + self.fake_pci_1, + [{'vendor_id': 'v4', 'device_id': 'd4'}, + {'vendor_id': 'v3', 'device_id': 'd3'}])) + + def test_spec_extra_key(self): + self.assertFalse(pci_utils.pci_device_prop_match( + self.fake_pci_1, + [{'vendor_id': 'v1', 'device_id': 'd1', 'wrong_key': 'k1'}])) + + +class PciDeviceAddressParserTestCase(test.TestCase): + def test_parse_address(self): + self.parse_result = pci_utils.parse_address("0000:04:12.6") + self.assertEqual(self.parse_result, ('0000', '04', '12', '6')) + + def test_parse_address_wrong(self): + self.assertRaises(exception.PciDeviceWrongAddressFormat, + pci_utils.parse_address, "0000:04.12:6") + + def test_parse_address_invalid_character(self): + self.assertRaises(exception.PciDeviceWrongAddressFormat, + pci_utils.parse_address, "0000:h4.12:6")