VF rate limiting support

This feature is part of 'Single NIC support'.
This commit implements the rate limit on VFs in terms of
max_tx_rate leveraging hardware NIC driver capability.
And adjust sriov-device-plugin config.json to make it easy
to allocate rate limited VFs to the target Pods.

Story: 2008470
Task: 41508

Depends-On: https://review.opendev.org/c/starlingx/config/+/770132
Signed-off-by: Litao Gao <litao.gao@windriver.com>
Change-Id: I3b609296b6b5b872f0bb0bd9733740b01e9d421c
This commit is contained in:
Litao Gao 2021-01-11 08:51:24 -05:00
parent c0fadef2c1
commit 69664edc58
14 changed files with 399 additions and 47 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_vf_driver', 'ptp_role']
'sriov_numvfs', 'sriov_vf_driver', 'ptp_role', 'max_tx_rate']
class iinterface(base.Resource):

View File

@ -20,7 +20,8 @@ def _print_iinterface_show(cc, iinterface):
'aemode', 'schedpolicy', 'txhashpolicy',
'uuid', 'ihost_uuid',
'vlan_id', 'uses', 'used_by',
'created_at', 'updated_at', 'sriov_numvfs', 'sriov_vf_driver']
'created_at', 'updated_at', 'sriov_numvfs',
'sriov_vf_driver', 'max_tx_rate']
optional_fields = ['ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool']
rename_fields = [{'field': 'dpdksupport', 'label': 'accelerated'}]
data = [(f, getattr(iinterface, f, '')) for f in fields]
@ -84,6 +85,8 @@ def do_host_if_list(cc, args):
attr_str = "%s,accelerated=False" % attr_str
else:
attr_str = "%s,accelerated=True" % attr_str
if i.max_tx_rate:
attr_str = "%s,max_tx_rate=%s" % (attr_str, i.max_tx_rate)
setattr(i, 'attrs', attr_str)
field_labels = ['uuid', 'name', 'class', 'type', 'vlan id', 'ports',
@ -169,13 +172,17 @@ def do_host_if_delete(cc, args):
metavar='<ptp role>',
choices=['master', 'slave', 'none'],
help='The PTP role for this interface')
@utils.arg('-r', '--max-tx-rate',
dest='max_tx_rate',
metavar='<max_tx_rate>',
help='The max tx rate (Mb/s) of the SR-IOV VF interface')
def do_host_if_add(cc, args):
"""Add an interface."""
field_list = ['ifname', 'iftype', 'imtu', 'ifclass', 'aemode',
'txhashpolicy', 'vlan_id', 'ptp_role',
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs', 'sriov_vf_driver']
'sriov_numvfs', 'sriov_vf_driver', 'max_tx_rate']
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
@ -267,13 +274,17 @@ def do_host_if_add(cc, args):
metavar='<ptp role>',
choices=['master', 'slave', 'none'],
help='The PTP role for this interface')
@utils.arg('-r', '--max-tx-rate',
dest='max_tx_rate',
metavar='<max_tx_rate>',
help='The max tx rate (Mb/s) of the VF interface')
def do_host_if_modify(cc, args):
"""Modify interface attributes."""
rwfields = ['iftype', 'ifname', 'imtu', 'aemode', 'txhashpolicy',
'ports', 'ifclass', 'ptp_role',
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs', 'sriov_vf_driver']
'sriov_numvfs', 'sriov_vf_driver', 'max_tx_rate']
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)

View File

@ -193,6 +193,9 @@ class Interface(base.APIBase):
ptp_role = wtypes.text
"The PTP role for this interface"
max_tx_rate = int
"The value of configured max tx rate of VF, Mbps"
def __init__(self, **kwargs):
self.fields = list(objects.interface.fields.keys())
for k in self.fields:
@ -217,7 +220,8 @@ class Interface(base.APIBase):
'aemode', 'schedpolicy', 'txhashpolicy',
'vlan_id', 'uses', 'usesmodify', 'used_by',
'ipv4_mode', 'ipv6_mode', 'ipv4_pool', 'ipv6_pool',
'sriov_numvfs', 'sriov_vf_driver', 'ptp_role'])
'sriov_numvfs', 'sriov_vf_driver', 'ptp_role',
'max_tx_rate'])
# never expose the ihost_id attribute
interface.ihost_id = wtypes.Unset
@ -672,7 +676,8 @@ def _set_defaults(interface):
'vlan_id': None,
'sriov_numvfs': 0,
'sriov_vf_driver': None,
'ptp_role': constants.INTERFACE_PTP_ROLE_NONE}
'ptp_role': constants.INTERFACE_PTP_ROLE_NONE,
'max_tx_rate': None}
if interface['ifclass'] == constants.INTERFACE_CLASS_DATA:
defaults['ipv4_mode'] = constants.IPV4_DISABLED
@ -1016,6 +1021,43 @@ def _check_network_type_and_port(interface, ihost,
raise wsme.exc.ClientSideError(msg)
def _check_interface_ratelimit(interface):
# Ensure rate limit is valid for VF interfaces
if interface['max_tx_rate'] is not None:
if not str(interface['max_tx_rate']).isdigit():
msg = _("max_tx_rate must be an integer value.")
raise wsme.exc.ClientSideError(msg)
if interface['iftype'] != constants.INTERFACE_TYPE_VF:
msg = _("max_tx_rate is only allowed to be configured for VF interfaces")
raise wsme.exc.ClientSideError(msg)
# check if an overcommitted config
max_tx_rate = interface['max_tx_rate']
ihost_uuid = interface['ihost_uuid']
lower_ifname = interface['uses'][0]
lower_iface = (
pecan.request.dbapi.iinterface_get(lower_ifname, ihost_uuid))
ports = pecan.request.dbapi.ethernet_port_get_by_interface(
lower_iface['uuid'])
if len(ports) > 0 and ports[0]['speed'] is not None:
# keep 10% of the bandwidth for PF traffic
total_rate_for_vf = int(ports[0]['speed'] * constants.VF_TOTAL_RATE_RATIO)
total_rate_used = 0
interface_list = pecan.request.dbapi.iinterface_get_all(
forihostid=ihost_uuid)
for i in interface_list:
if (i['iftype'] == constants.INTERFACE_TYPE_VF and
lower_ifname == i['uses'][0]):
if i['max_tx_rate'] is not None:
total_rate_used += i['max_tx_rate']
if total_rate_used + max_tx_rate > total_rate_for_vf:
msg = _("Configured max_tx_rate exceeds total link speed")
raise wsme.exc.ClientSideError(msg)
def _check_interface_ptp(interface):
# Ensure PTP settings are valid for this interface
# Validate PTP role value
@ -1302,6 +1344,7 @@ def _check_interface_data(op, interface, ihost, existing_interface,
(interface['sriov_numvfs'], avail_vfs, lower_iface['ifname']))
raise wsme.exc.ClientSideError(msg)
_check_interface_ptp(interface)
_check_interface_ratelimit(interface)
return interface

View File

@ -748,6 +748,9 @@ LINK_SPEED_1G = 1000
LINK_SPEED_10G = 10000
LINK_SPEED_25G = 25000
# VF rate limit
VF_TOTAL_RATE_RATIO = 0.9
# DRBD engineering limits.
# Link Util values are in Percentage.
DRBD_LINK_UTIL_MIN = 5

View File

@ -1142,6 +1142,10 @@ class HostLabelInvalid(Invalid):
message = _("Host label is invalid. Reason: %(reason)s")
class LinkSpeedInvalid(Invalid):
message = _("Link speed is invalid. Reason: %(reason)s")
class PickleableException(Exception):
"""
Pickleable Exception

View File

@ -1209,6 +1209,18 @@ def get_interface_os_ifname(interface, interfaces, ports):
return interface['ifname']
def get_sriov_vf_index(addr, addrs):
"""
Returns vf index of specified pci addr of the vf
Returns None if not found
"""
try:
return addrs.index(addr)
except ValueError:
LOG.error("Index not found for this addr %s." % addr)
return None
def get_dhcp_cid(hostname, network_type, mac):
"""Create the CID for use with dnsmasq. We use a unique identifier for a
client since different networks can operate over the same device (and hence

View File

@ -2328,6 +2328,7 @@ class Connection(api.Connection):
@objects.objectify(objects.interface)
def iinterface_update(self, iinterface_id, values):
self._interface_ratelimit_encode(values)
with _session_for_write() as session:
query = model_query(models.Interfaces, read_deleted="no",
session=session)
@ -2353,11 +2354,22 @@ class Connection(api.Connection):
def iinterface_destroy(self, iinterface_id):
return self._interface_destroy(models.Interfaces, iinterface_id)
def _interface_ratelimit_encode(self, values):
# we need to use 'ifcapabilities' dict to save ratelimit info
if values.get('max_tx_rate') is not None:
capabilities = {'max_tx_rate': values['max_tx_rate']}
if values.get('ifcapabilities') is not None:
values['ifcapabilities'].update(capabilities)
else:
values['ifcapabilities'] = capabilities
def _interface_create(self, obj, forihostid, values):
if not values.get('uuid'):
values['uuid'] = uuidutils.generate_uuid()
values['forihostid'] = int(forihostid)
self._interface_ratelimit_encode(values)
is_profile = values.get('interface_profile', False)
with _session_for_write() as session:

View File

@ -146,7 +146,8 @@ class Interface(base.SysinvObject):
'ipv6_pool': utils.uuid_or_none,
'sriov_numvfs': utils.int_or_none,
'sriov_vf_driver': utils.str_or_none,
'ptp_role': utils.str_or_none
'ptp_role': utils.str_or_none,
'max_tx_rate': utils.int_or_none,
}
_foreign_fields = {'uses': _get_interface_name_list,
@ -169,3 +170,17 @@ class Interface(base.SysinvObject):
def save_changes(self, context, updates):
self.dbapi.iinterface_update(self.uuid, # pylint: disable=no-member
updates)
@classmethod
def from_db_object(cls, db_obj):
cls._interface_ratelimit_decode(db_obj)
return cls._from_db_object(cls(), db_obj)
@classmethod
def _interface_ratelimit_decode(cls, db_obj):
if not isinstance(db_obj, list):
try:
capabilities = db_obj['ifcapabilities']
db_obj['max_tx_rate'] = capabilities['max_tx_rate']
except (ValueError, TypeError):
db_obj['max_tx_rate'] = None

View File

@ -1031,18 +1031,17 @@ def get_sriov_vf_config(context, iface, port, vf_config):
# Calculate the VF addresses to assign to a logical VF interface,
# taking into account any upper or lower interfaces.
vf_addr_list = ''
vf_addrs = port.get('sriov_vfs_pci_address', None)
if vf_addrs:
vf_addr_list = vf_addrs.split(',')
vf_addr_list = []
all_vf_addr_list = []
all_vf_addrs = port.get('sriov_vfs_pci_address', None)
if all_vf_addrs:
all_vf_addr_list = all_vf_addrs.split(',')
vf_addr_list = interface.get_sriov_interface_vf_addrs(
context, iface, vf_addr_list)
vf_addr_list = ",".join(vf_addr_list)
context, iface, all_vf_addr_list)
# Format the vf addresses as quoted strings in order to prevent
# puppet from treating the address as a time/date value
vf_addrs = [quoted_str(addr.strip())
for addr in vf_addr_list.split(",") if addr]
vf_addrs = [quoted_str(addr.strip()) for addr in vf_addr_list if addr]
# Get the user specified VF driver, if any. If the driver is
# None, the driver will be determined by the kernel. That is,
@ -1055,12 +1054,24 @@ def get_sriov_vf_config(context, iface, port, vf_config):
vf_driver = port.get('sriov_vf_driver', None)
for addr in vf_addrs:
vf_config.update({
addr: {
'addr': addr,
'driver': vf_driver
}
})
rate = iface.get('max_tx_rate', None)
if rate:
vfnum = utils.get_sriov_vf_index(addr, all_vf_addr_list)
vf_config.update({
addr: {
'addr': addr,
'driver': vf_driver,
'vfnumber': vfnum,
'max_tx_rate': rate
}
})
else:
vf_config.update({
addr: {
'addr': addr,
'driver': vf_driver
}
})
if iface.get('used_by', None):
upper_ifaces = iface['used_by']

View File

@ -513,8 +513,32 @@ class KubernetesPuppet(base.BasePuppet):
driver_list.append(driver)
pf_name_list = resource['selectors']['pfNames']
if port['name'] not in pf_name_list:
pf_name_list.append(port['name'])
if ifclass == constants.INTERFACE_CLASS_PCI_SRIOV:
# In sriov case, we need specify each VF for resource pool
# Get VF addresses assigned to this logical VF interface
vf_addr_list = []
all_vf_addr_list = []
vf_addrs = port.get('sriov_vfs_pci_address', None)
if vf_addrs:
all_vf_addr_list = vf_addrs.split(',')
vf_addr_list = interface.get_sriov_interface_vf_addrs(
self.context, iface, all_vf_addr_list)
vfnolst = [utils.get_sriov_vf_index(addr, all_vf_addr_list)
for addr in vf_addr_list]
vfnolst = [str(vfno) for vfno in vfnolst]
vfnolist_str = ",".join(vfnolst)
if vfnolist_str:
# concat into the form of 'ens785f0#0,2,7,9'
pfname_with_vfs = "%s#%s" % (port['name'], vfnolist_str)
pf_name_list.append(pfname_with_vfs)
else:
# error case, cannot find the vf numbers in sriov case
LOG.error("Failed to get vf numbers for pci device %s", port['name'])
continue
else:
if port['name'] not in pf_name_list:
pf_name_list.append(port['name'])
if interface.is_a_mellanox_device(self.context, iface):
resource['selectors']['isRdma'] = True

View File

@ -205,31 +205,40 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
dbutils.create_test_datanetwork(**dn_values)
def _create_ethernet(self, ifname=None, networktype=None, ifclass=None,
datanetworks=None, host=None, expect_errors=False):
datanetworks=None, host=None, expect_errors=False,
lower_iface=None):
interface_id = len(self.profile['interfaces']) + 1
port = None
if not ifname:
ifname = (networktype or 'eth') + str(interface_id)
if not host:
host = self.controller
if not ifclass and networktype in constants.PLATFORM_NETWORK_TYPES:
ifclass = constants.INTERFACE_CLASS_PLATFORM
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)
if not lower_iface:
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)
self.profile['ports'].append(port)
if not networktype:
interface = dbutils.create_test_interface(ifname=ifname,
forihostid=host.id,
ihost_uuid=host.uuid)
else:
if lower_iface:
uses = [lower_iface['ifname']]
else:
uses = None
interface = self._post_get_test_interface(
ifname=ifname,
ifclass=ifclass,
uses=uses,
forihostid=host.id, ihost_uuid=host.uuid)
response = self._post_and_check(interface, expect_errors)
if expect_errors is False:
@ -252,7 +261,6 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
datanetwork_id=dn.id)
self.profile['interfaces'].append(interface)
self.profile['ports'].append(port)
return port, interface
@ -380,7 +388,8 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
sriov_totalvfs=sriov_totalvfs,
sriov_numvfs=sriov_numvfs,
driver='i40e',
sriov_vf_driver='i40evf')
sriov_vf_driver='i40evf',
speed=10000)
ifclass = constants.INTERFACE_CLASS_PCI_SRIOV
interface = self._post_get_test_interface(
@ -409,7 +418,7 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
def _create_vf(self, ifname, ifclass=None,
lower_iface=None, sriov_numvfs=None,
sriov_vf_driver=None, datanetworks=None, host=None,
expect_errors=False):
expect_errors=False, max_tx_rate=None):
if not host:
host = self.controller
if not lower_iface:
@ -429,7 +438,8 @@ class InterfaceTestCase(base.FunctionalTest, dbbase.BaseHostTestCase):
uses=[lower_iface['ifname']],
forihostid=host.id, ihost_uuid=host.uuid,
sriov_numvfs=sriov_numvfs,
sriov_vf_driver=sriov_vf_driver)
sriov_vf_driver=sriov_vf_driver,
max_tx_rate=max_tx_rate)
response = self._post_and_check(interface, expect_errors)
if expect_errors is False:
interface['uuid'] = response.json['uuid']
@ -618,6 +628,36 @@ class InterfaceControllerVlanOverEthernet(InterfaceTestCase):
self._create_and_apply_profile(self.controller)
class InterfaceEthernetOverSriov(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()
lower_port, lower_iface = self._create_sriov(
'sriov1', sriov_numvfs=2)
self._create_vf('vf1', lower_iface=lower_iface, sriov_numvfs=1,
sriov_vf_driver='vfio', datanetworks='group0-data1')
port, iface = self._create_ethernet(
'pxeboot', constants.NETWORK_TYPE_PXEBOOT, lower_iface=lower_iface)
self._create_vlan('oam', constants.NETWORK_TYPE_OAM,
constants.INTERFACE_CLASS_PLATFORM, 1, lower_iface)
self._create_vlan('mgmt', constants.NETWORK_TYPE_MGMT,
constants.INTERFACE_CLASS_PLATFORM, 2, lower_iface)
self._create_vlan('cluster', constants.NETWORK_TYPE_CLUSTER_HOST,
constants.INTERFACE_CLASS_PLATFORM, 3, lower_iface)
def setUp(self):
super(InterfaceEthernetOverSriov, self).setUp()
def test_ethernet_over_sriov_profile(self):
self._create_and_apply_profile(self.controller)
class InterfaceWorkerEthernet(InterfaceTestCase):
def _setup_configuration(self):
@ -989,6 +1029,39 @@ class InterfaceAIOVfOverSriov(InterfaceTestCase):
self._create_and_apply_profile(self.controller)
class InterfaceAIOVfWithRatelimitOverSriov(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=3, datanetworks='group0-data0')
self._create_vf('vf1', lower_iface=lower_iface, sriov_numvfs=1,
sriov_vf_driver='vfio', datanetworks='group0-data1')
self._create_vf('vf2', lower_iface=lower_iface, sriov_numvfs=1,
sriov_vf_driver='vfio', datanetworks='group0-data1',
max_tx_rate=100)
def setUp(self):
super(InterfaceAIOVfWithRatelimitOverSriov, self).setUp()
def test_AIO_vf_with_ratelimit_over_sriov_profile(self):
self._create_and_apply_profile(self.controller)
# Test that the unsupported config is rejected
class InterfaceAIOVlanOverDataEthernet(InterfaceTestCase):
@ -1952,6 +2025,86 @@ class TestPostMixin(object):
self.assertIn('VF interfaces can only use one lower',
response.json['error_message'])
def test_create_vf_interface_with_valid_ratelimit(self):
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT,
host=self.worker)
port, lower_iface = self._create_sriov(
'sriov', host=self.worker, sriov_numvfs=4)
# default speed is 10Gbps
self._create_vf('vf1', lower_iface=lower_iface,
host=self.worker, sriov_numvfs=1,
expect_errors=False, max_tx_rate=1000)
self._create_vf('vf2', lower_iface=lower_iface,
host=self.worker, sriov_numvfs=1,
expect_errors=False, max_tx_rate=8000)
def test_create_vf_interface_with_invalid_ratelimit(self):
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT,
host=self.worker)
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, max_tx_rate=1000)
# exceeds total bandwidth
self._create_vf('vf2', lower_iface=lower_iface,
host=self.worker, sriov_numvfs=1,
expect_errors=True, max_tx_rate=9000)
# rate is not a numeric value
self._create_vf('vf3', lower_iface=lower_iface,
host=self.worker, sriov_numvfs=1,
expect_errors=True, max_tx_rate='ratelimit')
def test_interface_vf_ratelimit_modify_add_ratelimit(self):
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT,
host=self.worker)
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)
patch_result = self.patch_dict_json(
'%s' % self._get_path(vf['uuid']),
max_tx_rate=2000)
self.assertEqual('application/json', patch_result.content_type)
self.assertEqual(http_client.OK, patch_result.status_code)
self.assertEqual(2000, patch_result.json['max_tx_rate'])
def test_interface_vf_ratelimit_modify_adjust_ratelimit(self):
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT,
host=self.worker)
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, max_tx_rate=1000)
patch_result = self.patch_dict_json(
'%s' % self._get_path(vf['uuid']),
max_tx_rate=2000)
self.assertEqual('application/json', patch_result.content_type)
self.assertEqual(http_client.OK, patch_result.status_code)
self.assertEqual(2000, patch_result.json['max_tx_rate'])
def test_interface_vf_ratelimit_modify_clear_ratelimit(self):
self._create_ethernet('mgmt', constants.NETWORK_TYPE_MGMT,
host=self.worker)
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, max_tx_rate=1000)
patch_result = self.patch_dict_json(
'%s' % self._get_path(vf['uuid']),
max_tx_rate=0)
self.assertEqual('application/json', patch_result.content_type)
self.assertEqual(http_client.OK, patch_result.status_code)
self.assertEqual(0, patch_result.json['max_tx_rate'])
class TestAIOPost(InterfaceTestCase):
def setUp(self):

View File

@ -1046,7 +1046,8 @@ def get_test_interface(**kw):
'sriov_numvfs': kw.get('sriov_numvfs', None),
'sriov_vf_driver': kw.get('sriov_vf_driver', None),
'sriov_vf_pdevice_id': kw.get('sriov_vf_pdevice_id', None),
'ptp_role': kw.get('ptp_role', None)
'ptp_role': kw.get('ptp_role', None),
'max_tx_rate': kw.get('max_tx_rate', None)
}
return interface

View File

@ -8,7 +8,9 @@ from __future__ import print_function
import os
import uuid
import yaml
import mock
from sysinv.common import utils
from sysinv.common import constants
from sysinv.puppet import interface
from sysinv.puppet import puppet
@ -250,7 +252,7 @@ class InterfaceTestCaseMixin(base.PuppetTestCaseMixin):
return db_interface
def _create_vf_test(self, ifname, num_vfs, vf_driver=None,
lower_iface=None):
lower_iface=None, max_tx_rate=None):
if not lower_iface:
lower_port, lower_iface = self._create_ethernet_test(
'sriov', constants.INTERFACE_CLASS_PCI_SRIOV,
@ -277,7 +279,8 @@ class InterfaceTestCaseMixin(base.PuppetTestCaseMixin):
'networktype': constants.NETWORK_TYPE_PCI_SRIOV,
'imtu': 1500,
'sriov_numvfs': num_vfs,
'sriov_vf_driver': vf_driver}
'sriov_vf_driver': vf_driver,
'max_tx_rate': max_tx_rate}
lower_iface['used_by'].append(interface['ifname'])
db_interface = dbutils.create_test_interface(**interface)
self.interfaces.append(db_interface)
@ -1417,11 +1420,12 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
self.assertEqual(expected, config)
def _create_sriov_vf_config(self, iface_vf_driver, port_vf_driver,
vf_addr_list, num_vfs):
vf_addr_list, num_vfs, max_tx_rate=None):
self.iface['ifclass'] = constants.INTERFACE_CLASS_PCI_SRIOV
self.iface['networktype'] = constants.NETWORK_TYPE_PCI_SRIOV
self.iface['sriov_vf_driver'] = iface_vf_driver
self.iface['sriov_numvfs'] = num_vfs
self.iface['max_tx_rate'] = max_tx_rate
self.port['sriov_vf_driver'] = port_vf_driver
self.port['sriov_vfs_pci_address'] = vf_addr_list
self._update_context()
@ -1577,6 +1581,59 @@ class InterfaceTestCase(InterfaceTestCaseMixin, dbbase.BaseHostTestCase):
vf_config=expected_vf_config)
self.assertEqual(expected, config)
@mock.patch.object(utils, 'get_sriov_vf_index')
def test_get_sriov_config_with_ratelimit(self, mock_get_sriov_vf_index):
vf_addr1 = "0000:81:00.0"
vf_addr2 = "0000:81:01.0"
device_id = '1572'
port_name = 'eth0'
vf_addr_list = "{},{}".format(vf_addr1, vf_addr2)
num_vfs = 4
max_tx_rate = 1000
mock_get_sriov_vf_index.side_effect = [0, 1]
config = self._create_sriov_vf_config(
constants.SRIOV_DRIVER_TYPE_VFIO, 'i40evf', vf_addr_list,
num_vfs, max_tx_rate)
expected_vf_config = {
'0000:81:00.0': {'addr': '0000:81:00.0', 'driver': 'vfio-pci', 'max_tx_rate': 1000, 'vfnumber': 0},
'0000:81:01.0': {'addr': '0000:81:01.0', 'driver': 'vfio-pci', 'max_tx_rate': 1000, 'vfnumber': 1}
}
expected = self._get_sriov_config(
ifname=self.iface['ifname'],
vf_driver='vfio-pci',
num_vfs=num_vfs,
device_id=device_id,
port_name=port_name,
vf_config=expected_vf_config)
self.assertEqual(expected, config)
def test_get_sriov_config_vf_sibling_with_ratelimit(self):
port, iface = self._create_ethernet_test(
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=4,
iface_sriov_vf_driver=None,
port_sriov_vf_driver="iavf",
sriov_vfs_pci_address="0000:b1:02.0,0000:b1:02.1,0000:b1:02.2,0000:b1:02.3")
self._create_vf_test("vf1", 2, 'vfio', lower_iface=iface)
self._create_vf_test("vf2", 1, 'netdevice', lower_iface=iface, max_tx_rate=1000)
self._update_context()
config = interface.get_sriov_config(self.context, iface)
expected_vf_config = {
'0000:b1:02.0': {'addr': '0000:b1:02.0', 'driver': None},
'0000:b1:02.1': {'addr': '0000:b1:02.1', 'driver': 'iavf', 'max_tx_rate': 1000, 'vfnumber': 1},
'0000:b1:02.2': {'addr': '0000:b1:02.2', 'driver': 'vfio-pci'},
'0000:b1:02.3': {'addr': '0000:b1:02.3', 'driver': 'vfio-pci'}
}
expected = self._get_sriov_config(
iface['ifname'], None,
num_vfs=4, pf_addr=port['pciaddr'],
port_name="eth1",
vf_config=expected_vf_config)
self.assertEqual(expected, config)
def test_is_a_mellanox_cx3_device_false(self):
self.assertFalse(
interface.is_a_mellanox_cx3_device(self.context, self.iface))

View File

@ -4,9 +4,11 @@
#
import json
import mock
import re
import uuid
from sysinv.common import utils
from sysinv.common import constants
from sysinv.common import device as dconstants
from sysinv.puppet import interface
@ -39,7 +41,8 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
self.port, self.iface = self._create_ethernet_test(
'sriov1', constants.INTERFACE_CLASS_PCI_SRIOV,
constants.NETWORK_TYPE_PCI_SRIOV, sriov_numvfs=2,
sriov_vf_driver='ixgbevf')
sriov_vf_driver='ixgbevf',
sriov_vfs_pci_address="0000:b1:02.0,0000:b1:02.1")
# Create a datanetwork and assign the interface to it
dn_values = {
@ -154,8 +157,9 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
}
return config
def test_generate_sriovdp_config_8086(self):
@mock.patch.object(utils, 'get_sriov_vf_index')
def test_generate_sriovdp_config_8086(self, mock_get_sriov_vf_index):
mock_get_sriov_vf_index.side_effect = [1, 2]
self._setup_iface_configuration()
test_config = {
'pf_vendor': 'Intel Corporation [8086]',
@ -171,13 +175,15 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
self._get_pcidp_vendor_id(self.port),
test_config['vf_device'],
test_config['vf_driver'],
pfName=self.port['name'],
pfName="%s#1,2" % self.port['name'],
datanetwork=self.datanetwork['name']
)
mock_get_sriov_vf_index.assert_called()
self.assertEqual(expected, actual)
def test_generate_sriovdp_config_mlx(self):
@mock.patch.object(utils, 'get_sriov_vf_index')
def test_generate_sriovdp_config_mlx(self, mock_get_sriov_vf_index):
mock_get_sriov_vf_index.side_effect = [1, 2]
self._setup_iface_configuration()
test_config = {
'pf_vendor': 'Mellanox Technologies [15b3]',
@ -193,7 +199,7 @@ class SriovdpTestCase(test_interface.InterfaceTestCaseMixin, dbbase.BaseHostTest
self._get_pcidp_vendor_id(self.port),
test_config['vf_device'],
test_config['vf_driver'],
pfName=self.port['name'],
pfName="%s#1,2" % self.port['name'],
datanetwork=self.datanetwork['name']
)
self.assertEqual(expected, actual)