Add network QoS inheritance to floating IP

Added information of the floating IP network QoS policy to the
``FloatingIP`` OVO. The view-only parameter added allows to check
the network QoS policy in the floating IP object.

This patch does not implement any change in the L3 code (OVS or
OVN). This patch does not change any existing behaviour.

NOTE: bump neutron-lib version

Depends-On: https://review.opendev.org/c/openstack/neutron-lib/+/817936

Partial-Bug: #1950454
Change-Id: I9d7bb54b14fb983161fdf51c96b6fda107db4fe6
This commit is contained in:
Rodolfo Alonso Hernandez 2021-11-11 17:04:16 +00:00
parent 6a73a362c5
commit 42cfa055c2
8 changed files with 99 additions and 14 deletions

View File

@ -26,11 +26,12 @@ class FloatingQoSDbMixin(object):
@staticmethod
@resource_extend.extends([l3_apidef.FLOATINGIPS])
def _extend_extra_fip_dict(fip_res, fip_db):
if fip_db.get('qos_policy_binding'):
fip_res[qos_consts.QOS_POLICY_ID] = (
fip_db.qos_policy_binding.policy_id)
else:
fip_res[qos_consts.QOS_POLICY_ID] = None
qos_id = (fip_db.qos_policy_binding.policy_id if
fip_db.qos_policy_binding else None)
fip_res[qos_consts.QOS_POLICY_ID] = qos_id
qos_id = (fip_db.qos_network_policy_binding.policy_id if
fip_db.qos_network_policy_binding else None)
fip_res[qos_consts.QOS_NETWORK_POLICY_ID] = qos_id
return fip_res
def _create_fip_qos_db(self, context, fip_id, policy_id):

View File

@ -61,6 +61,14 @@ class QosNetworkPolicyBinding(model_base.BASEV2):
backref=sa.orm.backref('qos_network_policy_binding', uselist=False,
lazy='joined'),
sync_backref=False, viewonly=True)
floatingip = sa.orm.relationship(
l3.FloatingIP,
primaryjoin=('QosNetworkPolicyBinding.network_id == '
'FloatingIP.floating_network_id'),
foreign_keys=network_id,
backref=sa.orm.backref('qos_network_policy_binding', uselist=False,
lazy='joined'),
sync_backref=False, viewonly=True)
class QosFIPPolicyBinding(model_base.BASEV2):

View File

@ -0,0 +1,20 @@
# Copyright (c) 2021 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron_lib.api.definitions import qos_fip_network_policy
from neutron_lib.api import extensions as api_extensions
class Qos_fip_network_policy(api_extensions.APIExtensionDescriptor):
api_definition = qos_fip_network_policy

View File

@ -235,7 +235,8 @@ class Router(base.NeutronDbObject):
class FloatingIP(base.NeutronDbObject):
# Version 1.0: Initial version
# Version 1.1: Added qos_policy_id field
VERSION = '1.1'
# Version 1.2: Added qos_network_policy_id field
VERSION = '1.2'
db_model = l3.FloatingIP
@ -248,6 +249,8 @@ class FloatingIP(base.NeutronDbObject):
'fixed_port_id': common_types.UUIDField(nullable=True),
'fixed_ip_address': obj_fields.IPAddressField(nullable=True),
'qos_policy_id': common_types.UUIDField(nullable=True, default=None),
'qos_network_policy_id': common_types.UUIDField(nullable=True,
default=None),
'router_id': common_types.UUIDField(nullable=True),
'last_known_router_id': common_types.UUIDField(nullable=True),
'status': common_types.FloatingIPStatusEnumField(nullable=True),
@ -257,6 +260,7 @@ class FloatingIP(base.NeutronDbObject):
'floating_network_id', 'floating_port_id']
synthetic_fields = ['dns',
'qos_policy_id',
'qos_network_policy_id',
]
@classmethod
@ -314,13 +318,18 @@ class FloatingIP(base.NeutronDbObject):
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')
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)
if _target_version < (1, 1):
primitive.pop('qos_policy_id', None)
if _target_version < (1, 2):
primitive.pop('qos_network_policy_id', None)
@classmethod
def get_scoped_floating_ips(cls, context, router_ids):

View File

@ -2746,8 +2746,9 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
port_id=port_id,
router_id=router_id)
skip = ('description', 'dns_domain', 'dns_name',
'port_details', 'qos_policy_id', 'revision_number',
'status', 'standard_attr_id')
'port_details', 'qos_network_policy_id',
'qos_policy_id', 'revision_number', 'status',
'standard_attr_id')
for k in skip:
payload.states[0].pop(k, None)
payload.states[1].pop(k, None)
@ -2793,8 +2794,9 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
port_id=None,
router_id=None)
skip = ('description', 'dns_domain', 'dns_name',
'port_details', 'qos_policy_id', 'revision_number',
'status', 'standard_attr_id')
'port_details', 'qos_network_policy_id',
'qos_policy_id', 'revision_number', 'status',
'standard_attr_id')
for k in skip:
payload.states[0].pop(k, None)
payload.states[1].pop(k, None)
@ -2839,8 +2841,9 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
port_id=None,
router_id=None)
skip = ('description', 'dns_domain', 'dns_name',
'port_details', 'qos_policy_id', 'revision_number',
'status', 'standard_attr_id')
'port_details', 'qos_network_policy_id',
'qos_policy_id', 'revision_number', 'status',
'standard_attr_id')
for k in skip:
payload.states[0].pop(k, None)
payload.states[1].pop(k, None)

View File

@ -205,6 +205,20 @@ class FakeStandardAttribute(object):
self.revision_number = revision_number
class FakeQosNetworkPolicyBinding(object):
def __init__(self, policy_id=mock.ANY, network_id=mock.ANY):
self.policy_id = policy_id
self.network_id = network_id
class FakeQosFIPPolicyBinding(object):
def __init__(self, policy_id=mock.ANY, fip_id=mock.ANY):
self.policy_id = policy_id
self.fip_id = fip_id
class FakeResource(dict):
def __init__(self, manager=None, info=None, loaded=False, methods=None):
@ -688,6 +702,8 @@ class FakeFloatingIp(object):
'dns_name': '',
'project_id': '',
'standard_attr': FakeStandardAttribute(),
'qos_policy_binding': FakeQosFIPPolicyBinding(),
'qos_network_policy_binding': FakeQosNetworkPolicyBinding(),
}
# Overwrite default attributes.

View File

@ -46,7 +46,7 @@ object_data = {
'FlatAllocation': '1.0-bf666f24f4642b047eeca62311fbcb41',
'Flavor': '1.0-82194de5c9aafce08e8527bb7977f5c6',
'FlavorServiceProfileBinding': '1.0-a2c8731e16cefdac4571f80abf1f8930',
'FloatingIP': '1.1-595514f8e992849cfc67981d1fd6614e',
'FloatingIP': '1.2-fb6675ea98d2a37e5883dc5962ec401c',
'FloatingIPDNS': '1.0-ee3db848500fa1825235f701828c06d5',
'GeneveAllocation': '1.0-d5f76e8eac60a778914d61dd8e23e90f',
'GeneveEndpoint': '1.0-040f026996b5952e2ae4ccd40ac61ca6',

View File

@ -12,9 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from oslo_utils import uuidutils
from neutron.objects.qos import binding as qos_binding
from neutron.objects.qos import policy
from neutron.objects import router
from neutron.tests.unit.objects import test_base as obj_test_base
from neutron.tests.unit import testlib_api
@ -201,11 +204,36 @@ class FloatingIPDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
self.context, fip_id=fip_id)
self.assertEqual([], qos_fip_binding)
@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 = router.FloatingIP.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({'floating_network_id': network.id})
obj = self._make_object(self.obj_fields[1])
obj.create()
obj = router.FloatingIP.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_v1_1_to_v1_0_drops_qos_policy_id(self):
obj = self._make_object(self.obj_fields[0])
obj_v1_0 = obj.obj_to_primitive(target_version='1.0')
self.assertNotIn('qos_policy_id', obj_v1_0['versioned_object.data'])
def test_v1_2_to_v1_1_drops_qos_network_policy_id(self):
obj = self._make_object(self.obj_fields[0])
obj_v1_1 = obj.obj_to_primitive(target_version='1.1')
self.assertNotIn('qos_network_policy_id',
obj_v1_1['versioned_object.data'])
class DvrFipGatewayPortAgentBindingTestCase(
obj_test_base.BaseObjectIfaceTestCase):