[OVN] Fix the revision number retrieval method
The "ovn_revision_numbers" table has a unique constraint that is a combination of the "resource_uuid" and the "resource_type". There is a case where the resource_uuid can be the same for two registers. A router interface will create a single Neutron DB register ("ports") but it will require two OVN DB registers ("Logical_Switch_Port" and "Logical_Router_Ports"). In this case is needed to define the "resource_type" when retrieving the revision number. The exception "RevisionNumberNotDefined" will be thrown if only the "resource_uuid" is provided in the related case. Closes-Bug: #2085946 Change-Id: I12079de78773f7409503392d4791848aea90cb7b (cherry picked from commit a298a37fe7ee41d25db02fdde36e134b01ef5d9a)
This commit is contained in:
parent
264306492f
commit
2d376a76c9
@ -92,6 +92,12 @@ class UnknownResourceType(n_exc.NeutronException):
|
|||||||
message = 'Uknown resource type: %(resource_type)s'
|
message = 'Uknown resource type: %(resource_type)s'
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE(ralonsoh): to be moved to neutron-lib
|
||||||
|
class RevisionNumberNotDefined(n_exc.NeutronException):
|
||||||
|
message = ('Unique revision number not found for %(resource_uuid)s, '
|
||||||
|
'the resource type is required in query')
|
||||||
|
|
||||||
|
|
||||||
def _get_standard_attr_id(context, resource_uuid, resource_type):
|
def _get_standard_attr_id(context, resource_uuid, resource_type):
|
||||||
try:
|
try:
|
||||||
row = context.session.query(STD_ATTR_MAP[resource_type]).filter_by(
|
row = context.session.query(STD_ATTR_MAP[resource_type]).filter_by(
|
||||||
@ -155,14 +161,26 @@ def _ensure_revision_row_exist(context, resource, resource_type, std_attr_id):
|
|||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def get_revision_row(context, resource_uuid):
|
@db_api.CONTEXT_READER
|
||||||
|
def get_revision_row(context, resource_uuid, resource_type=None):
|
||||||
|
"""Retrieve the resource revision number
|
||||||
|
|
||||||
|
Only the Neutron ports can have two revision number registers, one for the
|
||||||
|
Logical_Switch_Port and another for the Logical_Router_Port, if this port
|
||||||
|
is a router interface. It is not strictly needed to filter by resource_type
|
||||||
|
if the resource is not a port.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
with db_api.CONTEXT_READER.using(context):
|
filters = {'resource_uuid': resource_uuid}
|
||||||
return context.session.query(
|
if resource_type:
|
||||||
ovn_models.OVNRevisionNumbers).filter_by(
|
filters['resource_type'] = resource_type
|
||||||
resource_uuid=resource_uuid).one()
|
return context.session.query(
|
||||||
|
ovn_models.OVNRevisionNumbers).filter_by(
|
||||||
|
**filters).one()
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
pass
|
pass
|
||||||
|
except exc.MultipleResultsFound:
|
||||||
|
raise RevisionNumberNotDefined(resource_uuid=resource_uuid)
|
||||||
|
|
||||||
|
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
|
@ -838,7 +838,8 @@ class OVNClient(object):
|
|||||||
# to allow at least one maintenance cycle before we delete the
|
# to allow at least one maintenance cycle before we delete the
|
||||||
# revision number so that the port doesn't stale and eventually
|
# revision number so that the port doesn't stale and eventually
|
||||||
# gets deleted by the maintenance task.
|
# gets deleted by the maintenance task.
|
||||||
rev_row = db_rev.get_revision_row(context, port_id)
|
rev_row = db_rev.get_revision_row(
|
||||||
|
context, port_id, resource_type=ovn_const.TYPE_PORTS)
|
||||||
time_ = (timeutils.utcnow() - datetime.timedelta(
|
time_ = (timeutils.utcnow() - datetime.timedelta(
|
||||||
seconds=ovn_const.DB_CONSISTENCY_CHECK_INTERVAL + 30))
|
seconds=ovn_const.DB_CONSISTENCY_CHECK_INTERVAL + 30))
|
||||||
if rev_row and rev_row.created_at >= time_:
|
if rev_row and rev_row.created_at >= time_:
|
||||||
|
@ -380,7 +380,8 @@ class TestMaintenance(_TestMaintenanceHelper):
|
|||||||
# Assert the revision number no longer exists
|
# Assert the revision number no longer exists
|
||||||
self.assertIsNone(db_rev.get_revision_row(
|
self.assertIsNone(db_rev.get_revision_row(
|
||||||
self.context,
|
self.context,
|
||||||
neutron_obj['id']))
|
neutron_obj['id'],
|
||||||
|
resource_type=ovn_const.TYPE_PORTS))
|
||||||
|
|
||||||
def test_subnet_global_dhcp4_opts(self):
|
def test_subnet_global_dhcp4_opts(self):
|
||||||
obj_name = 'globaltestsubnet'
|
obj_name = 'globaltestsubnet'
|
||||||
|
@ -24,6 +24,7 @@ from oslo_db import exception as db_exc
|
|||||||
|
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
from neutron.common import config
|
from neutron.common import config
|
||||||
|
from neutron.common.ovn import constants as ovn_const
|
||||||
from neutron.db.models import ovn as ovn_models
|
from neutron.db.models import ovn as ovn_models
|
||||||
from neutron.db import ovn_revision_numbers_db as ovn_rn_db
|
from neutron.db import ovn_revision_numbers_db as ovn_rn_db
|
||||||
import neutron.extensions
|
import neutron.extensions
|
||||||
@ -32,7 +33,6 @@ from neutron.tests.unit.db import test_db_base_plugin_v2
|
|||||||
from neutron.tests.unit.extensions import test_l3
|
from neutron.tests.unit.extensions import test_l3
|
||||||
from neutron.tests.unit.extensions import test_securitygroup
|
from neutron.tests.unit.extensions import test_securitygroup
|
||||||
|
|
||||||
|
|
||||||
EXTENSIONS_PATH = ':'.join(neutron.extensions.__path__)
|
EXTENSIONS_PATH = ':'.join(neutron.extensions.__path__)
|
||||||
PLUGIN_CLASS = (
|
PLUGIN_CLASS = (
|
||||||
'neutron.tests.unit.db.test_ovn_revision_numbers_db.TestMaintenancePlugin')
|
'neutron.tests.unit.db.test_ovn_revision_numbers_db.TestMaintenancePlugin')
|
||||||
@ -123,6 +123,24 @@ class TestRevisionNumber(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
|||||||
self.fail("create_initial_revision shouldn't raise "
|
self.fail("create_initial_revision shouldn't raise "
|
||||||
"DBDuplicateEntry when may_exist is True")
|
"DBDuplicateEntry when may_exist is True")
|
||||||
|
|
||||||
|
def test_get_revision_row_ports(self):
|
||||||
|
res = self._create_port(self.fmt, self.net['id'])
|
||||||
|
port = self.deserialize(self.fmt, res)['port']
|
||||||
|
with db_api.CONTEXT_WRITER.using(self.ctx):
|
||||||
|
for resource_type in (ovn_const.TYPE_PORTS,
|
||||||
|
ovn_const.TYPE_ROUTER_PORTS):
|
||||||
|
self._create_initial_revision(port['id'], resource_type)
|
||||||
|
|
||||||
|
for resource_type in (ovn_const.TYPE_PORTS,
|
||||||
|
ovn_const.TYPE_ROUTER_PORTS):
|
||||||
|
row = ovn_rn_db.get_revision_row(
|
||||||
|
self.ctx, port['id'], resource_type=resource_type)
|
||||||
|
self.assertEqual(resource_type, row.resource_type)
|
||||||
|
self.assertEqual(port['id'], row.resource_uuid)
|
||||||
|
|
||||||
|
self.assertRaises(ovn_rn_db.RevisionNumberNotDefined,
|
||||||
|
ovn_rn_db.get_revision_row, self.ctx, port['id'])
|
||||||
|
|
||||||
|
|
||||||
class TestMaintenancePlugin(test_securitygroup.SecurityGroupTestPlugin,
|
class TestMaintenancePlugin(test_securitygroup.SecurityGroupTestPlugin,
|
||||||
test_l3.TestL3NatBasePlugin):
|
test_l3.TestL3NatBasePlugin):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user