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