Support both list and dict for pci_passthrough_whitelist

In Icehouse, pci_passthrough_whitelist is a json docstring that
encodes a list. In Juno, it is a json docstring that encodes
a dict. This patch adds the list support back to
pci_passthrough_whitelist, and both list and dict are now supported.

(cherry picked from commit bb7bfd313c)

Conflicts:
	nova/pci/pci_whitelist.py
	nova/tests/pci/test_pci_devspec.py
Because juno imports module as pci_devspec since
kilo as devspec.

Closes-Bug: 1383345
Change-Id: I523cfb756a09c75e4f60015adadb3a1403298cd3
This commit is contained in:
Robert Li
2014-10-24 14:41:09 -04:00
committed by sahid
parent 34b4ffbc6b
commit c2ef829c09
4 changed files with 66 additions and 43 deletions

View File

@@ -15,7 +15,6 @@ import ast
import re
from nova import exception
from nova.openstack.common import jsonutils
from nova.pci import pci_utils
MAX_VENDOR_ID = 0xFFFF
@@ -128,16 +127,15 @@ class PciAddress(object):
class PciDeviceSpec(object):
def __init__(self, dev_spec):
self.dev_spec = dev_spec
self.tags = dev_spec
self._init_dev_details()
self.dev_count = 0
def _init_dev_details(self):
details = jsonutils.loads(self.dev_spec)
self.vendor_id = details.pop("vendor_id", ANY)
self.product_id = details.pop("product_id", ANY)
self.address = details.pop("address", None)
self.dev_name = details.pop("devname", None)
self.vendor_id = self.tags.pop("vendor_id", ANY)
self.product_id = self.tags.pop("product_id", ANY)
self.address = self.tags.pop("address", None)
self.dev_name = self.tags.pop("devname", None)
self.vendor_id = self.vendor_id.strip()
get_pci_dev_info(self, 'vendor_id', MAX_VENDOR_ID, '%04x')
@@ -156,7 +154,6 @@ class PciDeviceSpec(object):
self.address = "*:*:*.*"
self.address = PciAddress(self.address, pf)
self.tags = details
def match(self, dev_dict):
conditions = [

View File

@@ -16,6 +16,9 @@
from oslo.config import cfg
from nova import exception
from nova.i18n import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.pci import pci_devspec
@@ -46,8 +49,26 @@ class PciHostDevicesWhiteList(object):
"""Parse and validate the pci whitelist from the nova config."""
specs = []
for jsonspec in whitelists:
spec = pci_devspec.PciDeviceSpec(jsonspec)
specs.append(spec)
try:
dev_spec = jsonutils.loads(jsonspec)
except ValueError:
raise exception.PciConfigInvalidWhitelist(
reason=_("Invalid entry: '%s'") % jsonspec)
if isinstance(dev_spec, dict):
dev_spec = [dev_spec]
elif not isinstance(dev_spec, list):
raise exception.PciConfigInvalidWhitelist(
reason=_("Invalid entry: '%s'; "
"Expecting list or dict") % jsonspec)
for ds in dev_spec:
if not isinstance(ds, dict):
raise exception.PciConfigInvalidWhitelist(
reason=_("Invalid entry: '%s'; "
"Expecting dict") % ds)
spec = pci_devspec.PciDeviceSpec(ds)
specs.append(spec)
return specs

View File

@@ -27,26 +27,25 @@ dev = {"vendor_id": "8086",
class PciAddressTestCase(test.NoDBTestCase):
def test_wrong_address(self):
pci_info = ('{"vendor_id": "8086", "address": "*: *: *.6",' +
'"product_id": "5057", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8086", "address": "*: *: *.6",
"product_id": "5057", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertFalse(pci.match(dev))
def test_address_too_big(self):
pci_info = ('{"address": "0000:0a:0b:00.5", ' +
'"physical_network": "hr_net"}')
pci_info = {"address": "0000:0a:0b:00.5",
"physical_network": "hr_net"}
self.assertRaises(exception.PciDeviceWrongAddressFormat,
pci_devspec.PciDeviceSpec, pci_info)
def test_address_invalid_character(self):
pci_info = '{"address": "0000:h4.12:6", "physical_network": "hr_net"}'
pci_info = {"address": "0000:h4.12:6", "physical_network": "hr_net"}
self.assertRaises(exception.PciDeviceWrongAddressFormat,
pci_devspec.PciDeviceSpec, pci_info)
def test_max_func(self):
pci_info = (('{"address": "0000:0a:00.%s", ' +
'"physical_network": "hr_net"}') %
(pci_devspec.MAX_FUNC + 1))
pci_info = {"address": "0000:0a:00.%s" % (pci_devspec.MAX_FUNC + 1),
"physical_network": "hr_net"}
exc = self.assertRaises(exception.PciDeviceInvalidAddressField,
pci_devspec.PciDeviceSpec, pci_info)
msg = ('Invalid PCI Whitelist: '
@@ -55,8 +54,8 @@ class PciAddressTestCase(test.NoDBTestCase):
self.assertEqual(msg, unicode(exc))
def test_max_domain(self):
pci_info = ('{"address": "%x:0a:00.5", "physical_network":"hr_net"}'
% (pci_devspec.MAX_DOMAIN + 1))
pci_info = {"address": "%x:0a:00.5" % (pci_devspec.MAX_DOMAIN + 1),
"physical_network": "hr_net"}
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
pci_devspec.PciDeviceSpec, pci_info)
msg = ('Invalid PCI devices Whitelist config invalid domain %x'
@@ -64,8 +63,8 @@ class PciAddressTestCase(test.NoDBTestCase):
self.assertEqual(msg, unicode(exc))
def test_max_bus(self):
pci_info = ('{"address": "0000:%x:00.5", "physical_network":"hr_net"}'
% (pci_devspec.MAX_BUS + 1))
pci_info = {"address": "0000:%x:00.5" % (pci_devspec.MAX_BUS + 1),
"physical_network": "hr_net"}
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
pci_devspec.PciDeviceSpec, pci_info)
msg = ('Invalid PCI devices Whitelist config invalid bus %x'
@@ -73,8 +72,8 @@ class PciAddressTestCase(test.NoDBTestCase):
self.assertEqual(msg, unicode(exc))
def test_max_slot(self):
pci_info = ('{"address": "0000:0a:%x.5", "physical_network":"hr_net"}'
% (pci_devspec.MAX_SLOT + 1))
pci_info = {"address": "0000:0a:%x.5" % (pci_devspec.MAX_SLOT + 1),
"physical_network": "hr_net"}
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
pci_devspec.PciDeviceSpec, pci_info)
msg = ('Invalid PCI devices Whitelist config invalid slot %x'
@@ -82,12 +81,12 @@ class PciAddressTestCase(test.NoDBTestCase):
self.assertEqual(msg, unicode(exc))
def test_address_is_undefined(self):
pci_info = '{"vendor_id":"8086", "product_id":"5057"}'
pci_info = {"vendor_id": "8086", "product_id": "5057"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(dev))
def test_partial_address(self):
pci_info = '{"address":":0a:00.", "physical_network":"hr_net"}'
pci_info = {"address": ":0a:00.", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
dev = {"vendor_id": "1137",
"product_id": "0071",
@@ -97,7 +96,7 @@ class PciAddressTestCase(test.NoDBTestCase):
@mock.patch('nova.pci.pci_utils.is_physical_function', return_value = True)
def test_address_is_pf(self, mock_is_physical_function):
pci_info = '{"address":"0000:0a:00.0", "physical_network":"hr_net"}'
pci_info = {"address": "0000:0a:00.0", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(dev))
@@ -107,63 +106,63 @@ class PciDevSpecTestCase(test.NoDBTestCase):
super(PciDevSpecTestCase, self).setUp()
def test_spec_match(self):
pci_info = ('{"vendor_id": "8086","address": "*: *: *.5",' +
'"product_id": "5057", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8086", "address": "*: *: *.5",
"product_id": "5057", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(dev))
def test_invalid_vendor_id(self):
pci_info = ('{"vendor_id": "8087","address": "*: *: *.5", ' +
'"product_id": "5057", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8087", "address": "*: *: *.5",
"product_id": "5057", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertFalse(pci.match(dev))
def test_vendor_id_out_of_range(self):
pci_info = ('{"vendor_id": "80860", "address": "*:*:*.5", ' +
'"product_id": "5057", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "80860", "address": "*:*:*.5",
"product_id": "5057", "physical_network": "hr_net"}
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
pci_devspec.PciDeviceSpec, pci_info)
self.assertEqual("Invalid PCI devices Whitelist config "
"invalid vendor_id 80860", unicode(exc))
def test_invalid_product_id(self):
pci_info = ('{"vendor_id": "8086","address": "*: *: *.5", ' +
'"product_id": "5056", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8086", "address": "*: *: *.5",
"product_id": "5056", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertFalse(pci.match(dev))
def test_product_id_out_of_range(self):
pci_info = ('{"vendor_id": "8086","address": "*:*:*.5", ' +
'"product_id": "50570", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8086", "address": "*:*:*.5",
"product_id": "50570", "physical_network": "hr_net"}
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
pci_devspec.PciDeviceSpec, pci_info)
self.assertEqual("Invalid PCI devices Whitelist config "
"invalid product_id 50570", unicode(exc))
def test_devname_and_address(self):
pci_info = ('{"devname": "eth0", "vendor_id":"8086", ' +
'"address":"*:*:*.5", "physical_network": "hr_net"}')
pci_info = {"devname": "eth0", "vendor_id": "8086",
"address": "*:*:*.5", "physical_network": "hr_net"}
self.assertRaises(exception.PciDeviceInvalidDeviceName,
pci_devspec.PciDeviceSpec, pci_info)
@mock.patch('nova.pci.pci_utils.get_function_by_ifname',
return_value = ("0000:0a:00.0", True))
def test_by_name(self, mock_get_function_by_ifname):
pci_info = '{"devname": "eth0", "physical_network": "hr_net"}'
pci_info = {"devname": "eth0", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(dev))
@mock.patch('nova.pci.pci_utils.get_function_by_ifname',
return_value = (None, False))
def test_invalid_name(self, mock_get_function_by_ifname):
pci_info = '{"devname": "lo", "physical_network": "hr_net"}'
pci_info = {"devname": "lo", "physical_network": "hr_net"}
exc = self.assertRaises(exception.PciDeviceNotFoundById,
pci_devspec.PciDeviceSpec, pci_info)
self.assertEqual('PCI device lo not found', unicode(exc))
def test_pci_obj(self):
pci_info = ('{"vendor_id": "8086","address": "*:*:*.5", ' +
'"product_id": "5057", "physical_network": "hr_net"}')
pci_info = {"vendor_id": "8086", "address": "*:*:*.5",
"product_id": "5057", "physical_network": "hr_net"}
pci = pci_devspec.PciDeviceSpec(pci_info)
pci_dev = {

View File

@@ -36,6 +36,12 @@ class PciHostDevicesWhiteListTestCase(test.NoDBTestCase):
parsed = pci_whitelist.PciHostDevicesWhiteList([white_list])
self.assertEqual(1, len(parsed.specs))
def test_whitelist_list_format(self):
white_list = '[{"product_id":"0001", "vendor_id":"8086"},'\
'{"product_id":"0002", "vendor_id":"8086"}]'
parsed = pci_whitelist.PciHostDevicesWhiteList([white_list])
self.assertEqual(2, len(parsed.specs))
def test_whitelist_empty(self):
parsed = pci_whitelist.PciHostDevicesWhiteList()
self.assertFalse(parsed.device_assignable(dev_dict))