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:
parent
a24a646134
commit
ff41cd29dc
|
@ -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)
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
rpc_device[field] = getattr(device, 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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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.')
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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'])
|
|
@ -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',)
|
||||
|
|
|
@ -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',)
|
||||
|
|
|
@ -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='',)
|
||||
|
|
|
@ -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}]
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
self.port.save(self.admin_context)
|
||||
self.iface.save(self.admin_context)
|
||||
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,37 +91,72 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
|
|||
self.port['sriov_vf_pdevice_id'] = config['vf_device']
|
||||
self._update_context()
|
||||
|
||||
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_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("-", "_")
|
||||
config = [{
|
||||
"resourceName": 'pci_sriov_net_{}'.format(datanetwork),
|
||||
"selectors": {
|
||||
"vendors": ["{}".format(vf_vendor)],
|
||||
"devices": ["{}".format(vf_device)],
|
||||
"drivers": ["{}".format(vf_driver)],
|
||||
"pfNames": ["{}".format(pfName)]
|
||||
}
|
||||
}]
|
||||
if interface.is_a_mellanox_device(self.context, self.iface):
|
||||
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, datanetwork, vf_vendor, vf_device,
|
||||
vf_driver, pfName):
|
||||
datanetwork = datanetwork.replace("-", "_")
|
||||
sriovdp_config = {
|
||||
"resourceList": [
|
||||
{
|
||||
"resourceName": 'pci_sriov_net_{}'.format(datanetwork),
|
||||
"selectors": {
|
||||
"vendors": ["{}".format(vf_vendor)],
|
||||
"devices": ["{}".format(vf_device)],
|
||||
"drivers": ["{}".format(vf_driver)],
|
||||
"pfNames": ["{}".format(pfName)]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
def _get_sriovdp_config(self, vf_vendor, vf_device,
|
||||
vf_driver, pfName=None, datanetwork=None):
|
||||
|
||||
if interface.is_a_mellanox_device(self.context, self.iface):
|
||||
sriovdp_config['resourceList'][0]['selectors']['isRdma'] = True
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue