Merge "[OVN] Implement floating IP network QoS inheritance"
This commit is contained in:
commit
f94226c514
@ -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