Integration with latest SR-IOV CNI images

As part of the ongoing development of the sriov-cni and
sriov-device-plugin, the DPDK NetworkAttachmentDefinition
configuration options have been deprecated.

Previously, we used this functionality to have the sriov-cni
plugin perform the device bind from netdevice (kernel) to
vfio (userspace), and simply set sriov-device-plugin
deviceType configuration parameter to 'netdevice'.

Going forward, we must add a mechanism for a user to define
the deviceType at the interface configuration level.  This
means an SR-IOV enabled device can no longer have a mix of
netdevice, vfio chosen by the NeworkAttachmentDefinition.
That is, it must be determined by the user beforehand which
type of virtual function driver (kernel or DPDK) a device's
VFs should have.

This commit includes the cgtsclient, API, DB and puppet
related changes required for a user to set the VF driver type.

In terms of the cgts-client, the following parameter has been
added: --vf-driver.  Example usage for a device intended to
be used with a DPDK application is as follows:

system host-if-modify -m 1500 -n sriov0 -d ${DATANET} \
  -c pci-sriov -N ${NUM_VFS} --vf-driver=vfio ${WORKER_NAME} \
  ${INTERFACE_UUID}

If the user does not specify a vf-driver, the default device
type will remain as it is today as 'netdevice'.  The user can
also choose to explicitly set the --vf-driver to 'netdevice'
for the same effect.  In this case, a check is made to ensure
the VF driver has been detected and reported by the sysinv
agent.

Story: 2005208
Task: 33485
Closes-Bug: 1829565
Change-Id: I8f6f27b79c7fafa03873e71473f7694991142e50
Signed-off-by: Steven Webster <steven.webster@windriver.com>
This commit is contained in:
Steven Webster 2019-05-27 14:25:28 -05:00
parent 8460a3f046
commit 13b142ff8b
25 changed files with 329 additions and 18 deletions

View File

@ -110,13 +110,32 @@ class platform::addresses (
create_resources('network_address', $address_config, {}) create_resources('network_address', $address_config, {})
} }
define platform::interfaces::sriov_config(
$vf_addrs,
$vf_driver = undef
) {
if $vf_driver != undef {
exec { "load ${vf_driver}":
command => "modprobe ${vf_driver}",
path => '/bin:/sbin:/usr/bin:/usr/sbin',
unless => "egrep -q '^${vf_driver} ' /proc/modules",
logoutput => true
}
-> exec { "sriov-vf-bind-device: ${title}":
command => template('platform/sriov.bind-device.erb'),
logoutput => true
}
}
}
class platform::interfaces ( class platform::interfaces (
$network_config = {}, $network_config = {},
$route_config = {}, $route_config = {},
$sriov_config = {}
) { ) {
create_resources('network_config', $network_config, {}) create_resources('network_config', $network_config, {})
create_resources('network_route', $route_config, {}) create_resources('network_route', $route_config, {})
create_resources('platform::interfaces::sriov_config', $sriov_config, {})
} }
class platform::network::apply { class platform::network::apply {

View File

@ -0,0 +1,3 @@
<%- @vf_addrs.each_with_index do |addr, idx| -%>
/usr/share/openvswitch/scripts/dpdk-devbind.py --bind=<%= @vf_driver -%> <%= addr %>
<%- end -%>

View File

@ -22,7 +22,7 @@ def _print_iinterface_show(cc, iinterface):
'aemode', 'schedpolicy', 'txhashpolicy', 'aemode', 'schedpolicy', 'txhashpolicy',
'uuid', 'ihost_uuid', 'uuid', 'ihost_uuid',
'vlan_id', 'uses', 'used_by', 'vlan_id', 'uses', 'used_by',
'created_at', 'updated_at', 'sriov_numvfs'] 'created_at', 'updated_at', 'sriov_numvfs', 'sriov_vf_driver']
optional_fields = ['ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool'] optional_fields = ['ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool']
rename_fields = [{'field': 'dpdksupport', 'label': 'accelerated'}] rename_fields = [{'field': 'dpdksupport', 'label': 'accelerated'}]
network_names = "" network_names = ""
@ -272,13 +272,18 @@ def do_host_if_add(cc, args):
dest='sriov_numvfs', dest='sriov_numvfs',
metavar='<sriov numvfs>', metavar='<sriov numvfs>',
help='The number of SR-IOV VFs of the interface') help='The number of SR-IOV VFs of the interface')
@utils.arg('--vf-driver',
dest='sriov_vf_driver',
metavar='<sriov vf driver>',
choices=['netdevice', 'vfio'],
help='The SR-IOV VF driver for this device')
def do_host_if_modify(cc, args): def do_host_if_modify(cc, args):
"""Modify interface attributes.""" """Modify interface attributes."""
rwfields = ['iftype', 'ifname', 'imtu', 'aemode', 'txhashpolicy', rwfields = ['iftype', 'ifname', 'imtu', 'aemode', 'txhashpolicy',
'datanetworks', 'providernetworks', 'ports', 'ifclass', 'networks', 'datanetworks', 'providernetworks', 'ports', 'ifclass', 'networks',
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool', 'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs'] 'sriov_numvfs', 'sriov_vf_driver']
ihost = ihost_utils._find_ihost(cc, args.hostnameorid) ihost = ihost_utils._find_ihost(cc, args.hostnameorid)

View File

@ -19,8 +19,8 @@ def _print_port_show(port):
fields = ['name', 'namedisplay', fields = ['name', 'namedisplay',
'type', 'pciaddr', 'dev_id', 'numa_node', 'type', 'pciaddr', 'dev_id', 'numa_node',
'sriov_totalvfs', 'sriov_numvfs', 'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'driver', 'sriov_vfs_pci_address', 'sriov_vf_driver',
'pclass', 'pvendor', 'pdevice', 'driver', 'pclass', 'pvendor', 'pdevice',
'capabilities', 'capabilities',
'uuid', 'host_uuid', 'interface_uuid', 'uuid', 'host_uuid', 'interface_uuid',
'dpdksupport', 'dpdksupport',
@ -28,8 +28,8 @@ def _print_port_show(port):
labels = ['name', 'namedisplay', labels = ['name', 'namedisplay',
'type', 'pciaddr', 'dev_id', 'processor', 'type', 'pciaddr', 'dev_id', 'processor',
'sriov_totalvfs', 'sriov_numvfs', 'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'driver', 'sriov_vfs_pci_address', 'sriov_vf_driver',
'pclass', 'pvendor', 'pdevice', 'driver', 'pclass', 'pvendor', 'pdevice',
'capabilities', 'capabilities',
'uuid', 'host_uuid', 'interface_uuid', 'uuid', 'host_uuid', 'interface_uuid',
'accelerated', 'accelerated',

View File

@ -162,6 +162,7 @@
</xs:element> </xs:element>
</xs:sequence> </xs:sequence>
<xs:attribute name="virtualFunctions" type="xs:nonNegativeInteger" use="required" /> <xs:attribute name="virtualFunctions" type="xs:nonNegativeInteger" use="required" />
<xs:attribute name="virtualFunctionDriver" type="xs:string" />
</xs:complexType> </xs:complexType>
<xs:complexType name="route"> <xs:complexType name="route">

View File

@ -613,6 +613,7 @@ class AgentManager(service.PeriodicService):
'sriov_totalvfs': port.sriov_totalvfs, 'sriov_totalvfs': port.sriov_totalvfs,
'sriov_numvfs': port.sriov_numvfs, 'sriov_numvfs': port.sriov_numvfs,
'sriov_vfs_pci_address': port.sriov_vfs_pci_address, 'sriov_vfs_pci_address': port.sriov_vfs_pci_address,
'sriov_vf_driver': port.sriov_vf_driver,
'driver': port.driver, 'driver': port.driver,
'mac': port.mac, 'mac': port.mac,
'mtu': port.mtu, 'mtu': port.mtu,

View File

@ -127,6 +127,7 @@ class Port:
self.sriov_totalvfs = kwargs.get('sriov_totalvfs') self.sriov_totalvfs = kwargs.get('sriov_totalvfs')
self.sriov_numvfs = kwargs.get('sriov_numvfs') self.sriov_numvfs = kwargs.get('sriov_numvfs')
self.sriov_vfs_pci_address = kwargs.get('sriov_vfs_pci_address') self.sriov_vfs_pci_address = kwargs.get('sriov_vfs_pci_address')
self.sriov_vf_driver = kwargs.get('sriov_vf_driver')
self.driver = kwargs.get('driver') self.driver = kwargs.get('driver')
self.dpdksupport = kwargs.get('dpdksupport') self.dpdksupport = kwargs.get('dpdksupport')
@ -234,6 +235,31 @@ class PCIOperator(object):
LOG.debug("sriov_vfs_pci_address: %s" % sriov_vfs_pci_address) LOG.debug("sriov_vfs_pci_address: %s" % sriov_vfs_pci_address)
return sriov_vfs_pci_address return sriov_vfs_pci_address
def get_pci_sriov_vf_driver_name(self, pciaddr, sriov_vfs_pci_address):
vf_driver = None
for addr in sriov_vfs_pci_address:
try:
with open(os.devnull, "w") as fnull:
output = subprocess.check_output(['lspci', '-vmmks', addr],
stderr=fnull)
except Exception as e:
LOG.error("Error getting PCI data for SR-IOV "
"VF address %s: %s", addr, e)
continue
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]):
vf_driver = pci_attr[1]
break
# All VFs have the same driver per device.
if vf_driver:
break
return vf_driver
def get_pci_driver_name(self, pciaddr): def get_pci_driver_name(self, pciaddr):
ddriver = '/sys/bus/pci/devices/' + pciaddr + '/driver/module/drivers' ddriver = '/sys/bus/pci/devices/' + pciaddr + '/driver/module/drivers'
try: try:
@ -443,6 +469,7 @@ class PCIOperator(object):
sriov_totalvfs = self.get_pci_sriov_totalvfs(a) sriov_totalvfs = self.get_pci_sriov_totalvfs(a)
sriov_numvfs = self.get_pci_sriov_numvfs(a) sriov_numvfs = self.get_pci_sriov_numvfs(a)
sriov_vfs_pci_address = self.get_pci_sriov_vfs_pci_address(a, sriov_numvfs) 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) driver = self.get_pci_driver_name(a)
# Determine DPDK support # Determine DPDK support
@ -597,6 +624,7 @@ class PCIOperator(object):
"sriov_numvfs": sriov_numvfs, "sriov_numvfs": sriov_numvfs,
"sriov_vfs_pci_address": "sriov_vfs_pci_address":
','.join(str(x) for x in sriov_vfs_pci_address), ','.join(str(x) for x in sriov_vfs_pci_address),
"sriov_vf_driver": sriov_vf_driver,
"driver": driver, "driver": driver,
"pci_address": a, "pci_address": a,
"mac": mac, "mac": mac,

View File

@ -103,6 +103,9 @@ class EthernetPort(base.APIBase):
sriov_vfs_pci_address = wtypes.text sriov_vfs_pci_address = wtypes.text
"The PCI Addresses of the VFs" "The PCI Addresses of the VFs"
sriov_vf_driver = wtypes.text
"The SR-IOV VF driver for this device"
driver = wtypes.text driver = wtypes.text
"The kernel driver for this device" "The kernel driver for this device"
@ -170,8 +173,8 @@ class EthernetPort(base.APIBase):
'pclass', 'pvendor', 'pdevice', 'pclass', 'pvendor', 'pdevice',
'psvendor', 'psdevice', 'numa_node', 'psvendor', 'psdevice', 'numa_node',
'mac', 'sriov_totalvfs', 'sriov_numvfs', 'mac', 'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'driver', 'sriov_vfs_pci_address', 'sriov_vf_driver',
'mtu', 'speed', 'link_mode', 'driver', 'mtu', 'speed', 'link_mode',
'duplex', 'autoneg', 'bootp', 'duplex', 'autoneg', 'bootp',
'capabilities', 'capabilities',
'host_uuid', 'interface_uuid', 'host_uuid', 'interface_uuid',

View File

@ -3174,6 +3174,14 @@ class HostController(rest.RestController):
host['uuid']) host['uuid'])
raise wsme.exc.ClientSideError(msg) raise wsme.exc.ClientSideError(msg)
for p in ports:
if (interface.sriov_vf_driver == constants.SRIOV_DRIVER_TYPE_NETDEVICE and
p.sriov_vf_driver is None):
msg = (_("Value for SR-IOV VF driver is 'netdevice', but "
"corresponding port has an invalid driver"))
LOG.info(msg)
raise wsme.exc.ClientSideError(msg)
def _semantic_check_unlock_upgrade(self, ihost, force_unlock=False): def _semantic_check_unlock_upgrade(self, ihost, force_unlock=False):
""" """
Perform semantic checks related to upgrades prior to unlocking host. Perform semantic checks related to upgrades prior to unlocking host.

View File

@ -195,6 +195,9 @@ class Interface(base.APIBase):
sriov_numvfs = int sriov_numvfs = int
"The number of configured SR-IOV VFs" "The number of configured SR-IOV VFs"
sriov_vf_driver = wtypes.text
"The driver of configured SR-IOV VFs"
networks = [wtypes.text] networks = [wtypes.text]
"Represent the networks of the interface" "Represent the networks of the interface"
@ -223,7 +226,7 @@ class Interface(base.APIBase):
'aemode', 'schedpolicy', 'txhashpolicy', 'aemode', 'schedpolicy', 'txhashpolicy',
'vlan_id', 'uses', 'usesmodify', 'used_by', 'vlan_id', 'uses', 'usesmodify', 'used_by',
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool', 'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs', 'sriov_numvfs', 'sriov_vf_driver',
'datanetworks']) 'datanetworks'])
# never expose the ihost_id attribute # never expose the ihost_id attribute
@ -526,12 +529,16 @@ class InterfaceController(rest.RestController):
temp_interface['ifclass'] = p['value'] temp_interface['ifclass'] = p['value']
elif '/sriov_numvfs' == p['path']: elif '/sriov_numvfs' == p['path']:
temp_interface['sriov_numvfs'] = p['value'] temp_interface['sriov_numvfs'] = p['value']
elif '/sriov_vf_driver' == p['path']:
temp_interface['sriov_vf_driver'] = p['value']
# If network type is not pci-sriov, reset the sriov-numvfs to zero # If network type is not pci-sriov, reset the sriov-numvfs to zero
if (temp_interface['sriov_numvfs'] is not None and if (temp_interface['sriov_numvfs'] is not None and
temp_interface['ifclass'] is not None and temp_interface['ifclass'] is not None and
temp_interface[ temp_interface[
'ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV): 'ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV):
temp_interface['sriov_numvfs'] = None temp_interface['sriov_numvfs'] = None
temp_interface['sriov_vf_driver'] = None
sriov_update = _check_interface_sriov(temp_interface.as_dict(), ihost) sriov_update = _check_interface_sriov(temp_interface.as_dict(), ihost)
@ -591,6 +598,7 @@ class InterfaceController(rest.RestController):
# specific fields are reset as well # specific fields are reset as well
interface['networktype'] = None interface['networktype'] = None
interface['sriov_numvfs'] = 0 interface['sriov_numvfs'] = 0
interface['sriov_vf_driver'] = None
interface['ipv4_mode'] = None interface['ipv4_mode'] = None
interface['ipv6_mode'] = None interface['ipv6_mode'] = None
delete_addressing = True delete_addressing = True
@ -897,7 +905,8 @@ def _set_defaults(interface):
'aemode': 'active_standby', 'aemode': 'active_standby',
'txhashpolicy': None, 'txhashpolicy': None,
'vlan_id': None, 'vlan_id': None,
'sriov_numvfs': 0} 'sriov_numvfs': 0,
'sriov_vf_driver': None}
networktypelist = [] networktypelist = []
if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM: if interface['ifclass'] == constants.INTERFACE_CLASS_PLATFORM:
@ -1034,6 +1043,14 @@ def _check_interface_sriov(interface, ihost, from_profile=False):
"but interface class is not " "but interface class is not "
"pci-sriov.")) "pci-sriov."))
if ('sriov_vf_driver' in interface.keys() and interface['sriov_vf_driver']
is not None and
('ifclass' not in interface.keys() or
interface['ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV)):
raise wsme.exc.ClientSideError(_("SR-IOV VF driver is specified "
"but interface class is not "
"pci-sriov."))
if ('ifclass' in interface.keys() and if ('ifclass' in interface.keys() and
interface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV and interface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV and
'sriov_numvfs' in interface.keys()): 'sriov_numvfs' in interface.keys()):
@ -1047,6 +1064,12 @@ def _check_interface_sriov(interface, ihost, from_profile=False):
if interface['sriov_numvfs'] <= 0: if interface['sriov_numvfs'] <= 0:
raise wsme.exc.ClientSideError(_("Value for number of SR-IOV VFs must be > 0.")) raise wsme.exc.ClientSideError(_("Value for number of SR-IOV VFs must be > 0."))
if interface['sriov_vf_driver'] is not None:
if interface['sriov_vf_driver'] not in constants.SRIOV_DRIVER_TYPES:
msg = (_("Value for SR-IOV VF driver must be one of "
"{}").format(', '.join(constants.SRIOV_DRIVER_TYPES)))
raise wsme.exc.ClientSideError(msg)
ports = pecan.request.dbapi.ethernet_port_get_all(hostid=ihost['id']) ports = pecan.request.dbapi.ethernet_port_get_all(hostid=ihost['id'])
port_list = [ port_list = [
(p.name, p.sriov_totalvfs, p.driver) for p in ports (p.name, p.sriov_totalvfs, p.driver) for p in ports
@ -1062,10 +1085,10 @@ def _check_interface_sriov(interface, ihost, from_profile=False):
if int(interface['sriov_numvfs']) > sriov_totalvfs: if int(interface['sriov_numvfs']) > sriov_totalvfs:
raise wsme.exc.ClientSideError(_("The interface support a maximum of %s VFs" % sriov_totalvfs)) raise wsme.exc.ClientSideError(_("The interface support a maximum of %s VFs" % sriov_totalvfs))
driver = port_list[0][2] driver = port_list[0][2]
if driver is None or not driver: if driver is None or not driver:
raise wsme.exc.ClientSideError(_("Corresponding port has invalid driver")) raise wsme.exc.ClientSideError(_("Corresponding port has invalid driver"))
sriov_update = True sriov_update = True
return sriov_update return sriov_update

View File

@ -101,6 +101,9 @@ class Port(base.APIBase):
sriov_vfs_pci_address = wtypes.text sriov_vfs_pci_address = wtypes.text
"The PCI Addresses of the VFs" "The PCI Addresses of the VFs"
sriov_vf_driver = wtypes.text
"The SR-IOV VF driver for this device"
driver = wtypes.text driver = wtypes.text
"The kernel driver for this device" "The kernel driver for this device"
@ -150,8 +153,8 @@ class Port(base.APIBase):
'pclass', 'pvendor', 'pdevice', 'pclass', 'pvendor', 'pdevice',
'psvendor', 'psdevice', 'numa_node', 'psvendor', 'psdevice', 'numa_node',
'sriov_totalvfs', 'sriov_numvfs', 'sriov_totalvfs', 'sriov_numvfs',
'sriov_vfs_pci_address', 'driver', 'sriov_vfs_pci_address', 'sriov_vf_driver',
'capabilities', 'driver', 'capabilities',
'host_uuid', 'interface_uuid', 'host_uuid', 'interface_uuid',
'node_uuid', 'dpdksupport', 'node_uuid', 'dpdksupport',
'created_at', 'updated_at']) 'created_at', 'updated_at'])

View File

@ -78,7 +78,7 @@ INTERFACE_PROFILE_FIELDS = ['ifname', 'iftype', 'imtu', 'networktype',
'txhashpolicy', 'forihostid', 'datanetworks', 'txhashpolicy', 'forihostid', 'datanetworks',
'vlan_id', 'ipv4_mode', 'ipv6_mode', 'vlan_id', 'ipv4_mode', 'ipv6_mode',
'ipv4_pool', 'ipv6_pool', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs'] 'sriov_numvfs', 'sriov_vf_driver']
class Profile(base.APIBase): class Profile(base.APIBase):
@ -1353,6 +1353,7 @@ def _create_if_profile(profile_name, profile_node):
'ipv4_pool': ipv4_mode['pool'], 'ipv4_pool': ipv4_mode['pool'],
'ipv6_pool': ipv6_mode['pool'], 'ipv6_pool': ipv6_mode['pool'],
'sriov_numvfs': ethIf.virtualFunctions, 'sriov_numvfs': ethIf.virtualFunctions,
'sriov_vf_driver': ethIf.virtualFunctionDriver,
'interface_profile': True 'interface_profile': True
} }
newIf = interface_api._create(idict, from_profile=True) newIf = interface_api._create(idict, from_profile=True)
@ -1394,6 +1395,7 @@ def _create_if_profile(profile_name, profile_node):
'ipv6_pool': ipv6_pool, 'ipv6_pool': ipv6_pool,
'imtu': aeIf.mtu, 'imtu': aeIf.mtu,
'sriov_numvfs': ethIf.virtualFunctions, 'sriov_numvfs': ethIf.virtualFunctions,
'sriov_vf_driver': ethIf.virtualFunctionDriver,
'interface_profile': True 'interface_profile': True
} }
@ -1421,6 +1423,7 @@ def _create_if_profile(profile_name, profile_node):
'ipv6_pool': ipv6_pool, 'ipv6_pool': ipv6_pool,
'imtu': vlanIf.mtu, 'imtu': vlanIf.mtu,
'sriov_numvfs': ethIf.virtualFunctions, 'sriov_numvfs': ethIf.virtualFunctions,
'sriov_vf_driver': ethIf.virtualFunctionDriver,
'interface_profile': True 'interface_profile': True
} }

View File

@ -135,6 +135,7 @@ class PciSriov(Network):
def __init__(self, node): def __init__(self, node):
super(PciSriov, self).__init__(node, constants.NETWORK_TYPE_PCI_SRIOV) super(PciSriov, self).__init__(node, constants.NETWORK_TYPE_PCI_SRIOV)
self.virtualFunctions = int(node.get('virtualFunctions')) self.virtualFunctions = int(node.get('virtualFunctions'))
self.virtualFunctionDriver = node.get('virtualFunctionDriver')
class Interface(object): class Interface(object):
@ -148,6 +149,7 @@ class Interface(object):
self.ipv6Mode = {'mode': None, 'pool': None} self.ipv6Mode = {'mode': None, 'pool': None}
self.routes = [] self.routes = []
self.virtualFunctions = 0 self.virtualFunctions = 0
self.virtualFunctionDriver = None
networksNode = ifNode.find('networks') networksNode = ifNode.find('networks')
if networksNode is not None: if networksNode is not None:
for netNode in networksNode: for netNode in networksNode:
@ -168,6 +170,7 @@ class Interface(object):
self.routes = network.routes self.routes = network.routes
elif network.networkType == constants.NETWORK_TYPE_PCI_SRIOV: elif network.networkType == constants.NETWORK_TYPE_PCI_SRIOV:
self.virtualFunctions = network.virtualFunctions self.virtualFunctions = network.virtualFunctions
self.virtualFunctionDriver = network.virtualFunctionDriver
if isinstance(network, Network): if isinstance(network, Network):
self.providerNetworks = network.providerNetworks self.providerNetworks = network.providerNetworks

View File

@ -633,6 +633,11 @@ PLATFORM_NETWORK_TYPES = [NETWORK_TYPE_PXEBOOT,
PCI_NETWORK_TYPES = [NETWORK_TYPE_PCI_PASSTHROUGH, PCI_NETWORK_TYPES = [NETWORK_TYPE_PCI_PASSTHROUGH,
NETWORK_TYPE_PCI_SRIOV] NETWORK_TYPE_PCI_SRIOV]
SRIOV_DRIVER_TYPE_VFIO = 'vfio'
SRIOV_DRIVER_TYPE_NETDEVICE = 'netdevice'
SRIOV_DRIVER_TYPES = [SRIOV_DRIVER_TYPE_VFIO,
SRIOV_DRIVER_TYPE_NETDEVICE]
INTERFACE_TYPE_ETHERNET = 'ethernet' INTERFACE_TYPE_ETHERNET = 'ethernet'
INTERFACE_TYPE_VLAN = 'vlan' INTERFACE_TYPE_VLAN = 'vlan'
INTERFACE_TYPE_AE = 'ae' INTERFACE_TYPE_AE = 'ae'

View File

@ -2031,6 +2031,8 @@ class ConductorManager(service.PeriodicService):
'sriov_numvfs': inic['sriov_numvfs'], 'sriov_numvfs': inic['sriov_numvfs'],
'sriov_vfs_pci_address': 'sriov_vfs_pci_address':
inic['sriov_vfs_pci_address'], inic['sriov_vfs_pci_address'],
'sriov_vf_driver':
inic['sriov_vf_driver'],
'driver': inic['driver'], 'driver': inic['driver'],
'dpdksupport': inic['dpdksupport'], 'dpdksupport': inic['dpdksupport'],
'speed': inic['speed'], 'speed': inic['speed'],

View File

@ -0,0 +1,27 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2019 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
interfaces = Table('interfaces', meta, autoload=True)
interfaces.create_column(Column('sriov_vf_driver', String(255)))
ports = Table('ports', meta, autoload=True)
ports.create_column(Column('sriov_vf_driver', String(255)))
def downgrade(migrate_engine):
# Downgrade is unsupported in this release.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -343,6 +343,7 @@ class Interfaces(Base):
ifcapabilities = Column(JSONEncodedDict) ifcapabilities = Column(JSONEncodedDict)
farend = Column(JSONEncodedDict) farend = Column(JSONEncodedDict)
sriov_numvfs = Column(Integer) sriov_numvfs = Column(Integer)
sriov_vf_driver = Column(String(255))
used_by = relationship( used_by = relationship(
"Interfaces", "Interfaces",
@ -450,6 +451,7 @@ class Ports(Base):
dev_id = Column(Integer) dev_id = Column(Integer)
sriov_totalvfs = Column(Integer) sriov_totalvfs = Column(Integer)
sriov_numvfs = Column(Integer) sriov_numvfs = Column(Integer)
sriov_vf_driver = Column(String(255))
# Each PCI Address is 12 char, 1020 char is enough for 64 devices # Each PCI Address is 12 char, 1020 char is enough for 64 devices
sriov_vfs_pci_address = Column(String(1020)) sriov_vfs_pci_address = Column(String(1020))
driver = Column(String(255)) driver = Column(String(255))

View File

@ -145,7 +145,8 @@ class Interface(base.SysinvObject):
'ipv6_mode': utils.ipv6_mode_or_none, 'ipv6_mode': utils.ipv6_mode_or_none,
'ipv4_pool': utils.uuid_or_none, 'ipv4_pool': utils.uuid_or_none,
'ipv6_pool': utils.uuid_or_none, 'ipv6_pool': utils.uuid_or_none,
'sriov_numvfs': utils.int_or_none 'sriov_numvfs': utils.int_or_none,
'sriov_vf_driver': utils.str_or_none
} }
_foreign_fields = {'uses': _get_interface_name_list, _foreign_fields = {'uses': _get_interface_name_list,

View File

@ -41,6 +41,7 @@ class Port(base.SysinvObject):
'sriov_totalvfs': utils.int_or_none, 'sriov_totalvfs': utils.int_or_none,
'sriov_numvfs': utils.int_or_none, 'sriov_numvfs': utils.int_or_none,
'sriov_vfs_pci_address': utils.str_or_none, 'sriov_vfs_pci_address': utils.str_or_none,
'sriov_vf_driver': utils.str_or_none,
'driver': utils.str_or_none, 'driver': utils.str_or_none,
'capabilities': utils.dict_or_none, 'capabilities': utils.dict_or_none,
} }

View File

@ -19,6 +19,7 @@ from sysinv.conductor import openstack
from sysinv.openstack.common import log from sysinv.openstack.common import log
from sysinv.puppet import base from sysinv.puppet import base
from sysinv.puppet import quoted_str
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -56,6 +57,7 @@ DHCP_METHOD = 'dhcp'
NETWORK_CONFIG_RESOURCE = 'platform::interfaces::network_config' NETWORK_CONFIG_RESOURCE = 'platform::interfaces::network_config'
ROUTE_CONFIG_RESOURCE = 'platform::interfaces::route_config' ROUTE_CONFIG_RESOURCE = 'platform::interfaces::route_config'
SRIOV_CONFIG_RESOURCE = 'platform::interfaces::sriov_config'
ADDRESS_CONFIG_RESOURCE = 'platform::addresses::address_config' ADDRESS_CONFIG_RESOURCE = 'platform::addresses::address_config'
@ -88,6 +90,7 @@ class InterfacePuppet(base.BasePuppet):
NETWORK_CONFIG_RESOURCE: {}, NETWORK_CONFIG_RESOURCE: {},
ROUTE_CONFIG_RESOURCE: {}, ROUTE_CONFIG_RESOURCE: {},
ADDRESS_CONFIG_RESOURCE: {}, ADDRESS_CONFIG_RESOURCE: {},
SRIOV_CONFIG_RESOURCE: {},
} }
system = self._get_system() system = self._get_system()
@ -892,6 +895,38 @@ def get_route_config(route, ifname):
return config return config
def get_sriov_config(context, iface):
vf_driver = iface['sriov_vf_driver']
port = get_interface_port(context, iface)
vf_addr_list = port['sriov_vfs_pci_address']
if not vf_addr_list:
return {}
if vf_driver:
if "vfio" in vf_driver:
vf_driver = "vfio-pci"
elif "netdevice" in vf_driver:
if port['sriov_vf_driver'] is not None:
vf_driver = port['sriov_vf_driver']
else:
# Should not happen, but in this case the vf driver
# will be determined by the kernel. That is,
# no explicit bind will be performed by Puppet.
vf_driver = None
# Format the vf addresses as quoted strings in order to prevent
# puppet from treating the address as a time/date value
vf_addrs = [quoted_str(addr.strip()) for addr in vf_addr_list.split(",")]
config = {
'ifname': iface['ifname'],
'vf_driver': vf_driver,
'vf_addrs': vf_addrs
}
return config
def get_common_network_config(context, iface, config, network_id=None): def get_common_network_config(context, iface, config, network_id=None):
""" """
Augments a basic config dictionary with the attributes specific to an upper Augments a basic config dictionary with the attributes specific to an upper
@ -989,6 +1024,14 @@ def generate_network_config(context, config, iface):
route_config['name']: route_config route_config['name']: route_config
}) })
interface_class = iface['ifclass']
if interface_class == constants.INTERFACE_CLASS_PCI_SRIOV:
sriov_config = get_sriov_config(context, iface)
if sriov_config:
config[SRIOV_CONFIG_RESOURCE].update({
sriov_config['ifname']: format_sriov_config(sriov_config)
})
def find_network_by_pool_uuid(context, pool_uuid): def find_network_by_pool_uuid(context, pool_uuid):
for networktype, network in six.iteritems(context['networks']): for networktype, network in six.iteritems(context['networks']):
@ -1188,3 +1231,13 @@ def format_network_config(config):
network_config = copy.copy(config) network_config = copy.copy(config)
del network_config['ifname'] del network_config['ifname']
return network_config return network_config
def format_sriov_config(config):
"""
Converts a sriov_config resource dictionary to the equivalent puppet
resource definition parameters.
"""
sriov_config = copy.copy(config)
del sriov_config['ifname']
return sriov_config

View File

@ -250,11 +250,15 @@ class KubernetesPuppet(base.BasePuppet):
# Add to the list of pci addreses for this data network # Add to the list of pci addreses for this data network
resource['rootDevices'].append(port['pciaddr']) resource['rootDevices'].append(port['pciaddr'])
else: else:
device_type = iface.get('sriov_vf_driver', None)
if not device_type:
device_type = constants.SRIOV_DRIVER_TYPE_NETDEVICE
# PCI addresses don't exist for this data network yet # PCI addresses don't exist for this data network yet
resource = {dn_name: { resource = {dn_name: {
"resourceName": "{}_net_{}".format( "resourceName": "{}_net_{}".format(
ifclass, dn_name).replace("-", "_"), ifclass, dn_name).replace("-", "_"),
"deviceType": "netdevice", "deviceType": device_type,
"rootDevices": [port['pciaddr']], "rootDevices": [port['pciaddr']],
"sriovMode": "sriovMode":
ifclass == constants.INTERFACE_CLASS_PCI_SRIOV ifclass == constants.INTERFACE_CLASS_PCI_SRIOV

View File

@ -1121,6 +1121,38 @@ class TestPatch(InterfaceTestCase):
self.assertEqual('application/json', response.content_type) self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message']) self.assertTrue(response.json['error_message'])
def _create_sriov_vf_driver_valid(self, vf_driver, expect_errors=False):
interface = dbutils.create_test_interface(forihostid='1',
datanetworks='group0-data0')
dbutils.create_test_ethernet_port(
id=1, name='eth1', host_id=1, interface_id=interface.id,
pciaddr='0000:00:00.11', dev_id=0, sriov_totalvfs=1, sriov_numvfs=1,
driver='i40e',
sriov_vf_driver='i40evf')
response = self.patch_dict_json(
'%s' % self._get_path(interface['uuid']),
networktype=constants.NETWORK_TYPE_PCI_SRIOV,
ifclass=constants.INTERFACE_CLASS_PCI_SRIOV,
sriov_numvfs=1,
sriov_vf_driver=vf_driver,
expect_errors=expect_errors)
self.assertEqual('application/json', response.content_type)
if expect_errors:
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertTrue(response.json['error_message'])
else:
self.assertEqual(http_client.OK, response.status_code)
self.assertEqual(vf_driver, response.json['sriov_vf_driver'])
def test_create_sriov_vf_driver_netdevice_valid(self):
self._create_sriov_vf_driver_valid('netdevice')
def test_create_sriov_vf_driver_vfio_valid(self):
self._create_sriov_vf_driver_valid('vfio')
def test_create_sriov_vf_driver_invalid(self):
self._create_sriov_vf_driver_valid('bad_driver', expect_errors=True)
# No longer requires setting the network type back to none # No longer requires setting the network type back to none
# Expected error: The network type of an interface cannot be changed without # Expected error: The network type of an interface cannot be changed without
# first being reset back to none # first being reset back to none

View File

@ -1894,3 +1894,23 @@ class TestMigrations(BaseMigrationTestCase, WalkVersionsMixin):
for col, coltype in ptp_cols.items(): for col, coltype in ptp_cols.items():
self.assertTrue(isinstance(ptp.c[col].type, self.assertTrue(isinstance(ptp.c[col].type,
getattr(sqlalchemy.types, coltype))) getattr(sqlalchemy.types, coltype)))
def _check_085(self, engine, data):
# 085_sriov_vf_driver.py
# Assert data types for new columns in tables "interfaces" and "ports"
interfaces = db_utils.get_table(engine, 'interfaces')
interfaces_col = {
'sriov_vf_driver': 'String',
}
for col, coltype in interfaces_col.items():
self.assertTrue(isinstance(interfaces.c[col].type,
getattr(sqlalchemy.types, coltype)))
ports = db_utils.get_table(engine, 'ports')
ports_col = {
'sriov_vf_driver': 'String',
}
for col, coltype in ports_col.items():
self.assertTrue(isinstance(ports.c[col].type,
getattr(sqlalchemy.types, coltype)))

View File

@ -563,6 +563,7 @@ def get_test_port(**kw):
'sriov_totalvfs': kw.get('sriov_totalvfs'), 'sriov_totalvfs': kw.get('sriov_totalvfs'),
'sriov_numvfs': kw.get('sriov_numvfs'), 'sriov_numvfs': kw.get('sriov_numvfs'),
'sriov_vfs_pci_address': kw.get('sriov_vfs_pci_address'), 'sriov_vfs_pci_address': kw.get('sriov_vfs_pci_address'),
'sriov_vf_driver': kw.get('sriov_vf_driver'),
'driver': kw.get('driver'), 'driver': kw.get('driver'),
'capabilities': kw.get('capabilities'), 'capabilities': kw.get('capabilities'),
'created_at': kw.get('created_at'), 'created_at': kw.get('created_at'),
@ -604,6 +605,7 @@ def get_test_ethernet_port(**kw):
'dev_id': kw.get('dev_id'), 'dev_id': kw.get('dev_id'),
'sriov_totalvfs': kw.get('sriov_totalvfs'), 'sriov_totalvfs': kw.get('sriov_totalvfs'),
'sriov_numvfs': kw.get('sriov_numvfs'), 'sriov_numvfs': kw.get('sriov_numvfs'),
'sriov_vf_driver': kw.get('sriov_vf_driver'),
'driver': kw.get('driver') 'driver': kw.get('driver')
} }
return ethernet_port return ethernet_port
@ -685,6 +687,7 @@ def post_get_test_interface(**kw):
'ipv4_pool': kw.get('ipv4_pool'), 'ipv4_pool': kw.get('ipv4_pool'),
'ipv6_pool': kw.get('ipv6_pool'), 'ipv6_pool': kw.get('ipv6_pool'),
'sriov_numvfs': kw.get('sriov_numvfs', None), 'sriov_numvfs': kw.get('sriov_numvfs', None),
'sriov_vf_driver': kw.get('sriov_vf_driver', None),
} }
return interface return interface
@ -720,6 +723,7 @@ def get_test_interface(**kw):
'ipv4_pool': kw.get('ipv4_pool'), 'ipv4_pool': kw.get('ipv4_pool'),
'ipv6_pool': kw.get('ipv6_pool'), 'ipv6_pool': kw.get('ipv6_pool'),
'sriov_numvfs': kw.get('sriov_numvfs', None), 'sriov_numvfs': kw.get('sriov_numvfs', None),
'sriov_vf_driver': kw.get('sriov_vf_driver', None)
} }
return interface return interface

View File

@ -14,6 +14,7 @@ from sysinv.common import constants
from sysinv.common import utils from sysinv.common import utils
from sysinv.puppet import interface from sysinv.puppet import interface
from sysinv.puppet import puppet from sysinv.puppet import puppet
from sysinv.puppet import quoted_str
from sysinv.objects import base as objbase from sysinv.objects import base as objbase
from sysinv.tests.db import base as dbbase from sysinv.tests.db import base as dbbase
@ -160,7 +161,8 @@ class BaseTestCase(dbbase.DbTestCase):
'networks': networks, 'networks': networks,
'networktype': networktype, 'networktype': networktype,
'imtu': 1500, 'imtu': 1500,
'sriov_numvfs': kwargs.get('sriov_numvfs', 0)} 'sriov_numvfs': kwargs.get('sriov_numvfs', 0),
'sriov_vf_driver': kwargs.get('sriov_vf_driver', None)}
db_interface = dbutils.create_test_interface(**interface) db_interface = dbutils.create_test_interface(**interface)
self.interfaces.append(db_interface) self.interfaces.append(db_interface)
@ -175,7 +177,9 @@ class BaseTestCase(dbbase.DbTestCase):
'dpdksupport': kwargs.get('dpdksupport', True), 'dpdksupport': kwargs.get('dpdksupport', True),
'pciaddr': kwargs.get('pciaddr', 'pciaddr': kwargs.get('pciaddr',
'0000:00:00.' + str(port_id + 1)), '0000:00:00.' + str(port_id + 1)),
'dev_id': kwargs.get('dev_id', 0)} 'dev_id': kwargs.get('dev_id', 0),
'sriov_vf_driver': kwargs.get('sriov_vf_driver', None),
'sriov_vfs_pci_address': kwargs.get('sriov_vfs_pci_address', '')}
db_port = dbutils.create_test_ethernet_port(**port) db_port = dbutils.create_test_ethernet_port(**port)
self.ports.append(db_port) self.ports.append(db_port)
self._setup_address_and_routes(db_interface) self._setup_address_and_routes(db_interface)
@ -1103,6 +1107,13 @@ class InterfaceTestCase(BaseTestCase):
'options': 'metric ' + str(metric)} 'options': 'metric ' + str(metric)}
return config return config
def _get_sriov_config(self, ifname='default', vf_driver='vfio',
vf_addrs=[""]):
config = {'ifname': ifname,
'vf_driver': vf_driver,
'vf_addrs': vf_addrs}
return config
def _get_loopback_config(self): def _get_loopback_config(self):
network_config = self._get_network_config( network_config = self._get_network_config(
ifname=interface.LOOPBACK_IFNAME, method=interface.LOOPBACK_METHOD) ifname=interface.LOOPBACK_IFNAME, method=interface.LOOPBACK_METHOD)
@ -1355,6 +1366,55 @@ class InterfaceTestCase(BaseTestCase):
print(expected) print(expected)
self.assertEqual(expected, config) self.assertEqual(expected, config)
def _create_sriov_vf_driver_config(self, iface_vf_driver, port_vf_driver, vf_addr_list):
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_SRIOV
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_SRIOV
self.iface['sriov_vf_driver'] = iface_vf_driver
self.port['sriov_vf_driver'] = port_vf_driver
self.port['sriov_vfs_pci_address'] = vf_addr_list
self._update_context()
config = interface.get_sriov_config(self.context, self.iface)
return config
def test_get_sriov_config_netdevice(self):
vf_addr1 = "0000:81:00.0"
vf_addr2 = "0000:81:01.0"
vf_addr_list = "{},{}".format(vf_addr1, vf_addr2)
config = self._create_sriov_vf_driver_config(
constants.SRIOV_DRIVER_TYPE_NETDEVICE, 'i40evf', vf_addr_list)
expected = self._get_sriov_config(
self.iface['ifname'], 'i40evf',
[quoted_str(vf_addr1),
quoted_str(vf_addr2)])
self.assertEqual(expected, config)
def test_get_sriov_config_vfio(self):
vf_addr1 = "0000:81:00.0"
vf_addr2 = "0000:81:01.0"
vf_addr_list = "{},{}".format(vf_addr1, vf_addr2)
config = self._create_sriov_vf_driver_config(
constants.SRIOV_DRIVER_TYPE_VFIO, 'i40evf', vf_addr_list)
expected = self._get_sriov_config(
self.iface['ifname'], 'vfio-pci',
[quoted_str(vf_addr1),
quoted_str(vf_addr2)])
self.assertEqual(expected, config)
def test_get_sriov_config_default(self):
vf_addr1 = "0000:81:00.0"
vf_addr2 = "0000:81:01.0"
vf_addr_list = "{},{}".format(vf_addr1, vf_addr2)
config = self._create_sriov_vf_driver_config(
None, 'i40evf', vf_addr_list)
expected = self._get_sriov_config(
self.iface['ifname'], None,
[quoted_str(vf_addr1),
quoted_str(vf_addr2)])
self.assertEqual(expected, config)
def test_is_a_mellanox_cx3_device_false(self): def test_is_a_mellanox_cx3_device_false(self):
self.assertFalse( self.assertFalse(
interface.is_a_mellanox_cx3_device(self.context, self.iface)) interface.is_a_mellanox_cx3_device(self.context, self.iface))