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)
changes/51/701951/1
Lucas Alvares Gomes 3 years ago
committed by Jakub Libosvar
parent
commit
4804513907
  1. 6
      networking_ovn/db/revision.py
  2. 3
      networking_ovn/ml2/mech_driver.py
  3. 15
      networking_ovn/tests/unit/db/test_revision.py

6
networking_ovn/db/revision.py

@ -51,16 +51,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

3
networking_ovn/ml2/mech_driver.py

@ -507,7 +507,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
networking_ovn/tests/unit/db/test_revision.py

@ -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