diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py index e1d03ae10a5..3a4a4bdace2 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py @@ -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}) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py index a404e18f054..d1c276a0132 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py index 8a38879f1b7..d5ce6cc11ca 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py @@ -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 diff --git a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py index f983117c64e..8f8788600ed 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/mech_driver/mech_driver.py @@ -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, diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py index db4ca6bdbf0..527aba4dcd8 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py @@ -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) diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py index 385a1cd21f5..e57169a4f16 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py @@ -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." diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py index 768a521a961..1d367e7caf4 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py @@ -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, diff --git a/releasenotes/notes/add-minimum-bandwidth-support-sriov-63664b89f4dd1c1b.yaml b/releasenotes/notes/add-minimum-bandwidth-support-sriov-63664b89f4dd1c1b.yaml new file mode 100644 index 00000000000..dd0d1210908 --- /dev/null +++ b/releasenotes/notes/add-minimum-bandwidth-support-sriov-63664b89f4dd1c1b.yaml @@ -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``.