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
|
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})
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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."
|
||||||
|
@ -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,
|
||||||
|
@ -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