Merge "[Trivial] docstrings, typos, minor refactoring"
This commit is contained in:
@@ -1180,7 +1180,7 @@ class API(base.Base):
|
|||||||
block_device_mapping=block_device_mapping,
|
block_device_mapping=block_device_mapping,
|
||||||
tags=tags)
|
tags=tags)
|
||||||
|
|
||||||
return (instances, reservation_id)
|
return instances, reservation_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cleanup_build_artifacts(instances, instances_to_build):
|
def _cleanup_build_artifacts(instances, instances_to_build):
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class Claim(NopClaim):
|
|||||||
if pci_requests.requests:
|
if pci_requests.requests:
|
||||||
stats = self.tracker.pci_tracker.stats
|
stats = self.tracker.pci_tracker.stats
|
||||||
if not stats.support_requests(pci_requests.requests):
|
if not stats.support_requests(pci_requests.requests):
|
||||||
return _('Claim pci failed.')
|
return _('Claim pci failed')
|
||||||
|
|
||||||
def _test_numa_topology(self, resources, limit):
|
def _test_numa_topology(self, resources, limit):
|
||||||
host_topology = (resources.numa_topology
|
host_topology = (resources.numa_topology
|
||||||
@@ -219,12 +219,12 @@ class Claim(NopClaim):
|
|||||||
|
|
||||||
if requested_topology and not instance_topology:
|
if requested_topology and not instance_topology:
|
||||||
if pci_requests.requests:
|
if pci_requests.requests:
|
||||||
return (_("Requested instance NUMA topology together with"
|
return (_("Requested instance NUMA topology together with "
|
||||||
" requested PCI devices cannot fit the given"
|
"requested PCI devices cannot fit the given "
|
||||||
" host NUMA topology"))
|
"host NUMA topology"))
|
||||||
else:
|
else:
|
||||||
return (_("Requested instance NUMA topology cannot fit "
|
return (_("Requested instance NUMA topology cannot fit "
|
||||||
"the given host NUMA topology"))
|
"the given host NUMA topology"))
|
||||||
elif instance_topology:
|
elif instance_topology:
|
||||||
self.claimed_numa_topology = instance_topology
|
self.claimed_numa_topology = instance_topology
|
||||||
|
|
||||||
|
|||||||
@@ -1109,8 +1109,8 @@ class ComputeManager(manager.Manager):
|
|||||||
# if the configuration is wrong.
|
# if the configuration is wrong.
|
||||||
whitelist.Whitelist(CONF.pci.passthrough_whitelist)
|
whitelist.Whitelist(CONF.pci.passthrough_whitelist)
|
||||||
|
|
||||||
# NOTE(sbauza): We want the compute node to hard fail if it can't be
|
# NOTE(sbauza): We want the compute node to hard fail if it won't be
|
||||||
# able to provide its resources to the placement API, or it would not
|
# able to provide its resources to the placement API, or it will not
|
||||||
# be able to be eligible as a destination.
|
# be able to be eligible as a destination.
|
||||||
if CONF.placement.os_region_name is None:
|
if CONF.placement.os_region_name is None:
|
||||||
raise exception.PlacementNotConfigured()
|
raise exception.PlacementNotConfigured()
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class ResourceTracker(object):
|
|||||||
overhead = self.driver.estimate_instance_overhead(instance)
|
overhead = self.driver.estimate_instance_overhead(instance)
|
||||||
LOG.debug("Memory overhead for %(flavor)d MB instance; %(overhead)d "
|
LOG.debug("Memory overhead for %(flavor)d MB instance; %(overhead)d "
|
||||||
"MB", {'flavor': instance.flavor.memory_mb,
|
"MB", {'flavor': instance.flavor.memory_mb,
|
||||||
'overhead': overhead['memory_mb']})
|
'overhead': overhead['memory_mb']})
|
||||||
LOG.debug("Disk overhead for %(flavor)d GB instance; %(overhead)d "
|
LOG.debug("Disk overhead for %(flavor)d GB instance; %(overhead)d "
|
||||||
"GB", {'flavor': instance.flavor.root_gb,
|
"GB", {'flavor': instance.flavor.root_gb,
|
||||||
'overhead': overhead.get('disk_gb', 0)})
|
'overhead': overhead.get('disk_gb', 0)})
|
||||||
|
|||||||
@@ -1453,10 +1453,10 @@ class PciDevice(BASE, NovaBase, models.SoftDeleteMixin):
|
|||||||
|
|
||||||
parent_addr = Column(String(12), nullable=True)
|
parent_addr = Column(String(12), nullable=True)
|
||||||
instance = orm.relationship(Instance, backref="pci_devices",
|
instance = orm.relationship(Instance, backref="pci_devices",
|
||||||
foreign_keys=instance_uuid,
|
foreign_keys=instance_uuid,
|
||||||
primaryjoin='and_('
|
primaryjoin='and_('
|
||||||
'PciDevice.instance_uuid == Instance.uuid,'
|
'PciDevice.instance_uuid == Instance.uuid,'
|
||||||
'PciDevice.deleted == 0)')
|
'PciDevice.deleted == 0)')
|
||||||
|
|
||||||
|
|
||||||
class Tag(BASE, models.ModelBase):
|
class Tag(BASE, models.ModelBase):
|
||||||
|
|||||||
@@ -1713,7 +1713,7 @@ class PciRequestAliasNotDefined(NovaException):
|
|||||||
|
|
||||||
|
|
||||||
class PciConfigInvalidWhitelist(Invalid):
|
class PciConfigInvalidWhitelist(Invalid):
|
||||||
msg_fmt = _("Invalid PCI devices Whitelist config %(reason)s")
|
msg_fmt = _("Invalid PCI devices Whitelist config: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
# Cannot be templated, msg needs to be constructed when raised.
|
# Cannot be templated, msg needs to be constructed when raised.
|
||||||
|
|||||||
@@ -303,9 +303,9 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject):
|
|||||||
parent.status = fields.PciDeviceStatus.UNCLAIMABLE
|
parent.status = fields.PciDeviceStatus.UNCLAIMABLE
|
||||||
else:
|
else:
|
||||||
LOG.debug('Physical function addr: %(pf_addr)s parent of '
|
LOG.debug('Physical function addr: %(pf_addr)s parent of '
|
||||||
'VF addr: %(vf_addr)s was not found',
|
'VF addr: %(vf_addr)s was not found',
|
||||||
{'pf_addr': self.parent_addr,
|
{'pf_addr': self.parent_addr,
|
||||||
'vf_addr': self.address})
|
'vf_addr': self.address})
|
||||||
|
|
||||||
self.status = fields.PciDeviceStatus.CLAIMED
|
self.status = fields.PciDeviceStatus.CLAIMED
|
||||||
self.instance_uuid = instance_uuid
|
self.instance_uuid = instance_uuid
|
||||||
|
|||||||
@@ -31,22 +31,6 @@ ANY = '*'
|
|||||||
REGEX_ANY = '.*'
|
REGEX_ANY = '.*'
|
||||||
|
|
||||||
|
|
||||||
def get_pci_dev_info(pci_obj, property, max, hex_value):
|
|
||||||
a = getattr(pci_obj, property)
|
|
||||||
if a == ANY:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
v = int(a, 16)
|
|
||||||
except ValueError:
|
|
||||||
raise exception.PciConfigInvalidWhitelist(
|
|
||||||
reason = "invalid %s %s" % (property, a))
|
|
||||||
if v > max:
|
|
||||||
raise exception.PciConfigInvalidWhitelist(
|
|
||||||
reason=_("invalid %(property)s %(attr)s") %
|
|
||||||
{'property': property, 'attr': a})
|
|
||||||
setattr(pci_obj, property, hex_value % v)
|
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class PciAddressSpec(object):
|
class PciAddressSpec(object):
|
||||||
"""Abstract class for all PCI address spec styles
|
"""Abstract class for all PCI address spec styles
|
||||||
@@ -65,6 +49,23 @@ class PciAddressSpec(object):
|
|||||||
all(c in string.hexdigits for c in self.slot),
|
all(c in string.hexdigits for c in self.slot),
|
||||||
all(c in string.hexdigits for c in self.func)])
|
all(c in string.hexdigits for c in self.func)])
|
||||||
|
|
||||||
|
def _set_pci_dev_info(self, prop, maxval, hex_value):
|
||||||
|
a = getattr(self, prop)
|
||||||
|
if a == ANY:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
v = int(a, 16)
|
||||||
|
except ValueError:
|
||||||
|
raise exception.PciConfigInvalidWhitelist(
|
||||||
|
reason=_("property %(property)s ('%(attr)s') does not parse "
|
||||||
|
"as a hex number.") % {'property': prop, 'attr': a})
|
||||||
|
if v > maxval:
|
||||||
|
raise exception.PciConfigInvalidWhitelist(
|
||||||
|
reason=_("property %(property)s (%(attr)s) is greater than "
|
||||||
|
"the maximum allowable value (%(max)X).") %
|
||||||
|
{'property': prop, 'attr': a, 'max': maxval})
|
||||||
|
setattr(self, prop, hex_value % v)
|
||||||
|
|
||||||
|
|
||||||
class PhysicalPciAddress(PciAddressSpec):
|
class PhysicalPciAddress(PciAddressSpec):
|
||||||
"""Manages the address fields for a fully-qualified PCI address.
|
"""Manages the address fields for a fully-qualified PCI address.
|
||||||
@@ -82,10 +83,10 @@ class PhysicalPciAddress(PciAddressSpec):
|
|||||||
else:
|
else:
|
||||||
self.domain, self.bus, self.slot, self.func = (
|
self.domain, self.bus, self.slot, self.func = (
|
||||||
utils.get_pci_address_fields(pci_addr))
|
utils.get_pci_address_fields(pci_addr))
|
||||||
get_pci_dev_info(self, 'func', MAX_FUNC, '%1x')
|
self._set_pci_dev_info('func', MAX_FUNC, '%1x')
|
||||||
get_pci_dev_info(self, 'domain', MAX_DOMAIN, '%04x')
|
self._set_pci_dev_info('domain', MAX_DOMAIN, '%04x')
|
||||||
get_pci_dev_info(self, 'bus', MAX_BUS, '%02x')
|
self._set_pci_dev_info('bus', MAX_BUS, '%02x')
|
||||||
get_pci_dev_info(self, 'slot', MAX_SLOT, '%02x')
|
self._set_pci_dev_info('slot', MAX_SLOT, '%02x')
|
||||||
except (KeyError, ValueError):
|
except (KeyError, ValueError):
|
||||||
raise exception.PciDeviceWrongAddressFormat(address=pci_addr)
|
raise exception.PciDeviceWrongAddressFormat(address=pci_addr)
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ class PciAddressGlobSpec(PciAddressSpec):
|
|||||||
dbs, sep, func = pci_addr.partition('.')
|
dbs, sep, func = pci_addr.partition('.')
|
||||||
if func:
|
if func:
|
||||||
self.func = func.strip()
|
self.func = func.strip()
|
||||||
get_pci_dev_info(self, 'func', MAX_FUNC, '%01x')
|
self._set_pci_dev_info('func', MAX_FUNC, '%01x')
|
||||||
if dbs:
|
if dbs:
|
||||||
dbs_fields = dbs.split(':')
|
dbs_fields = dbs.split(':')
|
||||||
if len(dbs_fields) > 3:
|
if len(dbs_fields) > 3:
|
||||||
@@ -127,9 +128,9 @@ class PciAddressGlobSpec(PciAddressSpec):
|
|||||||
dbs_all.extend(dbs_fields)
|
dbs_all.extend(dbs_fields)
|
||||||
dbs_checked = [s.strip() or ANY for s in dbs_all]
|
dbs_checked = [s.strip() or ANY for s in dbs_all]
|
||||||
self.domain, self.bus, self.slot = dbs_checked
|
self.domain, self.bus, self.slot = dbs_checked
|
||||||
get_pci_dev_info(self, 'domain', MAX_DOMAIN, '%04x')
|
self._set_pci_dev_info('domain', MAX_DOMAIN, '%04x')
|
||||||
get_pci_dev_info(self, 'bus', MAX_BUS, '%02x')
|
self._set_pci_dev_info('bus', MAX_BUS, '%02x')
|
||||||
get_pci_dev_info(self, 'slot', MAX_SLOT, '%02x')
|
self._set_pci_dev_info('slot', MAX_SLOT, '%02x')
|
||||||
|
|
||||||
def match(self, phys_pci_addr):
|
def match(self, phys_pci_addr):
|
||||||
conditions = [
|
conditions = [
|
||||||
@@ -176,7 +177,7 @@ class WhitelistPciAddress(object):
|
|||||||
|
|
||||||
This class checks the address fields of the pci.passthrough_whitelist
|
This class checks the address fields of the pci.passthrough_whitelist
|
||||||
configuration option, validating the address fields.
|
configuration option, validating the address fields.
|
||||||
Example config are:
|
Example configs:
|
||||||
|
|
||||||
| [pci]
|
| [pci]
|
||||||
| passthrough_whitelist = {"address":"*:0a:00.*",
|
| passthrough_whitelist = {"address":"*:0a:00.*",
|
||||||
@@ -226,8 +227,8 @@ class WhitelistPciAddress(object):
|
|||||||
|
|
||||||
# Try to match on the parent PCI address if the PciDeviceSpec is a
|
# Try to match on the parent PCI address if the PciDeviceSpec is a
|
||||||
# PF (sriov is available) and the device to match is a VF. This
|
# PF (sriov is available) and the device to match is a VF. This
|
||||||
# makes possible to specify the PCI address of a PF in the
|
# makes it possible to specify the PCI address of a PF in the
|
||||||
# pci_passthrough_whitelist to match any of it's VFs PCI devices.
|
# pci.passthrough_whitelist to match any of its VFs' PCI addresses.
|
||||||
if self.is_physical_function and pci_phys_addr:
|
if self.is_physical_function and pci_phys_addr:
|
||||||
pci_phys_addr_obj = PhysicalPciAddress(pci_phys_addr)
|
pci_phys_addr_obj = PhysicalPciAddress(pci_phys_addr)
|
||||||
if self.pci_address_spec.match(pci_phys_addr_obj):
|
if self.pci_address_spec.match(pci_phys_addr_obj):
|
||||||
@@ -238,7 +239,7 @@ class WhitelistPciAddress(object):
|
|||||||
return self.pci_address_spec.match(pci_addr_obj)
|
return self.pci_address_spec.match(pci_addr_obj)
|
||||||
|
|
||||||
|
|
||||||
class PciDeviceSpec(object):
|
class PciDeviceSpec(PciAddressSpec):
|
||||||
def __init__(self, dev_spec):
|
def __init__(self, dev_spec):
|
||||||
self.tags = dev_spec
|
self.tags = dev_spec
|
||||||
self._init_dev_details()
|
self._init_dev_details()
|
||||||
@@ -253,8 +254,8 @@ class PciDeviceSpec(object):
|
|||||||
self.dev_name = self.tags.pop("devname", None)
|
self.dev_name = self.tags.pop("devname", None)
|
||||||
|
|
||||||
self.vendor_id = self.vendor_id.strip()
|
self.vendor_id = self.vendor_id.strip()
|
||||||
get_pci_dev_info(self, 'vendor_id', MAX_VENDOR_ID, '%04x')
|
self._set_pci_dev_info('vendor_id', MAX_VENDOR_ID, '%04x')
|
||||||
get_pci_dev_info(self, 'product_id', MAX_PRODUCT_ID, '%04x')
|
self._set_pci_dev_info('product_id', MAX_PRODUCT_ID, '%04x')
|
||||||
|
|
||||||
if self.address and self.dev_name:
|
if self.address and self.dev_name:
|
||||||
raise exception.PciDeviceInvalidDeviceName()
|
raise exception.PciDeviceInvalidDeviceName()
|
||||||
@@ -281,9 +282,9 @@ class PciDeviceSpec(object):
|
|||||||
|
|
||||||
def match_pci_obj(self, pci_obj):
|
def match_pci_obj(self, pci_obj):
|
||||||
return self.match({'vendor_id': pci_obj.vendor_id,
|
return self.match({'vendor_id': pci_obj.vendor_id,
|
||||||
'product_id': pci_obj.product_id,
|
'product_id': pci_obj.product_id,
|
||||||
'address': pci_obj.address,
|
'address': pci_obj.address,
|
||||||
'parent_addr': pci_obj.parent_addr})
|
'parent_addr': pci_obj.parent_addr})
|
||||||
|
|
||||||
def get_tags(self):
|
def get_tags(self):
|
||||||
return self.tags
|
return self.tags
|
||||||
|
|||||||
@@ -38,14 +38,13 @@ class PciDevTracker(object):
|
|||||||
|
|
||||||
It's called by compute node resource tracker to allocate and free
|
It's called by compute node resource tracker to allocate and free
|
||||||
devices to/from instances, and to update the available pci passthrough
|
devices to/from instances, and to update the available pci passthrough
|
||||||
devices information from hypervisor periodically.
|
device information from the hypervisor periodically.
|
||||||
|
|
||||||
`pci_devs` attribute of this class is the in-memory "master copy" of all
|
The `pci_devs` attribute of this class is the in-memory "master copy" of
|
||||||
devices on each compute host, and all data changes that happen when
|
all devices on each compute host, and all data changes that happen when
|
||||||
claiming/allocating/freeing
|
claiming/allocating/freeing devices HAVE TO be made against instances
|
||||||
devices HAVE TO be made against instances contained in `pci_devs` list,
|
contained in `pci_devs` list, because they are periodically flushed to the
|
||||||
because they are periodically flushed to the DB when the save()
|
DB when the save() method is called.
|
||||||
method is called.
|
|
||||||
|
|
||||||
It is unsafe to fetch PciDevice objects elsewhere in the code for update
|
It is unsafe to fetch PciDevice objects elsewhere in the code for update
|
||||||
purposes as those changes will end up being overwritten when the `pci_devs`
|
purposes as those changes will end up being overwritten when the `pci_devs`
|
||||||
|
|||||||
@@ -17,24 +17,24 @@
|
|||||||
|
|
||||||
| [pci]
|
| [pci]
|
||||||
| alias = '{
|
| alias = '{
|
||||||
| "name": "QuicAssist",
|
| "name": "QuickAssist",
|
||||||
| "product_id": "0443",
|
| "product_id": "0443",
|
||||||
| "vendor_id": "8086",
|
| "vendor_id": "8086",
|
||||||
| "device_type": "type-PCI",
|
| "device_type": "type-PCI",
|
||||||
| }'
|
| }'
|
||||||
|
|
||||||
Aliases with the same name and the same device_type are OR operation::
|
Aliases with the same name and the same device_type are ORed::
|
||||||
|
|
||||||
| [pci]
|
| [pci]
|
||||||
| alias = '{
|
| alias = '{
|
||||||
| "name": "QuicAssist",
|
| "name": "QuickAssist",
|
||||||
| "product_id": "0442",
|
| "product_id": "0442",
|
||||||
| "vendor_id": "8086",
|
| "vendor_id": "8086",
|
||||||
| "device_type": "type-PCI",
|
| "device_type": "type-PCI",
|
||||||
| }'
|
| }'
|
||||||
|
|
||||||
These 2 aliases define a device request meaning: vendor_id is "8086" and
|
These two aliases define a device request meaning: vendor_id is "8086" and
|
||||||
product id is "0442" or "0443".
|
product_id is "0442" or "0443".
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -156,11 +156,11 @@ def get_pci_requests_from_flavor(flavor):
|
|||||||
'alias_name_x:count, alias_name_y:count, ... '. The alias_name is
|
'alias_name_x:count, alias_name_y:count, ... '. The alias_name is
|
||||||
defined in 'pci.alias' configurations.
|
defined in 'pci.alias' configurations.
|
||||||
|
|
||||||
The flavor's requirement is translated into pci requests list,
|
The flavor's requirement is translated into a pci requests list.
|
||||||
each entry in the list is a dictionary. The dictionary has
|
Each entry in the list is a dict-ish object with three keys/attributes.
|
||||||
three keys. The 'specs' gives the pci device properties
|
The 'specs' gives the pci device properties requirement, the 'count' gives
|
||||||
requirement, the 'count' gives the number of devices, and the
|
the number of devices, and the optional 'alias_name' is the corresponding
|
||||||
optional 'alias_name' is the corresponding alias definition name.
|
alias definition name.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
Assume alias configuration is::
|
Assume alias configuration is::
|
||||||
|
|||||||
@@ -156,12 +156,12 @@ class PciDeviceStats(object):
|
|||||||
# Failed to allocate the required number of devices
|
# Failed to allocate the required number of devices
|
||||||
# Return the devices already allocated back to their pools
|
# Return the devices already allocated back to their pools
|
||||||
if sum([pool['count'] for pool in pools]) < count:
|
if sum([pool['count'] for pool in pools]) < count:
|
||||||
LOG.error("Failed to allocate PCI devices for instance."
|
LOG.error("Failed to allocate PCI devices for instance. "
|
||||||
" Unassigning devices back to pools."
|
"Unassigning devices back to pools. "
|
||||||
" This should not happen, since the scheduler"
|
"This should not happen, since the scheduler "
|
||||||
" should have accurate information, and allocation"
|
"should have accurate information, and allocation "
|
||||||
" during claims is controlled via a hold"
|
"during claims is controlled via a hold "
|
||||||
" on the compute node semaphore")
|
"on the compute node semaphore.")
|
||||||
for d in range(len(alloc_devices)):
|
for d in range(len(alloc_devices)):
|
||||||
self.add_device(alloc_devices.pop())
|
self.add_device(alloc_devices.pop())
|
||||||
return None
|
return None
|
||||||
@@ -223,7 +223,7 @@ class PciDeviceStats(object):
|
|||||||
|
|
||||||
def _filter_non_requested_pfs(self, request, matching_pools):
|
def _filter_non_requested_pfs(self, request, matching_pools):
|
||||||
# Remove SRIOV_PFs from pools, unless it has been explicitly requested
|
# Remove SRIOV_PFs from pools, unless it has been explicitly requested
|
||||||
# This is especially needed in cases where PFs and VFs has the same
|
# This is especially needed in cases where PFs and VFs have the same
|
||||||
# product_id.
|
# product_id.
|
||||||
if all(spec.get('dev_type') != fields.PciDeviceType.SRIOV_PF for
|
if all(spec.get('dev_type') != fields.PciDeviceType.SRIOV_PF for
|
||||||
spec in request.spec):
|
spec in request.spec):
|
||||||
|
|||||||
@@ -72,12 +72,26 @@ def parse_address(address):
|
|||||||
|
|
||||||
|
|
||||||
def get_pci_address_fields(pci_addr):
|
def get_pci_address_fields(pci_addr):
|
||||||
|
"""Parse a fully-specified PCI device address.
|
||||||
|
|
||||||
|
Does not validate that the components are valid hex or wildcard values.
|
||||||
|
|
||||||
|
:param pci_addr: A string of the form "<domain>:<bus>:<slot>.<function>".
|
||||||
|
:return: A 4-tuple of strings ("<domain>", "<bus>", "<slot>", "<function>")
|
||||||
|
"""
|
||||||
dbs, sep, func = pci_addr.partition('.')
|
dbs, sep, func = pci_addr.partition('.')
|
||||||
domain, bus, slot = dbs.split(':')
|
domain, bus, slot = dbs.split(':')
|
||||||
return (domain, bus, slot, func)
|
return domain, bus, slot, func
|
||||||
|
|
||||||
|
|
||||||
def get_pci_address(domain, bus, slot, func):
|
def get_pci_address(domain, bus, slot, func):
|
||||||
|
"""Assembles PCI address components into a fully-specified PCI address.
|
||||||
|
|
||||||
|
Does not validate that the components are valid hex or wildcard values.
|
||||||
|
|
||||||
|
:param domain, bus, slot, func: Hex or wildcard strings.
|
||||||
|
:return: A string of the form "<domain>:<bus>:<slot>.<function>".
|
||||||
|
"""
|
||||||
return '%s:%s:%s.%s' % (domain, bus, slot, func)
|
return '%s:%s:%s.%s' % (domain, bus, slot, func)
|
||||||
|
|
||||||
|
|
||||||
@@ -103,7 +117,6 @@ def is_physical_function(domain, bus, slot, function):
|
|||||||
dev_path = "/sys/bus/pci/devices/%(d)s:%(b)s:%(s)s.%(f)s/" % {
|
dev_path = "/sys/bus/pci/devices/%(d)s:%(b)s:%(s)s.%(f)s/" % {
|
||||||
"d": domain, "b": bus, "s": slot, "f": function}
|
"d": domain, "b": bus, "s": slot, "f": function}
|
||||||
if os.path.isdir(dev_path):
|
if os.path.isdir(dev_path):
|
||||||
sriov_totalvfs = 0
|
|
||||||
try:
|
try:
|
||||||
with open(dev_path + _SRIOV_TOTALVFS) as fd:
|
with open(dev_path + _SRIOV_TOTALVFS) as fd:
|
||||||
sriov_totalvfs = int(fd.read())
|
sriov_totalvfs = int(fd.read())
|
||||||
@@ -119,12 +132,12 @@ def _get_sysfs_netdev_path(pci_addr, pf_interface):
|
|||||||
Assumes a networking device - will not check for the existence of the path.
|
Assumes a networking device - will not check for the existence of the path.
|
||||||
"""
|
"""
|
||||||
if pf_interface:
|
if pf_interface:
|
||||||
return "/sys/bus/pci/devices/%s/physfn/net" % (pci_addr)
|
return "/sys/bus/pci/devices/%s/physfn/net" % pci_addr
|
||||||
return "/sys/bus/pci/devices/%s/net" % (pci_addr)
|
return "/sys/bus/pci/devices/%s/net" % pci_addr
|
||||||
|
|
||||||
|
|
||||||
def get_ifname_by_pci_address(pci_addr, pf_interface=False):
|
def get_ifname_by_pci_address(pci_addr, pf_interface=False):
|
||||||
"""Get the interface name based on a VF's pci address
|
"""Get the interface name based on a VF's pci address.
|
||||||
|
|
||||||
The returned interface name is either the parent PF's or that of the VF
|
The returned interface name is either the parent PF's or that of the VF
|
||||||
itself based on the argument of pf_interface.
|
itself based on the argument of pf_interface.
|
||||||
@@ -138,7 +151,7 @@ def get_ifname_by_pci_address(pci_addr, pf_interface=False):
|
|||||||
|
|
||||||
|
|
||||||
def get_mac_by_pci_address(pci_addr, pf_interface=False):
|
def get_mac_by_pci_address(pci_addr, pf_interface=False):
|
||||||
"""Get the MAC address of the nic based on it's PCI address
|
"""Get the MAC address of the nic based on its PCI address.
|
||||||
|
|
||||||
Raises PciDeviceNotFoundById in case the pci device is not a NIC
|
Raises PciDeviceNotFoundById in case the pci device is not a NIC
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -26,15 +26,16 @@ CONF = nova.conf.CONF
|
|||||||
|
|
||||||
class Whitelist(object):
|
class Whitelist(object):
|
||||||
|
|
||||||
"""White list class to decide assignable pci devices.
|
"""White list class to represent assignable pci devices.
|
||||||
|
|
||||||
Not all devices on compute node can be assigned to guest, the
|
Not all devices on a compute node can be assigned to a guest. The
|
||||||
cloud administrator decides the devices that can be assigned
|
cloud administrator decides which devices can be assigned
|
||||||
based on vendor_id or product_id etc. If no white list specified,
|
based on vendor_id or product_id, etc. If no white list is specified,
|
||||||
no device will be assignable.
|
no devices will be assignable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _parse_white_list_from_config(self, whitelists):
|
@staticmethod
|
||||||
|
def _parse_white_list_from_config(whitelists):
|
||||||
"""Parse and validate the pci whitelist from the nova config."""
|
"""Parse and validate the pci whitelist from the nova config."""
|
||||||
specs = []
|
specs = []
|
||||||
for jsonspec in whitelists:
|
for jsonspec in whitelists:
|
||||||
@@ -64,14 +65,16 @@ class Whitelist(object):
|
|||||||
def __init__(self, whitelist_spec=None):
|
def __init__(self, whitelist_spec=None):
|
||||||
"""White list constructor
|
"""White list constructor
|
||||||
|
|
||||||
For example, followed json string specifies that devices whose
|
For example, the following json string specifies that devices whose
|
||||||
vendor_id is '8086' and product_id is '1520' can be assigned
|
vendor_id is '8086' and product_id is '1520' can be assigned
|
||||||
to guest.
|
to guests.
|
||||||
'[{"product_id":"1520", "vendor_id":"8086"}]'
|
'[{"product_id":"1520", "vendor_id":"8086"}]'
|
||||||
|
|
||||||
:param whitelist_spec: A json string for a list of dictionaries,
|
:param whitelist_spec: A json string for a dictionary or list thereof.
|
||||||
each dictionary specifies the pci device
|
Each dictionary specifies the pci device
|
||||||
properties requirement.
|
properties requirement. See the definition of
|
||||||
|
passthrough_whitelist in nova.conf.pci for
|
||||||
|
details and examples.
|
||||||
"""
|
"""
|
||||||
super(Whitelist, self).__init__()
|
super(Whitelist, self).__init__()
|
||||||
if whitelist_spec:
|
if whitelist_spec:
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ class HostState(object):
|
|||||||
self.updated = compute.updated_at
|
self.updated = compute.updated_at
|
||||||
self.numa_topology = compute.numa_topology
|
self.numa_topology = compute.numa_topology
|
||||||
self.pci_stats = pci_stats.PciDeviceStats(
|
self.pci_stats = pci_stats.PciDeviceStats(
|
||||||
compute.pci_device_pools)
|
stats=compute.pci_device_pools)
|
||||||
|
|
||||||
# All virt drivers report host_ip
|
# All virt drivers report host_ip
|
||||||
self.host_ip = compute.host_ip
|
self.host_ip = compute.host_ip
|
||||||
@@ -264,7 +264,7 @@ class HostState(object):
|
|||||||
@set_update_time_on_success
|
@set_update_time_on_success
|
||||||
def _locked(self, spec_obj):
|
def _locked(self, spec_obj):
|
||||||
# Scheduler API is inherently multi-threaded as every incoming RPC
|
# Scheduler API is inherently multi-threaded as every incoming RPC
|
||||||
# message will be dispatched in it's own green thread. So the
|
# message will be dispatched in its own green thread. So the
|
||||||
# shared host state should be consumed in a consistent way to make
|
# shared host state should be consumed in a consistent way to make
|
||||||
# sure its data is valid under concurrent write operations.
|
# sure its data is valid under concurrent write operations.
|
||||||
self._locked_consume_from_request(spec_obj)
|
self._locked_consume_from_request(spec_obj)
|
||||||
|
|||||||
@@ -210,7 +210,8 @@ class PciAddressTestCase(test.NoDBTestCase):
|
|||||||
pci_info = {"address": "0000:h4.12:6", "physical_network": "hr_net"}
|
pci_info = {"address": "0000:h4.12:6", "physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
msg = ('Invalid PCI devices Whitelist config invalid func 12:6')
|
msg = ("Invalid PCI devices Whitelist config: property func ('12:6') "
|
||||||
|
"does not parse as a hex number.")
|
||||||
self.assertEqual(msg, six.text_type(exc))
|
self.assertEqual(msg, six.text_type(exc))
|
||||||
|
|
||||||
def test_max_func(self):
|
def test_max_func(self):
|
||||||
@@ -218,8 +219,9 @@ class PciAddressTestCase(test.NoDBTestCase):
|
|||||||
"physical_network": "hr_net"}
|
"physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
msg = ('Invalid PCI devices Whitelist config invalid func %x'
|
msg = ('Invalid PCI devices Whitelist config: property func (%x) is '
|
||||||
% (devspec.MAX_FUNC + 1))
|
'greater than the maximum allowable value (%x).'
|
||||||
|
% (devspec.MAX_FUNC + 1, devspec.MAX_FUNC))
|
||||||
self.assertEqual(msg, six.text_type(exc))
|
self.assertEqual(msg, six.text_type(exc))
|
||||||
|
|
||||||
def test_max_domain(self):
|
def test_max_domain(self):
|
||||||
@@ -227,8 +229,9 @@ class PciAddressTestCase(test.NoDBTestCase):
|
|||||||
"physical_network": "hr_net"}
|
"physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
msg = ('Invalid PCI devices Whitelist config invalid domain %x'
|
msg = ('Invalid PCI devices Whitelist config: property domain (%X) '
|
||||||
% (devspec.MAX_DOMAIN + 1))
|
'is greater than the maximum allowable value (%X).'
|
||||||
|
% (devspec.MAX_DOMAIN + 1, devspec.MAX_DOMAIN))
|
||||||
self.assertEqual(msg, six.text_type(exc))
|
self.assertEqual(msg, six.text_type(exc))
|
||||||
|
|
||||||
def test_max_bus(self):
|
def test_max_bus(self):
|
||||||
@@ -236,8 +239,9 @@ class PciAddressTestCase(test.NoDBTestCase):
|
|||||||
"physical_network": "hr_net"}
|
"physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
msg = ('Invalid PCI devices Whitelist config invalid bus %x'
|
msg = ('Invalid PCI devices Whitelist config: property bus (%X) is '
|
||||||
% (devspec.MAX_BUS + 1))
|
'greater than the maximum allowable value (%X).'
|
||||||
|
% (devspec.MAX_BUS + 1, devspec.MAX_BUS))
|
||||||
self.assertEqual(msg, six.text_type(exc))
|
self.assertEqual(msg, six.text_type(exc))
|
||||||
|
|
||||||
def test_max_slot(self):
|
def test_max_slot(self):
|
||||||
@@ -245,8 +249,9 @@ class PciAddressTestCase(test.NoDBTestCase):
|
|||||||
"physical_network": "hr_net"}
|
"physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
msg = ('Invalid PCI devices Whitelist config invalid slot %x'
|
msg = ('Invalid PCI devices Whitelist config: property slot (%X) is '
|
||||||
% (devspec.MAX_SLOT + 1))
|
'greater than the maximum allowable value (%X).'
|
||||||
|
% (devspec.MAX_SLOT + 1, devspec.MAX_SLOT))
|
||||||
self.assertEqual(msg, six.text_type(exc))
|
self.assertEqual(msg, six.text_type(exc))
|
||||||
|
|
||||||
def test_address_is_undefined(self):
|
def test_address_is_undefined(self):
|
||||||
@@ -380,8 +385,10 @@ class PciDevSpecTestCase(test.NoDBTestCase):
|
|||||||
"product_id": "5057", "physical_network": "hr_net"}
|
"product_id": "5057", "physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
self.assertEqual("Invalid PCI devices Whitelist config "
|
self.assertEqual(
|
||||||
"invalid vendor_id 80860", six.text_type(exc))
|
"Invalid PCI devices Whitelist config: property vendor_id (80860) "
|
||||||
|
"is greater than the maximum allowable value (FFFF).",
|
||||||
|
six.text_type(exc))
|
||||||
|
|
||||||
def test_invalid_product_id(self):
|
def test_invalid_product_id(self):
|
||||||
pci_info = {"vendor_id": "8086", "address": "*: *: *.5",
|
pci_info = {"vendor_id": "8086", "address": "*: *: *.5",
|
||||||
@@ -394,8 +401,10 @@ class PciDevSpecTestCase(test.NoDBTestCase):
|
|||||||
"product_id": "50570", "physical_network": "hr_net"}
|
"product_id": "50570", "physical_network": "hr_net"}
|
||||||
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
exc = self.assertRaises(exception.PciConfigInvalidWhitelist,
|
||||||
devspec.PciDeviceSpec, pci_info)
|
devspec.PciDeviceSpec, pci_info)
|
||||||
self.assertEqual("Invalid PCI devices Whitelist config "
|
self.assertEqual(
|
||||||
"invalid product_id 50570", six.text_type(exc))
|
"Invalid PCI devices Whitelist config: property product_id "
|
||||||
|
"(50570) is greater than the maximum allowable value (FFFF).",
|
||||||
|
six.text_type(exc))
|
||||||
|
|
||||||
def test_devname_and_address(self):
|
def test_devname_and_address(self):
|
||||||
pci_info = {"devname": "eth0", "vendor_id": "8086",
|
pci_info = {"devname": "eth0", "vendor_id": "8086",
|
||||||
|
|||||||
Reference in New Issue
Block a user