[OVN] Implement floating IP network QoS inheritance
Floating IP now have information of the QoS policy of the external network. The OVN QoS extension will use this network QoS policy if there is no floating IP QoS policy. Partial-Bug: #1950454 Change-Id: I380a130d97e8bfe54caa5f3a129877507d1ce2a6
This commit is contained in:
parent
7cbec53543
commit
8dfe5cc95b
@ -19,6 +19,7 @@ from neutron_lib.objects import common_types
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import exists
|
||||
|
||||
from neutron.db.models import l3 as models_l3
|
||||
from neutron.db import models_v2
|
||||
from neutron.db.qos import models as qos_db_model
|
||||
from neutron.objects import base
|
||||
@ -101,6 +102,25 @@ class QosPolicyFloatingIPBinding(base.NeutronDbObject, _QosPolicyBindingMixin):
|
||||
fields_no_update = ['policy_id', 'fip_id']
|
||||
_bound_model_id = db_model.fip_id
|
||||
|
||||
@classmethod
|
||||
def get_fips_by_network_id(cls, context, network_id, policy_id=None):
|
||||
"""Return the FIP belonging to a network, filtered by a QoS policy
|
||||
|
||||
This method returns the floating IPs belonging to a network, with a
|
||||
QoS policy associated. If no QoS policy is passed, this method returns
|
||||
all floating IPs without any QoS policy associated.
|
||||
"""
|
||||
query = context.session.query(models_l3.FloatingIP).filter(
|
||||
models_l3.FloatingIP.floating_network_id == network_id)
|
||||
if policy_id:
|
||||
query = query.filter(exists().where(and_(
|
||||
cls.db_model.fip_id == models_l3.FloatingIP.id,
|
||||
cls.db_model.policy_id == policy_id)))
|
||||
else:
|
||||
query = query.filter(~exists().where(
|
||||
cls.db_model.fip_id == models_l3.FloatingIP.id))
|
||||
return query.all()
|
||||
|
||||
|
||||
@base.NeutronObjectRegistry.register
|
||||
class QosPolicyRouterGatewayIPBinding(base.NeutronDbObject,
|
||||
|
@ -333,8 +333,8 @@ class QosPolicy(rbac_db.NeutronRbacObject):
|
||||
self.id)
|
||||
|
||||
def get_bound_floatingips(self):
|
||||
return binding.QosPolicyFloatingIPBinding.get_objects(
|
||||
self.obj_context, policy_id=self.id)
|
||||
return binding.QosPolicyFloatingIPBinding.get_bound_ids(
|
||||
self.obj_context, self.id)
|
||||
|
||||
def get_bound_routers(self):
|
||||
return binding.QosPolicyRouterGatewayIPBinding.get_objects(
|
||||
|
@ -247,16 +247,18 @@ class OVNClientQosExtension(object):
|
||||
def update_network(self, txn, network, original_network, reset=False,
|
||||
qos_rules=None):
|
||||
updated_port_ids = set([])
|
||||
updated_fip_ids = set([])
|
||||
if not reset and not original_network:
|
||||
# If there is no information about the previous QoS policy, do not
|
||||
# make any change.
|
||||
return updated_port_ids
|
||||
return updated_port_ids, updated_fip_ids
|
||||
|
||||
qos_policy_id = network.get('qos_policy_id')
|
||||
if not reset:
|
||||
original_qos_policy_id = original_network.get('qos_policy_id')
|
||||
if qos_policy_id == original_qos_policy_id:
|
||||
return updated_port_ids # No QoS policy change
|
||||
# No QoS policy change
|
||||
return updated_port_ids, updated_fip_ids
|
||||
|
||||
# NOTE(ralonsoh): we don't use the transaction context because some
|
||||
# ports can belong to other projects.
|
||||
@ -271,14 +273,23 @@ class OVNClientQosExtension(object):
|
||||
qos_policy_id, qos_rules)
|
||||
updated_port_ids.add(port['id'])
|
||||
|
||||
return updated_port_ids
|
||||
fips = qos_binding.QosPolicyFloatingIPBinding.get_fips_by_network_id(
|
||||
admin_context, network['id'])
|
||||
fip_ids = [fip.id for fip in fips]
|
||||
for floatingip in self._plugin_l3.get_floatingips(
|
||||
admin_context, filters={'id': fip_ids}):
|
||||
self.update_floatingip(txn, floatingip)
|
||||
updated_fip_ids.add(floatingip['id'])
|
||||
|
||||
return updated_port_ids, updated_fip_ids
|
||||
|
||||
def create_floatingip(self, txn, floatingip):
|
||||
self.update_floatingip(txn, floatingip)
|
||||
|
||||
def update_floatingip(self, txn, floatingip):
|
||||
router_id = floatingip.get('router_id')
|
||||
qos_policy_id = floatingip.get('qos_policy_id')
|
||||
qos_policy_id = (floatingip.get('qos_policy_id') or
|
||||
floatingip.get('qos_network_policy_id'))
|
||||
if floatingip['floating_network_id']:
|
||||
lswitch_name = utils.ovn_name(floatingip['floating_network_id'])
|
||||
txn.add(self._driver._nb_idl.qos_del_ext_ids(
|
||||
@ -319,8 +330,10 @@ class OVNClientQosExtension(object):
|
||||
|
||||
def update_policy(self, context, policy):
|
||||
updated_port_ids = set([])
|
||||
updated_fip_ids = set([])
|
||||
bound_networks = policy.get_bound_networks()
|
||||
bound_ports = policy.get_bound_ports()
|
||||
bound_fips = policy.get_bound_floatingips()
|
||||
qos_rules = self._qos_rules(context, policy.id)
|
||||
# TODO(ralonsoh): we need to benchmark this transaction in systems with
|
||||
# a huge amount of ports. This can take a while and could block other
|
||||
@ -328,19 +341,24 @@ class OVNClientQosExtension(object):
|
||||
with self._driver._nb_idl.transaction(check_error=True) as txn:
|
||||
for network_id in bound_networks:
|
||||
network = {'qos_policy_id': policy.id, 'id': network_id}
|
||||
updated_port_ids.update(
|
||||
self.update_network(txn, network, {}, reset=True,
|
||||
qos_rules=qos_rules))
|
||||
port_ids, fip_ids = self.update_network(
|
||||
txn, network, {}, reset=True, qos_rules=qos_rules)
|
||||
updated_port_ids.update(port_ids)
|
||||
updated_fip_ids.update(fip_ids)
|
||||
|
||||
# Update each port bound to this policy, not handled previously in
|
||||
# the network update loop
|
||||
port_ids = [p for p in bound_ports if p not in updated_port_ids]
|
||||
for port in self._plugin.get_ports(context,
|
||||
filters={'id': port_ids}):
|
||||
self.update_port(txn, port, {}, reset=True,
|
||||
qos_rules=qos_rules)
|
||||
if port_ids:
|
||||
for port in self._plugin.get_ports(context,
|
||||
filters={'id': port_ids}):
|
||||
self.update_port(txn, port, {}, reset=True,
|
||||
qos_rules=qos_rules)
|
||||
|
||||
for fip_binding in policy.get_bound_floatingips():
|
||||
fip = self._plugin_l3.get_floatingip(context,
|
||||
fip_binding.fip_id)
|
||||
self.update_floatingip(txn, fip)
|
||||
# Update each FIP bound to this policy, not handled previously in
|
||||
# the network update loop
|
||||
fip_ids = [fip for fip in bound_fips if fip not in updated_fip_ids]
|
||||
if fip_ids:
|
||||
for fip in self._plugin_l3.get_floatingips(
|
||||
context, filters={'id': fip_ids}):
|
||||
self.update_floatingip(txn, fip)
|
||||
|
@ -352,7 +352,7 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
self.networks[0].qos_policy_id = qos_policy_id
|
||||
self.networks[0].update()
|
||||
original_network = {'qos_policy_id': self.qos_policies[0]}
|
||||
reviewed_port_ids = self.qos_driver.update_network(
|
||||
reviewed_port_ids, _ = self.qos_driver.update_network(
|
||||
mock.ANY, self.networks[0], original_network)
|
||||
self.assertEqual(reference_ports, reviewed_port_ids)
|
||||
calls = [mock.call(mock.ANY, self.ports[0].id,
|
||||
@ -361,6 +361,26 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
self.mock_rules.assert_has_calls(calls)
|
||||
self.mock_rules.reset_mock()
|
||||
|
||||
def test_update_external_network(self):
|
||||
"""Test update external network (floating IPs).
|
||||
|
||||
- fip0: qos_policy0
|
||||
- fip1: no QoS FIP policy (inherits from external network QoS)
|
||||
"""
|
||||
network_policies = [
|
||||
(self.qos_policies[1].id, {self.fips[1].id}),
|
||||
(None, {self.fips[1].id})]
|
||||
|
||||
self.fips[0].qos_policy_id = self.qos_policies[0].id
|
||||
self.fips[0].update()
|
||||
for qos_policy_id, reference_fips in network_policies:
|
||||
self.fips_network.qos_policy_id = qos_policy_id
|
||||
self.fips_network.update()
|
||||
original_network = {'qos_policy_id': self.qos_policies[0]}
|
||||
_, reviewed_fips_ids = self.qos_driver.update_network(
|
||||
mock.Mock(), self.fips_network, original_network)
|
||||
self.assertEqual(reference_fips, reviewed_fips_ids)
|
||||
|
||||
def test_update_network_no_policy_change(self):
|
||||
"""Test update network if the QoS policy is the same.
|
||||
|
||||
@ -371,9 +391,10 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
self.networks[0].qos_policy_id = qos_policy_id
|
||||
self.networks[0].update()
|
||||
original_network = {'qos_policy_id': qos_policy_id}
|
||||
reviewed_port_ids = self.qos_driver.update_network(
|
||||
port_ids, fip_ids = self.qos_driver.update_network(
|
||||
mock.ANY, self.networks[0], original_network)
|
||||
self.assertEqual(set([]), reviewed_port_ids)
|
||||
self.assertEqual(set([]), port_ids)
|
||||
self.assertEqual(set([]), fip_ids)
|
||||
self.mock_rules.assert_not_called()
|
||||
|
||||
def test_update_network_reset(self):
|
||||
@ -397,7 +418,7 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
self.networks[0].qos_policy_id = qos_policy_id
|
||||
self.networks[0].update()
|
||||
original_network = {'qos_policy_id': self.qos_policies[0]}
|
||||
reviewed_port_ids = self.qos_driver.update_network(
|
||||
reviewed_port_ids, _ = self.qos_driver.update_network(
|
||||
mock.ANY, self.networks[0], original_network, reset=True)
|
||||
self.assertEqual(reference_ports, reviewed_port_ids)
|
||||
calls = [mock.call(mock.ANY, self.ports[0].id,
|
||||
@ -427,7 +448,7 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
self.networks[0].qos_policy_id = qos_policy_id
|
||||
self.networks[0].update()
|
||||
original_network = {'qos_policy_id': self.qos_policies[0]}
|
||||
reviewed_port_ids = self.qos_driver.update_network(
|
||||
reviewed_port_ids, _ = self.qos_driver.update_network(
|
||||
mock.ANY, self.networks[0], original_network, reset=True)
|
||||
self.assertEqual(reference_ports, reviewed_port_ids)
|
||||
calls = [mock.call(
|
||||
@ -524,6 +545,14 @@ class TestOVNClientQosExtension(test_plugin.Ml2PluginV2TestCase):
|
||||
nb_idl.qos_add.assert_not_called()
|
||||
nb_idl.reset_mock()
|
||||
|
||||
# Add network QoS policy
|
||||
fip.qos_network_policy_id = self.qos_policies[0].id
|
||||
fip.update()
|
||||
self.qos_driver.update_floatingip(txn, fip)
|
||||
nb_idl.qos_del_ext_ids.assert_called_once()
|
||||
nb_idl.qos_add.assert_called_once()
|
||||
nb_idl.reset_mock()
|
||||
|
||||
# Add again another QoS policy
|
||||
fip.qos_policy_id = self.qos_policies[1].id
|
||||
fip.update()
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Floating IP QoS network inheritance is now available for OVN L3 plugin
|
||||
QoS extension. If a network, hosting a floating IP, has a QoS associated,
|
||||
the floating IP addresses will inherit the network QoS policy and will
|
||||
apply on the OVN backend.
|
Loading…
Reference in New Issue
Block a user