[ovn] Specify port type if it's a router port when updating

In order to avoid multiple LogicalSwitchPortUpdateUpEvent and
LogicalSwitchPortUpdateDownEvent is mandatory to specify the
router port type when udpating the port.

If the port is not specified when updating the port, the
transactions will trigger a modification on the ovnnb db
that will set the port status to down[0]. Triggering an unnecessary
DownEvent followed by another UpEvent. Those unnecessary event
most likely will trigger a revision conflict.

[0] - https://github.com/ovn-org/ovn/blob/
4f93381d7d38aa21f56fb3ff4ec00490fca12614/northd/northd.c#L15604

Conflicts:
	neutron/common/ovn/constants.py

Closes-Bug: #1955578
Change-Id: I296003a936db16dd3a7d184ec44908fb3f261876
(cherry picked from commit 8c482b83f2)
This commit is contained in:
Arnau Verdaguer 2021-12-16 21:53:27 +01:00
parent fdacb80cc9
commit 6e1b7d15d7
3 changed files with 77 additions and 0 deletions

View File

@ -288,6 +288,7 @@ PORT_SECURITYGROUPS = 'security_groups'
LSP_TYPE_LOCALNET = 'localnet'
LSP_TYPE_VIRTUAL = 'virtual'
LSP_TYPE_EXTERNAL = 'external'
LSP_TYPE_ROUTER = 'router'
LSP_OPTIONS_VIRTUAL_PARENTS_KEY = 'virtual-parents'
LSP_OPTIONS_VIRTUAL_IP_KEY = 'virtual-ip'
LSP_OPTIONS_MCAST_FLOOD_REPORTS = 'mcast_flood_reports'

View File

@ -491,6 +491,13 @@ class OVNClient(object):
txn.add(check_rev_cmd)
columns_dict = {}
if utils.is_lsp_router_port(port):
# It is needed to specify the port type, if not specified
# the AddLSwitchPortCommand will trigger a change
# on the northd status column from UP to DOWN, triggering a
# LogicalSwitchPortUpdateDownEvent, that will most likely
# cause a revision conflict.
# https://bugs.launchpad.net/neutron/+bug/1955578
columns_dict['type'] = ovn_const.LSP_TYPE_ROUTER
port_info.options.update(
self._nb_idl.get_router_port_options(port['id']))
else:

View File

@ -34,6 +34,7 @@ from neutron.db import ovn_revision_numbers_db as db_rev
from neutron.plugins.ml2.drivers.ovn.mech_driver import mech_driver
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import impl_idl_ovn
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovsdb_monitor
from neutron.tests import base as tests_base
from neutron.tests.functional import base
@ -507,6 +508,74 @@ class TestExternalPorts(base.TestOVNFunctionalBase):
self.assertEqual(str(self.default_ch_grp.uuid),
str(ovn_port.ha_chassis_group[0].uuid))
def _create_router_port(self, vnic_type):
net_id = self.n1['network']['id']
port_data = {
'port': {'network_id': net_id,
'tenant_id': self._tenant_id,
portbindings.VNIC_TYPE: 'normal'}}
# Create port
port_req = self.new_create_request('ports', port_data, self.fmt)
port_res = port_req.get_response(self.api)
port = self.deserialize(self.fmt, port_res)['port']
# Update it as lsp port
port_upt_data = {
'port': {'device_owner': "network:router_gateway"}
}
port_req = self.new_update_request(
'ports', port_upt_data, port['id'], self.fmt)
port_res = port_req.get_response(self.api)
def test_add_external_port_avoid_flapping(self):
class LogicalSwitchPortUpdateUpEventTest(event.RowEvent):
def __init__(self):
self.count = 0
table = 'Logical_Switch_Port'
events = (self.ROW_UPDATE,)
super(LogicalSwitchPortUpdateUpEventTest, self).__init__(
events, table, (('up', '=', True),),
old_conditions=(('up', '=', False),))
def run(self, event, row, old):
self.count += 1
def get_count(self):
return self.count
class LogicalSwitchPortUpdateDownEventTest(event.RowEvent):
def __init__(self):
self.count = 0
table = 'Logical_Switch_Port'
events = (self.ROW_UPDATE,)
super(LogicalSwitchPortUpdateDownEventTest, self).__init__(
events, table, (('up', '=', False),),
old_conditions=(('up', '=', True),))
def run(self, event, row, old):
self.count += 1
def get_count(self):
return self.count
og_up_event = ovsdb_monitor.LogicalSwitchPortUpdateUpEvent(None)
og_down_event = ovsdb_monitor.LogicalSwitchPortUpdateDownEvent(None)
test_down_event = LogicalSwitchPortUpdateDownEventTest()
test_up_event = LogicalSwitchPortUpdateUpEventTest()
self.nb_api.idl.notify_handler.unwatch_events(
[og_up_event, og_down_event])
self.nb_api.idl.notify_handler.watch_events(
[test_down_event, test_up_event])
# Creating a port the same way as the osp cli cmd
# openstack router add port ROUTER PORT
# shouldn't trigger an status flapping (up -> down -> up)
# it should be created with status false and then change the
# status as up, triggering only a LogicalSwitchPortUpdateUpEvent.
self._create_router_port(portbindings.VNIC_DIRECT)
self.assertEqual(test_down_event.get_count(), 0)
self.assertEqual(test_up_event.get_count(), 1)
def test_external_port_create_vnic_direct(self):
self._test_external_port_create(portbindings.VNIC_DIRECT)