SR-IOV device plugin support for FPGA FEC devices

This commit introduces SR-IOV device plugin support for forward
error correction (FEC) devices that are enabled on an Intel
N3000 FPGA device.

The FEC device is intended to be used with a DPDK application,
with the device itself being bound to the igb_uio driver. As
such, support has been added for configuring the PF and VF
driver for the device, as well as the number of requested
VFs.

An example usage for setting the drivers and VFs is as follows:

system host-device-modify <host> <device_name> \
  --driver <driver> \
  --vf-driver <driver> \
  -N <number of vfs>

Story: 2006740
Task: 39947

Depends-On: https://review.opendev.org/733723
Change-Id: Iee0f0e449652219939ed5369423efdfb906ca3f1
Signed-off-by: Steven Webster <Steven.Webster@windriver.com>
This commit is contained in:
Steven Webster 2020-05-25 17:49:52 -04:00
parent a24a646134
commit ff41cd29dc
23 changed files with 928 additions and 67 deletions

View File

@ -16,22 +16,24 @@ def _print_device_show(device):
fields = ['name', 'pciaddr', 'pclass_id', 'pvendor_id', 'pdevice_id',
'pclass', 'pvendor', 'pdevice', 'numa_node', 'enabled',
'sriov_totalvfs', 'sriov_numvfs', 'sriov_vfs_pci_address',
'extra_info', 'created_at', 'updated_at']
'sriov_vf_pdevice_id', 'extra_info', 'created_at', 'updated_at']
labels = ['name', 'address', 'class id', 'vendor id', 'device id',
'class name', 'vendor name', 'device name', 'numa_node',
'enabled', 'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'extra_info', 'created_at',
'updated_at']
'sriov_vfs_pci_address', 'sriov_vf_pdevice_id',
'extra_info', 'created_at', 'updated_at']
pclass_id = getattr(device, 'pclass_id')
if pclass_id == PCI_DEVICE_CLASS_FPGA:
fields += ['root_key', 'revoked_key_ids',
'boot_page', 'bitstream_id',
'bmc_build_version', 'bmc_fw_version']
'bmc_build_version', 'bmc_fw_version',
'driver', 'sriov_vf_driver']
labels += ['root_key', 'revoked_key_ids',
'boot_page', 'bitstream_id',
'bmc_build_version', 'bmc_fw_version']
'bmc_build_version', 'bmc_fw_version',
'driver', 'sriov_vf_driver']
data = [(f, getattr(device, f, '')) for f in fields]
utils.print_tuple_list(data, labels)
@ -99,11 +101,25 @@ def do_host_device_list(cc, args):
@utils.arg('-e', '--enabled',
metavar='<enabled status>',
help='The enabled status of the device')
@utils.arg('-d', '--driver',
metavar='<new driver>',
help='The new driver of the device')
@utils.arg('-v', '--vf-driver',
dest='sriov_vf_driver',
metavar='<new VF driver>',
help='The new VF driver of the device')
@utils.arg('-N', '--num-vfs',
dest='sriov_numvfs',
metavar='<sriov numvfs>',
help='The number of SR-IOV VFs of the device')
def do_host_device_modify(cc, args):
"""Modify device availability for worker nodes."""
rwfields = ['enabled',
'name']
'name',
'driver',
'sriov_numvfs',
'sriov_vf_driver']
host = ihost_utils._find_ihost(cc, args.hostnameorid)

View File

@ -685,6 +685,8 @@ class AgentManager(service.PeriodicService):
'sriov_totalvfs': dev.sriov_totalvfs,
'sriov_numvfs': dev.sriov_numvfs,
'sriov_vfs_pci_address': dev.sriov_vfs_pci_address,
'sriov_vf_driver': dev.sriov_vf_driver,
'sriov_vf_pdevice_id': dev.sriov_vf_pdevice_id,
'driver': dev.driver,
'enabled': dev.enabled,
'extra_info': dev.extra_info}

View File

@ -19,6 +19,7 @@ import shlex
from oslo_log import log as logging
from sysinv._i18n import _
from sysinv.common import constants
from sysinv.common import device as dconstants
from sysinv.common import utils
LOG = logging.getLogger(__name__)
@ -34,7 +35,8 @@ KNOWN_PCI_DEVICES = [{"vendor_id": constants.NOVA_PCI_ALIAS_QAT_PF_VENDOR,
{"vendor_id": constants.NOVA_PCI_ALIAS_QAT_PF_VENDOR,
"device_id": constants.NOVA_PCI_ALIAS_QAT_C62X_PF_DEVICE,
"class_id": constants.NOVA_PCI_ALIAS_QAT_CLASS},
{"class_id": constants.NOVA_PCI_ALIAS_GPU_CLASS}]
{"class_id": constants.NOVA_PCI_ALIAS_GPU_CLASS},
{"class_id": dconstants.PCI_DEVICE_CLASS_FPGA}]
# PCI-SIG 0x06 bridge devices to not inventory.
IGNORE_BRIDGE_PCI_CLASSES = ['bridge', 'isa bridge', 'host bridge']
@ -156,6 +158,8 @@ class PCIDevice(object):
self.sriov_totalvfs = kwargs.get('sriov_totalvfs')
self.sriov_numvfs = kwargs.get('sriov_numvfs')
self.sriov_vfs_pci_address = kwargs.get('sriov_vfs_pci_address')
self.sriov_vf_driver = kwargs.get('sriov_vf_driver')
self.sriov_vf_pdevice_id = kwargs.get('sriov_vf_pdevice_id')
self.driver = kwargs.get('driver')
self.enabled = kwargs.get('enabled')
self.extra_info = kwargs.get('extra_info')
@ -250,8 +254,8 @@ class PCIOperator(object):
with open(fdevice, 'r') as f:
# Device id is a 16 bit hex value. Strip off the hex
# identifier and trailing whitespace
vf_device_id = hex(int(f.readline().rstrip(), 16))[2:]
if len(vf_device_id) <= 4:
vf_device_id = f.readline().rstrip()[2:]
if len(vf_device_id) < 4:
# Should never happen
raise ValueError(_(
"Device Id must be a 16 bit hex value"))
@ -281,7 +285,7 @@ class PCIOperator(object):
for line in output.split('\n'):
pci_attr = shlex.split(line.strip())
if (pci_attr and len(pci_attr) == 2 and 'Module' in pci_attr[0]):
if (pci_attr and len(pci_attr) == 2 and 'Driver' in pci_attr[0]):
vf_driver = pci_attr[1]
break
@ -396,6 +400,7 @@ class PCIOperator(object):
sriov_totalvfs = self.get_pci_sriov_totalvfs(a)
sriov_numvfs = self.get_pci_sriov_numvfs(a)
sriov_vfs_pci_address = self.get_pci_sriov_vfs_pci_address(a, sriov_numvfs)
sriov_vf_driver = self.get_pci_sriov_vf_driver_name(a, sriov_vfs_pci_address)
driver = self.get_pci_driver_name(a)
fclass = dirpcideva + '/class'
@ -423,6 +428,8 @@ class PCIOperator(object):
pclass_id = None
name = "pci_" + a.replace(':', '_').replace('.', '_')
sriov_vf_pdevice_id = self.get_pci_sriov_vf_device_id(
a, sriov_vfs_pci_address)
attrs = {
"name": name,
@ -435,6 +442,8 @@ class PCIOperator(object):
"sriov_numvfs": sriov_numvfs,
"sriov_vfs_pci_address":
','.join(str(x) for x in sriov_vfs_pci_address),
"sriov_vf_driver": sriov_vf_driver,
"sriov_vf_pdevice_id": sriov_vf_pdevice_id,
"driver": driver,
"enabled": self.pci_get_enabled_attr(pclass_id,
pvendor_id, pdevice_id),

View File

@ -3354,6 +3354,52 @@ class HostController(rest.RestController):
self._check_sriovdp_interface_datanets(interface)
def _semantic_check_fpga_fec_device(self, host, dev, force_unlock=False):
"""
Perform semantic checks on an FPGA FEC device.
"""
if (force_unlock or
dev.pdevice_id != device.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF):
return
sriov_numvfs = dev.sriov_numvfs
if not sriov_numvfs:
return
if (dev.sriov_vfs_pci_address and
sriov_numvfs == len(dev.sriov_vfs_pci_address.split(','))):
LOG.info("check sriov_numvfs=%s sriov_vfs_pci_address=%s" %
(sriov_numvfs, dev.sriov_vfs_pci_address))
else:
msg = (_("Expecting number of FPGA device sriov_numvfs=%s. "
"Please wait a few minutes for inventory update and "
"retry host-unlock." %
sriov_numvfs))
LOG.info(msg)
pecan.request.rpcapi.update_sriov_config(
pecan.request.context,
host['uuid'])
raise wsme.exc.ClientSideError(msg)
def _semantic_check_fpga_device(self, host, dev, force_unlock=False):
"""
Perform semantic checks on an FPGA device.
"""
if dev.pclass_id != device.PCI_DEVICE_CLASS_FPGA:
return
if dev.pdevice_id == device.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF:
self._semantic_check_fpga_fec_device(host, dev, force_unlock)
def _semantic_check_devices(self, host, force_unlock=False):
"""
Perform semantic checks on pci devices.
"""
devices = (
pecan.request.dbapi.pci_device_get_by_host(host['uuid']))
for dev in devices:
if dev.pclass_id == device.PCI_DEVICE_CLASS_FPGA:
self._semantic_check_fpga_device(host, dev, force_unlock)
def _semantic_check_unlock_kube_upgrade(self, ihost, force_unlock=False):
"""
Perform semantic checks related to kubernetes upgrades prior to unlocking host.
@ -5478,6 +5524,9 @@ class HostController(rest.RestController):
self._semantic_check_data_interfaces(ihost,
force_unlock)
# Check whether a device has completed configuration
self._semantic_check_devices(ihost, force_unlock)
# Check if cpu assignments are valid
self._semantic_check_worker_cpu_assignments(ihost)

View File

@ -89,6 +89,12 @@ class PCIDevice(base.APIBase):
sriov_vfs_pci_address = wtypes.text
"The PCI Addresses of the VFs"
sriov_vf_driver = wtypes.text
"The driver of configured SR-IOV VFs"
sriov_vf_pdevice_id = wtypes.text
"The SR-IOV VF PCI device id for this device"
driver = wtypes.text
"The kernel driver for this device"
@ -140,7 +146,9 @@ class PCIDevice(base.APIBase):
'pvendor', 'pdevice', 'psvendor',
'psdevice', 'numa_node',
'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'driver',
'sriov_vfs_pci_address',
'sriov_vf_driver',
'sriov_vf_pdevice_id', 'driver',
'host_uuid', 'enabled',
'bmc_build_version', 'bmc_fw_version',
'root_key', 'revoked_key_ids',
@ -287,7 +295,7 @@ class PCIDeviceController(rest.RestController):
rpc_device = objects.pci_device.get_by_uuid(
pecan.request.context, device_uuid)
# replace host_uuid and with corresponding
# replace host_uuid with corresponding host_id
patch_obj = jsonpatch.JsonPatch(patch)
for p in patch_obj:
if p['path'] == '/host_uuid':
@ -307,13 +315,25 @@ class PCIDeviceController(rest.RestController):
host = pecan.request.dbapi.ihost_get(device.host_id)
_check_host(host)
sriov_update = _check_device_sriov(device.as_dict(), host)
# Update fields that have changed
for field in objects.pci_device.fields:
if rpc_device[field] != getattr(device, field):
value = getattr(device, field)
if rpc_device[field] != value:
_check_field(field)
if (field in ['sriov_vf_driver', 'driver'] and
value == 'none'):
rpc_device[field] = None
else:
rpc_device[field] = getattr(device, field)
rpc_device.save()
if sriov_update:
pecan.request.rpcapi.update_sriov_config(
pecan.request.context, host['uuid'])
return PCIDevice.convert_with_links(rpc_device)
@ -328,5 +348,65 @@ def _check_host(host):
def _check_field(field):
if field not in ["enabled", "name"]:
if field not in ["enabled", "name", "driver", "sriov_numvfs", "sriov_vf_driver"]:
raise wsme.exc.ClientSideError(_('Modifying %s attribute restricted') % field)
def _check_device_sriov(device, host):
sriov_update = False
if (device['pdevice_id'] not in dconstants.SRIOV_ENABLED_DEVICE_IDS and
'sriov_numvfs' in device.keys() and device['sriov_numvfs']):
raise wsme.exc.ClientSideError(_("The number of SR-IOV VFs is specified "
"but the device is not supported for SR-IOV"))
if (device['pdevice_id'] not in dconstants.SRIOV_ENABLED_DEVICE_IDS and
'sriov_vf_driver' in device.keys() and device['sriov_vf_driver']):
raise wsme.exc.ClientSideError(_("The SR-IOV VF driver is specified "
"but the device is not supported for SR-IOV"))
if device['pdevice_id'] not in dconstants.SRIOV_ENABLED_DEVICE_IDS:
return sriov_update
if 'sriov_numvfs' not in device.keys():
raise wsme.exc.ClientSideError(_("The number of SR-IOV VFs must be specified"))
else:
if ('sriov_vf_driver' in device.keys() and device['sriov_vf_driver'] and
device['sriov_vf_driver'] != dconstants.FPGA_INTEL_5GNR_FEC_DRIVER_NONE and
device['sriov_numvfs'] is None):
raise wsme.exc.ClientSideError(_("Value for number of SR-IOV VFs must be specified."))
if device['sriov_numvfs'] and device['sriov_numvfs'] < 0:
raise wsme.exc.ClientSideError(_("Value for number of SR-IOV VFs must be >= 0."))
if ('sriov_vf_driver' in device.keys() and device['sriov_vf_driver'] and
device['sriov_vf_driver'] != dconstants.FPGA_INTEL_5GNR_FEC_DRIVER_NONE and
device['sriov_numvfs'] == 0):
raise wsme.exc.ClientSideError(_("Value for number of SR-IOV VFs must be > 0."))
if 'sriov_totalvfs' in device.keys():
if not device['sriov_totalvfs']:
raise wsme.exc.ClientSideError(_("SR-IOV cannot be configured on this interface"))
if device['sriov_numvfs'] and device['sriov_numvfs'] > device['sriov_totalvfs']:
raise wsme.exc.ClientSideError(_(
"The device supports a maximum of %s VFs" % device['sriov_totalvfs']))
if 'sriov_vf_driver' not in device.keys():
raise wsme.exc.ClientSideError(_("The SR-IOV VF driver must be specified"))
else:
if (device['sriov_vf_driver'] is not None and
device['pdevice_id'] == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF and
device['sriov_vf_driver'] not in dconstants.FPGA_INTEL_5GNR_FEC_VF_VALID_DRIVERS):
msg = (_("Value for SR-IOV VF driver must be one of "
"{}").format(', '.join(dconstants.FPGA_INTEL_5GNR_FEC_VF_VALID_DRIVERS)))
raise wsme.exc.ClientSideError(msg)
if ('driver' in device.keys() and device['driver'] and
device['pdevice_id'] == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF and
device['driver'] not in dconstants.FPGA_INTEL_5GNR_FEC_PF_VALID_DRIVERS):
msg = (_("Value for SR-IOV PF driver must be one of "
"{}").format(', '.join(dconstants.FPGA_INTEL_5GNR_FEC_PF_VALID_DRIVERS)))
raise wsme.exc.ClientSideError(msg)
sriov_update = True
return sriov_update

View File

@ -7,6 +7,24 @@
# PCI Device Class ID in hexidecimal string
PCI_DEVICE_CLASS_FPGA = '120000'
# Device Vendors
PCI_DEVICE_VENDOR_INTEL = "8086"
# Device Ids
PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF = "0d8f"
PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_VF = "0d90"
# SR-IOV enabled devices
SRIOV_ENABLED_DEVICE_IDS = [PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF]
FPGA_INTEL_5GNR_FEC_DRIVER_IGB_UIO = "igb_uio"
FPGA_INTEL_5GNR_FEC_DRIVER_NONE = "none"
FPGA_INTEL_5GNR_FEC_VF_VALID_DRIVERS = [FPGA_INTEL_5GNR_FEC_DRIVER_IGB_UIO,
FPGA_INTEL_5GNR_FEC_DRIVER_NONE]
FPGA_INTEL_5GNR_FEC_PF_VALID_DRIVERS = [FPGA_INTEL_5GNR_FEC_DRIVER_IGB_UIO,
FPGA_INTEL_5GNR_FEC_DRIVER_NONE]
# Device Image
DEVICE_IMAGE_TMP_PATH = '/tmp/device_images'
DEVICE_IMAGE_PATH = '/opt/platform/device_images'

View File

@ -2542,8 +2542,23 @@ class ConductorManager(service.PeriodicService):
'sriov_numvfs': pci_dev['sriov_numvfs'],
'sriov_vfs_pci_address':
pci_dev['sriov_vfs_pci_address'],
'sriov_vf_driver': pci_dev.get('sriov_vf_driver', None),
'sriov_vf_pdevice_id':
pci_dev.get('sriov_vf_pdevice_id', None),
'driver': pci_dev['driver']}
LOG.info("attr: %s" % attr)
if (host['administrative'] == constants.ADMIN_LOCKED and
pci_dev['pdevice_id'] == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF):
# For the FPGA FEC device, the actual VF driver
# is only updated on an unlocked host. The set
# of VF PCI addresses may not be known when the
# value of sriov_numvfs changes and is applied
# to create the VFs on a puppet runtime manifest
# apply. This prevents the intended VF driver
# from being reported as None (reset) when the
# binding of the intended driver has not had a
# chance to be applied.
del attr['sriov_vf_driver']
dev = self.dbapi.pci_device_update(dev['uuid'], attr)
except Exception:
LOG.exception("Failed to update port %s" %
@ -5980,7 +5995,8 @@ class ConductorManager(service.PeriodicService):
config_dict = {
"personalities": personalities,
'host_uuids': [host_uuid],
"classes": 'platform::interfaces::sriov::runtime',
"classes": ['platform::interfaces::sriov::runtime',
'platform::devices::fpga::fec::runtime'],
puppet_common.REPORT_INVENTORY_UPDATE:
puppet_common.REPORT_PCI_SRIOV_CONFIG,
}

View File

@ -0,0 +1,25 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from sqlalchemy import Column, MetaData, String, Table
ENGINE = 'InnoDB'
CHARSET = 'utf8'
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
devices = Table('pci_devices', meta, autoload=True)
devices.create_column(Column('sriov_vf_driver', String(255)))
devices.create_column(Column('sriov_vf_pdevice_id', String(4)))
def downgrade(migrate_engine):
# Downgrade is unsupported in this release.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -1458,6 +1458,8 @@ class PciDevice(Base):
sriov_totalvfs = Column(Integer)
sriov_numvfs = Column(Integer)
sriov_vfs_pci_address = Column(String(1020))
sriov_vf_driver = Column(String(255))
sriov_vf_pdevice_id = Column(String(4))
driver = Column(String(255))
enabled = Column(Boolean)
extra_info = Column(Text)

View File

@ -32,6 +32,8 @@ class PCIDevice(base.SysinvObject):
'sriov_totalvfs': utils.int_or_none,
'sriov_numvfs': utils.int_or_none,
'sriov_vfs_pci_address': utils.str_or_none,
'sriov_vf_driver': utils.str_or_none,
'sriov_vf_pdevice_id': utils.str_or_none,
'driver': utils.str_or_none,
'enabled': utils.bool_or_none,
'extra_info': utils.str_or_none,

View File

@ -6,8 +6,10 @@
import collections
from sysinv.common import constants
from sysinv.common import device as dconstants
from sysinv.puppet import base
from sysinv.puppet import quoted_str
class DevicePuppet(base.BasePuppet):
@ -56,6 +58,60 @@ class DevicePuppet(base.BasePuppet):
'platform::devices::qat::service_enabled': True,
}
def _get_host_fpga_fec_device_config(self, fpga_fec_devices):
"""
Builds a config dictionary for FPGA FEC devices to be used by the
platform devices (worker) puppet resource.
"""
device_config = {}
vf_config = {}
for device in fpga_fec_devices:
name = 'pci-%s' % device.pciaddr
# Format the vf addresses as quoted strings in order to prevent
# puppet from treating the address as a time/date value
vf_addrs = device.get('sriov_vfs_pci_address', [])
if vf_addrs:
vf_addrs = [quoted_str(addr.strip())
for addr in vf_addrs.split(",") if addr]
if len(vf_addrs) == device.get('sriov_numvfs', 0):
for addr in vf_addrs:
vf_config.update({
addr: {
'addr': addr,
'driver': device['sriov_vf_driver']
}
})
pf_config = {
device.pciaddr: {
'num_vfs': device['sriov_numvfs'],
'addr': quoted_str(device['pciaddr'].strip()),
'driver': device['driver']
}
}
device_config = {
name: {
'pf_config': pf_config,
'vf_config': vf_config
}
}
return {
'platform::devices::fpga::fec::params::device_config': device_config
}
def _get_host_fpga_device_config(self, pci_device_list):
"""
Builds a config dictionary for FPGA devices to be used by the platform
devices (worker) puppet resource.
"""
fpga_config = {}
fpga_fec_devices = pci_device_list[dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF]
if fpga_fec_devices:
fec_config = self._get_host_fpga_fec_device_config(fpga_fec_devices)
fpga_config.update(fec_config)
return fpga_config
def get_host_config(self, host):
if constants.WORKER not in host.subfunctions:
# configuration only required for compute hosts
@ -72,4 +128,8 @@ class DevicePuppet(base.BasePuppet):
if qat_devices:
device_config.update(qat_devices)
fpga_devices = self._get_host_fpga_device_config(devices)
if fpga_devices:
device_config.update(fpga_devices)
return device_config

View File

@ -1022,6 +1022,7 @@ def get_sriov_config(context, iface):
Returns an SR-IOV interface config dictionary.
"""
vf_driver = iface['sriov_vf_driver']
vf_config = {}
port = interface.get_sriov_interface_port(context, iface)
if not port:
@ -1052,6 +1053,14 @@ def get_sriov_config(context, iface):
vf_addrs = [quoted_str(addr.strip())
for addr in vf_addr_list.split(",") if addr]
for addr in vf_addrs:
vf_config.update({
addr: {
'addr': addr,
'driver': vf_driver
}
})
# Include the desired number of VFs if the device supports SR-IOV
# config via sysfs and is not a sub-interface
num_vfs = None
@ -1061,10 +1070,9 @@ def get_sriov_config(context, iface):
config = {
'ifname': iface['ifname'],
'pf_addr': quoted_str(port['pciaddr'].strip()),
'addr': quoted_str(port['pciaddr'].strip()),
'num_vfs': num_vfs,
'vf_driver': vf_driver,
'vf_addrs': vf_addrs
'vf_config': vf_config
}
return config

View File

@ -19,6 +19,7 @@ from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import kubernetes
from sysinv.common import utils
from sysinv.common import device as dconstants
from sysinv import objects
from sysinv.puppet import base
from sysinv.puppet import interface
@ -400,8 +401,8 @@ class KubernetesPuppet(base.BasePuppet):
if (sriovdp_worker is True):
config.update({
'platform::kubernetes::worker::pci::pcidp_network_resources':
self._get_pcidp_network_resources(),
'platform::kubernetes::worker::pci::pcidp_resources':
self._get_pcidp_resources(host),
})
return config
@ -446,6 +447,15 @@ class KubernetesPuppet(base.BasePuppet):
driver = port['driver']
return driver
def _get_pcidp_fpga_driver(self, device):
sriov_vf_driver = device.get('sriov_vf_driver', None)
if (sriov_vf_driver and
constants.SRIOV_DRIVER_TYPE_VFIO in sriov_vf_driver):
driver = constants.SRIOV_DRIVER_VFIO_PCI
else:
driver = device['sriov_vf_driver']
return driver
def _get_pcidp_network_resources_by_ifclass(self, ifclass):
resources = {}
@ -486,7 +496,7 @@ class KubernetesPuppet(base.BasePuppet):
continue
driver = self._get_pcidp_driver(port, iface, ifclass)
if not device:
if not driver:
LOG.error("Failed to get driver for pci device %s", port['pciaddr'])
continue
@ -513,11 +523,62 @@ class KubernetesPuppet(base.BasePuppet):
return list(resources.values())
def _get_pcidp_network_resources(self):
def _get_pcidp_fpga_resources(self, host):
resources = {}
fec_name = "intel_fpga_fec"
for d in self.dbapi.pci_device_get_by_host(host.id):
if (d['pclass_id'] == dconstants.PCI_DEVICE_CLASS_FPGA
and d['pdevice_id'] == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF):
resource = resources.get(fec_name, None)
if not resource:
resource = {
"resourceName": fec_name,
"deviceType": "accelerator",
"selectors": {
"vendors": [],
"devices": [],
"drivers": []
}
}
vendor = d.get('pvendor_id', None)
if not vendor:
LOG.error("Failed to get vendor id for pci device %s", d['pciaddr'])
continue
device = d.get('sriov_vf_pdevice_id', None)
if not device:
LOG.error("Failed to get device id for pci device %s", d['pciaddr'])
continue
driver = self._get_pcidp_fpga_driver(d)
if not driver:
LOG.error("Failed to get driver for pci device %s", d['pciaddr'])
continue
vendor_list = resource['selectors']['vendors']
if vendor not in vendor_list:
vendor_list.append(vendor)
device_list = resource['selectors']['devices']
if device not in device_list:
device_list.append(device)
driver_list = resource['selectors']['drivers']
if driver not in driver_list:
driver_list.append(driver)
resources[fec_name] = resource
return list(resources.values())
def _get_pcidp_resources(self, host):
# Construct a list of all PCI passthrough and SRIOV resources
# for use with the SRIOV device plugin
sriov_resources = self._get_pcidp_network_resources_by_ifclass(
constants.INTERFACE_CLASS_PCI_SRIOV)
pcipt_resources = self._get_pcidp_network_resources_by_ifclass(
constants.INTERFACE_CLASS_PCI_PASSTHROUGH)
return json.dumps({'resourceList': sriov_resources + pcipt_resources})
fpga_resources = self._get_pcidp_fpga_resources(host)
return json.dumps({'resourceList': sriov_resources + pcipt_resources + fpga_resources})

View File

@ -0,0 +1,381 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- encoding: utf-8 -*-
#
#
# Copyright (c) 2020 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Tests for the API /interfaces/ methods.
"""
import time
from six.moves import http_client
from sysinv.common import constants
from sysinv.common import device as dconstants
from sysinv.tests.api import base
from sysinv.tests.db import base as dbbase
from sysinv.tests.db import utils as dbutils
class TestDevice(base.FunctionalTest, dbbase.BaseHostTestCase):
def _setup_configuration(self):
pass
def setUp(self):
super(TestDevice, self).setUp()
self._setup_context()
def _get_path(self, device_uuid=None):
if device_uuid:
return '/pci_devices/' + device_uuid
else:
return '/pci_devices'
def _post_get_test_device(self, **kw):
device = dbutils.get_test_pci_device(**kw)
# When invoking a POST the following fields should not be populated:
del device['id']
return device
def _create_host(self, personality, subfunction=None,
mgmt_mac=None, mgmt_ip=None,
admin=None,
invprovision=constants.PROVISIONED, **kw):
host = self._create_test_host(
personality=personality,
subfunction=subfunction,
administrative=admin or constants.ADMIN_UNLOCKED,
invprovision=invprovision,
**kw)
if personality == constants.CONTROLLER:
self.controller = host
else:
self.worker = host
return
def _create_device(self, **kw):
device = dbutils.create_test_pci_device(**kw)
return device
def _post_and_check_success(self, ndict):
response = self.post_json('%s' % self._get_path(), ndict)
self.assertEqual(http_client.OK, response.status_int)
return response
def _post_and_check_not_allowed(self, ndict):
response = self.post_json('%s' % self._get_path(), ndict,
expect_errors=True)
self.assertEqual(http_client.METHOD_NOT_ALLOWED, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
return response
def _post_and_check(self, ndict, expect_errors=False, error_message=None):
response = self.post_json('%s' % self._get_path(), ndict,
expect_errors)
if expect_errors:
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
if error_message:
self.assertIn(error_message, response.json['error_message'])
else:
self.assertEqual(http_client.OK, response.status_int)
return response
def _patch_and_check(self, data, path, expect_errors=False, error_message=None):
response = self.patch_dict('%s' % path, expect_errors=expect_errors, data=data)
if expect_errors:
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
if error_message:
self.assertIn(error_message, response.json['error_message'])
else:
self.assertEqual(http_client.OK, response.status_int)
return response
def _setup_context(self):
self.controller = None
self.worker = None
self._create_host(constants.WORKER, admin=constants.ADMIN_LOCKED)
class TestListDevice(TestDevice):
def setUp(self):
super(TestListDevice, self).setUp()
def test_device_list_one(self):
device = dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass_id='030000',
pvendor_id='80ee',
pdevice_id='beef',
sriov_totalvfs=64
)
result = self.get_json(self._get_path(device['uuid']))
self.assertEqual(result['pclass_id'], '030000')
self.assertEqual(result['pvendor_id'], '80ee')
self.assertEqual(result['pdevice_id'], 'beef')
self.assertEqual(result['sriov_totalvfs'], 64)
def test_device_list_all(self):
dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass_id='030000',
pvendor_id='80ee',
pdevice_id='beef',
sriov_totalvfs=64
)
data = self.get_json(self._get_path())
self.assertEqual(1, len(data['pci_devices']))
self.assertEqual(data['pci_devices'][0]['pclass_id'], '030000')
self.assertEqual(data['pci_devices'][0]['pvendor_id'], '80ee')
self.assertEqual(data['pci_devices'][0]['pdevice_id'], 'beef')
self.assertEqual(data['pci_devices'][0]['sriov_totalvfs'], 64)
class TestPostDevice(TestDevice, dbbase.ControllerHostTestCase):
def test_device_post_failure(self):
# Test creation of a device
ndict = self._post_get_test_device(
host_uuid=self.worker.uuid,
name='device0',
pclass_id='030000',
pvendor_id='80ee',
pdevice_id='beef',
sriov_totalvfs=64)
self._post_and_check_not_allowed(ndict)
class TestPatchDevice(TestDevice):
def setUp(self):
super(TestPatchDevice, self).setUp()
# Create a pci_device
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id,
pciaddr='0000:b7:00.0',
name='pci_0000_b7_00_0',
pclass='Processing accelerators',
pclass_id=dconstants.PCI_DEVICE_CLASS_FPGA,
pvendor='Intel Corporation',
pvendor_id='8086',
pdevice='Device [0d8f]',
pdevice_id=dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF,
driver=None,
enabled=False,
sriov_totalvfs=8,
sriov_numvfs=None,
sriov_vf_driver=None)
time.sleep(2)
response = self.get_json(self._get_path(self.pci_device['uuid']))
self.assertEqual('0000:b7:00.0', response['pciaddr'])
self.assertEqual('pci_0000_b7_00_0', response['name'])
self.assertEqual('Processing accelerators', response['pclass'])
self.assertEqual(dconstants.PCI_DEVICE_CLASS_FPGA, response['pclass_id'])
self.assertEqual('Intel Corporation', response['pvendor'])
self.assertEqual('8086', response['pvendor_id'])
self.assertEqual('Device [0d8f]', response['pdevice'])
self.assertEqual(dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF, response['pdevice_id'])
self.assertEqual(None, response['driver'])
self.assertEqual(False, response['enabled'])
self.assertEqual(8, response['sriov_totalvfs'])
self.assertEqual(None, response['sriov_numvfs'])
self.assertEqual(None, response['sriov_vf_driver'])
def test_device_modify_name(self):
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id,
pdevice_id='FFFF')
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
name='new_name',
expect_errors=False)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual('new_name', response.json['name'])
def test_device_modify_enabled(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
enabled=True,
expect_errors=False)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual(True, response.json['enabled'])
def test_device_modify_driver(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
driver='igb_uio',
expect_errors=False)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual('igb_uio', response.json['driver'])
def test_device_modify_sriov_numvfs(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=2,
expect_errors=False)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual(2, response.json['sriov_numvfs'])
def test_device_modify_sriov_numvfs_negative(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=-1,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Value for number of SR-IOV VFs must be >= 0.',
response.json['error_message'])
def test_device_modify_sriov_numvfs_none(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_vf_driver='igb_uio',
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Value for number of SR-IOV VFs must be specified.',
response.json['error_message'])
def test_device_modify_sriov_numvfs_zero(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_vf_driver='igb_uio',
sriov_numvfs=0,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Value for number of SR-IOV VFs must be > 0.',
response.json['error_message'])
def test_device_modify_sriov_numvfs_badvalue(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs="bad",
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Invalid input for field/attribute sriov_numvfs',
response.json['error_message'])
def test_device_modify_sriov_numvfs_toohigh(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=1000,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('The device supports a maximum of',
response.json['error_message'])
def test_device_modify_sriov_numvfs_unsupported_stx_device(self):
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id, device_id="FFFF")
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=2,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('device is not supported for SR-IOV',
response.json['error_message'])
def test_device_modify_sriov_numvfs_unsupported_hw_device(self):
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass_id=dconstants.PCI_DEVICE_CLASS_FPGA,
pdevice_id=dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF,
sriov_totalvfs=None)
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_numvfs=2,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('SR-IOV cannot be configured on this interface',
response.json['error_message'])
def test_device_modify_sriov_vf_driver(self):
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass_id=dconstants.PCI_DEVICE_CLASS_FPGA,
pdevice_id=dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF,
sriov_totalvfs=8,
sriov_numvfs=2)
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_vf_driver='igb_uio',
expect_errors=False)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual('igb_uio', response.json['sriov_vf_driver'])
def test_device_modify_sriov_vf_driver_unsupported_device(self):
self.pci_device = dbutils.create_test_pci_device(
host_id=self.worker.id, device_id="FFFF")
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_vf_driver='igb_uio',
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('device is not supported for SR-IOV',
response.json['error_message'])
def test_device_modify_sriov_vf_driver_invalid(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_vf_driver='bad',
sriov_numvfs=2,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Value for SR-IOV VF driver must be one of',
response.json['error_message'])
def test_device_modify_sriov_pf_driver_invalid(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
driver='bad',
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('Value for SR-IOV PF driver must be one of',
response.json['error_message'])
def test_device_modify_restricted_field(self):
response = self.patch_dict_json(
'%s' % self._get_path(self.pci_device['uuid']),
sriov_totalvfs=4,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
self.assertIn('attribute restricted', response.json['error_message'])

View File

@ -289,7 +289,7 @@ class TestPatch(TestDeviceImage):
invprovision=constants.PROVISIONED
)
# Create a pci_device and fpga_device object
self.pci_device = dbutils.create_test_pci_devices(
self.pci_device = dbutils.create_test_pci_device(
host_id=self.controller.id,
pclass='Processing accelerators',
pclass_id='120000',)

View File

@ -18,7 +18,7 @@ class DeviceLabelTestCase(base.FunctionalTest, dbbase.ControllerHostTestCase):
super(DeviceLabelTestCase, self).setUp()
self.dbapi = dbapi.get_instance()
# Create a pci_device and fpga_device object
self.pci_device = dbutils.create_test_pci_devices(
self.pci_device = dbutils.create_test_pci_device(
host_id=self.host.id,
pclass='Processing accelerators',
pclass_id='120000',)

View File

@ -176,7 +176,7 @@ class LabelAssignTestCase(LabelTestCase):
@mock.patch('sysinv.api.controllers.v1.label._get_system_enabled_k8s_plugins',
mock_get_system_enabled_k8s_plugins_return_plugins)
def test_create_plugin_labels_on_supported_node(self):
dbutils.create_test_pci_devices(
dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass='VGA compatible controller',
driver='i915',)
@ -190,7 +190,7 @@ class LabelAssignTestCase(LabelTestCase):
@mock.patch('sysinv.api.controllers.v1.label._get_system_enabled_k8s_plugins',
mock_get_system_enabled_k8s_plugins_return_plugins)
def test_create_plugin_labels_on_unsupported_node(self):
dbutils.create_test_pci_devices(
dbutils.create_test_pci_device(
host_id=self.worker.id,
pclass='VGA compatible controller',
driver='',)

View File

@ -1468,6 +1468,8 @@ class ManagerTestCase(base.DbTestCase):
'psdevice': 'qat',
'sriov_totalvfs': 32,
'sriov_numvfs': 4,
'sriov_vf_driver': None,
'sriov_vf_pdevice_id': '0450',
'sriov_vfs_pci_address': '',
'driver': ''}]
self.service.pci_device_update_by_host(self.context, host_uuid, pci_dev_dict_update1)
@ -1488,6 +1490,8 @@ class ManagerTestCase(base.DbTestCase):
'psdevice': 'qat',
'sriov_totalvfs': 32,
'sriov_numvfs': 4,
'sriov_vf_driver': None,
'sriov_vf_pdevice_id': '0450',
'sriov_vfs_pci_address': '',
'driver': '',
'uuid': 1122}]

View File

@ -2001,3 +2001,16 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
for col, coltype in fpga_ports_cols.items():
self.assertTrue(isinstance(fpga_ports.c[col].type,
getattr(sqlalchemy.types, coltype)))
def _check_107(self, engine, data):
# 107_device_vf_attrs.py
# Assert data types for new columns in device table
devices = db_utils.get_table(engine, 'pci_devices')
devices_col = {
'sriov_vf_driver': 'String',
'sriov_vf_pdevice_id': 'String'
}
for col, coltype in devices_col.items():
self.assertTrue(isinstance(devices.c[col].type,
getattr(sqlalchemy.types, coltype)))

View File

@ -142,7 +142,7 @@ class DbNodeTestCase(base.DbTestCase):
forihostid = n['id']
d = self.dbapi.pci_device_create(forihostid,
utils.get_test_pci_devices())
utils.get_test_pci_device())
self.assertEqual(n['id'], d['host_id'])
def test_create_storageVolume_on_a_server(self):

View File

@ -1294,8 +1294,8 @@ def create_test_app(**kw):
return dbapi.kube_app_create(app_data)
def get_test_pci_devices(**kw):
pci_devices = {
def get_test_pci_device(**kw):
pci_device = {
'id': kw.get('id', 2345),
'host_id': kw.get('host_id', 2),
'name': kw.get('name', 'pci_0000_00_02_0 '),
@ -1308,23 +1308,28 @@ def get_test_pci_devices(**kw):
'pdevice': kw.get('pdevice', 'Iris Plus Graphics 655'),
'numa_node': kw.get('numa_node', -1),
'enabled': kw.get('enabled', True),
'driver': kw.get('driver', '')
'driver': kw.get('driver', None),
'sriov_totalvfs': kw.get('sriov_totalvfs', None),
'sriov_numvfs': kw.get('sriov_numvfs', 0),
'sriov_vfs_pci_address': kw.get('sriov_vfs_pci_address', ''),
'sriov_vf_driver': kw.get('sriov_vf_driver', None),
'sriov_vf_pdevice_id': kw.get('sriov_vf_pdevice_id', None)
}
return pci_devices
return pci_device
def create_test_pci_devices(**kw):
def create_test_pci_device(**kw):
"""Create test pci devices entry in DB and return PciDevice DB object.
Function to be used to create test pci device objects in the database.
:param kw: kwargs with overriding values for pci device attributes.
:returns: Test PciDevice DB object.
"""
pci_devices = get_test_pci_devices(**kw)
pci_device = get_test_pci_device(**kw)
# Let DB generate ID if it isn't specified explicitly
if 'id' not in kw:
del pci_devices['id']
del pci_device['id']
dbapi = db_api.get_instance()
return dbapi.pci_device_create(pci_devices['host_id'], pci_devices)
return dbapi.pci_device_create(pci_device['host_id'], pci_device)
def get_test_fpga_device(**kw):

View File

@ -1118,10 +1118,23 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
if vf_addrs is None:
vf_addrs = []
config = {'ifname': ifname,
'pf_addr': pf_addr if pf_addr else self.port['pciaddr'],
'addr': pf_addr if pf_addr else self.port['pciaddr'],
'num_vfs': num_vfs,
'vf_driver': vf_driver,
'vf_addrs': vf_addrs}
'vf_config': {}}
if vf_addrs:
for addr in vf_addrs:
vf_config = config['vf_config']
if addr in vf_config.keys():
vf_config.update({addr: {
'addr': addr,
'driver': vf_driver
}})
else:
vf_config[addr] = {
'addr': addr,
'driver': vf_driver
}
return config
def _get_loopback_config(self):

View File

@ -8,6 +8,7 @@ import re
import uuid
from sysinv.common import constants
from sysinv.common import device as dconstants
from sysinv.puppet import interface
from sysinv.tests.db import base as dbbase
from sysinv.tests.db import utils as dbutils
@ -29,7 +30,11 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
host_id=self.host.id,
label_key=sriovdp_key,
label_value=sriovdp_val)
self.port = None
self.iface = None
self.device = None
def _setup_iface_configuration(self):
# Setup a single port/SR-IOV interface
self.port, self.iface = self._create_ethernet_test(
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
@ -47,11 +52,25 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
dbutils.create_test_interface_datanetwork(
interface_id=self.iface.id, datanetwork_id=self.datanetwork.id)
def _setup_fpga_configuration(self):
# Setup a single FPGA FEC device
self.device = dbutils.create_test_pci_device(
host_id=self.host.id,
pclass_id='030000',
pvendor_id='80ee',
pdevice_id='beef',
sriov_totalvfs=64
)
def _update_context(self):
# ensure DB entries are updated prior to updating the context which
# will re-read the entries from the DB.
if self.port:
self.port.save(self.admin_context)
if self.iface:
self.iface.save(self.admin_context)
if self.device:
self.device.save(self.admin_context)
super(SriovdpTestCase, self)._update_context()
def _get_pcidp_vendor_id(self, port):
@ -72,15 +91,35 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
self.port['sriov_vf_pdevice_id'] = config['vf_device']
self._update_context()
def _generate_sriovdp_config(self):
return self.operator.kubernetes._get_host_pcidp_config(self.host) # pylint: disable=no-member
def _update_sriov_fpga_config(self, config):
# Update the SR-IOV port config with NIC specific information
self.device['pclass_id'] = config['pf_class_id']
self.device['pvendor_id'] = config['pf_vendor_id']
self.device['pdevice_id'] = config['pf_device_id']
self.device['driver'] = config['pf_driver']
self.device['sriov_vf_driver'] = config['vf_driver']
self.device['sriov_vf_pdevice_id'] = config['vf_device_id']
self._update_context()
def _get_sriovdp_config(self, datanetwork, vf_vendor, vf_device,
vf_driver, pfName):
def _get_sriovdp_fpga_config(self, vf_vendor, vf_device,
vf_driver):
name = "intel_fpga_fec"
config = [{
"resourceName": name,
"deviceType": "accelerator",
"selectors": {
"vendors": ["{}".format(vf_vendor)],
"devices": ["{}".format(vf_device)],
"drivers": ["{}".format(vf_driver)]
}
}]
return config
def _get_sriovdp_iface_config(self, vf_vendor, vf_device,
vf_driver, pfName, datanetwork):
datanetwork = datanetwork.replace("-", "_")
sriovdp_config = {
"resourceList": [
{
config = [{
"resourceName": 'pci_sriov_net_{}'.format(datanetwork),
"selectors": {
"vendors": ["{}".format(vf_vendor)],
@ -88,21 +127,36 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
"drivers": ["{}".format(vf_driver)],
"pfNames": ["{}".format(pfName)]
}
}
]
}
}]
if interface.is_a_mellanox_device(self.context, self.iface):
sriovdp_config['resourceList'][0]['selectors']['isRdma'] = True
config[0]['selectors']['isRdma'] = True
return config
def _generate_sriovdp_config(self):
return self.operator.kubernetes._get_host_pcidp_config(self.host) # pylint: disable=no-member
def _get_sriovdp_config(self, vf_vendor, vf_device,
vf_driver, pfName=None, datanetwork=None):
iface_config = []
if datanetwork:
iface_config = self._get_sriovdp_iface_config(
vf_vendor, vf_device, vf_driver, pfName, datanetwork)
fpga_config = []
if vf_device == dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_VF:
fpga_config = self._get_sriovdp_fpga_config(
vf_vendor, vf_device, vf_driver)
config = {
"platform::kubernetes::worker::pci::pcidp_network_resources":
json.dumps(sriovdp_config)
"platform::kubernetes::worker::pci::pcidp_resources":
json.dumps({'resourceList': iface_config + fpga_config})
}
return config
def test_generate_sriovdp_config_8086(self):
self._setup_iface_configuration()
test_config = {
'pf_vendor': 'Intel Corporation [8086]',
'pf_device': '10fd',
@ -114,16 +168,17 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
actual = self._generate_sriovdp_config()
expected = self._get_sriovdp_config(
self.datanetwork['name'],
self._get_pcidp_vendor_id(self.port),
test_config['vf_device'],
test_config['vf_driver'],
self.port['name']
pfName=self.port['name'],
datanetwork=self.datanetwork['name']
)
self.assertEqual(expected, actual)
def test_generate_sriovdp_config_mlx(self):
self._setup_iface_configuration()
test_config = {
'pf_vendor': 'Mellanox Technologies [15b3]',
'pf_device': '1015',
@ -135,23 +190,65 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
actual = self._generate_sriovdp_config()
expected = self._get_sriovdp_config(
self.datanetwork['name'],
self._get_pcidp_vendor_id(self.port),
test_config['vf_device'],
test_config['vf_driver'],
self.port['name']
pfName=self.port['name'],
datanetwork=self.datanetwork['name']
)
self.assertEqual(expected, actual)
def test_generate_sriovdp_config_invalid(self):
self._setup_iface_configuration()
self.iface['ifclass'] = constants.INTERFACE_CLASS_DATA
self.iface['networktype'] = constants.NETWORK_TYPE_DATA
self._update_context()
actual = self._generate_sriovdp_config()
expected = {
"platform::kubernetes::worker::pci::pcidp_network_resources":
"platform::kubernetes::worker::pci::pcidp_resources":
json.dumps({"resourceList": []})
}
self.assertEqual(expected, actual)
def test_generate_sriovdp_config_fpga_fec(self):
self._setup_fpga_configuration()
test_config = {
'pf_class_id': dconstants.PCI_DEVICE_CLASS_FPGA,
'pf_vendor_id': dconstants.PCI_DEVICE_VENDOR_INTEL,
'pf_device_id': dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_PF,
'pf_driver': 'igb_uio',
'vf_device_id': dconstants.PCI_DEVICE_ID_FPGA_INTEL_5GNR_FEC_VF,
'vf_driver': 'igb_uio'
}
self._update_sriov_fpga_config(test_config)
actual = self._generate_sriovdp_config()
expected = self._get_sriovdp_config(
test_config['pf_vendor_id'],
test_config['vf_device_id'],
test_config['vf_driver']
)
self.assertEqual(expected, actual)
def test_generate_sriovdp_config_fpga_unsupported(self):
self._setup_fpga_configuration()
test_config = {
'pf_class_id': 'AAAA',
'pf_vendor_id': 'BBBB',
'pf_device_id': 'CCCC',
'pf_driver': 'igb_uio',
'vf_device_id': 'DDDD',
'vf_driver': 'igb_uio'
}
self._update_sriov_fpga_config(test_config)
actual = self._generate_sriovdp_config()
expected = {
"platform::kubernetes::worker::pci::pcidp_resources":
json.dumps({'resourceList': []})
}
self.assertEqual(expected, actual)