Support SR-IOV networking in the PCI modules
This patch takes care of changing the PCI modules in support of SR-IOV networking: -- Updated pci_whitelist to use devspec that is introduced in this patch: https://review.openstack.org/#/c/99043/ -- For stats pool, to keep the existing behavior, pool keys always include product_id and vendor_id. Meanwhile, to work with tags and support pci requests based on tags, pools are ordered based on the number of keys of the pool. This makes sure that legacy pci requests will be satisfied with the pools in the beginning, possiblly with devices that are not tagged. Partially Implements: blueprint pci-passthrough-sriov Change-Id: I9da7bd921f098c958d6fb711a4d03a76862e95c7
This commit is contained in:
parent
e274c45dcd
commit
5a8ae0f1d0
|
@ -14,18 +14,21 @@ from nova import db
|
|||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import utils
|
||||
|
||||
|
||||
class InstancePCIRequest(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
# Version 1.1: Add request_id
|
||||
VERSION = '1.1'
|
||||
|
||||
fields = {
|
||||
'count': fields.IntegerField(),
|
||||
'spec': fields.ListOfDictOfNullableStringsField(),
|
||||
'alias_name': fields.StringField(),
|
||||
'alias_name': fields.StringField(nullable=True),
|
||||
# A stashed request related to a resize, not current
|
||||
'is_new': fields.BooleanField(default=False),
|
||||
'request_id': fields.UUIDField(nullable=True),
|
||||
}
|
||||
|
||||
def obj_load_attr(self, attr):
|
||||
|
@ -39,16 +42,30 @@ class InstancePCIRequest(base.NovaObject):
|
|||
def new(self):
|
||||
return self.is_new
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
target_version = utils.convert_version_to_tuple(target_version)
|
||||
if target_version < (1, 1) and 'request_id' in primitive:
|
||||
del primitive['request_id']
|
||||
|
||||
|
||||
class InstancePCIRequests(base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
# Version 1.1: InstancePCIRequest 1.1
|
||||
VERSION = '1.1'
|
||||
|
||||
fields = {
|
||||
'instance_uuid': fields.UUIDField(),
|
||||
'requests': fields.ListOfObjectsField('InstancePCIRequest'),
|
||||
}
|
||||
|
||||
def obj_make_compatible(self, primitive, target_version):
|
||||
target_version = utils.convert_version_to_tuple(target_version)
|
||||
if target_version < (1, 1) and 'requests' in primitive:
|
||||
for index, request in enumerate(self.requests):
|
||||
request.obj_make_compatible(
|
||||
primitive['requests'][index]['nova_object.data'], '1.0')
|
||||
primitive['requests'][index]['nova_object.version'] = '1.0'
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_instance_uuid(cls, context, instance_uuid):
|
||||
obj_pci_requests = cls(instance_uuid=instance_uuid)
|
||||
|
@ -65,7 +82,8 @@ class InstancePCIRequests(base.NovaObject):
|
|||
for request in requests:
|
||||
request_obj = InstancePCIRequest(
|
||||
count=request['count'], spec=request['spec'],
|
||||
alias_name=request['alias_name'], is_new=request['is_new'])
|
||||
alias_name=request['alias_name'], is_new=request['is_new'],
|
||||
request_id=request['request_id'])
|
||||
request_obj.obj_reset_changes()
|
||||
obj_pci_requests.requests.append(request_obj)
|
||||
|
||||
|
@ -117,7 +135,8 @@ class InstancePCIRequests(base.NovaObject):
|
|||
blob = [{'count': x.count,
|
||||
'spec': x.spec,
|
||||
'alias_name': x.alias_name,
|
||||
'is_new': x.is_new} for x in self.requests]
|
||||
'is_new': x.is_new,
|
||||
'request_id': x.request_id} for x in self.requests]
|
||||
requests = jsonutils.dumps(blob)
|
||||
db.instance_extra_update_by_uuid(context, self.instance_uuid,
|
||||
{'pci_requests': requests})
|
||||
|
|
|
@ -267,11 +267,20 @@ class PciDevTracker(object):
|
|||
dev.compute_node_id = node_id
|
||||
|
||||
|
||||
def get_instance_pci_devs(inst):
|
||||
"""Get the devices assigned to the instances."""
|
||||
def get_instance_pci_devs(inst, request_id=None):
|
||||
"""Get the devices allocated to one or all requests for an instance.
|
||||
|
||||
- For generic PCI request, the request id is None.
|
||||
- For sr-iov networking, the request id is a valid uuid
|
||||
- There are a couple of cases where all the PCI devices allocated to an
|
||||
instance need to be returned. Refer to libvirt driver that handles
|
||||
soft_reboot and hard_boot of 'xen' instances.
|
||||
"""
|
||||
if isinstance(inst, objects.Instance):
|
||||
return inst.pci_devices
|
||||
pci_devices = inst.pci_devices
|
||||
else:
|
||||
ctxt = context.get_admin_context()
|
||||
return objects.PciDeviceList.get_by_instance_uuid(
|
||||
pci_devices = objects.PciDeviceList.get_by_instance_uuid(
|
||||
ctxt, inst['uuid'])
|
||||
return [device for device in pci_devices if
|
||||
device.request_id == request_id or request_id == 'all']
|
||||
|
|
|
@ -21,6 +21,7 @@ from nova.i18n import _LE
|
|||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.pci import pci_utils
|
||||
from nova.pci import pci_whitelist
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -50,31 +51,57 @@ class PciDeviceStats(object):
|
|||
This summary information will be helpful for cloud management also.
|
||||
"""
|
||||
|
||||
pool_keys = ['product_id', 'vendor_id', 'extra_info']
|
||||
pool_keys = ['product_id', 'vendor_id']
|
||||
|
||||
def __init__(self, stats=None):
|
||||
super(PciDeviceStats, self).__init__()
|
||||
self.pools = jsonutils.loads(stats) if stats else []
|
||||
self.pools.sort(self.pool_cmp)
|
||||
|
||||
def _equal_properties(self, dev, entry):
|
||||
def _equal_properties(self, dev, entry, matching_keys):
|
||||
return all(dev.get(prop) == entry.get(prop)
|
||||
for prop in self.pool_keys)
|
||||
for prop in matching_keys)
|
||||
|
||||
def _get_first_pool(self, dev):
|
||||
def _find_pool(self, dev_pool):
|
||||
"""Return the first pool that matches dev."""
|
||||
return next((pool for pool in self.pools
|
||||
if self._equal_properties(dev, pool)), None)
|
||||
for pool in self.pools:
|
||||
pool_keys = pool.copy()
|
||||
del pool_keys['count']
|
||||
del pool_keys['devices']
|
||||
if (len(pool_keys.keys()) == len(dev_pool.keys()) and
|
||||
self._equal_properties(dev_pool, pool_keys, dev_pool.keys())):
|
||||
return pool
|
||||
|
||||
def _create_pool_keys_from_dev(self, dev):
|
||||
"""create a stats pool dict that this dev is supposed to be part of
|
||||
|
||||
Note that this pool dict contains the stats pool's keys and their
|
||||
values. 'count' and 'devices' are not included.
|
||||
"""
|
||||
# Don't add a device that doesn't have a matching device spec.
|
||||
# This can happen during initial sync up with the controller
|
||||
devspec = pci_whitelist.get_pci_device_devspec(dev)
|
||||
if not devspec:
|
||||
return
|
||||
tags = devspec.get_tags()
|
||||
pool = dict((k, dev.get(k)) for k in self.pool_keys)
|
||||
if tags:
|
||||
pool.update(tags)
|
||||
return pool
|
||||
|
||||
def add_device(self, dev):
|
||||
"""Add a device to the first matching pool."""
|
||||
pool = self._get_first_pool(dev)
|
||||
if not pool:
|
||||
pool = dict((k, dev.get(k)) for k in self.pool_keys)
|
||||
pool['count'] = 0
|
||||
pool['devices'] = []
|
||||
self.pools.append(pool)
|
||||
pool['count'] += 1
|
||||
pool['devices'].append(dev)
|
||||
"""Add a device to its matching pool."""
|
||||
dev_pool = self._create_pool_keys_from_dev(dev)
|
||||
if dev_pool:
|
||||
pool = self._find_pool(dev_pool)
|
||||
if not pool:
|
||||
dev_pool['count'] = 0
|
||||
dev_pool['devices'] = []
|
||||
self.pools.append(dev_pool)
|
||||
self.pools.sort(self.pool_cmp)
|
||||
pool = dev_pool
|
||||
pool['count'] += 1
|
||||
pool['devices'].append(dev)
|
||||
|
||||
@staticmethod
|
||||
def _decrease_pool_count(pool_list, pool, count=1):
|
||||
|
@ -92,7 +119,8 @@ class PciDeviceStats(object):
|
|||
|
||||
def remove_device(self, dev):
|
||||
"""Remove one device from the first pool that it matches."""
|
||||
pool = self._get_first_pool(dev)
|
||||
dev_pool = self._create_pool_keys_from_dev(dev)
|
||||
pool = self._find_pool(dev_pool)
|
||||
if not pool:
|
||||
raise exception.PciDevicePoolEmpty(
|
||||
compute_node_id=dev.compute_node_id, address=dev.address)
|
||||
|
@ -108,8 +136,8 @@ class PciDeviceStats(object):
|
|||
def consume_requests(self, pci_requests):
|
||||
alloc_devices = []
|
||||
for request in pci_requests:
|
||||
count = request.get('count', 1)
|
||||
spec = request.get('spec', [])
|
||||
count = request.count
|
||||
spec = request.spec
|
||||
# For now, keep the same algorithm as during scheduling:
|
||||
# a spec may be able to match multiple pools.
|
||||
pools = self._filter_pools_for_spec(self.pools, spec)
|
||||
|
@ -135,6 +163,7 @@ class PciDeviceStats(object):
|
|||
pool['count'] -= num_alloc
|
||||
for d in range(num_alloc):
|
||||
pci_dev = pool['devices'].pop()
|
||||
pci_dev.request_id = request.request_id
|
||||
alloc_devices.append(pci_dev)
|
||||
if count == 0:
|
||||
break
|
||||
|
@ -146,7 +175,7 @@ class PciDeviceStats(object):
|
|||
if pci_utils.pci_device_prop_match(pool, request_specs)]
|
||||
|
||||
def _apply_request(self, pools, request):
|
||||
count = request['count']
|
||||
count = request.count
|
||||
matching_pools = self._filter_pools_for_spec(pools, request['spec'])
|
||||
if sum([pool['count'] for pool in matching_pools]) < count:
|
||||
return False
|
||||
|
@ -178,6 +207,10 @@ class PciDeviceStats(object):
|
|||
if not all([self._apply_request(self.pools, r) for r in requests]):
|
||||
raise exception.PciDeviceRequestFailed(requests=requests)
|
||||
|
||||
@staticmethod
|
||||
def pool_cmp(dev1, dev2):
|
||||
return len(dev1) - len(dev2)
|
||||
|
||||
def __iter__(self):
|
||||
# 'devices' shouldn't be part of stats
|
||||
pools = []
|
||||
|
|
|
@ -14,14 +14,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import jsonschema
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.pci import pci_utils
|
||||
from nova.pci import pci_devspec
|
||||
|
||||
pci_opts = [cfg.MultiStrOpt('pci_passthrough_whitelist',
|
||||
default=[],
|
||||
|
@ -36,28 +32,6 @@ CONF.register_opts(pci_opts)
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_PCI_VENDOR_PATTERN = "^(hex{4})$".replace("hex", "[\da-fA-F]")
|
||||
_WHITELIST_SCHEMA = {
|
||||
"type": "array",
|
||||
"items":
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"product_id": {
|
||||
"type": "string",
|
||||
"pattern": _PCI_VENDOR_PATTERN
|
||||
},
|
||||
"vendor_id": {
|
||||
"type": "string",
|
||||
"pattern": _PCI_VENDOR_PATTERN
|
||||
},
|
||||
},
|
||||
"required": ["product_id", "vendor_id"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PciHostDevicesWhiteList(object):
|
||||
|
||||
"""White list class to decide assignable pci devices.
|
||||
|
@ -71,13 +45,9 @@ class PciHostDevicesWhiteList(object):
|
|||
def _parse_white_list_from_config(self, whitelists):
|
||||
"""Parse and validate the pci whitelist from the nova config."""
|
||||
specs = []
|
||||
try:
|
||||
for jsonspecs in whitelists:
|
||||
spec = jsonutils.loads(jsonspecs)
|
||||
jsonschema.validate(spec, _WHITELIST_SCHEMA)
|
||||
specs.extend(spec)
|
||||
except Exception as e:
|
||||
raise exception.PciConfigInvalidWhitelist(reason=six.text_type(e))
|
||||
for jsonspec in whitelists:
|
||||
spec = pci_devspec.PciDeviceSpec(jsonspec)
|
||||
specs.append(spec)
|
||||
|
||||
return specs
|
||||
|
||||
|
@ -95,19 +65,29 @@ class PciHostDevicesWhiteList(object):
|
|||
"""
|
||||
super(PciHostDevicesWhiteList, self).__init__()
|
||||
if whitelist_spec:
|
||||
self.spec = self._parse_white_list_from_config(whitelist_spec)
|
||||
self.specs = self._parse_white_list_from_config(whitelist_spec)
|
||||
else:
|
||||
self.spec = None
|
||||
self.specs = []
|
||||
|
||||
def device_assignable(self, dev):
|
||||
"""Check if a device can be assigned to a guest.
|
||||
|
||||
:param dev: A dictionary describing the device properties
|
||||
"""
|
||||
if self.spec is None:
|
||||
return False
|
||||
return pci_utils.pci_device_prop_match(dev, self.spec)
|
||||
for spec in self.specs:
|
||||
if spec.match(dev):
|
||||
return spec
|
||||
|
||||
def get_devspec(self, pci_dev):
|
||||
for spec in self.specs:
|
||||
if spec.match_pci_obj(pci_dev):
|
||||
return spec
|
||||
|
||||
|
||||
def get_pci_devices_filter():
|
||||
return PciHostDevicesWhiteList(CONF.pci_passthrough_whitelist)
|
||||
|
||||
|
||||
def get_pci_device_devspec(pci_dev):
|
||||
dev_filter = get_pci_devices_filter()
|
||||
return dev_filter.get_devspec(pci_dev)
|
||||
|
|
|
@ -26,6 +26,7 @@ from nova import exception
|
|||
from nova import objects
|
||||
from nova.pci import pci_manager
|
||||
from nova import test
|
||||
from nova.tests.pci import pci_fakes
|
||||
|
||||
|
||||
class FakeResourceHandler(object):
|
||||
|
@ -166,6 +167,7 @@ class ClaimTestCase(test.NoDBTestCase):
|
|||
self._claim, limits=limits, root_gb=10, ephemeral_gb=40,
|
||||
memory_mb=16384)
|
||||
|
||||
@pci_fakes.patch_pci_whitelist
|
||||
def test_pci_pass(self, mock_get):
|
||||
dev_dict = {
|
||||
'compute_node_id': 1,
|
||||
|
@ -180,8 +182,9 @@ class ClaimTestCase(test.NoDBTestCase):
|
|||
spec=[{'vendor_id': 'v', 'product_id': 'p'}])
|
||||
mock_get.return_value = objects.InstancePCIRequests(
|
||||
requests=[request])
|
||||
claim._test_pci()
|
||||
self.assertIsNone(claim._test_pci())
|
||||
|
||||
@pci_fakes.patch_pci_whitelist
|
||||
def test_pci_fail(self, mock_get):
|
||||
dev_dict = {
|
||||
'compute_node_id': 1,
|
||||
|
@ -198,6 +201,7 @@ class ClaimTestCase(test.NoDBTestCase):
|
|||
requests=[request])
|
||||
claim._test_pci()
|
||||
|
||||
@pci_fakes.patch_pci_whitelist
|
||||
def test_pci_pass_no_requests(self, mock_get):
|
||||
dev_dict = {
|
||||
'compute_node_id': 1,
|
||||
|
@ -208,7 +212,7 @@ class ClaimTestCase(test.NoDBTestCase):
|
|||
self.tracker.new_pci_tracker()
|
||||
self.tracker.pci_tracker.set_hvdevs([dev_dict])
|
||||
claim = self._claim()
|
||||
claim._test_pci()
|
||||
self.assertIsNone(claim._test_pci())
|
||||
|
||||
def test_ext_resources(self, mock_get):
|
||||
self._claim()
|
||||
|
|
|
@ -35,6 +35,7 @@ from nova import rpc
|
|||
from nova import test
|
||||
from nova.tests.compute.monitors import test_monitors
|
||||
from nova.tests.objects import test_migration
|
||||
from nova.tests.pci import pci_fakes
|
||||
from nova.virt import driver
|
||||
|
||||
|
||||
|
@ -89,8 +90,7 @@ class FakeVirtDriver(driver.ComputeDriver):
|
|||
self.pci_stats = [{
|
||||
'count': 1,
|
||||
'vendor_id': 'v1',
|
||||
'product_id': 'p1',
|
||||
'extra_info': {'extra_k1': 'v1'}}] if self.pci_support else []
|
||||
'product_id': 'p1'}] if self.pci_support else []
|
||||
if stats is not None:
|
||||
self.stats = stats
|
||||
|
||||
|
@ -453,6 +453,10 @@ class BaseTrackerTestCase(BaseTestCase):
|
|||
self.stubs.Set(db, 'migration_get_in_progress_by_host_and_node',
|
||||
self._fake_migration_get_in_progress_by_host_and_node)
|
||||
|
||||
# Note that this must be called before the call to _init_tracker()
|
||||
patcher = pci_fakes.fake_pci_whitelist()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
self._init_tracker()
|
||||
self.limits = self._limits()
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova.tests.objects import test_objects
|
|||
|
||||
|
||||
FAKE_UUID = '79a53d6b-0893-4838-a971-15f4f382e7c2'
|
||||
FAKE_REQUEST_UUID = '69b53d6b-0793-4839-c981-f5c4f382e7d2'
|
||||
|
||||
# NOTE(danms): Yes, these are the same right now, but going forward,
|
||||
# we have changes to make which will be reflected in the format
|
||||
|
@ -27,12 +28,14 @@ fake_pci_requests = [
|
|||
'spec': [{'vendor_id': '8086',
|
||||
'device_id': '1502'}],
|
||||
'alias_name': 'alias_1',
|
||||
'is_new': False},
|
||||
'is_new': False,
|
||||
'request_id': FAKE_REQUEST_UUID},
|
||||
{'count': 2,
|
||||
'spec': [{'vendor_id': '6502',
|
||||
'device_id': '07B5'}],
|
||||
'alias_name': 'alias_2',
|
||||
'is_new': True},
|
||||
'is_new': True,
|
||||
'request_id': FAKE_REQUEST_UUID},
|
||||
]
|
||||
|
||||
fake_legacy_pci_requests = [
|
||||
|
@ -117,13 +120,15 @@ class _TestInstancePCIRequests(object):
|
|||
count=1,
|
||||
spec=[{'foo': 'bar'}, {'baz': 'bat'}],
|
||||
alias_name='alias_1',
|
||||
is_new=False)])
|
||||
is_new=False,
|
||||
request_id=FAKE_REQUEST_UUID)])
|
||||
requests.save()
|
||||
self.assertEqual(FAKE_UUID, mock_update.call_args_list[0][0][1])
|
||||
self.assertEqual(
|
||||
[{'count': 1, 'is_new': False,
|
||||
'alias_name': 'alias_1',
|
||||
'spec': [{'foo': 'bar'}, {'baz': 'bat'}]}],
|
||||
'spec': [{'foo': 'bar'}, {'baz': 'bat'}],
|
||||
'request_id': FAKE_REQUEST_UUID}],
|
||||
jsonutils.loads(
|
||||
mock_update.call_args_list[0][0][2]['pci_requests']))
|
||||
|
||||
|
@ -161,6 +166,20 @@ class _TestInstancePCIRequests(object):
|
|||
request = objects.InstancePCIRequest(is_new=False)
|
||||
self.assertFalse(request.new)
|
||||
|
||||
def test_backport_1_0(self):
|
||||
requests = objects.InstancePCIRequests(
|
||||
requests=[objects.InstancePCIRequest(count=1,
|
||||
request_id=FAKE_UUID),
|
||||
objects.InstancePCIRequest(count=2,
|
||||
request_id=FAKE_UUID)])
|
||||
primitive = requests.obj_to_primitive(target_version='1.0')
|
||||
backported = objects.InstancePCIRequests.obj_from_primitive(
|
||||
primitive)
|
||||
self.assertEqual('1.0', backported.VERSION)
|
||||
self.assertEqual(2, len(backported.requests))
|
||||
self.assertFalse(backported.requests[0].obj_attr_is_set('request_id'))
|
||||
self.assertFalse(backported.requests[1].obj_attr_is_set('request_id'))
|
||||
|
||||
|
||||
class TestInstancePCIRequests(test_objects._LocalTest,
|
||||
_TestInstancePCIRequests):
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# Copyright (c) 2014 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 functools
|
||||
|
||||
import mock
|
||||
|
||||
from nova.pci import pci_whitelist
|
||||
|
||||
|
||||
def fake_pci_whitelist():
|
||||
devspec = mock.Mock()
|
||||
devspec.get_tags.return_value = None
|
||||
patcher = mock.patch.object(pci_whitelist, 'get_pci_device_devspec',
|
||||
return_value=devspec)
|
||||
patcher.start()
|
||||
return patcher
|
||||
|
||||
|
||||
def patch_pci_whitelist(f):
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
patcher = fake_pci_whitelist()
|
||||
f(self, *args, **kwargs)
|
||||
patcher.stop()
|
||||
return wrapper
|
|
@ -27,6 +27,7 @@ from nova.pci import pci_device
|
|||
from nova.pci import pci_manager
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
from nova.tests.pci import pci_fakes
|
||||
|
||||
|
||||
fake_pci = {
|
||||
|
@ -105,6 +106,8 @@ class PciDevTrackerTestCase(test.TestCase):
|
|||
super(PciDevTrackerTestCase, self).setUp()
|
||||
self.stubs.Set(db, 'pci_device_get_all_by_node',
|
||||
self._fake_get_pci_devices)
|
||||
patcher = pci_fakes.fake_pci_whitelist()
|
||||
self.addCleanup(patcher.stop)
|
||||
self._create_fake_instance()
|
||||
self.tracker = pci_manager.PciDevTracker(1)
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from nova import objects
|
|||
from nova.openstack.common import jsonutils
|
||||
from nova.pci import pci_stats as pci
|
||||
from nova import test
|
||||
from nova.tests.pci import pci_fakes
|
||||
|
||||
fake_pci_1 = {
|
||||
'compute_node_id': 1,
|
||||
|
@ -38,16 +39,16 @@ fake_pci_2 = dict(fake_pci_1, vendor_id='v2',
|
|||
fake_pci_3 = dict(fake_pci_1, address='0000:00:00.3')
|
||||
|
||||
|
||||
pci_requests = [{'count': 1,
|
||||
'spec': [{'vendor_id': 'v1'}]},
|
||||
{'count': 1,
|
||||
'spec': [{'vendor_id': 'v2'}]}]
|
||||
pci_requests = [objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v1'}]),
|
||||
objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v2'}])]
|
||||
|
||||
|
||||
pci_requests_multiple = [{'count': 1,
|
||||
'spec': [{'vendor_id': 'v1'}]},
|
||||
{'count': 3,
|
||||
'spec': [{'vendor_id': 'v2'}]}]
|
||||
pci_requests_multiple = [objects.InstancePCIRequest(count=1,
|
||||
spec=[{'vendor_id': 'v1'}]),
|
||||
objects.InstancePCIRequest(count=3,
|
||||
spec=[{'vendor_id': 'v2'}])]
|
||||
|
||||
|
||||
class PciDeviceStatsTestCase(test.NoDBTestCase):
|
||||
|
@ -62,6 +63,9 @@ class PciDeviceStatsTestCase(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(PciDeviceStatsTestCase, self).setUp()
|
||||
self.pci_stats = pci.PciDeviceStats()
|
||||
# The following two calls need to be made before adding the devices.
|
||||
patcher = pci_fakes.fake_pci_whitelist()
|
||||
self.addCleanup(patcher.stop)
|
||||
self._create_fake_devs()
|
||||
|
||||
def test_add_device(self):
|
||||
|
|
|
@ -13,18 +13,17 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova import exception
|
||||
from nova import objects
|
||||
from nova.pci import pci_whitelist
|
||||
from nova import test
|
||||
|
||||
|
||||
dev_dict = {
|
||||
'compute_node_id': 1,
|
||||
'address': 'a',
|
||||
'address': '0000:00:0a.1',
|
||||
'product_id': '0001',
|
||||
'vendor_id': '8086',
|
||||
'status': 'available',
|
||||
'phys_function': '0000:00:0a.0',
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,66 +31,39 @@ class PciHostDevicesWhiteListTestCase(test.NoDBTestCase):
|
|||
def setUp(self):
|
||||
super(PciHostDevicesWhiteListTestCase, self).setUp()
|
||||
|
||||
def test_whitelist_wrong_format(self):
|
||||
white_list = '[{"vendor_x_id":"8086", "product_id":"0001"}]'
|
||||
self.assertRaises(
|
||||
exception.PciConfigInvalidWhitelist,
|
||||
pci_whitelist.PciHostDevicesWhiteList, white_list
|
||||
)
|
||||
|
||||
white_list = '[{"vendor_id":"80863", "product_id":"0001"}]'
|
||||
self.assertRaises(
|
||||
exception.PciConfigInvalidWhitelist,
|
||||
pci_whitelist.PciHostDevicesWhiteList, white_list
|
||||
)
|
||||
|
||||
def test_whitelist_missed_fields(self):
|
||||
white_list = '[{"vendor_id":"80863"}]'
|
||||
self.assertRaises(
|
||||
exception.PciConfigInvalidWhitelist,
|
||||
pci_whitelist.PciHostDevicesWhiteList, white_list
|
||||
)
|
||||
|
||||
def test_whitelist(self):
|
||||
white_list = '[{"product_id":"0001", "vendor_id":"8086"}]'
|
||||
white_list = '{"product_id":"0001", "vendor_id":"8086"}'
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList([white_list])
|
||||
self.assertEqual(parsed.spec, [{'vendor_id': '8086',
|
||||
'product_id': '0001'}])
|
||||
self.assertEqual(1, len(parsed.specs))
|
||||
|
||||
def test_whitelist_empty(self):
|
||||
dev = objects.PciDevice.create(dev_dict)
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList()
|
||||
self.assertEqual(parsed.device_assignable(dev), False)
|
||||
self.assertFalse(parsed.device_assignable(dev_dict))
|
||||
|
||||
def test_whitelist_multiple(self):
|
||||
white_list_1 = '[{"product_id":"0001", "vendor_id":"8086"}]'
|
||||
white_list_2 = '[{"product_id":"0002", "vendor_id":"8087"}]'
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList(
|
||||
[white_list_1, white_list_2])
|
||||
self.assertEqual(parsed.spec,
|
||||
[{'vendor_id': '8086', 'product_id': '0001'},
|
||||
{'vendor_id': '8087', 'product_id': '0002'}])
|
||||
wl1 = '{"product_id":"0001", "vendor_id":"8086"}'
|
||||
wl2 = '{"product_id":"0002", "vendor_id":"8087"}'
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList([wl1, wl2])
|
||||
self.assertEqual(2, len(parsed.specs))
|
||||
|
||||
def test_device_assignable(self):
|
||||
dev = objects.PciDevice.create(dev_dict)
|
||||
white_list = '[{"product_id":"0001", "vendor_id":"8086"}]'
|
||||
white_list = '{"product_id":"0001", "vendor_id":"8086"}'
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList([white_list])
|
||||
self.assertEqual(parsed.device_assignable(dev), True)
|
||||
self.assertIsNotNone(parsed.device_assignable(dev_dict))
|
||||
|
||||
def test_device_assignable_multiple(self):
|
||||
dev = objects.PciDevice.create(dev_dict)
|
||||
white_list_1 = '[{"product_id":"0001", "vendor_id":"8086"}]'
|
||||
white_list_2 = '[{"product_id":"0002", "vendor_id":"8087"}]'
|
||||
white_list_1 = '{"product_id":"0001", "vendor_id":"8086"}'
|
||||
white_list_2 = '{"product_id":"0002", "vendor_id":"8087"}'
|
||||
parsed = pci_whitelist.PciHostDevicesWhiteList(
|
||||
[white_list_1, white_list_2])
|
||||
self.assertEqual(parsed.device_assignable(dev), True)
|
||||
dev.vendor_id = '8087'
|
||||
dev.product_id = '0002'
|
||||
self.assertEqual(parsed.device_assignable(dev), True)
|
||||
self.assertIsNotNone(parsed.device_assignable(dev_dict))
|
||||
dev_dict1 = dev_dict.copy()
|
||||
dev_dict1['vendor_id'] = '8087'
|
||||
dev_dict1['product_id'] = '0002'
|
||||
self.assertIsNotNone(parsed.device_assignable(dev_dict1))
|
||||
|
||||
def test_get_pci_devices_filter(self):
|
||||
white_list_1 = '[{"product_id":"0001", "vendor_id":"8086"}]'
|
||||
white_list_1 = '{"product_id":"0001", "vendor_id":"8086"}'
|
||||
self.flags(pci_passthrough_whitelist=[white_list_1])
|
||||
pci_filter = pci_whitelist.get_pci_devices_filter()
|
||||
dev = objects.PciDevice.create(dev_dict)
|
||||
self.assertEqual(pci_filter.device_assignable(dev), True)
|
||||
self.assertIsNotNone(pci_filter.device_assignable(dev_dict))
|
||||
|
|
|
@ -2053,8 +2053,8 @@ class XenAPIHostTestCase(stubs.XenAPITestBase):
|
|||
def test_pci_passthrough_devices_whitelist(self):
|
||||
# NOTE(guillaume-thouvenin): This pci whitelist will be used to
|
||||
# match with _plugin_xenhost_get_pci_device_details method in fake.py.
|
||||
self.flags(pci_passthrough_whitelist=
|
||||
['[{"vendor_id":"10de", "product_id":"11bf"}]'])
|
||||
white_list = '{"vendor_id":"10de", "product_id":"11bf"}'
|
||||
self.flags(pci_passthrough_whitelist=[white_list])
|
||||
stats = self.conn.get_host_stats()
|
||||
self.assertEqual(len(stats['pci_passthrough_devices']), 1)
|
||||
|
||||
|
|
Loading…
Reference in New Issue