diff --git a/neutron/db/l3_gateway_ip_qos.py b/neutron/db/l3_gateway_ip_qos.py index 9e8f6fb276b..3eeccf82ba0 100644 --- a/neutron/db/l3_gateway_ip_qos.py +++ b/neutron/db/l3_gateway_ip_qos.py @@ -43,6 +43,7 @@ class L3_gw_ip_qos_dbonly_mixin(l3_gwmode_db.L3_NAT_dbonly_mixin): policy_id = router_db.qos_policy_binding.policy_id router_res[l3_apidef.EXTERNAL_GW_INFO].update( {qos_consts.QOS_POLICY_ID: policy_id}) + router_res[qos_consts.QOS_POLICY_ID] = policy_id @property def _is_gw_ip_qos_supported(self): diff --git a/neutron/objects/router.py b/neutron/objects/router.py index e9ae872a39c..ffc5c8b8df6 100644 --- a/neutron/objects/router.py +++ b/neutron/objects/router.py @@ -194,7 +194,8 @@ class DVRMacAddress(base.NeutronDbObject): @base.NeutronObjectRegistry.register class Router(base.NeutronDbObject): # Version 1.0: Initial version - VERSION = '1.0' + # Version 1.1: Added "qos_policy_id" field + VERSION = '1.1' db_model = l3.Router @@ -209,9 +210,12 @@ class Router(base.NeutronDbObject): 'flavor_id': common_types.UUIDField(nullable=True), 'extra_attributes': obj_fields.ObjectField( 'RouterExtraAttributes', nullable=True), + 'qos_policy_id': common_types.UUIDField(nullable=True, default=None), } - synthetic_fields = ['extra_attributes'] + synthetic_fields = ['extra_attributes', + 'qos_policy_id', + ] fields_no_update = ['project_id'] @@ -230,6 +234,46 @@ class Router(base.NeutronDbObject): return bool(query.count()) + def _attach_qos_policy(self, qos_policy_id): + qos_binding.QosPolicyRouterGatewayIPBinding.delete_objects( + self.obj_context, router_id=self.id) + if qos_policy_id: + qos_binding.QosPolicyRouterGatewayIPBinding( + self.obj_context, policy_id=qos_policy_id, + router_id=self.id).create() + + self.qos_policy_id = qos_policy_id + self.obj_reset_changes(['qos_policy_id']) + + def create(self): + fields = self.obj_get_changes() + with self.db_context_writer(self.obj_context): + qos_policy_id = self.qos_policy_id + super().create() + if 'qos_policy_id' in fields: + self._attach_qos_policy(qos_policy_id) + + def update(self): + fields = self.obj_get_changes() + with self.db_context_writer(self.obj_context): + super().update() + if 'qos_policy_id' in fields: + self._attach_qos_policy(fields['qos_policy_id']) + + def from_db_object(self, db_obj): + super().from_db_object(db_obj) + fields_to_change = [] + if db_obj.get('qos_policy_binding'): + self.qos_policy_id = db_obj.qos_policy_binding.policy_id + fields_to_change.append('qos_policy_id') + + self.obj_reset_changes(fields_to_change) + + def obj_make_compatible(self, primitive, target_version): + _target_version = versionutils.convert_version_to_tuple(target_version) + if _target_version < (1, 1): + primitive.pop('qos_policy_id', None) + @base.NeutronObjectRegistry.register class FloatingIP(base.NeutronDbObject): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py index 81e8203a6a8..194a9b2ebc4 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py @@ -385,7 +385,7 @@ class OVNClientQosExtension(object): def update_router(self, txn, router): gw_info = router.get(l3_api.EXTERNAL_GW_INFO) or {} - qos_policy_id = gw_info.get('qos_policy_id') + qos_policy_id = router.get('qos_policy_id') router_id = router.get('id') gw_port_id = router.get('gw_port_id') gw_network_id = gw_info.get('network_id') diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index d806729e4ee..01907a4bc46 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -104,7 +104,7 @@ object_data = { 'Reservation': '1.0-49929fef8e82051660342eed51b48f2a', 'ResourceDelta': '1.0-a980b37e0a52618b5af8db29af18be76', 'Route': '1.0-a9883a63b416126f9e345523ec09483b', - 'Router': '1.0-adb984d9b73aa11566d40abbeb790df1', + 'Router': '1.1-614fa16cc99c60e4fc19ac1b31a52291', 'RouterExtraAttributes': '1.0-ef8d61ae2864f0ec9af0ab7939cab318', 'RouterL3AgentBinding': '1.0-c5ba6c95e3a4c1236a55f490cd67da82', 'RouterNDPProxyState': '1.0-4042e475bf173d1d8d17adb962eae1b2', diff --git a/neutron/tests/unit/objects/test_router.py b/neutron/tests/unit/objects/test_router.py index b73d157d815..806cba8d786 100644 --- a/neutron/tests/unit/objects/test_router.py +++ b/neutron/tests/unit/objects/test_router.py @@ -127,6 +127,45 @@ class RouterDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, [project, new_project]) self.assertFalse(router_exist) + def test_qos_policy(self): + _qos_policy_1 = self._create_test_qos_policy() + _qos_policy_2 = self._create_test_qos_policy() + + self.obj_fields[0]['qos_policy_id'] = _qos_policy_1.id + obj = self._test_class( + self.context, **obj_test_base.remove_timestamps_from_fields( + self.obj_fields[0], self._test_class.fields)) + obj.create() + self.assertEqual(_qos_policy_1.id, obj.qos_policy_id) + + obj.qos_policy_id = _qos_policy_2.id + obj.update() + self.assertEqual(_qos_policy_2.id, obj.qos_policy_id) + + obj.qos_policy_id = None + obj.update() + self.assertIsNone(obj.qos_policy_id) + + obj.qos_policy_id = _qos_policy_1.id + obj.update() + router_id = obj.id + gw_binding = qos_binding.QosPolicyRouterGatewayIPBinding.get_objects( + self.context, router_id=router_id) + self.assertEqual(1, len(gw_binding)) + self.assertEqual(_qos_policy_1.id, gw_binding[0].policy_id) + obj.delete() + gw_binding = qos_binding.QosPolicyRouterGatewayIPBinding.get_objects( + self.context, router_id=router_id) + self.assertEqual([], gw_binding) + + def test_object_version_degradation_1_1_to_1_0_no_qos_policy_id(self): + self.objs[0].create() + router_obj = self.objs[0] + router_dict = router_obj.obj_to_primitive('1.1') + self.assertIn('qos_policy_id', router_dict['versioned_object.data']) + router_dict = router_obj.obj_to_primitive('1.0') + self.assertNotIn('qos_policy_id', router_dict['versioned_object.data']) + class RouterPortIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase):