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:
Steven Webster 2019-11-26 17:17:43 -06:00
parent 28b3bd8ba2
commit 71e290f767
17 changed files with 813 additions and 88 deletions

View File

@ -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):

View File

@ -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)

View File

@ -3281,40 +3281,40 @@ class HostController(rest.RestController):
interface.ifclass != constants.INTERFACE_CLASS_PCI_SRIOV):
return
if_configured_sriov_numvfs = interface.sriov_numvfs
if not if_configured_sriov_numvfs:
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 ==
len(p.sriov_vfs_pci_address.split(','))):
LOG.info("check sriov_numvfs=%s sriov_vfs_pci_address=%s" %
(if_configured_sriov_numvfs, p.sriov_vfs_pci_address))
break
else:
msg = (_("Expecting number of interface sriov_numvfs=%s. "
"Please wait a few minutes for inventory update and "
"retry host-unlock." %
if_configured_sriov_numvfs))
LOG.info(msg)
pecan.request.rpcapi.update_sriov_config(
pecan.request.context,
host['uuid'])
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 %s, but "
"corresponding port has an invalid driver" %
constants.SRIOV_DRIVER_TYPE_NETDEVICE))
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 ==
len(p.sriov_vfs_pci_address.split(','))):
LOG.info("check sriov_numvfs=%s sriov_vfs_pci_address=%s" %
(if_configured_sriov_numvfs, p.sriov_vfs_pci_address))
break
else:
msg = (_("Expecting number of interface sriov_numvfs=%s. "
"Please wait a few minutes for inventory update and "
"retry host-unlock." %
if_configured_sriov_numvfs))
LOG.info(msg)
pecan.request.rpcapi.update_sriov_config(
pecan.request.context,
host['uuid'])
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 %s, but "
"corresponding port has an invalid driver" %
constants.SRIOV_DRIVER_TYPE_NETDEVICE))
LOG.info(msg)
raise wsme.exc.ClientSideError(msg)
self._check_sriovdp_interface_datanets(interface)
def _semantic_check_pcipt_interface(self, host, interface, force_unlock=False):

View File

@ -800,24 +800,24 @@ def _check_interface_sriov(interface, ihost, from_profile=False):
"{}").format(', '.join(constants.SRIOV_DRIVER_TYPES)))
raise wsme.exc.ClientSideError(msg)
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 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(_("Exactly one port must be enabled."))
if len(port_list) != 1:
raise wsme.exc.ClientSideError(_("At most one port must be enabled."))
sriov_totalvfs = port_list[0][1]
if sriov_totalvfs is None or sriov_totalvfs == 0:
raise wsme.exc.ClientSideError(_("SR-IOV can't be configured on this interface"))
sriov_totalvfs = port_list[0][1]
if sriov_totalvfs is None or sriov_totalvfs == 0:
raise wsme.exc.ClientSideError(_("SR-IOV can't be configured on this interface"))
if int(interface['sriov_numvfs']) > sriov_totalvfs:
raise wsme.exc.ClientSideError(_("The interface support a maximum of %s VFs" % sriov_totalvfs))
driver = port_list[0][2]
if driver is None or not driver:
raise wsme.exc.ClientSideError(_("Corresponding port has invalid driver"))
if int(interface['sriov_numvfs']) > sriov_totalvfs:
raise wsme.exc.ClientSideError(_("The interface support a maximum of %s VFs" % sriov_totalvfs))
driver = port_list[0][2]
if driver is None or not driver:
raise wsme.exc.ClientSideError(_("Corresponding port has invalid driver"))
sriov_update = True
return sriov_update
@ -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

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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.')

View File

@ -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'

View File

@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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,10 +934,12 @@ 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
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
get_interface_port_name(context, iface))
options['pre_up'] = "echo 0 > %s; echo %s > %s" % (
sriovfs_path, iface['sriov_numvfs'], sriovfs_path)
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" % (
sriovfs_path, iface['sriov_numvfs'], sriovfs_path)
elif interface_class == constants.INTERFACE_CLASS_PCI_PASSTHROUGH:
sriovfs_path = ("/sys/class/net/%s/device/sriov_numvfs" %
get_interface_port_name(context, iface))
@ -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,16 +1140,18 @@ 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)
config[NETWORK_CONFIG_RESOURCE].update({
net_config['ifname']: format_network_config(net_config)
})
if net_config:
config[NETWORK_CONFIG_RESOURCE].update({
net_config['ifname']: format_network_config(net_config)
})
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)
config[NETWORK_CONFIG_RESOURCE].update({
net_config['ifname']: format_network_config(net_config)
})
if net_config:
config[NETWORK_CONFIG_RESOURCE].update({
net_config['ifname']: format_network_config(net_config)
})
# Add complementary puppet resource definitions (if needed)
for route in get_interface_routes(context, iface):

View File

@ -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()

View File

@ -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):

View File

@ -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