Browse Source

Fix revision number race condition with attaching router interfaces

There's a special case in the update_port_precommit() method to handle
the case where an existing port is being added to a router. The method
will try to create an entry in the ovn_revision_numbers table but, if
that entry is already present a DBDuplicateEntry exception will be
raised and the whole update method will fail.

The update_port_precommit() is the only method at present that have this
special handler, for all other resources creating a new record in the
ovn_revision_numbers table happens at the create_*_precommit() methods
(this include create_port_precommit() as well).

This patch is fixing the problem by adding a new parameter to the
reate_initial_revision() method called "may_exist". When set to True the
code will not raise an DBDuplicateEntry if the record already exists. By
default this method is False.

Closes-Bug: #1800875
Change-Id: Iddd89725e617b22c35e30ae7f20a7f87de296cdb
Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
(cherry picked from commit b5642d93a6)
tags/5.1.0
Lucas Alvares Gomes Jakub Libosvar 1 year ago
parent
commit
64ac6d274e
3 changed files with 21 additions and 3 deletions
  1. +4
    -2
      networking_ovn/db/revision.py
  2. +2
    -1
      networking_ovn/ml2/mech_driver.py
  3. +15
    -0
      networking_ovn/tests/unit/db/test_revision.py

+ 4
- 2
networking_ovn/db/revision.py View File

@@ -53,16 +53,18 @@ def _get_standard_attr_id(session, resource_uuid, resource_type):

@_wrap_db_retry
def create_initial_revision(resource_uuid, resource_type, session,
revision_number=ovn_const.INITIAL_REV_NUM):
revision_number=ovn_const.INITIAL_REV_NUM,
may_exist=False):
LOG.debug('create_initial_revision uuid=%s, type=%s, rev=%s',
resource_uuid, resource_type, revision_number)
db_func = session.merge if may_exist else session.add
with session.begin(subtransactions=True):
std_attr_id = _get_standard_attr_id(
session, resource_uuid, resource_type)
row = models.OVNRevisionNumbers(
resource_uuid=resource_uuid, resource_type=resource_type,
standard_attr_id=std_attr_id, revision_number=revision_number)
session.add(row)
db_func(row)


@_wrap_db_retry


+ 2
- 1
networking_ovn/ml2/mech_driver.py View File

@@ -520,7 +520,8 @@ class OVNMechanismDriver(api.MechanismDriver):
if not utils.is_lsp_router_port(original_port):
db_rev.create_initial_revision(port['id'],
ovn_const.TYPE_ROUTER_PORTS,
context._plugin_context.session)
context._plugin_context.session,
may_exist=True)

def update_port_postcommit(self, context):
"""Update a port.


+ 15
- 0
networking_ovn/tests/unit/db/test_revision.py View File

@@ -17,6 +17,7 @@ import mock

from neutron.tests.unit.plugins.ml2 import test_plugin
from neutron_lib.db import api as db_api
from oslo_db import exception as db_exc

from networking_ovn.common import constants
from networking_ovn.db import revision as db_rev
@@ -64,3 +65,17 @@ class TestRevisionNumber(db_base.DBTestCase, test_plugin.Ml2PluginV2TestCase):
db_rev.delete_revision(self.net['id'], constants.TYPE_NETWORKS)
row = db_rev.get_revision_row(self.net['id'])
self.assertIsNone(row)

def test_create_initial_revision_may_exist_duplicated_entry(self):
args = (self.net['id'], constants.TYPE_NETWORKS, self.session)
db_rev.create_initial_revision(*args)

# assert DBDuplicateEntry is raised when may_exist is False (default)
self.assertRaises(db_exc.DBDuplicateEntry,
db_rev.create_initial_revision, *args)

try:
db_rev.create_initial_revision(*args, may_exist=True)
except db_exc.DBDuplicateEntry:
self.fail("create_initial_revision shouldn't raise "
"DBDuplicateEntry when may_exist is True")

Loading…
Cancel
Save