[OVN] Prevent deleting the only IP of a router port

Neutron allows deleting the only IP of a router port but
the OVN NB DB doesn't, since it expects that the
network value of a port is greater than 0. This should
not be possible since it causes that the DB are not
perfectly sync.

It is needed to check BEFORE_UPDATE if the port
that will be updated is of type router owned and
if it will have an IP after the update. If not
an error needs to be raised.

Closes-Bug: #1948457
Change-Id: I206c31201470f178efdde8839622be7900c6ae3e
This commit is contained in:
Arnau Verdaguer 2021-11-17 16:59:28 +01:00
parent 90b5456b8c
commit fa2179278f
3 changed files with 43 additions and 2 deletions

View File

@ -28,6 +28,7 @@ from neutron_lib.services import base as service_base
from oslo_log import log
from oslo_utils import excutils
from neutron._i18n import _
from neutron.common.ovn import constants as ovn_const
from neutron.common.ovn import extensions
from neutron.common.ovn import utils
@ -462,7 +463,10 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
context, router_id, add, remove, txn=txn)
@staticmethod
@registry.receives(resources.PORT, [events.AFTER_UPDATE])
@registry.receives(
resources.PORT,
[events.BEFORE_UPDATE, events.AFTER_UPDATE]
)
def _port_update(resource, event, trigger, payload):
l3plugin = directory.get_plugin(plugin_constants.L3)
if not l3plugin:
@ -470,8 +474,23 @@ class OVNL3RouterPlugin(service_base.ServicePluginBase,
context = payload.context
current = payload.latest_state
original = payload.states[0]
if utils.is_lsp_router_port(current):
# The OVN NB DB has a constraint where network has to be
# greater than 0. Updating it with an empty network would
# cause a constraint violation error. This problem happens
# when the last IP of a LRP is deleted, in order to avoid it
# an exception needs to be thrown before any write is performed
# to the DB, since if not it would leave the Neutron DB and the
# OVN DB unsync.
# https://bugs.launchpad.net/neutron/+bug/1948457
if (event == events.BEFORE_UPDATE and
'fixed_ips' in current and not current['fixed_ips'] and
utils.is_lsp_router_port(original)):
reason = _("Router port must have at least one IP.")
raise n_exc.ServicePortInUse(port_id=original['id'], reason=reason)
if event == events.AFTER_UPDATE and utils.is_lsp_router_port(current):
# We call the update_router port with if_exists, because neutron,
# internally creates the port, and then calls update, which will
# trigger this callback even before we had the chance to create

View File

@ -1414,6 +1414,22 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
port,
if_exists=True)
def test_port_update_before_update_router_port_without_ip(self):
context = 'fake_context'
port = {'device_owner': constants.DEVICE_OWNER_ROUTER_INTF,
'fixed_ips': [],
'id': 'port-id'}
self.assertRaises(
n_exc.ServicePortInUse,
self.l3_inst._port_update,
resources.PORT,
events.BEFORE_UPDATE,
None,
payload=events.DBEventPayload(
context,
states=(port,))
)
@mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.update_port_status')
@mock.patch('neutron.plugins.ml2.plugin.Ml2Plugin.update_port')
@mock.patch('neutron.db.db_base_plugin_v2.NeutronDbPluginV2.get_ports')

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Changes the API behaviour while using OVN driver to enforce that it's not possible to delete
all the IPs from a router port.
For more info see `bug LP#1948457 <https://bugs.launchpad.net/neutron/+bug/1948457>`_