diff options
17 files changed, 264 insertions, 163 deletions
diff --git a/doc/source/devref/quality_of_service.rst b/doc/source/devref/quality_of_service.rst index 0418aa2a35..bd4a8c716d 100644 --- a/doc/source/devref/quality_of_service.rst +++ b/doc/source/devref/quality_of_service.rst @@ -267,20 +267,17 @@ Open vSwitch Open vSwitch implementation relies on the new ovs_lib OVSBridge functions: -* create_qos_bw_limit_for_port -* get_qos_bw_limit_for_port -* del_qos_bw_limit_for_port +* get_egress_bw_limit_for_port +* create_egress_bw_limit_for_port +* delete_egress_bw_limit_for_port -An egress bandwidth limit is effectively configured on the port by creating a -single QoS queue with min-rate=rule.max_kbps, max-rate=rule.max_kbps and -burst=rule.max_burst_kbps. Then a linux-htb QoS policy is defined on the port, -attached to the queue. - -HTB queues are supported at least in all 2.x versions of Open vSwitch. - -More details about HTB in `the blog post -<https://virtualandy.wordpress.com/2013/04/29/deep-dive-htb-rate-limiting-qos-on-with-open-vswitch-and-xenserver/>`_. +An egress bandwidth limit is effectively configured on the port by setting +the port Interface parameters ingress_policing_rate and +ingress_policing_burst. +That approach is less flexible than linux-htb, Queues and OvS QoS profiles, +which we may explore in the future, but which will need to be used in +combination with openflow rules. SR-IOV ~~~~~~ diff --git a/neutron/agent/common/ovs_lib.py b/neutron/agent/common/ovs_lib.py index a4b22ea127..9c23dd6ba6 100644 --- a/neutron/agent/common/ovs_lib.py +++ b/neutron/agent/common/ovs_lib.py @@ -489,80 +489,35 @@ class OVSBridge(BaseOVS): txn.add(self.ovsdb.db_set('Controller', controller_uuid, *attr)) - def _create_qos_bw_limit_queue(self, port_name, max_bw_in_bits, - max_burst_in_bits): - external_ids = {'id': port_name} - queue_other_config = {'min-rate': max_bw_in_bits, - 'max-rate': max_bw_in_bits, - 'burst': max_burst_in_bits} - - self.ovsdb.db_create( - 'Queue', external_ids=external_ids, - other_config=queue_other_config).execute(check_error=True) - - def _create_qos_bw_limit_profile(self, port_name, max_bw_in_bits): - external_ids = {'id': port_name} - queue = self.ovsdb.db_find( - 'Queue', - ('external_ids', '=', {'id': port_name}), - columns=['_uuid']).execute( - check_error=True) - queues = {} - queues[0] = queue[0]['_uuid'] - qos_other_config = {'max-rate': max_bw_in_bits} - self.ovsdb.db_create('QoS', external_ids=external_ids, - other_config=qos_other_config, - type='linux-htb', - queues=queues).execute(check_error=True) - - def create_qos_bw_limit_for_port(self, port_name, max_kbps, - max_burst_kbps): - # TODO(QoS) implement this with transactions, - # or roll back on failure - max_bw_in_bits = str(max_kbps * 1000) - max_burst_in_bits = str(max_burst_kbps * 1000) - - self._create_qos_bw_limit_queue(port_name, max_bw_in_bits, - max_burst_in_bits) - self._create_qos_bw_limit_profile(port_name, max_bw_in_bits) - - qos = self.ovsdb.db_find('QoS', - ('external_ids', '=', {'id': port_name}), - columns=['_uuid']).execute(check_error=True) - qos_profile = qos[0]['_uuid'] - self.set_db_attribute('Port', port_name, 'qos', qos_profile, - check_error=True) + def _set_egress_bw_limit_for_port(self, port_name, max_kbps, + max_burst_kbps): + with self.ovsdb.transaction(check_error=True) as txn: + txn.add(self.ovsdb.db_set('Interface', port_name, + ('ingress_policing_rate', max_kbps))) + txn.add(self.ovsdb.db_set('Interface', port_name, + ('ingress_policing_burst', + max_burst_kbps))) - def get_qos_bw_limit_for_port(self, port_name): + def create_egress_bw_limit_for_port(self, port_name, max_kbps, + max_burst_kbps): + self._set_egress_bw_limit_for_port( + port_name, max_kbps, max_burst_kbps) - res = self.ovsdb.db_find( - 'Queue', - ('external_ids', '=', {'id': port_name}), - columns=['other_config']).execute(check_error=True) + def get_egress_bw_limit_for_port(self, port_name): - if res is None or len(res) == 0: - return None, None + max_kbps = self.db_get_val('Interface', port_name, + 'ingress_policing_rate') + max_burst_kbps = self.db_get_val('Interface', port_name, + 'ingress_policing_burst') - other_config = res[0]['other_config'] - max_kbps = int(other_config['max-rate']) / 1000 - max_burst_kbps = int(other_config['burst']) / 1000 - return max_kbps, max_burst_kbps + max_kbps = max_kbps or None + max_burst_kbps = max_burst_kbps or None - def del_qos_bw_limit_for_port(self, port_name): - qos = self.ovsdb.db_find('QoS', - ('external_ids', '=', {'id': port_name}), - columns=['_uuid']).execute(check_error=True) - qos_row = qos[0]['_uuid'] - - queue = self.ovsdb.db_find('Queue', - ('external_ids', '=', {'id': port_name}), - columns=['_uuid']).execute(check_error=True) - queue_row = queue[0]['_uuid'] + return max_kbps, max_burst_kbps - with self.ovsdb.transaction(check_error=True) as txn: - txn.add(self.ovsdb.db_set('Port', port_name, ('qos', []))) - txn.add(self.ovsdb.db_destroy('QoS', qos_row)) - txn.add(self.ovsdb.db_destroy('Queue', queue_row)) + def delete_egress_bw_limit_for_port(self, port_name): + self._set_egress_bw_limit_for_port( + port_name, 0, 0) def __enter__(self): self.create() diff --git a/neutron/common/utils.py b/neutron/common/utils.py index 579766fb42..6c9d9b17b7 100644 --- a/neutron/common/utils.py +++ b/neutron/common/utils.py @@ -19,6 +19,7 @@ """Utilities and helper functions.""" import datetime +import decimal import errno import functools import hashlib @@ -442,3 +443,10 @@ class DelayedStringRenderer(object): def camelize(s): return ''.join(s.replace('_', ' ').title().split()) + + +def round_val(val): + # we rely on decimal module since it behaves consistently across Python + # versions (2.x vs. 3.x) + return int(decimal.Decimal(val).quantize(decimal.Decimal('1'), + rounding=decimal.ROUND_HALF_UP)) diff --git a/neutron/core_extensions/qos.py b/neutron/core_extensions/qos.py index c2caae0cf8..72fb898836 100644 --- a/neutron/core_extensions/qos.py +++ b/neutron/core_extensions/qos.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.common import exceptions as n_exc from neutron.core_extensions import base from neutron.db import api as db_api from neutron import manager @@ -31,7 +32,10 @@ class QosCoreResourceExtension(base.CoreResourceExtension): return self._plugin_loaded def _get_policy_obj(self, context, policy_id): - return policy_object.QosPolicy.get_by_id(context, policy_id) + obj = policy_object.QosPolicy.get_by_id(context, policy_id) + if obj is None: + raise n_exc.QosPolicyNotFound(policy_id=policy_id) + return obj def _update_port_policy(self, context, port, port_changes): old_policy = policy_object.QosPolicy.get_port_policy( @@ -42,9 +46,6 @@ class QosCoreResourceExtension(base.CoreResourceExtension): qos_policy_id = port_changes.get(qos_consts.QOS_POLICY_ID) if qos_policy_id is not None: policy = self._get_policy_obj(context, qos_policy_id) - #TODO(QoS): If the policy doesn't exist (or if it is not shared and - # the tenant id doesn't match the context's), this will - # raise an exception (policy is None). policy.attach_port(port['id']) port[qos_consts.QOS_POLICY_ID] = qos_policy_id @@ -57,9 +58,6 @@ class QosCoreResourceExtension(base.CoreResourceExtension): qos_policy_id = network_changes.get(qos_consts.QOS_POLICY_ID) if qos_policy_id is not None: policy = self._get_policy_obj(context, qos_policy_id) - #TODO(QoS): If the policy doesn't exist (or if it is not shared and - # the tenant id doesn't match the context's), this will - # raise an exception (policy is None). policy.attach_network(network['id']) network[qos_consts.QOS_POLICY_ID] = qos_policy_id diff --git a/neutron/plugins/ml2/common/exceptions.py b/neutron/plugins/ml2/common/exceptions.py index ed94b1e1f1..166711d8ee 100644 --- a/neutron/plugins/ml2/common/exceptions.py +++ b/neutron/plugins/ml2/common/exceptions.py @@ -21,3 +21,8 @@ from neutron.common import exceptions class MechanismDriverError(exceptions.NeutronException): """Mechanism driver call failed.""" message = _("%(method)s failed.") + + +class ExtensionDriverError(exceptions.InvalidInput): + """Extension driver call failed.""" + message = _("Extension %(driver)s failed.") 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 c426794373..12168883e8 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 @@ import re from oslo_log import log as logging import six +from neutron.common import utils from neutron.i18n import _LE, _LW from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc @@ -163,7 +164,30 @@ class EmbSwitch(object): @param max_kbps: device max rate in kbps """ vf_index = self._get_vf_index(pci_slot) - return self.pci_dev_wrapper.set_vf_max_rate(vf_index, max_kbps) + #(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 + else: + max_mbps = utils.round_val(max_kbps / 1000.0) + + log_dict = { + 'max_rate': max_mbps, + 'max_kbps': max_kbps, + 'vf_index': vf_index + } + 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", + log_dict) + else: + LOG.debug("Setting %(max_rate)s Mbps limit for port %(vf_index)s", + log_dict) + + return self.pci_dev_wrapper.set_vf_max_rate(vf_index, max_mbps) def _get_vf_index(self, pci_slot): vf_index = self.pci_slot_map.get(pci_slot) 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 a1e31cd666..8f984e0aac 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py @@ -126,7 +126,7 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper): """sets vf max rate. @param vf_index: vf index - @param max_tx_rate: vf max tx rate + @param max_tx_rate: vf max tx rate in Mbps """ try: self._as_root([], "link", ("set", self.dev_name, "vf", diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py b/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py index 51c6564f58..ce9f286878 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/extension_drivers/qos_driver.py @@ -67,18 +67,10 @@ class QosOVSAgentDriver(qos.QosAgentDriver): max_kbps = rule.max_kbps max_burst_kbps = rule.max_burst_kbps - current_max_kbps, current_max_burst = ( - self.br_int.get_qos_bw_limit_for_port(port_name)) - if current_max_kbps is not None or current_max_burst is not None: - self.br_int.del_qos_bw_limit_for_port(port_name) - - self.br_int.create_qos_bw_limit_for_port(port_name, - max_kbps, - max_burst_kbps) + self.br_int.create_egress_bw_limit_for_port(port_name, + max_kbps, + max_burst_kbps) def _delete_bandwidth_limit(self, port): port_name = port['vif_port'].port_name - current_max_kbps, current_max_burst = ( - self.br_int.get_qos_bw_limit_for_port(port_name)) - if current_max_kbps is not None or current_max_burst is not None: - self.br_int.del_qos_bw_limit_for_port(port_name) + self.br_int.delete_egress_bw_limit_for_port(port_name) diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index d4b4908811..690e4ab4e2 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -15,6 +15,7 @@ from oslo_config import cfg from oslo_log import log +from oslo_utils import excutils import six import stevedore @@ -800,10 +801,10 @@ class ExtensionManager(stevedore.named.NamedExtensionManager): try: getattr(driver.obj, method_name)(plugin_context, data, result) except Exception: - LOG.exception( - _LE("Extension driver '%(name)s' failed in %(method)s"), - {'name': driver.name, 'method': method_name} - ) + with excutils.save_and_reraise_exception(): + LOG.info(_LI("Extension driver '%(name)s' failed in " + "%(method)s"), + {'name': driver.name, 'method': method_name}) def process_create_network(self, plugin_context, data, result): """Notify all extension drivers during network creation.""" @@ -835,23 +836,30 @@ class ExtensionManager(stevedore.named.NamedExtensionManager): self._call_on_ext_drivers("process_update_port", plugin_context, data, result) + def _call_on_dict_driver(self, method_name, session, base_model, result): + for driver in self.ordered_ext_drivers: + try: + getattr(driver.obj, method_name)(session, base_model, result) + except Exception: + LOG.error(_LE("Extension driver '%(name)s' failed in " + "%(method)s"), + {'name': driver.name, 'method': method_name}) + raise ml2_exc.ExtensionDriverError(driver=driver.name) + + LOG.debug("%(method)s succeeded for driver %(driver)s", + {'method': method_name, 'driver': driver.name}) + def extend_network_dict(self, session, base_model, result): """Notify all extension drivers to extend network dictionary.""" - for driver in self.ordered_ext_drivers: - driver.obj.extend_network_dict(session, base_model, result) - LOG.debug("Extended network dict for driver '%(drv)s'", - {'drv': driver.name}) + self._call_on_dict_driver("extend_network_dict", session, base_model, + result) def extend_subnet_dict(self, session, base_model, result): """Notify all extension drivers to extend subnet dictionary.""" - for driver in self.ordered_ext_drivers: - driver.obj.extend_subnet_dict(session, base_model, result) - LOG.debug("Extended subnet dict for driver '%(drv)s'", - {'drv': driver.name}) + self._call_on_dict_driver("extend_subnet_dict", session, base_model, + result) def extend_port_dict(self, session, base_model, result): """Notify all extension drivers to extend port dictionary.""" - for driver in self.ordered_ext_drivers: - driver.obj.extend_port_dict(session, base_model, result) - LOG.debug("Extended port dict for driver '%(drv)s'", - {'drv': driver.name}) + self._call_on_dict_driver("extend_port_dict", session, base_model, + result) diff --git a/neutron/tests/api/test_qos.py b/neutron/tests/api/test_qos.py index b4cb4cc864..d281094b36 100644 --- a/neutron/tests/api/test_qos.py +++ b/neutron/tests/api/test_qos.py @@ -158,18 +158,24 @@ class QosTestJSON(base.BaseAdminNetworkTest): self._disassociate_network(self.client, network['id']) @test.attr(type='smoke') + @test.idempotent_id('9efe63d0-836f-4cc2-b00c-468e63aa614e') + def test_policy_association_with_network_nonexistent_policy(self): + self.assertRaises( + exceptions.NotFound, + self.create_network, + 'test network', + qos_policy_id='9efe63d0-836f-4cc2-b00c-468e63aa614e') + + @test.attr(type='smoke') @test.idempotent_id('1aa55a79-324f-47d9-a076-894a8fc2448b') def test_policy_association_with_network_non_shared_policy(self): policy = self.create_qos_policy(name='test-policy', description='test policy', shared=False) - #TODO(QoS): This currently raises an exception on the server side. See - # core_extensions/qos.py for comments on this subject. - network = self.create_network('test network', - qos_policy_id=policy['id']) - - retrieved_network = self.admin_client.show_network(network['id']) - self.assertIsNone(retrieved_network['network']['qos_policy_id']) + self.assertRaises( + exceptions.NotFound, + self.create_network, + 'test network', qos_policy_id=policy['id']) @test.attr(type='smoke') @test.idempotent_id('09a9392c-1359-4cbb-989f-fb768e5834a8') @@ -210,18 +216,26 @@ class QosTestJSON(base.BaseAdminNetworkTest): self._disassociate_port(port['id']) @test.attr(type='smoke') + @test.idempotent_id('49e02f5a-e1dd-41d5-9855-cfa37f2d195e') + def test_policy_association_with_port_nonexistent_policy(self): + network = self.create_shared_network('test network') + self.assertRaises( + exceptions.NotFound, + self.create_port, + network, + qos_policy_id='49e02f5a-e1dd-41d5-9855-cfa37f2d195e') + + @test.attr(type='smoke') @test.idempotent_id('f53d961c-9fe5-4422-8b66-7add972c6031') def test_policy_association_with_port_non_shared_policy(self): policy = self.create_qos_policy(name='test-policy', description='test policy', shared=False) network = self.create_shared_network('test network') - #TODO(QoS): This currently raises an exception on the server side. See - # core_extensions/qos.py for comments on this subject. - port = self.create_port(network, qos_policy_id=policy['id']) - - retrieved_port = self.admin_client.show_port(port['id']) - self.assertIsNone(retrieved_port['port']['qos_policy_id']) + self.assertRaises( + exceptions.NotFound, + self.create_port, + network, qos_policy_id=policy['id']) @test.attr(type='smoke') @test.idempotent_id('f8163237-fba9-4db5-9526-bad6d2343c76') diff --git a/neutron/tests/common/agents/l2_extensions.py b/neutron/tests/common/agents/l2_extensions.py index 0d46d3676d..11b354eeb3 100644 --- a/neutron/tests/common/agents/l2_extensions.py +++ b/neutron/tests/common/agents/l2_extensions.py @@ -18,7 +18,7 @@ from neutron.agent.linux import utils as agent_utils def wait_until_bandwidth_limit_rule_applied(bridge, port_vif, rule): def _bandwidth_limit_rule_applied(): - bw_rule = bridge.get_qos_bw_limit_for_port(port_vif) + bw_rule = bridge.get_egress_bw_limit_for_port(port_vif) expected = None, None if rule: expected = rule.max_kbps, rule.max_burst_kbps diff --git a/neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py b/neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py index 8fd8ee18b4..ad6e38b214 100644 --- a/neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py +++ b/neutron/tests/functional/agent/l2/extensions/test_ovs_agent_qos_extension.py @@ -90,13 +90,13 @@ class OVSAgentQoSExtensionTestFramework(base.OVSAgentTestFramework): def _assert_bandwidth_limit_rule_is_set(self, port, rule): max_rate, burst = ( - self.agent.int_br.get_qos_bw_limit_for_port(port['vif_name'])) + self.agent.int_br.get_egress_bw_limit_for_port(port['vif_name'])) self.assertEqual(max_rate, rule.max_kbps) self.assertEqual(burst, rule.max_burst_kbps) def _assert_bandwidth_limit_rule_not_set(self, port): max_rate, burst = ( - self.agent.int_br.get_qos_bw_limit_for_port(port['vif_name'])) + self.agent.int_br.get_egress_bw_limit_for_port(port['vif_name'])) self.assertIsNone(max_rate) self.assertIsNone(burst) @@ -104,6 +104,15 @@ class OVSAgentQoSExtensionTestFramework(base.OVSAgentTestFramework): l2_extensions.wait_until_bandwidth_limit_rule_applied( self.agent.int_br, port['vif_name'], rule) + def _create_port_with_qos(self): + port_dict = self._create_test_port_dict() + port_dict['qos_policy_id'] = TEST_POLICY_ID1 + self.setup_agent_and_ports([port_dict]) + self.wait_until_ports_state(self.ports, up=True) + self.wait_until_bandwidth_limit_rule_applied(port_dict, + TEST_BW_LIMIT_RULE_1) + return port_dict + class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework): @@ -156,12 +165,7 @@ class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework): """Test that qos_policy_id set to None will remove all qos rules from given port. """ - port_dict = self._create_test_port_dict() - port_dict['qos_policy_id'] = TEST_POLICY_ID1 - self.setup_agent_and_ports([port_dict]) - self.wait_until_ports_state(self.ports, up=True) - self.wait_until_bandwidth_limit_rule_applied(port_dict, - TEST_BW_LIMIT_RULE_1) + port_dict = self._create_port_with_qos() port_dict['qos_policy_id'] = None self.agent.port_update(None, port=port_dict) @@ -172,15 +176,19 @@ class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework): """Test that change of qos policy id on given port refreshes all its rules. """ - port_dict = self._create_test_port_dict() - port_dict['qos_policy_id'] = TEST_POLICY_ID1 - self.setup_agent_and_ports([port_dict]) - self.wait_until_ports_state(self.ports, up=True) - self.wait_until_bandwidth_limit_rule_applied(port_dict, - TEST_BW_LIMIT_RULE_1) + port_dict = self._create_port_with_qos() port_dict['qos_policy_id'] = TEST_POLICY_ID2 self.agent.port_update(None, port=port_dict) self.wait_until_bandwidth_limit_rule_applied(port_dict, TEST_BW_LIMIT_RULE_2) + + def test_policy_rule_delete(self): + port_dict = self._create_port_with_qos() + + policy_copy = copy.deepcopy(self.qos_policies[TEST_POLICY_ID1]) + policy_copy.rules = list() + consumer_reg.push(resources.QOS_POLICY, policy_copy, events.UPDATED) + + self.wait_until_bandwidth_limit_rule_applied(port_dict, None) diff --git a/neutron/tests/functional/agent/test_ovs_lib.py b/neutron/tests/functional/agent/test_ovs_lib.py index fee80d8d3c..768209424a 100644 --- a/neutron/tests/functional/agent/test_ovs_lib.py +++ b/neutron/tests/functional/agent/test_ovs_lib.py @@ -311,14 +311,14 @@ class OVSBridgeTestCase(OVSBridgeTestBase): controller, 'connection_mode')) - def test_qos_bw_limit(self): + def test_egress_bw_limit(self): port_name, _ = self.create_ovs_port() - self.br.create_qos_bw_limit_for_port(port_name, 700, 70) - max_rate, burst = self.br.get_qos_bw_limit_for_port(port_name) + self.br.create_egress_bw_limit_for_port(port_name, 700, 70) + max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name) self.assertEqual(700, max_rate) self.assertEqual(70, burst) - self.br.del_qos_bw_limit_for_port(port_name) - max_rate, burst = self.br.get_qos_bw_limit_for_port(port_name) + self.br.delete_egress_bw_limit_for_port(port_name) + max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name) self.assertIsNone(max_rate) self.assertIsNone(burst) diff --git a/neutron/tests/unit/common/test_utils.py b/neutron/tests/unit/common/test_utils.py index 20e764bfad..b604bbb27a 100644 --- a/neutron/tests/unit/common/test_utils.py +++ b/neutron/tests/unit/common/test_utils.py @@ -690,3 +690,13 @@ class TestCamelize(base.BaseTestCase): for s, expected in data.items(): self.assertEqual(expected, utils.camelize(s)) + + +class TestRoundVal(base.BaseTestCase): + def test_round_val_ok(self): + for expected, value in ((0, 0), + (0, 0.1), + (1, 0.5), + (1, 1.49), + (2, 1.5)): + self.assertEqual(expected, utils.round_val(value)) 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 a2b480c705..b3a7d958a8 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 @@ -297,8 +297,39 @@ class TestEmbSwitch(base.BaseTestCase): def test_set_device_max_rate_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." - "PciDeviceIPWrapper.set_vf_max_rate"): - self.emb_switch.set_device_max_rate(self.PCI_SLOT, 1000) + "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) + + 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) + + 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) + + 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) + + 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) + + 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) def test_set_device_max_rate_fail(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py index 7b6c430b7f..c9e276c72a 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/extension_drivers/test_qos_driver.py @@ -30,13 +30,13 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase): self.qos_driver = qos_driver.QosOVSAgentDriver() self.qos_driver.initialize() self.qos_driver.br_int = mock.Mock() - self.qos_driver.br_int.get_qos_bw_limit_for_port = mock.Mock( + self.qos_driver.br_int.get_egress_bw_limit_for_port = mock.Mock( return_value=(1000, 10)) - self.get = self.qos_driver.br_int.get_qos_bw_limit_for_port - self.qos_driver.br_int.del_qos_bw_limit_for_port = mock.Mock() - self.delete = self.qos_driver.br_int.del_qos_bw_limit_for_port - self.qos_driver.br_int.create_qos_bw_limit_for_port = mock.Mock() - self.create = self.qos_driver.br_int.create_qos_bw_limit_for_port + self.get = self.qos_driver.br_int.get_egress_bw_limit_for_port + self.qos_driver.br_int.del_egress_bw_limit_for_port = mock.Mock() + self.delete = self.qos_driver.br_int.delete_egress_bw_limit_for_port + self.qos_driver.br_int.create_egress_bw_limit_for_port = mock.Mock() + self.create = self.qos_driver.br_int.create_egress_bw_limit_for_port self.rule = self._create_bw_limit_rule_obj() self.qos_policy = self._create_qos_policy_obj([self.rule]) self.port = self._create_fake_port() @@ -69,12 +69,12 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase): return {'vif_port': FakeVifPort()} def test_create_new_rule(self): - self.qos_driver.br_int.get_qos_bw_limit_for_port = mock.Mock( + self.qos_driver.br_int.get_egress_bw_limit_for_port = mock.Mock( return_value=(None, None)) self.qos_driver.create(self.port, self.qos_policy) # Assert create is the last call self.assertEqual( - 'create_qos_bw_limit_for_port', + 'create_egress_bw_limit_for_port', self.qos_driver.br_int.method_calls[-1][0]) self.assertEqual(0, self.delete.call_count) self.create.assert_called_once_with( @@ -96,11 +96,9 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase): def _assert_rule_create_updated(self): # Assert create is the last call self.assertEqual( - 'create_qos_bw_limit_for_port', + 'create_egress_bw_limit_for_port', self.qos_driver.br_int.method_calls[-1][0]) - self.delete.assert_called_once_with(self.port_name) - self.create.assert_called_once_with( self.port_name, self.rule.max_kbps, self.rule.max_burst_kbps) diff --git a/neutron/tests/unit/plugins/ml2/test_extension_driver_api.py b/neutron/tests/unit/plugins/ml2/test_extension_driver_api.py index bff70fecb5..78d6353385 100644 --- a/neutron/tests/unit/plugins/ml2/test_extension_driver_api.py +++ b/neutron/tests/unit/plugins/ml2/test_extension_driver_api.py @@ -11,6 +11,7 @@ # under the License. import mock +import uuid from neutron import context from neutron import manager @@ -31,6 +32,58 @@ class ExtensionDriverTestCase(test_plugin.Ml2PluginV2TestCase): self._plugin = manager.NeutronManager.get_plugin() self._ctxt = context.get_admin_context() + def _verify_network_create(self, code, exc_reason): + tenant_id = str(uuid.uuid4()) + data = {'network': {'name': 'net1', + 'tenant_id': tenant_id}} + req = self.new_create_request('networks', data) + res = req.get_response(self.api) + self.assertEqual(code, res.status_int) + + network = self.deserialize(self.fmt, res) + if exc_reason: + self.assertEqual(exc_reason, + network['NeutronError']['type']) + + return (network, tenant_id) + + def _verify_network_update(self, network, code, exc_reason): + net_id = network['network']['id'] + new_name = 'a_brand_new_name' + data = {'network': {'name': new_name}} + req = self.new_update_request('networks', data, net_id) + res = req.get_response(self.api) + self.assertEqual(code, res.status_int) + error = self.deserialize(self.fmt, res) + self.assertEqual(exc_reason, + error['NeutronError']['type']) + + def test_faulty_process_create(self): + with mock.patch.object(ext_test.TestExtensionDriver, + 'process_create_network', + side_effect=TypeError): + net, tenant_id = self._verify_network_create(500, + 'HTTPInternalServerError') + # Verify the operation is rolled back + query_params = "tenant_id=%s" % tenant_id + nets = self._list('networks', query_params=query_params) + self.assertFalse(nets['networks']) + + def test_faulty_process_update(self): + with mock.patch.object(ext_test.TestExtensionDriver, + 'process_update_network', + side_effect=TypeError): + network, tid = self._verify_network_create(201, None) + self._verify_network_update(network, 500, + 'HTTPInternalServerError') + + def test_faulty_extend_dict(self): + with mock.patch.object(ext_test.TestExtensionDriver, + 'extend_network_dict', + side_effect=TypeError): + network, tid = self._verify_network_create(201, None) + self._verify_network_update(network, 400, 'ExtensionDriverError') + def test_network_attr(self): with self.network() as network: # Test create network |