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:
parent
a2dc3c35e3
commit
46de63c42e
@ -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,9 +305,26 @@ class ESwitchManager(object):
|
||||
"""
|
||||
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
|
||||
if embedded_switch:
|
||||
embedded_switch.set_device_max_rate(pci_slot,
|
||||
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})
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
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."
|
||||
|
@ -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):
|
||||
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_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 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,
|
||||
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_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,
|
||||
|
@ -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``.
|
Loading…
Reference in New Issue
Block a user