Introduce VF interface type
This commit introduces a new interface type: 'VF'. A VF (virtual function) interface is a sub-interface of an existing pci-sriov class interface. It can have a different (less than) number of VFs as the parent interface as well as a different virtual function driver. In addition, the VF interface can be assigned to a different data network as the parent SR-IOV interface. The purpose of the VF interface is to enable a user to split the virtual functions of a parent device between the parent and child so that each can be assigned a different VF driver. This may be beneficial to a user that has a mix of (DPDK) accelerated containers and containers that just depend on a kernel netdevice. An example of configuring a VF interface from the client is as follows: system host-if-add -c pci-sriov <host> \ <ifname> vf <parent sriov interface> \ -N <numvfs> --vf-driver <vfio|netdevice> Story: 2006842 Task: 37422 Change-Id: I1c9712f511c395c532d9b36721d94e22760210af Signed-off-by: Steven Webster <steven.webster@windriver.com>
This commit is contained in:
parent
28b3bd8ba2
commit
71e290f767
|
@ -17,7 +17,7 @@ CREATION_ATTRIBUTES = ['ifname', 'iftype', 'ihost_uuid', 'imtu', 'ifclass',
|
|||
'providernetworks', 'datanetworks', 'ifcapabilities', 'ports', 'imac',
|
||||
'vlan_id', 'uses', 'used_by',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs']
|
||||
'sriov_numvfs', 'sriov_vf_driver']
|
||||
|
||||
|
||||
class iinterface(base.Resource):
|
||||
|
|
|
@ -116,7 +116,7 @@ def do_host_if_delete(cc, args):
|
|||
help="Name of interface [REQUIRED]")
|
||||
@utils.arg('iftype',
|
||||
metavar='<iftype>',
|
||||
choices=['ae', 'vlan', 'virtual'],
|
||||
choices=['ae', 'vlan', 'virtual', 'vf'],
|
||||
nargs='?',
|
||||
help="Type of the interface")
|
||||
@utils.arg('-a', '--aemode',
|
||||
|
@ -155,12 +155,22 @@ def do_host_if_delete(cc, args):
|
|||
@utils.arg('--ipv6-pool',
|
||||
metavar='<ipv6 pool uuid or name>',
|
||||
help='The IPv6 address pool name or uuid if mode is set to \'pool\'')
|
||||
@utils.arg('-N', '--num-vfs',
|
||||
dest='sriov_numvfs',
|
||||
metavar='<sriov numvfs>',
|
||||
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_add(cc, args):
|
||||
"""Add an interface."""
|
||||
|
||||
field_list = ['ifname', 'iftype', 'imtu', 'ifclass', 'aemode',
|
||||
'txhashpolicy', 'vlan_id',
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool']
|
||||
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
|
||||
'sriov_numvfs', 'sriov_vf_driver']
|
||||
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
|
||||
|
@ -174,6 +184,9 @@ def do_host_if_add(cc, args):
|
|||
elif args.iftype == 'virtual':
|
||||
uses = None
|
||||
portnamesoruuids = []
|
||||
elif args.iftype == 'vf':
|
||||
uses = args.portsorifaces
|
||||
portnamesoruuids = None
|
||||
else:
|
||||
uses = None
|
||||
portnamesoruuids = ','.join(args.portsorifaces)
|
||||
|
|
|
@ -3281,13 +3281,13 @@ class HostController(rest.RestController):
|
|||
interface.ifclass != constants.INTERFACE_CLASS_PCI_SRIOV):
|
||||
return
|
||||
|
||||
if interface.iftype == constants.INTERFACE_TYPE_ETHERNET:
|
||||
if_configured_sriov_numvfs = interface.sriov_numvfs
|
||||
if not if_configured_sriov_numvfs:
|
||||
return
|
||||
|
||||
ports = pecan.request.dbapi.port_get_by_host_interface(
|
||||
host['id'], interface.id)
|
||||
|
||||
for p in ports:
|
||||
if (p.sriov_vfs_pci_address and
|
||||
if_configured_sriov_numvfs ==
|
||||
|
|
|
@ -800,14 +800,14 @@ def _check_interface_sriov(interface, ihost, from_profile=False):
|
|||
"{}").format(', '.join(constants.SRIOV_DRIVER_TYPES)))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
if interface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
||||
ports = pecan.request.dbapi.ethernet_port_get_all(hostid=ihost['id'])
|
||||
port_list = [
|
||||
(p.name, p.sriov_totalvfs, p.driver) for p in ports
|
||||
if p.interface_id and p.interface_id == interface['id']
|
||||
]
|
||||
|
||||
if len(port_list) != 1:
|
||||
raise wsme.exc.ClientSideError(_("At most one port must be enabled."))
|
||||
raise wsme.exc.ClientSideError(_("Exactly one port must be enabled."))
|
||||
|
||||
sriov_totalvfs = port_list[0][1]
|
||||
if sriov_totalvfs is None or sriov_totalvfs == 0:
|
||||
|
@ -847,9 +847,10 @@ def _check_interface_class_and_host_type(ihost, interface):
|
|||
|
||||
def _check_interface_class_and_type(interface):
|
||||
if (interface['ifclass'] in PCI_INTERFACE_CLASS and
|
||||
interface['iftype'] != "ethernet"):
|
||||
msg = (_("The {} interface class is only valid on Ethernet interfaces").
|
||||
format(', '.join(PCI_INTERFACE_CLASS)))
|
||||
interface['iftype'] not in [constants.INTERFACE_TYPE_ETHERNET,
|
||||
constants.INTERFACE_TYPE_VF]):
|
||||
msg = (_("The {} interface class is only valid on Ethernet and "
|
||||
"VF interfaces").format(', '.join(PCI_INTERFACE_CLASS)))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
|
||||
|
@ -1080,11 +1081,12 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
"must have the interface class set to 'none'.")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# Ensure that the interfaces being used in the AE interface
|
||||
# are not changed after the AE interface has been created
|
||||
if interface['used_by']:
|
||||
used_vfs = 0
|
||||
for i in interface['used_by']:
|
||||
iface = pecan.request.dbapi.iinterface_get(i, ihost_uuid)
|
||||
# Ensure that the interfaces being used in the AE interface
|
||||
# are not changed after the AE interface has been created
|
||||
if iface.iftype == constants.INTERFACE_TYPE_AE and \
|
||||
interface['ifclass']:
|
||||
msg = _("Interface '{}' is being used by interface '{}' "
|
||||
|
@ -1092,6 +1094,26 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
"class cannot be changed from 'none'.".format(interface['ifname'],
|
||||
iface.ifname))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
# Ensure that if the interface has any virtual function (VF)
|
||||
# interfaces, that the interface class does not change from
|
||||
# pci-sriov, and that the number of requested virtual functions is
|
||||
# greater than the sum of the virtual functions on the upper
|
||||
# VF interfaces.
|
||||
if iface.iftype == constants.INTERFACE_TYPE_VF:
|
||||
if interface['ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV:
|
||||
msg = _("Interface '{}' is being used by VF interface '{}' "
|
||||
"and therefore the interface class cannot be changed "
|
||||
"from 'pci-sriov'.".format(interface['ifname'],
|
||||
iface.ifname))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
else:
|
||||
used_vfs += iface['sriov_numvfs']
|
||||
if interface['sriov_numvfs'] <= used_vfs:
|
||||
msg = _("The number of virtual functions (%s) must be greater "
|
||||
"than the number of consumed VFs used by the "
|
||||
"upper VF interfaces (%s)" %
|
||||
(interface['sriov_numvfs'], interface['used_by']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# check interface class validity
|
||||
_check_interface_class(ihost, interface, existing_interface)
|
||||
|
@ -1120,7 +1142,8 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
supported_type = [constants.INTERFACE_TYPE_AE,
|
||||
constants.INTERFACE_TYPE_VLAN,
|
||||
constants.INTERFACE_TYPE_ETHERNET,
|
||||
constants.INTERFACE_TYPE_VIRTUAL]
|
||||
constants.INTERFACE_TYPE_VIRTUAL,
|
||||
constants.INTERFACE_TYPE_VF]
|
||||
if not iftype or iftype not in supported_type:
|
||||
msg = (_("Device interface type must be one of "
|
||||
"{}").format(', '.join(supported_type)))
|
||||
|
@ -1169,8 +1192,9 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
|
||||
# Ensure that data and non-data interfaces are not using the same
|
||||
# shared device
|
||||
if (iftype != constants.INTERFACE_TYPE_VLAN and
|
||||
iftype != constants.INTERFACE_TYPE_VIRTUAL):
|
||||
if iftype not in [constants.INTERFACE_TYPE_VLAN,
|
||||
constants.INTERFACE_TYPE_VF,
|
||||
constants.INTERFACE_TYPE_VIRTUAL]:
|
||||
port_list_host = \
|
||||
pecan.request.dbapi.ethernet_port_get_all(hostid=ihost['id'])
|
||||
for name in interface['uses']:
|
||||
|
@ -1184,13 +1208,14 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
host_port)
|
||||
|
||||
# check MTU
|
||||
if interface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
||||
vlan_mtu = interface['imtu']
|
||||
if interface['iftype'] in [constants.INTERFACE_TYPE_VLAN,
|
||||
constants.INTERFACE_TYPE_VF]:
|
||||
interface_mtu = interface['imtu']
|
||||
for name in interface['uses']:
|
||||
parent = pecan.request.dbapi.iinterface_get(name, ihost_uuid)
|
||||
if int(vlan_mtu) > int(parent['imtu']):
|
||||
msg = _("VLAN MTU (%s) cannot be larger than MTU of "
|
||||
"underlying interface (%s)" % (vlan_mtu, parent['imtu']))
|
||||
if int(interface_mtu) > int(parent['imtu']):
|
||||
msg = _("Interface MTU (%s) cannot be larger than MTU of "
|
||||
"underlying interface (%s)" % (interface_mtu, parent['imtu']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
elif interface['used_by']:
|
||||
mtus = _get_interface_mtus(ihost_uuid, interface)
|
||||
|
@ -1201,6 +1226,38 @@ def _check_interface_data(op, interface, ihost, existing_interface,
|
|||
(interface['imtu'], mtu))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
|
||||
# Check vf interfaces
|
||||
if iftype == constants.INTERFACE_TYPE_VF:
|
||||
if interface['ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV:
|
||||
msg = _("VF interfaces must have an interface class of %s" %
|
||||
constants.INTERFACE_CLASS_PCI_SRIOV)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if len(interface['uses']) > 1:
|
||||
msg = _("VF interfaces can only use one lower pci-sriov interface")
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
lower_ifname = interface['uses'][0]
|
||||
lower_iface = (
|
||||
pecan.request.dbapi.iinterface_get(lower_ifname, ihost_uuid))
|
||||
if lower_iface['iftype'] not in [constants.INTERFACE_TYPE_VF,
|
||||
constants.INTERFACE_TYPE_ETHERNET]:
|
||||
msg = _("VF interfaces cannot be created over interfaces "
|
||||
"of type %s" % lower_iface['iftype'])
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
if (lower_iface['ifclass'] != constants.INTERFACE_CLASS_PCI_SRIOV):
|
||||
msg = _("VF interfaces must be created over an interface of class"
|
||||
" %s" % constants.INTERFACE_CLASS_PCI_SRIOV)
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
avail_vfs = lower_iface['sriov_numvfs'] - 1
|
||||
for i in lower_iface['used_by']:
|
||||
if i != interface['ifname']:
|
||||
iface = pecan.request.dbapi.iinterface_get(i, ihost_uuid)
|
||||
avail_vfs -= iface.get('sriov_numvfs', 0)
|
||||
if interface['sriov_numvfs'] > avail_vfs:
|
||||
msg = _("The number of virtual functions (%s) must be less "
|
||||
"than or equal to the available VFs (%s) available "
|
||||
"on the underlying interface (%s)" %
|
||||
(interface['sriov_numvfs'], avail_vfs, lower_iface['ifname']))
|
||||
raise wsme.exc.ClientSideError(msg)
|
||||
return interface
|
||||
|
||||
|
||||
|
|
|
@ -662,6 +662,7 @@ INTERFACE_TYPE_ETHERNET = 'ethernet'
|
|||
INTERFACE_TYPE_VLAN = 'vlan'
|
||||
INTERFACE_TYPE_AE = 'ae'
|
||||
INTERFACE_TYPE_VIRTUAL = 'virtual'
|
||||
INTERFACE_TYPE_VF = 'vf'
|
||||
|
||||
INTERFACE_CLASS_NONE = 'none'
|
||||
INTERFACE_CLASS_PLATFORM = 'platform'
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"""Common interface utility and helper functions."""
|
||||
|
||||
import collections
|
||||
import copy
|
||||
|
||||
from oslo_log import log
|
||||
from sysinv.common import constants
|
||||
|
@ -102,3 +103,63 @@ def get_interface_port(context, iface):
|
|||
"""
|
||||
assert iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET
|
||||
return context['ports'][iface['id']]
|
||||
|
||||
|
||||
def get_lower_interface(context, iface):
|
||||
assert iface['iftype'] in [constants.INTERFACE_TYPE_VLAN,
|
||||
constants.INTERFACE_TYPE_VF]
|
||||
lower_ifname = iface['uses'][0]
|
||||
return context['interfaces'][lower_ifname]
|
||||
|
||||
|
||||
def get_sriov_interface_port(context, iface):
|
||||
"""
|
||||
Determine the underlying port of the SR-IOV interface.
|
||||
"""
|
||||
if iface['iftype'] == constants.INTERFACE_TYPE_VF:
|
||||
lower_iface = get_lower_interface(context, iface)
|
||||
return get_sriov_interface_port(context, lower_iface)
|
||||
else:
|
||||
assert iface['ifclass'] == constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
return get_interface_port(context, iface)
|
||||
|
||||
|
||||
def get_sriov_interface_vf_addrs(context, iface, vf_addr_list):
|
||||
"""
|
||||
Determine the virtual function addresses of SR-IOV interface,
|
||||
given the list of vf addresses on the port.
|
||||
"""
|
||||
vf_addrs = copy.deepcopy(vf_addr_list)
|
||||
|
||||
if iface['uses']:
|
||||
lower_iface = get_lower_interface(context, iface)
|
||||
lower_vf_addrs = get_sriov_interface_vf_addrs(context, lower_iface, vf_addr_list)
|
||||
|
||||
# Remove the VF addresses reserved for the lower SR-IOV interface
|
||||
vf_addrs = [addr for addr in vf_addr_list if addr not in lower_vf_addrs]
|
||||
|
||||
sibling_ifaces = lower_iface['used_by']
|
||||
for sibling_ifname in sibling_ifaces:
|
||||
sibling_iface = context['interfaces'][sibling_ifname]
|
||||
sibling_numvfs = sibling_iface['sriov_numvfs']
|
||||
if sibling_ifname == iface['ifname']:
|
||||
# Reserve the appropriate number of VF addresses from
|
||||
# the end of the list for the interface.
|
||||
vf_addrs = vf_addrs[-iface['sriov_numvfs']:]
|
||||
break
|
||||
else:
|
||||
# Remove the VF addresses reserved for any sibling SR-IOV
|
||||
# interface
|
||||
del vf_addrs[-sibling_numvfs:]
|
||||
|
||||
if iface['used_by']:
|
||||
upper_ifaces = iface['used_by']
|
||||
for upper_ifname in upper_ifaces:
|
||||
upper_iface = context['interfaces'][upper_ifname]
|
||||
upper_numvfs = upper_iface['sriov_numvfs']
|
||||
if upper_numvfs:
|
||||
# Remove the VF addresses reserved for any child
|
||||
# SR-IOV interface
|
||||
del vf_addrs[-upper_numvfs:]
|
||||
|
||||
return vf_addrs
|
||||
|
|
|
@ -2120,6 +2120,8 @@ class Connection(api.Connection):
|
|||
interface = models.VlanInterfaces()
|
||||
elif values['iftype'] == constants.INTERFACE_TYPE_VIRTUAL:
|
||||
interface = models.VirtualInterfaces()
|
||||
elif values['iftype'] == constants.INTERFACE_TYPE_VF:
|
||||
interface = models.SriovVFInterfaces()
|
||||
else:
|
||||
interface = models.EthernetInterfaces()
|
||||
return self._interface_create(interface, forihostid, values)
|
||||
|
@ -2235,10 +2237,12 @@ class Connection(api.Connection):
|
|||
raise exception.InvalidParameterValue(
|
||||
err="Multiple entries found for interface %s" % iinterface_id)
|
||||
|
||||
if result.iftype == 'ae':
|
||||
if result.iftype == constants.INTERFACE_TYPE_AE:
|
||||
return self._interface_update(models.AeInterfaces, iinterface_id, values)
|
||||
elif result.iftype == 'vlan':
|
||||
elif result.iftype == constants.INTERFACE_TYPE_VLAN:
|
||||
return self._interface_update(models.VlanInterfaces, iinterface_id, values)
|
||||
elif result.iftype == constants.INTERFACE_TYPE_VF:
|
||||
return self._interface_update(models.SriovVFInterfaces, iinterface_id, values)
|
||||
else:
|
||||
return self._interface_update(models.EthernetInterfaces, iinterface_id, values)
|
||||
|
||||
|
@ -2562,6 +2566,39 @@ class Connection(api.Connection):
|
|||
def virtual_interface_destroy(self, interface_id):
|
||||
return self._interface_destroy(models.VirtualInterfaces, interface_id)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_create(self, forihostid, values):
|
||||
interface = models.SriovVFInterfaces()
|
||||
return self._interface_create(interface, forihostid, values)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_get_all(self, forihostid=None):
|
||||
return self._interface_get_all(models.SriovVFInterfaces, forihostid)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_get(self, interface_id):
|
||||
return self._interface_get(models.SriovVFInterfaces, interface_id)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_get_list(self, limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_get_list(models.SriovVFInterfaces, limit, marker,
|
||||
sort_key, sort_dir)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_get_by_ihost(self, ihost,
|
||||
limit=None, marker=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
return self._interface_get_by_ihost(models.SriovVFInterfaces, ihost, limit,
|
||||
marker, sort_key, sort_dir)
|
||||
|
||||
@objects.objectify(objects.sriov_vf_interface)
|
||||
def sriov_vf_interface_update(self, interface_id, values):
|
||||
return self._interface_update(models.SriovVFInterfaces, interface_id, values)
|
||||
|
||||
def sriov_vf_interface_destroy(self, interface_id):
|
||||
return self._interface_destroy(models.SriovVFInterfaces, interface_id)
|
||||
|
||||
def _disk_get(self, disk_id, forihostid=None):
|
||||
query = model_query(models.idisk)
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# 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, Integer, DateTime
|
||||
from sqlalchemy import ForeignKey
|
||||
|
||||
|
||||
ENGINE = 'InnoDB'
|
||||
CHARSET = 'utf8'
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta = MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
Table('interfaces', meta, autoload=True)
|
||||
|
||||
sriov_vf_interfaces = Table(
|
||||
'vf_interfaces',
|
||||
meta,
|
||||
Column('created_at', DateTime),
|
||||
Column('updated_at', DateTime),
|
||||
Column('deleted_at', DateTime),
|
||||
Column('id', Integer, ForeignKey('interfaces.id', ondelete="CASCADE"),
|
||||
primary_key=True, nullable=False),
|
||||
|
||||
Column('imac', String(255)),
|
||||
Column('imtu', Integer),
|
||||
Column('sriov_numvfs', Integer),
|
||||
Column('sriov_vf_driver', String(255)),
|
||||
|
||||
mysql_engine=ENGINE,
|
||||
mysql_charset=CHARSET,
|
||||
)
|
||||
sriov_vf_interfaces.create()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
# Downgrade is unsupported in this release.
|
||||
raise NotImplementedError('SysInv database downgrade is unsupported.')
|
|
@ -422,6 +422,14 @@ class VlanInterfaces(EthernetCommon, Interfaces):
|
|||
}
|
||||
|
||||
|
||||
class SriovVFInterfaces(EthernetCommon, Interfaces):
|
||||
__tablename__ = 'vf_interfaces'
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'vf',
|
||||
}
|
||||
|
||||
|
||||
class VirtualInterfaces(EthernetCommon, Interfaces):
|
||||
__tablename__ = 'virtual_interfaces'
|
||||
|
||||
|
|
|
@ -271,6 +271,10 @@ class NeutronHelm(openstack.OpenstackBaseHelm):
|
|||
"""
|
||||
Determine the port name of the underlying device.
|
||||
"""
|
||||
if (iface['iftype'] == constants.INTERFACE_TYPE_VF and iface['uses']):
|
||||
lower_iface = self.dbapi.iinterface_get(iface['uses'][0])
|
||||
if lower_iface:
|
||||
return self._get_interface_port_name(lower_iface)
|
||||
assert iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET
|
||||
port = self.dbapi.port_get_by_interface(iface.id)
|
||||
if port:
|
||||
|
|
|
@ -269,11 +269,12 @@ class NovaHelm(openstack.OpenstackBaseHelm):
|
|||
devices = []
|
||||
for iface in iface_context['interfaces'].values():
|
||||
if iface['ifclass'] in [constants.INTERFACE_CLASS_PCI_SRIOV]:
|
||||
port = interface.get_interface_port(iface_context, iface)
|
||||
port = interface.get_sriov_interface_port(iface_context, iface)
|
||||
dnames = interface._get_datanetwork_names(iface_context, iface)
|
||||
vf_addrs = port['sriov_vfs_pci_address']
|
||||
vf_addrs = port['sriov_vfs_pci_address'].split(",")
|
||||
vf_addrs = interface.get_sriov_interface_vf_addrs(iface_context, iface, vf_addrs)
|
||||
if vf_addrs:
|
||||
for vf_addr in vf_addrs.split(","):
|
||||
for vf_addr in vf_addrs:
|
||||
device = {
|
||||
'address': vf_addr,
|
||||
'physical_network': dnames,
|
||||
|
|
|
@ -46,6 +46,7 @@ from sysinv.objects import interface_ae
|
|||
from sysinv.objects import interface_ethernet
|
||||
from sysinv.objects import interface_datanetwork
|
||||
from sysinv.objects import interface_network
|
||||
from sysinv.objects import interface_vf
|
||||
from sysinv.objects import interface_virtual
|
||||
from sysinv.objects import interface_vlan
|
||||
from sysinv.objects import journal
|
||||
|
@ -132,6 +133,7 @@ ethernet_interface = interface_ethernet.EthernetInterface
|
|||
ae_interface = interface_ae.AEInterface
|
||||
virtual_interface = interface_virtual.VirtualInterface
|
||||
vlan_interface = interface_vlan.VLANInterface
|
||||
sriov_vf_interface = interface_vf.SriovVFInterface
|
||||
interface_network = interface_network.InterfaceNetwork
|
||||
interface_datanetwork = interface_datanetwork.InterfaceDataNetwork
|
||||
port = port.Port
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Copyright (c) 2019 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
# coding=utf-8
|
||||
#
|
||||
|
||||
from sysinv.objects import base
|
||||
from sysinv.objects import utils
|
||||
from sysinv.objects import interface_ethernet
|
||||
|
||||
|
||||
class SriovVFInterface(interface_ethernet.EthernetInterface):
|
||||
|
||||
fields = dict({
|
||||
'sriov_numvfs': utils.int_or_none,
|
||||
'sriov_vf_driver': utils.str_or_none,
|
||||
}, **interface_ethernet.EthernetInterface.fields)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_uuid(cls, context, uuid):
|
||||
return cls.dbapi.sriov_vf_interface_get(uuid)
|
||||
|
||||
def save_changes(self, context, updates):
|
||||
self.dbapi.sriov_vf_interface_update(self.uuid, # pylint: disable=no-member
|
||||
updates)
|
|
@ -509,9 +509,7 @@ def get_lower_interface(context, iface):
|
|||
"""
|
||||
Return the interface object that is used to implement a VLAN interface.
|
||||
"""
|
||||
assert iface['iftype'] == constants.INTERFACE_TYPE_VLAN
|
||||
lower_ifname = iface['uses'][0]
|
||||
return context['interfaces'][lower_ifname]
|
||||
return interface.get_lower_interface(context, iface)
|
||||
|
||||
|
||||
def get_lower_interface_os_ifname(context, iface):
|
||||
|
@ -936,6 +934,8 @@ def get_ethernet_network_config(context, iface, config):
|
|||
if not is_a_mellanox_cx3_device(context, iface):
|
||||
# CX3 device can only use kernel module options to enable vfs
|
||||
# others share the same pci-sriov sysfs enabling mechanism
|
||||
|
||||
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
||||
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
|
||||
get_interface_port_name(context, iface))
|
||||
options['pre_up'] = "echo 0 > %s; echo %s > %s" % (
|
||||
|
@ -973,14 +973,40 @@ def get_route_config(route, ifname):
|
|||
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']
|
||||
def get_sriov_interface_port(context, iface):
|
||||
"""
|
||||
Determine the underlying port of the SR-IOV interface.
|
||||
"""
|
||||
return interface.get_sriov_interface_port(context, iface)
|
||||
|
||||
if not vf_addr_list:
|
||||
|
||||
def get_sriov_interface_vf_addrs(context, iface, vf_addr_list):
|
||||
"""
|
||||
Determine the virtual function addresses of SR-IOV interface,
|
||||
given the list of vf addresses on the port.
|
||||
"""
|
||||
return interface.get_sriov_interface_vf_addrs(context, iface, vf_addr_list)
|
||||
|
||||
|
||||
def get_sriov_config(context, iface):
|
||||
"""
|
||||
Returns an SR-IOV interface config dictionary.
|
||||
"""
|
||||
vf_driver = iface['sriov_vf_driver']
|
||||
|
||||
port = interface.get_sriov_interface_port(context, iface)
|
||||
if not port:
|
||||
return {}
|
||||
|
||||
vf_addrs = port.get('sriov_vfs_pci_address', None)
|
||||
if not vf_addrs:
|
||||
return {}
|
||||
|
||||
vf_addr_list = vf_addrs.split(',')
|
||||
vf_addr_list = interface.get_sriov_interface_vf_addrs(
|
||||
context, iface, vf_addr_list)
|
||||
vf_addr_list = ",".join(vf_addr_list)
|
||||
|
||||
if vf_driver:
|
||||
if constants.SRIOV_DRIVER_TYPE_VFIO in vf_driver:
|
||||
vf_driver = constants.SRIOV_DRIVER_VFIO_PCI
|
||||
|
@ -1054,6 +1080,11 @@ def get_interface_network_config(context, iface, network_id=None):
|
|||
"""
|
||||
Builds a network_config resource dictionary for a given interface
|
||||
"""
|
||||
|
||||
if iface['iftype'] == constants.INTERFACE_TYPE_VF:
|
||||
# Only the parent SR-IOV interface needs a network config
|
||||
return {}
|
||||
|
||||
# Create a basic network config resource
|
||||
os_ifname = get_interface_os_ifname(context, iface)
|
||||
method = get_interface_address_method(context, iface, network_id)
|
||||
|
@ -1109,6 +1140,7 @@ def generate_network_config(context, config, iface):
|
|||
# overridden if there is a specific network type configuration, otherwise
|
||||
# it will act as the parent device for the aliases
|
||||
net_config = get_interface_network_config(context, iface)
|
||||
if net_config:
|
||||
config[NETWORK_CONFIG_RESOURCE].update({
|
||||
net_config['ifname']: format_network_config(net_config)
|
||||
})
|
||||
|
@ -1116,6 +1148,7 @@ def generate_network_config(context, config, iface):
|
|||
for net_type in iface.networktypelist:
|
||||
net_id = find_network_id_by_networktype(context, net_type)
|
||||
net_config = get_interface_network_config(context, iface, net_id)
|
||||
if net_config:
|
||||
config[NETWORK_CONFIG_RESOURCE].update({
|
||||
net_config['ifname']: format_network_config(net_config)
|
||||
})
|
||||
|
|
|
@ -353,7 +353,11 @@ class KubernetesPuppet(base.BasePuppet):
|
|||
|
||||
interfaces = self._get_network_interfaces_by_class(ifclass)
|
||||
for iface in interfaces:
|
||||
port = interface.get_interface_port(self.context, iface)
|
||||
|
||||
port = interface.get_sriov_interface_port(self.context, iface)
|
||||
if not port:
|
||||
continue
|
||||
|
||||
datanets = interface.get_interface_datanets(self.context, iface)
|
||||
for datanet in datanets:
|
||||
dn_name = datanet['name'].strip()
|
||||
|
|
|
@ -355,6 +355,110 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
|
|||
lower_iface,
|
||||
datanetworks, self.worker, expect_errors)
|
||||
|
||||
def _create_sriov(self, ifname,
|
||||
sriov_totalvfs=None, sriov_numvfs=None,
|
||||
sriov_vf_driver=None,
|
||||
datanetworks=None, host=None, expect_errors=False):
|
||||
interface_id = len(self.profile['interfaces']) + 1
|
||||
if not ifname:
|
||||
ifname = 'sriov' + str(interface_id)
|
||||
if not host:
|
||||
host = self.controller
|
||||
if not sriov_totalvfs:
|
||||
sriov_totalvfs = 64
|
||||
if not sriov_numvfs:
|
||||
sriov_numvfs = 64
|
||||
|
||||
port_id = len(self.profile['ports'])
|
||||
port = dbutils.create_test_ethernet_port(
|
||||
id=port_id,
|
||||
name='eth' + str(port_id),
|
||||
host_id=host.id,
|
||||
interface_id=interface_id,
|
||||
pciaddr='0000:00:00.' + str(port_id + 1),
|
||||
dev_id=0,
|
||||
sriov_totalvfs=sriov_totalvfs,
|
||||
sriov_numvfs=sriov_numvfs,
|
||||
driver='i40e',
|
||||
sriov_vf_driver='i40evf')
|
||||
|
||||
ifclass = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
interface = self._post_get_test_interface(
|
||||
ifname=ifname,
|
||||
ifclass=ifclass,
|
||||
forihostid=host.id, ihost_uuid=host.uuid,
|
||||
sriov_numvfs=sriov_numvfs,
|
||||
sriov_vf_driver=sriov_vf_driver)
|
||||
response = self._post_and_check(interface, expect_errors)
|
||||
if expect_errors is False:
|
||||
interface['uuid'] = response.json['uuid']
|
||||
iface = self.dbapi.iinterface_get(interface['uuid'])
|
||||
if datanetworks:
|
||||
for dn_name in datanetworks:
|
||||
dn = self.dbapi.datanetworks_get_all({'name': dn_name})
|
||||
if dn:
|
||||
dbutils.create_test_interface_datanetwork(
|
||||
interface_id=iface.id,
|
||||
datanetwork_id=dn.id)
|
||||
|
||||
self.profile['interfaces'].append(interface)
|
||||
self.profile['ports'].append(port)
|
||||
|
||||
return port, interface
|
||||
|
||||
def _create_worker_sriov(self, ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface=None, datanetworks=None,
|
||||
host=None, expect_errors=False):
|
||||
return self._create_sriov(ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface,
|
||||
datanetworks, self.worker, expect_errors)
|
||||
|
||||
def _create_vf(self, ifname, ifclass=None,
|
||||
lower_iface=None, sriov_numvfs=None,
|
||||
sriov_vf_driver=None, datanetworks=None, host=None,
|
||||
expect_errors=False):
|
||||
if not host:
|
||||
host = self.controller
|
||||
if not lower_iface:
|
||||
lower_port, lower_iface = self._create_sriov(
|
||||
'sriov', host=host, sriov_numvfs=sriov_numvfs)
|
||||
if not ifname:
|
||||
ifname = 'vf'
|
||||
if not ifclass:
|
||||
ifclass = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
if not sriov_numvfs:
|
||||
sriov_numvfs = lower_iface['sriov_numvfs'] - 1
|
||||
|
||||
interface = self._post_get_test_interface(
|
||||
ifname=ifname,
|
||||
iftype=constants.INTERFACE_TYPE_VF,
|
||||
ifclass=ifclass,
|
||||
uses=[lower_iface['ifname']],
|
||||
forihostid=host.id, ihost_uuid=host.uuid,
|
||||
sriov_numvfs=sriov_numvfs,
|
||||
sriov_vf_driver=sriov_vf_driver)
|
||||
response = self._post_and_check(interface, expect_errors)
|
||||
if expect_errors is False:
|
||||
interface['uuid'] = response.json['uuid']
|
||||
iface = self.dbapi.iinterface_get(interface['uuid'])
|
||||
if ifclass == constants.INTERFACE_CLASS_PCI_SRIOV and datanetworks:
|
||||
for dn_name in datanetworks:
|
||||
dn = self.dbapi.datanetworks_get_all({'name': dn_name})
|
||||
if dn:
|
||||
dbutils.create_test_interface_datanetwork(
|
||||
interface_id=iface.id,
|
||||
datanetwork_id=dn.id)
|
||||
|
||||
self.profile['interfaces'].append(interface)
|
||||
return interface
|
||||
|
||||
def _create_worker_vf(self, ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface=None, datanetworks=None,
|
||||
host=None, expect_errors=False):
|
||||
return self._create_vf(ifname, networktype, ifclass, vlan_id,
|
||||
lower_iface,
|
||||
datanetworks, self.worker, expect_errors)
|
||||
|
||||
def _post_and_check_success(self, ndict):
|
||||
response = self.post_json('%s' % self._get_path(), ndict)
|
||||
self.assertEqual(http_client.OK, response.status_int)
|
||||
|
@ -879,6 +983,36 @@ class InterfaceAIOVlanOverBond(InterfaceTestCase):
|
|||
self._create_and_apply_profile(self.controller)
|
||||
|
||||
|
||||
class InterfaceAIOVfOverSriov(InterfaceTestCase):
|
||||
|
||||
def _setup_configuration(self):
|
||||
# Setup a sample configuration where the personality is set to a
|
||||
# controller with a worker subfunction and all interfaces are
|
||||
# ethernet aside from a VF over SR-IOV interface.
|
||||
self._create_host(constants.CONTROLLER, constants.WORKER,
|
||||
admin=constants.ADMIN_LOCKED)
|
||||
self._create_datanetworks()
|
||||
self._create_ethernet('oam', constants.NETWORK_TYPE_OAM)
|
||||
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT)
|
||||
self._create_ethernet('cluster', constants.NETWORK_TYPE_CLUSTER_HOST)
|
||||
self._create_ethernet('data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA,
|
||||
'group0-data0')
|
||||
self._create_ethernet('pthru', constants.NETWORK_TYPE_PCI_PASSTHROUGH,
|
||||
constants.INTERFACE_CLASS_PCI_PASSTHROUGH,
|
||||
'group0-ext0')
|
||||
lower_port, lower_iface = self._create_sriov(
|
||||
'sriov1', sriov_numvfs=2, datanetworks='group0-data0')
|
||||
self._create_vf('vf1', lower_iface=lower_iface, sriov_numvfs=1,
|
||||
sriov_vf_driver='vfio', datanetworks='group0-data1')
|
||||
|
||||
def setUp(self):
|
||||
super(InterfaceAIOVfOverSriov, self).setUp()
|
||||
|
||||
def test_AIO_vf_over_sriov_profile(self):
|
||||
self._create_and_apply_profile(self.controller)
|
||||
|
||||
|
||||
# Test that the unsupported config is rejected
|
||||
class InterfaceAIOVlanOverDataEthernet(InterfaceTestCase):
|
||||
|
||||
|
@ -1038,7 +1172,7 @@ class TestPatchMixin(object):
|
|||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
# Expected error: VLAN MTU ___ cannot be larger than MTU of underlying
|
||||
# Expected error: Interface MTU ___ cannot be larger than MTU of underlying
|
||||
# interface ___
|
||||
def test_vlan_mtu_smaller_than_users(self):
|
||||
port, lower_interface = self._create_ethernet(
|
||||
|
@ -1063,6 +1197,32 @@ class TestPatchMixin(object):
|
|||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
# Expected error: Interface MTU ___ cannot be larger than MTU of underlying
|
||||
# interface ___
|
||||
def test_vf_mtu_smaller_than_users(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
upper = dbutils.create_test_interface(
|
||||
forihostid='2',
|
||||
ihost_uuid=self.worker.uuid,
|
||||
ifname='vf0',
|
||||
networktype=constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
ifclass=constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
iftype=constants.INTERFACE_TYPE_VF,
|
||||
sriov_numvfs=2,
|
||||
sriov_vf_driver='vfio',
|
||||
datanetworks='group0-ext0',
|
||||
aemode='balanced',
|
||||
txhashpolicy='layer2',
|
||||
uses=['sriov'],
|
||||
imtu=1500)
|
||||
response = self.patch_dict_json(
|
||||
'%s' % self._get_path(upper['uuid']), imtu=1800,
|
||||
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'])
|
||||
|
||||
def _create_sriov_vf_driver_valid(self, vf_driver, expect_errors=False):
|
||||
interface = dbutils.create_test_interface(forihostid='1',
|
||||
datanetworks='group0-data0')
|
||||
|
@ -1565,6 +1725,116 @@ class TestPostMixin(object):
|
|||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
# Expected message:
|
||||
# VF interfaces must be created over an interface of class pci-sriov
|
||||
def test_create_vf_over_invalid_interface(self):
|
||||
port, lower_iface = self._create_ethernet(
|
||||
'data', constants.NETWORK_TYPE_DATA,
|
||||
constants.INTERFACE_CLASS_DATA, 'group0-data0', host=self.worker)
|
||||
self._create_vf('vf1', lower_iface=lower_iface, sriov_numvfs=1,
|
||||
host=self.worker, sriov_vf_driver='vfio',
|
||||
datanetworks='group0-data0', expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# VF interfaces must have an interface class of pci-sriov
|
||||
def test_create_invalid_vf_interface_class(self):
|
||||
self._create_vf('vf1', ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
sriov_numvfs=1,
|
||||
host=self.worker, sriov_vf_driver='vfio',
|
||||
datanetworks='group0-data0', expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# The number of virtual functions _ must be less than or equal to the
|
||||
# available VFs _ available on the underlying interface _
|
||||
def test_create_invalid_vf_interface_numvfs(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=4, expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# The number of virtual functions _ must be less than or equal to the
|
||||
# available VFs _ available on the underlying interface _
|
||||
def test_create_invalid_vf_interface_numvfs_multiple_children(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=1, expect_errors=False)
|
||||
self._create_vf('vf2', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=3, expect_errors=True)
|
||||
|
||||
# Expected message:
|
||||
# Interface _ is being used by VF interface _ and therefore the interface
|
||||
# class cannot be changed from 'pci-sriov'.
|
||||
def test_modify_sriov_interface_invalid_class_with_upper_vf(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=1, expect_errors=False)
|
||||
|
||||
response = self.patch_dict_json(
|
||||
'%s' % self._get_path(lower_iface['uuid']),
|
||||
ifclass=constants.INTERFACE_CLASS_DATA,
|
||||
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 interface class cannot be changed from 'pci-sriov'",
|
||||
response.json['error_message'])
|
||||
|
||||
# Expected message:
|
||||
# The number of virtual functions _ must be greater than the number of
|
||||
# consumed VFs _ used by the upper VF interfaces _
|
||||
def test_modify_sriov_interface_invalid_numvfs_with_upper_vf(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=3, expect_errors=False)
|
||||
|
||||
response = self.patch_dict_json(
|
||||
'%s' % self._get_path(lower_iface['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('must be greater than the number of consumed VFs',
|
||||
response.json['error_message'])
|
||||
|
||||
def test_interface_vf_usesmodify_success(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov', host=self.worker, sriov_numvfs=4)
|
||||
vf = self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=3, expect_errors=False)
|
||||
|
||||
port, new_lower_iface = self._create_sriov(
|
||||
'sriov2', host=self.worker, sriov_numvfs=4)
|
||||
# Modify VF interface to another SRIOV interface
|
||||
|
||||
patch_result = self.patch_dict_json(
|
||||
'%s' % self._get_path(vf['uuid']),
|
||||
usesmodify=new_lower_iface['uuid'])
|
||||
self.assertEqual('application/json', patch_result.content_type)
|
||||
self.assertEqual(http_client.OK, patch_result.status_code)
|
||||
|
||||
def test_interface_vf_usesmodify_invalid(self):
|
||||
port, lower_iface = self._create_sriov(
|
||||
'sriov1', host=self.worker, sriov_numvfs=4)
|
||||
vf = self._create_vf('vf1', lower_iface=lower_iface,
|
||||
host=self.worker, sriov_numvfs=3, expect_errors=False)
|
||||
|
||||
port, new_lower_iface = self._create_sriov(
|
||||
'sriov2', host=self.worker, sriov_numvfs=4)
|
||||
uses = ','.join(vf['uses'])
|
||||
response = self.patch_dict_json(
|
||||
'%s' % self._get_path(vf['uuid']),
|
||||
usesmodify=uses + ',' + new_lower_iface['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertIn('VF interfaces can only use one lower',
|
||||
response.json['error_message'])
|
||||
|
||||
|
||||
class TestAIOPost(InterfaceTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -249,6 +249,41 @@ class InterfaceTestCaseMixin(base.PuppetTestCaseMixin):
|
|||
self._setup_address_and_routes(db_interface)
|
||||
return db_interface
|
||||
|
||||
def _create_vf_test(self, ifname, num_vfs, vf_driver=None,
|
||||
lower_iface=None):
|
||||
if not lower_iface:
|
||||
lower_port, lower_iface = self._create_ethernet_test(
|
||||
'sriov', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV)
|
||||
if not ifname:
|
||||
ifname = 'vf-' + lower_iface['ifname']
|
||||
if not num_vfs:
|
||||
num_vfs = 1
|
||||
|
||||
networks = []
|
||||
|
||||
ifclass = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
interface_id = len(self.interfaces)
|
||||
interface = {'id': interface_id,
|
||||
'uuid': str(uuid.uuid4()),
|
||||
'forihostid': self.host.id,
|
||||
'ifname': ifname,
|
||||
'iftype': constants.INTERFACE_TYPE_VF,
|
||||
'imac': '02:11:22:33:44:' + str(10 + interface_id),
|
||||
'uses': [lower_iface['ifname']],
|
||||
'used_by': [],
|
||||
'ifclass': ifclass,
|
||||
'networks': networks,
|
||||
'networktype': constants.NETWORK_TYPE_PCI_SRIOV,
|
||||
'imtu': 1500,
|
||||
'sriov_numvfs': num_vfs,
|
||||
'sriov_vf_driver': vf_driver}
|
||||
lower_iface['used_by'].append(interface['ifname'])
|
||||
db_interface = dbutils.create_test_interface(**interface)
|
||||
self.interfaces.append(db_interface)
|
||||
self._setup_address_and_routes(db_interface)
|
||||
return db_interface
|
||||
|
||||
def _create_test_host(self, personality, subfunction=None):
|
||||
subfunctions = [personality]
|
||||
if subfunction:
|
||||
|
@ -408,7 +443,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
value = interface.get_interface_port_name(self.context, self.iface)
|
||||
self.assertEqual(value, self.port['name'])
|
||||
|
||||
def test_get_lower_interface(self):
|
||||
def test_get_lower_interface_vlan(self):
|
||||
vlan = self._create_vlan_test(
|
||||
"cluster-host", constants.INTERFACE_CLASS_PLATFORM,
|
||||
constants.NETWORK_TYPE_CLUSTER_HOST, 1, self.iface)
|
||||
|
@ -416,6 +451,16 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
value = interface.get_lower_interface(self.context, vlan)
|
||||
self.assertEqual(value, self.iface)
|
||||
|
||||
def test_get_lower_interface_vf(self):
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
|
||||
sriov_vf_driver=None)
|
||||
vf = self._create_vf_test("vf1", 1, None, lower_iface=iface)
|
||||
self._update_context()
|
||||
value = interface.get_lower_interface(self.context, vf)
|
||||
self.assertEqual(value, iface)
|
||||
|
||||
def test_get_interface_os_ifname_ethernet(self):
|
||||
value = interface.get_interface_os_ifname(self.context, self.iface)
|
||||
self.assertEqual(value, self.port['name'])
|
||||
|
@ -711,6 +756,77 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
self.context, self.iface)
|
||||
self.assertIsNone(classifier)
|
||||
|
||||
def test_get_sriov_interface_port(self):
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
|
||||
sriov_vf_driver=None)
|
||||
vf = self._create_vf_test("vf1", 1, None, lower_iface=iface)
|
||||
self._update_context()
|
||||
value = interface.get_sriov_interface_port(self.context, vf)
|
||||
self.assertEqual(value, port)
|
||||
|
||||
def test_get_sriov_interface_vf_addrs(self):
|
||||
vf_addr1 = "0000:81:00.0"
|
||||
vf_addr2 = "0000:81:01.0"
|
||||
vf_addr_list = [vf_addr1, vf_addr2]
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
|
||||
sriov_vf_driver=None)
|
||||
vf1 = self._create_vf_test("vf1", 1, None, lower_iface=iface)
|
||||
self._update_context()
|
||||
addrs1 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, iface, vf_addr_list)
|
||||
self.assertEqual(len(addrs1), 1)
|
||||
addrs2 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, vf1, vf_addr_list)
|
||||
self.assertEqual(len(addrs2), 1)
|
||||
|
||||
def test_get_sriov_interface_vf_addrs_multiple_children(self):
|
||||
vf_addr1 = "0000:81:00.0"
|
||||
vf_addr2 = "0000:81:01.0"
|
||||
vf_addr3 = "0000:81:02.0"
|
||||
vf_addr_list = [vf_addr1, vf_addr2, vf_addr3]
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=3,
|
||||
sriov_vf_driver=None)
|
||||
vf1 = self._create_vf_test("vf1", 1, None, lower_iface=iface)
|
||||
vf2 = self._create_vf_test("vf2", 1, None, lower_iface=iface)
|
||||
self._update_context()
|
||||
addrs1 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, vf1, vf_addr_list)
|
||||
self.assertEqual(len(addrs1), 1)
|
||||
addrs2 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, vf2, vf_addr_list)
|
||||
self.assertEqual(len(addrs2), 1)
|
||||
addrs3 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, iface, vf_addr_list)
|
||||
self.assertEqual(len(addrs3), 1)
|
||||
|
||||
def test_get_sriov_interface_vf_addrs_multiple_parents(self):
|
||||
vf_addr1 = "0000:81:00.0"
|
||||
vf_addr2 = "0000:81:01.0"
|
||||
vf_addr3 = "0000:81:02.0"
|
||||
vf_addr_list = [vf_addr1, vf_addr2, vf_addr3]
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=3,
|
||||
sriov_vf_driver=None)
|
||||
vf1 = self._create_vf_test("vf1", 2, None, lower_iface=iface)
|
||||
vf2 = self._create_vf_test("vf2", 1, None, lower_iface=vf1)
|
||||
self._update_context()
|
||||
addrs1 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, vf1, vf_addr_list)
|
||||
self.assertEqual(len(addrs1), 1)
|
||||
addrs2 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, vf2, vf_addr_list)
|
||||
self.assertEqual(len(addrs2), 1)
|
||||
addrs3 = interface.get_sriov_interface_vf_addrs(
|
||||
self.context, iface, vf_addr_list)
|
||||
self.assertEqual(len(addrs3), 1)
|
||||
|
||||
def test_get_bridge_interface_name_none_dpdk_supported(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_DATA
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_DATA
|
||||
|
@ -830,6 +946,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
|
||||
def test_needs_interface_config_sriov_worker(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
self.iface['iftype'] = constants.INTERFACE_TYPE_ETHERNET
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_SRIOV
|
||||
self.host['personality'] = constants.WORKER
|
||||
self._update_context()
|
||||
|
@ -838,6 +955,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
|
||||
def test_needs_interface_config_pthru_worker(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_PASSTHROUGH
|
||||
self.iface['iftype'] = constants.INTERFACE_TYPE_ETHERNET
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_PASSTHROUGH
|
||||
self.host['personality'] = constants.WORKER
|
||||
self._update_context()
|
||||
|
@ -886,6 +1004,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
|
||||
def test_needs_interface_config_sriov_cpe(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
self.iface['iftype'] = constants.INTERFACE_TYPE_ETHERNET
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_SRIOV
|
||||
self.host['personality'] = constants.CONTROLLER
|
||||
self.host['subfunctions'] = constants.CONTROLLER
|
||||
|
@ -895,6 +1014,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
|
||||
def test_needs_interface_config_sriov_cpe_worker(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_SRIOV
|
||||
self.iface['iftype'] = constants.INTERFACE_TYPE_ETHERNET
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_SRIOV
|
||||
self.host['personality'] = constants.CONTROLLER
|
||||
self.host['subfunctions'] = constants.WORKER
|
||||
|
@ -904,6 +1024,7 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
|
||||
def test_needs_interface_config_pthru_cpe_worker(self):
|
||||
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_PASSTHROUGH
|
||||
self.iface['iftype'] = constants.INTERFACE_TYPE_ETHERNET
|
||||
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_PASSTHROUGH
|
||||
self.host['personality'] = constants.CONTROLLER
|
||||
self.host['subfunctions'] = constants.WORKER
|
||||
|
@ -1204,6 +1325,18 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
|
|||
print(expected)
|
||||
self.assertEqual(expected, config)
|
||||
|
||||
def test_get_worker_ethernet_config_pci_sriov_vf(self):
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
|
||||
sriov_vf_driver=None)
|
||||
vf = self._create_vf_test("vf", 1, None, lower_iface=iface)
|
||||
self._update_context()
|
||||
config = interface.get_interface_network_config(self.context, vf)
|
||||
expected = {}
|
||||
print(expected)
|
||||
self.assertEqual(expected, config)
|
||||
|
||||
def test_get_route_config(self):
|
||||
route = {'network': '1.2.3.0',
|
||||
'prefix': 24,
|
||||
|
@ -1669,6 +1802,34 @@ class InterfaceComputeVlanOverEthernet(InterfaceHostTestCase):
|
|||
self.expected_pci_interfaces = ['sriov', 'pthru']
|
||||
|
||||
|
||||
class InterfaceComputeVfOverSriov(InterfaceHostTestCase):
|
||||
def _setup_configuration(self):
|
||||
# Setup a sample configuration where the personality is set to a
|
||||
# worker and all interfaces are ethernet interfaces, aside from
|
||||
# a VF interface over SR-IOV
|
||||
self.host = self._create_test_host(constants.WORKER)
|
||||
self._create_ethernet_test('mgmt', constants.INTERFACE_CLASS_PLATFORM,
|
||||
constants.NETWORK_TYPE_MGMT)
|
||||
self._create_ethernet_test('cluster-host', constants.INTERFACE_CLASS_PLATFORM,
|
||||
constants.NETWORK_TYPE_CLUSTER_HOST)
|
||||
self._create_ethernet_test('data', constants.INTERFACE_CLASS_DATA)
|
||||
port, iface = self._create_ethernet_test(
|
||||
'sriov', constants.INTERFACE_CLASS_PCI_SRIOV,
|
||||
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
|
||||
sriov_vf_driver=None)
|
||||
self._create_vf_test("vf", 1, None, lower_iface=iface)
|
||||
self._create_ethernet_test('pthru', constants.INTERFACE_CLASS_PCI_PASSTHROUGH,
|
||||
constants.NETWORK_TYPE_PCI_PASSTHROUGH)
|
||||
|
||||
def setUp(self):
|
||||
super(InterfaceComputeVfOverSriov, self).setUp()
|
||||
self.expected_bmc_interface = 'pxeboot'
|
||||
self.expected_platform_interfaces = ['pxeboot', 'mgmt',
|
||||
'eth2', 'cluster-host']
|
||||
self.expected_data_interfaces = ['eth4', 'data']
|
||||
self.expected_pci_interfaces = ['sriov', 'pthru', 'vf']
|
||||
|
||||
|
||||
class InterfaceComputeBond(InterfaceHostTestCase):
|
||||
def _setup_configuration(self):
|
||||
# Setup a sample configuration where the personality is set to a
|
||||
|
|
Loading…
Reference in New Issue