From 66fca96e5293040d23fa76380ba3f88b4117a5d7 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 27 Jun 2019 15:02:33 +0000 Subject: [PATCH] Add qos_network_policy_id to Port OVO Added field "qos_network_policy_id" to Port OVO. This parameter will be used to retrieve the QoS policy bound to the port network. This reduces the number of calls to the database by creating a join between the QosNetworkPolicyBinding table and the Port table, based on the network ID. This backref association is not persistent (marked as "viewonly"). This relationship is using for loading the QoS policy ID of the port network in the Port OVO. Related-Bug: #1834484 Change-Id: I219a925d5e269b8c73a0481daa879d72c399fd8f --- neutron/db/qos/models.py | 6 ++++++ neutron/objects/ports.py | 24 ++++++++++++++------- neutron/tests/unit/objects/test_base.py | 6 ++++-- neutron/tests/unit/objects/test_objects.py | 2 +- neutron/tests/unit/objects/test_ports.py | 25 ++++++++++++++++++++++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/neutron/db/qos/models.py b/neutron/db/qos/models.py index f123b7d213f..b8b8411a6c4 100644 --- a/neutron/db/qos/models.py +++ b/neutron/db/qos/models.py @@ -54,6 +54,12 @@ class QosNetworkPolicyBinding(model_base.BASEV2): models_v2.Network, load_on_pending=True, backref=sa.orm.backref("qos_policy_binding", uselist=False, cascade='delete', lazy='joined')) + port = sa.orm.relationship( + models_v2.Port, + primaryjoin='QosNetworkPolicyBinding.network_id == Port.network_id', + foreign_keys=network_id, + backref=sa.orm.backref('qos_network_policy_binding', uselist=False, + viewonly=True)) class QosFIPPolicyBinding(model_base.BASEV2): diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py index 315f085ac88..6ef11dd48f5 100644 --- a/neutron/objects/ports.py +++ b/neutron/objects/ports.py @@ -265,7 +265,8 @@ class Port(base.NeutronDbObject): # Version 1.2: Added segment_id to binding_levels # Version 1.3: distributed_binding -> distributed_bindings # Version 1.4: Attribute binding becomes ListOfObjectsField - VERSION = '1.4' + # Version 1.5: Added qos_network_policy_id field + VERSION = '1.5' db_model = models_v2.Port @@ -309,6 +310,8 @@ class Port(base.NeutronDbObject): default=None, ), 'qos_policy_id': common_types.UUIDField(nullable=True, default=None), + 'qos_network_policy_id': common_types.UUIDField(nullable=True, + default=None), 'binding_levels': obj_fields.ListOfObjectsField( 'PortBindingLevel', nullable=True @@ -332,6 +335,7 @@ class Port(base.NeutronDbObject): 'dns', 'fixed_ips', 'qos_policy_id', + 'qos_network_policy_id', 'security', 'security_group_ids', ] @@ -461,16 +465,18 @@ class Port(base.NeutronDbObject): } else: self.security_group_ids = set() - self.obj_reset_changes(['security_group_ids']) + fields_to_change = ['security_group_ids'] # extract qos policy binding if db_obj.get('qos_policy_binding'): - self.qos_policy_id = ( - db_obj.qos_policy_binding.policy_id - ) - else: - self.qos_policy_id = None - self.obj_reset_changes(['qos_policy_id']) + self.qos_policy_id = db_obj.qos_policy_binding.policy_id + fields_to_change.append('qos_policy_id') + if db_obj.get('qos_network_policy_binding'): + self.qos_network_policy_id = ( + db_obj.qos_network_policy_binding.policy_id) + fields_to_change.append('qos_network_policy_binding') + + self.obj_reset_changes(fields_to_change) def obj_make_compatible(self, primitive, target_version): _target_version = versionutils.convert_version_to_tuple(target_version) @@ -498,6 +504,8 @@ class Port(base.NeutronDbObject): constants.ACTIVE): primitive['binding'] = a_binding break + if _target_version < (1, 5): + primitive.pop('qos_network_policy_id', None) @classmethod def get_ports_by_router(cls, context, router_id, owner, subnet): diff --git a/neutron/tests/unit/objects/test_base.py b/neutron/tests/unit/objects/test_base.py index 85f7eaaf676..44ab5e0914c 100644 --- a/neutron/tests/unit/objects/test_base.py +++ b/neutron/tests/unit/objects/test_base.py @@ -1524,10 +1524,12 @@ class BaseDbObjectTestCase(_BaseObjectTestCase, objclass.db_model(**objclass_fields) ] - def _create_test_network(self, name='test-network1', network_id=None): + def _create_test_network(self, name='test-network1', network_id=None, + qos_policy_id=None): network_id = (uuidutils.generate_uuid() if network_id is None else network_id) - _network = net_obj.Network(self.context, name=name, id=network_id) + _network = net_obj.Network(self.context, name=name, id=network_id, + qos_policy_id=qos_policy_id) _network.create() return _network diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index 8281fa8abc0..f7e161ae3e0 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -64,7 +64,7 @@ object_data = { 'NetworkRBAC': '1.2-192845c5ed0718e1c54fac36936fcd7d', 'NetworkSegment': '1.0-57b7f2960971e3b95ded20cbc59244a8', 'NetworkSegmentRange': '1.0-bdec1fffc9058ea676089b1f2f2b3cf3', - 'Port': '1.4-1b6183bccfc2cd210919a1a72faefce1', + 'Port': '1.5-98f35183d876c9beb188f4bf44d4d886', 'PortBinding': '1.0-3306deeaa6deb01e33af06777d48d578', 'PortBindingLevel': '1.1-50d47f63218f87581b6cd9a62db574e5', 'PortDataPlaneStatus': '1.0-25be74bda46c749653a10357676c0ab2', diff --git a/neutron/tests/unit/objects/test_ports.py b/neutron/tests/unit/objects/test_ports.py index 939f05a613a..091e1ed4406 100644 --- a/neutron/tests/unit/objects/test_ports.py +++ b/neutron/tests/unit/objects/test_ports.py @@ -354,6 +354,25 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, self.context, policy_id=old_policy_id) self.assertEqual(0, len(qos_binding_obj)) + @mock.patch.object(policy.QosPolicy, 'unset_default') + def test_qos_network_policy_id(self, *mocks): + policy_obj = policy.QosPolicy(self.context) + policy_obj.create() + + obj = self._make_object(self.obj_fields[0]) + obj.create() + obj = ports.Port.get_object(self.context, id=obj.id) + self.assertIsNone(obj.qos_network_policy_id) + self.assertIsNone(obj.qos_policy_id) + + network = self._create_test_network(qos_policy_id=policy_obj.id) + self.update_obj_fields({'network_id': network.id}) + obj = self._make_object(self.obj_fields[1]) + obj.create() + obj = ports.Port.get_object(self.context, id=obj.id) + self.assertEqual(policy_obj.id, obj.qos_network_policy_id) + self.assertIsNone(obj.qos_policy_id) + def test_get_objects_queries_constant(self): self.skipTest( 'Port object loads segment info without relationships') @@ -461,6 +480,12 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, port_v1_4_no_binding = port_v1_4.obj_from_primitive(primitive) port_v1_4_no_binding.obj_to_primitive(target_version='1.3') + def test_v1_5_to_v1_4_drops_qos_network_policy_id(self): + port_new = self._create_test_port() + port_v1_4 = port_new.obj_to_primitive(target_version='1.4') + self.assertNotIn('qos_network_policy_id', + port_v1_4['versioned_object.data']) + def test_get_ports_ids_by_security_groups_except_router(self): sg_id = self._create_test_security_group_id() filter_owner = constants.ROUTER_INTERFACE_OWNERS_SNAT