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 import six
from neutron._i18n import _, _LE, _LW from neutron._i18n import _, _LE, _LW
from neutron.agent.linux import ip_link_support
from neutron.common import utils from neutron.common import utils
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc import exceptions as exc
@ -170,37 +171,40 @@ class EmbSwitch(object):
vf_index = self._get_vf_index(pci_slot) vf_index = self._get_vf_index(pci_slot)
return self.pci_dev_wrapper.set_vf_state(vf_index, state) return self.pci_dev_wrapper.set_vf_state(vf_index, state)
def set_device_max_rate(self, pci_slot, max_kbps): def set_device_rate(self, pci_slot, rate_type, rate_kbps):
"""Set device max rate. """Set device rate: rate (max_tx_rate), min_tx_rate
@param pci_slot: Virtual Function address @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) vf_index = self._get_vf_index(pci_slot)
#(Note): ip link set max rate in Mbps therefore #NOTE(ralonsoh): ip link sets rate in Mbps therefore we need to convert
#we need to convert the max_kbps to Mbps. #the rate_kbps value from kbps to Mbps.
#Zero means to disable the rate so the lowest rate #Zero means to disable the rate so the lowest rate available is 1Mbps.
#available is 1Mbps. Floating numbers are not allowed #Floating numbers are not allowed
if max_kbps > 0 and max_kbps < 1000: if rate_kbps > 0 and rate_kbps < 1000:
max_mbps = 1 rate_mbps = 1
else: else:
max_mbps = utils.round_val(max_kbps / 1000.0) rate_mbps = utils.round_val(rate_kbps / 1000.0)
log_dict = { log_dict = {
'max_rate': max_mbps, 'rate_mbps': rate_mbps,
'max_kbps': max_kbps, 'rate_kbps': rate_kbps,
'vf_index': vf_index 'vf_index': vf_index,
'rate_type': rate_type
} }
if max_kbps % 1000 != 0: if rate_kbps % 1000 != 0:
LOG.debug("Maximum rate for SR-IOV ports is counted in Mbps; " LOG.debug("'%(rate_type)s' for SR-IOV ports is counted in Mbps; "
"setting %(max_rate)s Mbps limit for port %(vf_index)s " "setting %(rate_mbps)s Mbps limit for port %(vf_index)s "
"instead of %(max_kbps)s kbps", "instead of %(rate_kbps)s kbps",
log_dict) log_dict)
else: 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) 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): def _get_vf_index(self, pci_slot):
vf_index = self.pci_slot_map.get(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) embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch: if embedded_switch:
embedded_switch.set_device_max_rate(pci_slot, embedded_switch.set_device_rate(
max_kbps) 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): def set_device_state(self, device_mac, pci_slot, admin_state_up):
"""Set device state """Set device state
@ -367,27 +388,29 @@ class ESwitchManager(object):
embedded_switch = None embedded_switch = None
return embedded_switch return embedded_switch
def clear_max_rate(self, pci_slot): def clear_rate(self, pci_slot, rate_type):
"""Clear the max rate """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 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 #NOTE(Moshe Levi): we don't use the self._get_emb_eswitch here, because
#clearing the VF it may be not assigned. This happens when libvirt #when clearing the VF it may be not assigned. This happens when
#releases the VF back to the hypervisor on delete VM. Therefore we #libvirt releases the VF back to the hypervisor on delete VM. Therefore
#should just clear the VF max rate according to pci_slot no matter #we should just clear the VF rate according to pci_slot no matter
#if VF is assigned or not. #if VF is assigned or not.
embedded_switch = self.pci_slot_map.get(pci_slot) embedded_switch = self.pci_slot_map.get(pci_slot)
if embedded_switch: if embedded_switch:
#(Note): check the pci_slot is not assigned to some #NOTE(Moshe Levi): check the pci_slot is not assigned to some
# other port before resetting the max rate. #other port before resetting the rate.
if embedded_switch.get_pci_device(pci_slot) is None: 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: else:
LOG.warning(_LW("VF with PCI slot %(pci_slot)s is already " LOG.warning(_LW("VF with PCI slot %(pci_slot)s is already "
"assigned; skipping reset maximum rate"), "assigned; skipping reset for '%(rate_type)s' "
{'pci_slot': pci_slot}) "device configuration parameter"),
{'pci_slot': pci_slot, 'rate_type': rate_type})
else: else:
LOG.error(_LE("PCI slot %(pci_slot)s has no mapping to Embedded " LOG.error(_LE("PCI slot %(pci_slot)s has no mapping to Embedded "
"Switch; skipping"), {'pci_slot': pci_slot}) "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) _LE("Failed to set device %s max rate"), device)
else: else:
LOG.info(_LI("No device with MAC %s defined on agent."), device) 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" setting = "on" if enabled else "off"
self._set_feature(vf_index, "spoofchk", setting) self._set_feature(vf_index, "spoofchk", setting)
def set_vf_max_rate(self, vf_index, max_tx_rate): def set_vf_rate(self, vf_index, rate_type, rate_value):
"""sets vf max rate. """sets vf rate.
@param vf_index: vf index @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): def _get_vf_link_show(self, vf_list, link_show_out):
"""Get link show output for VFs """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. 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, def __init__(self,
agent_type=constants.AGENT_TYPE_NIC_SWITCH, agent_type=constants.AGENT_TYPE_NIC_SWITCH,

View File

@ -16,6 +16,7 @@
import mock import mock
from oslo_utils import uuidutils from oslo_utils import uuidutils
from neutron.common import constants
from neutron import context from neutron import context
from neutron.objects.qos import policy from neutron.objects.qos import policy
from neutron.objects.qos import rule from neutron.objects.qos import rule
@ -38,11 +39,20 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
self.qos_driver.initialize() self.qos_driver.initialize()
self.qos_driver.eswitch_mgr = mock.Mock() 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_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_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.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_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 = 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 = 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) self.port = self._create_fake_port(self.qos_policy.id)
def _create_bw_limit_rule_obj(self): def _create_bw_limit_rule_obj(self):
@ -53,6 +63,14 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
rule_obj.obj_reset_changes() rule_obj.obj_reset_changes()
return rule_obj 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): def _create_qos_policy_obj(self, rules):
policy_dict = {'id': uuidutils.generate_uuid(), policy_dict = {'id': uuidutils.generate_uuid(),
'tenant_id': uuidutils.generate_uuid(), 'tenant_id': uuidutils.generate_uuid(),
@ -104,3 +122,23 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
return_value=False): return_value=False):
self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT) self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT)
self.assertFalse(self.max_rate_mock.called) 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 import mock
from neutron.agent.linux import ip_link_support
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm 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' PCI_SLOT = '0000:06:00.1'
WRONG_MAC = '00:00:00:00:00:67' WRONG_MAC = '00:00:00:00:00:67'
WRONG_PCI = "0000:06:00.6" 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): def setUp(self):
super(TestESwitchManagerApi, self).setUp() super(TestESwitchManagerApi, self).setUp()
@ -174,13 +177,26 @@ class TestESwitchManagerApi(base.BaseTestCase):
"eswitch_manager.EmbSwitch.get_pci_device", "eswitch_manager.EmbSwitch.get_pci_device",
return_value=self.ASSIGNED_MAC) as get_pci_mock,\ return_value=self.ASSIGNED_MAC) as get_pci_mock,\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.set_device_max_rate")\ "eswitch_manager.EmbSwitch.set_device_rate")\
as set_device_max_rate_mock: as set_device_rate_mock:
self.eswitch_mgr.set_device_max_rate(self.ASSIGNED_MAC, self.eswitch_mgr.set_device_max_rate(self.ASSIGNED_MAC,
self.PCI_SLOT, 1000) self.PCI_SLOT, 1000)
get_pci_mock.assert_called_once_with(self.PCI_SLOT) 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, 1000) 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): def test_set_device_status_mismatch(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
@ -229,30 +245,42 @@ class TestESwitchManagerApi(base.BaseTestCase):
'device_mac': self.WRONG_MAC}) 'device_mac': self.WRONG_MAC})
self.assertFalse(result) self.assertFalse(result)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." def _test_clear_rate(self, rate_type, pci_slot, passed, mac_address):
"PciDeviceIPWrapper.get_assigned_macs", with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.'
return_value={}) 'eswitch_manager.EmbSwitch.set_device_rate') \
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." as set_rate_mock, \
"eswitch_manager.EmbSwitch.set_device_max_rate") mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.'
def test_clear_max_rate_existing_pci_slot(self, max_rate_mock, *args): 'pci_lib.PciDeviceIPWrapper.get_assigned_macs',
self.eswitch_mgr.clear_max_rate(self.PCI_SLOT) return_value=mac_address):
max_rate_mock.assert_called_once_with(self.PCI_SLOT, 0) 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." def test_clear_rate_max_rate_existing_pci_slot(self):
"PciDeviceIPWrapper.get_assigned_macs", self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=True,
return_value={0: ASSIGNED_MAC}) mac_address={})
@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)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." def test_clear_rate_max_rate_exist_and_assigned_pci(self):
"eswitch_manager.EmbSwitch.set_device_max_rate") self._test_clear_rate(self.MAX_RATE, self.PCI_SLOT, passed=False,
def test_clear_max_rate_nonexisting_pci_slot(self, max_rate_mock): mac_address={0: self.ASSIGNED_MAC})
self.eswitch_mgr.clear_max_rate(self.WRONG_PCI)
self.assertFalse(max_rate_mock.called) 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): class TestEmbSwitch(base.BaseTestCase):
@ -365,48 +393,68 @@ class TestEmbSwitch(base.BaseTestCase):
self.emb_switch.set_device_spoofcheck, self.emb_switch.set_device_spoofcheck,
self.WRONG_PCI_SLOT, True) 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." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2000) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 2) 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): def test_set_device_max_rate_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 99) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 1) 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): def test_set_device_max_rate_rounded_ok(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2001) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 2) 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): def test_set_device_max_rate_rounded_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2499) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 2) 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): def test_set_device_max_rate_rounded_ok3(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 2500) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 3) 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): def test_set_device_max_rate_disable(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_max_rate(self.PCI_SLOT, 0) self.emb_switch.set_device_rate(
pci_lib_mock.assert_called_with(0, 0) 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): def test_set_device_max_rate_fail(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_max_rate"): "PciDeviceIPWrapper.set_vf_rate"):
self.assertRaises(exc.InvalidPciSlotError, self.assertRaises(
self.emb_switch.set_device_max_rate, exc.InvalidPciSlotError,
self.WRONG_PCI_SLOT, 1000) 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): def test_get_pci_device(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."

View File

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