SR-IOV: add agent QoS driver to support egress minimum bandwidth

This patch adds SR-IOV agent driver, which uses eswitch manager, to set
VF min_tx_rate parameter. This parameter defines the guaranteed minimum
bandwidth for egress traffic.

DocImpact
Partial-Bug: #1560963

Change-Id: Iefe5e698e99d186202d6ef170f84e93bfbba46dd
This commit is contained in:
Rodolfo Alonso Hernandez 2016-07-18 11:52:12 +01:00 committed by Ihar Hrachyshka
parent a2dc3c35e3
commit 46de63c42e
8 changed files with 273 additions and 102 deletions

View File

@ -20,6 +20,7 @@ from oslo_log import log as logging
import six
from neutron._i18n import _, _LE, _LW
from neutron.agent.linux import ip_link_support
from neutron.common import utils
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
@ -170,37 +171,40 @@ class EmbSwitch(object):
vf_index = self._get_vf_index(pci_slot)
return self.pci_dev_wrapper.set_vf_state(vf_index, state)
def set_device_max_rate(self, pci_slot, max_kbps):
"""Set device max rate.
def set_device_rate(self, pci_slot, rate_type, rate_kbps):
"""Set device rate: rate (max_tx_rate), min_tx_rate
@param pci_slot: Virtual Function address
@param max_kbps: device max rate in kbps
@param rate_type: device rate name type. Could be 'rate' and
'min_tx_rate'.
@param rate_kbps: device rate in kbps
"""
vf_index = self._get_vf_index(pci_slot)
#(Note): ip link set max rate in Mbps therefore
#we need to convert the max_kbps to Mbps.
#Zero means to disable the rate so the lowest rate
#available is 1Mbps. Floating numbers are not allowed
if max_kbps > 0 and max_kbps < 1000:
max_mbps = 1
#NOTE(ralonsoh): ip link sets rate in Mbps therefore we need to convert
#the rate_kbps value from kbps to Mbps.
#Zero means to disable the rate so the lowest rate available is 1Mbps.
#Floating numbers are not allowed
if rate_kbps > 0 and rate_kbps < 1000:
rate_mbps = 1
else:
max_mbps = utils.round_val(max_kbps / 1000.0)
rate_mbps = utils.round_val(rate_kbps / 1000.0)
log_dict = {
'max_rate': max_mbps,
'max_kbps': max_kbps,
'vf_index': vf_index
'rate_mbps': rate_mbps,
'rate_kbps': rate_kbps,
'vf_index': vf_index,
'rate_type': rate_type
}
if max_kbps % 1000 != 0:
LOG.debug("Maximum rate for SR-IOV ports is counted in Mbps; "
"setting %(max_rate)s Mbps limit for port %(vf_index)s "
"instead of %(max_kbps)s kbps",
if rate_kbps % 1000 != 0:
LOG.debug("'%(rate_type)s' for SR-IOV ports is counted in Mbps; "
"setting %(rate_mbps)s Mbps limit for port %(vf_index)s "
"instead of %(rate_kbps)s kbps",
log_dict)
else:
LOG.debug("Setting %(max_rate)s Mbps limit for port %(vf_index)s",
LOG.debug("Setting %(rate_mbps)s Mbps limit for port %(vf_index)s",
log_dict)
return self.pci_dev_wrapper.set_vf_max_rate(vf_index, max_mbps)
return self.pci_dev_wrapper.set_vf_rate(vf_index, rate_type, rate_mbps)
def _get_vf_index(self, pci_slot):
vf_index = self.pci_slot_map.get(pci_slot)
@ -301,8 +305,25 @@ class ESwitchManager(object):
"""
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch:
embedded_switch.set_device_max_rate(pci_slot,
max_kbps)
embedded_switch.set_device_rate(
pci_slot,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE,
max_kbps)
def set_device_min_tx_rate(self, device_mac, pci_slot, min_kbps):
"""Set device min_tx_rate
Sets the device min_tx_rate in kbps
@param device_mac: device mac
@param pci_slot: pci slot
@param max_kbps: device min_tx_rate in kbps
"""
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch:
embedded_switch.set_device_rate(
pci_slot,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE,
min_kbps)
def set_device_state(self, device_mac, pci_slot, admin_state_up):
"""Set device state
@ -367,27 +388,29 @@ class ESwitchManager(object):
embedded_switch = None
return embedded_switch
def clear_max_rate(self, pci_slot):
"""Clear the max rate
def clear_rate(self, pci_slot, rate_type):
"""Clear the VF rate
Clear the max rate configuration from VF by setting it to 0
Clear the rate configuration from VF by setting it to 0.
@param pci_slot: VF PCI slot
@param rate_type: rate to clear ('rate', 'min_tx_rate')
"""
#(Note): we don't use the self._get_emb_eswitch here, because when
#clearing the VF it may be not assigned. This happens when libvirt
#releases the VF back to the hypervisor on delete VM. Therefore we
#should just clear the VF max rate according to pci_slot no matter
#NOTE(Moshe Levi): we don't use the self._get_emb_eswitch here, because
#when clearing the VF it may be not assigned. This happens when
#libvirt releases the VF back to the hypervisor on delete VM. Therefore
#we should just clear the VF rate according to pci_slot no matter
#if VF is assigned or not.
embedded_switch = self.pci_slot_map.get(pci_slot)
if embedded_switch:
#(Note): check the pci_slot is not assigned to some
# other port before resetting the max rate.
#NOTE(Moshe Levi): check the pci_slot is not assigned to some
#other port before resetting the rate.
if embedded_switch.get_pci_device(pci_slot) is None:
embedded_switch.set_device_max_rate(pci_slot, 0)
embedded_switch.set_device_rate(pci_slot, rate_type, 0)
else:
LOG.warning(_LW("VF with PCI slot %(pci_slot)s is already "
"assigned; skipping reset maximum rate"),
{'pci_slot': pci_slot})
"assigned; skipping reset for '%(rate_type)s' "
"device configuration parameter"),
{'pci_slot': pci_slot, 'rate_type': rate_type})
else:
LOG.error(_LE("PCI slot %(pci_slot)s has no mapping to Embedded "
"Switch; skipping"), {'pci_slot': pci_slot})

View File

@ -63,3 +63,32 @@ class QosSRIOVAgentDriver(qos.QosAgentDriver):
_LE("Failed to set device %s max rate"), device)
else:
LOG.info(_LI("No device with MAC %s defined on agent."), device)
# TODO(ihrachys): those handlers are pretty similar, probably could make
# use of some code deduplication
def create_minimum_bandwidth(self, port, rule):
self.update_minimum_bandwidth(port, rule)
def update_minimum_bandwidth(self, port, rule):
pci_slot = port['profile'].get('pci_slot')
device = port['device']
self._set_vf_min_tx_rate(device, pci_slot, rule.min_kbps)
def delete_minimum_bandwidth(self, port):
pci_slot = port['profile'].get('pci_slot')
if port.get('device_owner') is None:
self.eswitch_mgr.clear_min_tx_rate(pci_slot)
else:
device = port['device']
self._set_vf_min_tx_rate(device, pci_slot)
def _set_vf_min_tx_rate(self, device, pci_slot, min_tx_kbps=0):
if self.eswitch_mgr.device_exists(device, pci_slot):
try:
self.eswitch_mgr.set_device_min_tx_rate(
device, pci_slot, min_tx_kbps)
except exc.SriovNicError:
LOG.exception(
_LE("Failed to set device %s min_tx_rate"), device)
else:
LOG.info(_LI("No device with MAC %s defined on agent."), device)

View File

@ -138,13 +138,14 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
setting = "on" if enabled else "off"
self._set_feature(vf_index, "spoofchk", setting)
def set_vf_max_rate(self, vf_index, max_tx_rate):
"""sets vf max rate.
def set_vf_rate(self, vf_index, rate_type, rate_value):
"""sets vf rate.
@param vf_index: vf index
@param max_tx_rate: vf max tx rate in Mbps
@param rate_type: vf rate type ('rate', 'min_tx_rate')
@param rate_value: vf rate in Mbps
"""
self._set_feature(vf_index, "rate", str(max_tx_rate))
self._set_feature(vf_index, rate_type, str(rate_value))
def _get_vf_link_show(self, vf_list, link_show_out):
"""Get link show output for VFs

View File

@ -62,7 +62,10 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
L2 Agent presents in order to manage port update events.
"""
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT]
supported_qos_rule_types = [
qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH,
]
def __init__(self,
agent_type=constants.AGENT_TYPE_NIC_SWITCH,

View File

@ -16,6 +16,7 @@
import mock
from oslo_utils import uuidutils
from neutron.common import constants
from neutron import context
from neutron.objects.qos import policy
from neutron.objects.qos import rule
@ -38,11 +39,20 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
self.qos_driver.initialize()
self.qos_driver.eswitch_mgr = mock.Mock()
self.qos_driver.eswitch_mgr.set_device_max_rate = mock.Mock()
self.qos_driver.eswitch_mgr.set_device_min_tx_rate = mock.Mock()
self.qos_driver.eswitch_mgr.clear_max_rate = mock.Mock()
self.qos_driver.eswitch_mgr.clear_min_tx_rate = mock.Mock()
self.max_rate_mock = self.qos_driver.eswitch_mgr.set_device_max_rate
self.min_tx_rate_mock = \
self.qos_driver.eswitch_mgr.set_device_min_tx_rate
self.clear_max_rate_mock = self.qos_driver.eswitch_mgr.clear_max_rate
self.clear_min_tx_rate_mock = \
self.qos_driver.eswitch_mgr.clear_min_tx_rate
self.rule = self._create_bw_limit_rule_obj()
self.rule_min_tx_rate = self._create_minimum_bandwidth_rule_obj()
self.qos_policy = self._create_qos_policy_obj([self.rule])
self.qos_policy_min_tx_rate = self._create_qos_policy_obj(
[self.rule_min_tx_rate])
self.port = self._create_fake_port(self.qos_policy.id)
def _create_bw_limit_rule_obj(self):
@ -53,6 +63,14 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
rule_obj.obj_reset_changes()
return rule_obj
def _create_minimum_bandwidth_rule_obj(self):
rule_obj = rule.QosMinimumBandwidthRule()
rule_obj.id = uuidutils.generate_uuid()
rule_obj.min_kbps = 200
rule_obj.direction = constants.EGRESS_DIRECTION
rule_obj.obj_reset_changes()
return rule_obj
def _create_qos_policy_obj(self, rules):
policy_dict = {'id': uuidutils.generate_uuid(),
'tenant_id': uuidutils.generate_uuid(),
@ -104,3 +122,23 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
return_value=False):
self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT)
self.assertFalse(self.max_rate_mock.called)
def test_create_minimum_bandwidth(self):
self.qos_driver.create(self.port, self.qos_policy_min_tx_rate)
self.min_tx_rate_mock.assert_called_once_with(
self.ASSIGNED_MAC, self.PCI_SLOT, self.rule_min_tx_rate.min_kbps)
def test_update_minimum_bandwidth(self):
self.qos_driver.update(self.port, self.qos_policy_min_tx_rate)
self.min_tx_rate_mock.assert_called_once_with(
self.ASSIGNED_MAC, self.PCI_SLOT, self.rule_min_tx_rate.min_kbps)
def test_delete_minimum_bandwidth_on_assigned_vf(self):
self.qos_driver.delete(self.port, self.qos_policy_min_tx_rate)
self.min_tx_rate_mock.assert_called_once_with(
self.ASSIGNED_MAC, self.PCI_SLOT, 0)
def test_delete_minimum_bandwidth_on_released_vf(self):
del self.port['device_owner']
self.qos_driver.delete(self.port, self.qos_policy_min_tx_rate)
self.clear_min_tx_rate_mock.assert_called_once_with(self.PCI_SLOT)

View File

@ -18,6 +18,7 @@ import os
import mock
from neutron.agent.linux import ip_link_support
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
@ -71,6 +72,8 @@ class TestESwitchManagerApi(base.BaseTestCase):
PCI_SLOT = '0000:06:00.1'
WRONG_MAC = '00:00:00:00:00:67'
WRONG_PCI = "0000:06:00.6"
MAX_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE
MIN_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE
def setUp(self):
super(TestESwitchManagerApi, self).setUp()
@ -174,13 +177,26 @@ class TestESwitchManagerApi(base.BaseTestCase):
"eswitch_manager.EmbSwitch.get_pci_device",
return_value=self.ASSIGNED_MAC) as get_pci_mock,\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_max_rate")\
as set_device_max_rate_mock:
"eswitch_manager.EmbSwitch.set_device_rate")\
as set_device_rate_mock:
self.eswitch_mgr.set_device_max_rate(self.ASSIGNED_MAC,
self.PCI_SLOT, 1000)
get_pci_mock.assert_called_once_with(self.PCI_SLOT)
set_device_max_rate_mock.assert_called_once_with(
self.PCI_SLOT, 1000)
set_device_rate_mock.assert_called_once_with(
self.PCI_SLOT, self.MAX_RATE, 1000)
def test_set_device_min_tx_rate(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.get_pci_device",
return_value=self.ASSIGNED_MAC) as get_pci_mock,\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_rate")\
as set_device_rate_mock:
self.eswitch_mgr.set_device_min_tx_rate(self.ASSIGNED_MAC,
self.PCI_SLOT, 1000)
get_pci_mock.assert_called_once_with(self.PCI_SLOT)
set_device_rate_mock.assert_called_once_with(
self.PCI_SLOT, self.MIN_RATE, 1000)
def test_set_device_status_mismatch(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
@ -229,30 +245,42 @@ class TestESwitchManagerApi(base.BaseTestCase):
'device_mac': self.WRONG_MAC})
self.assertFalse(result)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
return_value={})
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_max_rate")
def test_clear_max_rate_existing_pci_slot(self, max_rate_mock, *args):
self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
max_rate_mock.assert_called_once_with(self.PCI_SLOT, 0)
def _test_clear_rate(self, rate_type, pci_slot, passed, mac_address):
with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.'
'eswitch_manager.EmbSwitch.set_device_rate') \
as set_rate_mock, \
mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.'
'pci_lib.PciDeviceIPWrapper.get_assigned_macs',
return_value=mac_address):
self.eswitch_mgr.clear_rate(pci_slot, rate_type)
if passed:
set_rate_mock.assert_called_once_with(pci_slot, rate_type, 0)
else:
self.assertFalse(set_rate_mock.called)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
return_value={0: ASSIGNED_MAC})
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_max_rate")
def test_clear_max_rate_exist_and_assigned_pci(
self, max_rate_mock, *args):
self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
self.assertFalse(max_rate_mock.called)
def test_clear_rate_max_rate_existing_pci_slot(self):
self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=True,
mac_address={})
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_max_rate")
def test_clear_max_rate_nonexisting_pci_slot(self, max_rate_mock):
self.eswitch_mgr.clear_max_rate(self.WRONG_PCI)
self.assertFalse(max_rate_mock.called)
def test_clear_rate_max_rate_exist_and_assigned_pci(self):
self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=False,
mac_address={0: self.ASSIGNED_MAC})
def test_clear_rate_max_rate_nonexisting_pci_slot(self):
self._test_clear_rate(self.MAX_RATE, self.WRONG_PCI, passed=False,
mac_address={})
def test_clear_rate_min_tx_rate_existing_pci_slot(self):
self._test_clear_rate(self.MIN_RATE, self.PCI_SLOT, passed=True,
mac_address={})
def test_clear_rate_min_tx_rate_exist_and_assigned_pci(self):
self._test_clear_rate(self.MIN_RATE, self.PCI_SLOT, passed=False,
mac_address={0: self.ASSIGNED_MAC})
def test_clear_rate_min_tx_rate_nonexisting_pci_slot(self):
self._test_clear_rate(self.MIN_RATE, self.WRONG_PCI, passed=False,
mac_address={})
class TestEmbSwitch(base.BaseTestCase):
@ -365,48 +393,68 @@ class TestEmbSwitch(base.BaseTestCase):
self.emb_switch.set_device_spoofcheck,
self.WRONG_PCI_SLOT, True)
def test_set_device_max_rate_ok(self):
def test_set_device_rate_ok(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2000)
pci_lib_mock.assert_called_with(0, 2)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2000)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 99)
pci_lib_mock.assert_called_with(0, 1)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 99)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1)
def test_set_device_max_rate_rounded_ok(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2001)
pci_lib_mock.assert_called_with(0, 2)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2001)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_rounded_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2499)
pci_lib_mock.assert_called_with(0, 2)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2499)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_rounded_ok3(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2500)
pci_lib_mock.assert_called_with(0, 3)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2500)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 3)
def test_set_device_max_rate_disable(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 0)
pci_lib_mock.assert_called_with(0, 0)
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate(
self.PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0)
def test_set_device_max_rate_fail(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate"):
self.assertRaises(exc.InvalidPciSlotError,
self.emb_switch.set_device_max_rate,
self.WRONG_PCI_SLOT, 1000)
"PciDeviceIPWrapper.set_vf_rate"):
self.assertRaises(
exc.InvalidPciSlotError,
self.emb_switch.set_device_rate,
self.WRONG_PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1000)
def test_get_pci_device(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."

View File

@ -16,6 +16,7 @@
import mock
from neutron.agent.linux import ip_link_support
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
@ -127,22 +128,42 @@ class TestPciLib(base.BaseTestCase):
self.VF_INDEX,
True)
def test_set_vf_max_rate(self):
with mock.patch.object(self.pci_wrapper, "_as_root") \
as mock_as_root:
result = self.pci_wrapper.set_vf_max_rate(self.VF_INDEX, 1000)
self.assertIsNone(result)
mock_as_root.assert_called_once_with([], "link",
("set", self.DEV_NAME, "vf", str(self.VF_INDEX), "rate", '1000'))
def _set_vf_rate(self, rate, passed=True):
if passed:
with mock.patch.object(self.pci_wrapper, "_as_root") \
as mock_as_root:
result = self.pci_wrapper.set_vf_rate(
self.VF_INDEX,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE,
1000)
self.assertIsNone(result)
mock_as_root.assert_called_once_with(
[], "link", ("set", self.DEV_NAME, "vf",
str(self.VF_INDEX), "rate", '1000'))
else:
with mock.patch.object(self.pci_wrapper, "_execute",
side_effect=Exception()):
self.assertRaises(exc.IpCommandDeviceError,
self.pci_wrapper.set_vf_rate,
self.VF_INDEX,
rate,
1000)
def test_set_vf_max_rate_fail(self):
with mock.patch.object(self.pci_wrapper,
"_execute") as mock_exec:
mock_exec.side_effect = Exception()
self.assertRaises(exc.IpCommandDeviceError,
self.pci_wrapper.set_vf_max_rate,
self.VF_INDEX,
1000)
def test_set_vf_rate_max_rate(self):
self._set_vf_rate(
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE)
def test_set_vf_rate_max_rate_fail(self):
self._set_vf_rate('rate', passed=False)
def test_set_vf_rate_min_tx_rate(self):
self._set_vf_rate(
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE)
def test_set_vf_rate_min_tx_rate_fail(self):
self._set_vf_rate(
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE,
passed=False)
def test_set_vf_state_not_supported(self):
with mock.patch.object(self.pci_wrapper,

View File

@ -0,0 +1,8 @@
---
features:
- SR-IOV now supports egress minimum bandwidth configuration.
other:
- In order to use QoS egress minimum bandwidth limit feature, 'ip-link' must
support the extended VF management parameter ``min_tx_rate``. Minimum
version of ``ip-link`` supporting this parameter is ``iproute2-ss140804``,
git tag ``v3.16.0``.