Add an active wait in the "Backend.lookup"
Added an active wait in the method "Backend.lookup". If a timeout and a RowEventHandler instance, listening on the same IDL connection, are passed, if the "lookup" method does not find the requested register, the method creates a WaitEvent instance related to the table and register requested. If the wait event returns, the expected record will be stored in the event "result" member and will be the value returned by this method. If the wait event timeout expires, the method will throw an ``idlutils.RowNotFound`` exception. Closes-Bug: #1922934 Change-Id: I580bfb640d8157b4e749d7b1fe5de7568e99943d
This commit is contained in:
parent
627b14e2a7
commit
bc004045cd
@ -14,6 +14,7 @@ import logging
|
||||
import uuid
|
||||
|
||||
from ovsdbapp.backend.ovs_idl import command as cmd
|
||||
from ovsdbapp.backend.ovs_idl import event
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp.backend.ovs_idl import transaction
|
||||
from ovsdbapp import exceptions
|
||||
@ -22,6 +23,27 @@ LOG = logging.getLogger(__name__)
|
||||
_NO_DEFAULT = object()
|
||||
|
||||
|
||||
class LookupWaitEvent(event.WaitEvent):
|
||||
|
||||
def __init__(self, backend, table, record, timeout):
|
||||
events = (self.ROW_CREATE, self.ROW_UPDATE)
|
||||
super().__init__(events, table, None, timeout=timeout)
|
||||
self.backend = backend
|
||||
self.record = record
|
||||
self.event_name = 'LookupWaitEvent_%s' % table
|
||||
self.result = None
|
||||
|
||||
def match_fn(self, event, row, old):
|
||||
try:
|
||||
# Normally, we would use run() to do things on match, but in this
|
||||
# case, that would mean we'd have to run lookup() again.
|
||||
with self.backend.ovsdb_connection.lock:
|
||||
self.result = self.backend.lookup(self.table, self.record)
|
||||
return bool(self.result)
|
||||
except idlutils.RowNotFound:
|
||||
return False
|
||||
|
||||
|
||||
class Backend(object):
|
||||
lookup_table = {}
|
||||
_ovsdb_connection = None
|
||||
@ -171,14 +193,36 @@ class Backend(object):
|
||||
return cmd.DbRemoveCommand(self, table, record, column,
|
||||
*values, **keyvalues)
|
||||
|
||||
def lookup(self, table, record, default=_NO_DEFAULT):
|
||||
def lookup(self, table, record, default=_NO_DEFAULT, timeout=None,
|
||||
notify_handler=None):
|
||||
"""Search for a record in a table
|
||||
|
||||
If timeout and notify_handler of type ``row_event.RowEventHandler``
|
||||
are passed, in case the record is not present in the selected table,
|
||||
the method creates an event, waiting for this record (UUID), on this
|
||||
table and events CREATE and UPDATE. The event returns with the record
|
||||
memoized if the record was created or updated.
|
||||
"""
|
||||
try:
|
||||
with self.ovsdb_connection.lock:
|
||||
return self._lookup(table, record)
|
||||
except idlutils.RowNotFound:
|
||||
if default is not _NO_DEFAULT:
|
||||
return default
|
||||
raise
|
||||
if not notify_handler:
|
||||
notify_handler = getattr(self, 'notify_handler', None)
|
||||
if not (timeout and notify_handler):
|
||||
raise
|
||||
|
||||
wait_event = LookupWaitEvent(self, table, record, timeout)
|
||||
notify_handler.watch_event(wait_event)
|
||||
if not wait_event.wait():
|
||||
LOG.info('Record %s from table %s was not registered in the '
|
||||
'IDL DB cache after %d seconds', record, table,
|
||||
timeout)
|
||||
notify_handler.unwatch_event(wait_event)
|
||||
raise
|
||||
return wait_event.result
|
||||
|
||||
def _lookup(self, table, record):
|
||||
if record == "":
|
||||
|
63
ovsdbapp/tests/functional/backend/ovs_idl/test_backend.py
Normal file
63
ovsdbapp/tests/functional/backend/ovs_idl/test_backend.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from ovsdbapp.backend.ovs_idl import event
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp.schema.ovn_northbound import impl_idl
|
||||
from ovsdbapp.tests.functional import base
|
||||
|
||||
|
||||
class TestOvnNbIndex(base.FunctionalTestCase):
|
||||
schemas = ['OVN_Northbound']
|
||||
|
||||
def setUp(self):
|
||||
super(TestOvnNbIndex, self).setUp()
|
||||
self.api = impl_idl.OvnNbApiIdlImpl(self.connection)
|
||||
self.lsp_name = str(uuid.uuid4())
|
||||
self.a = None
|
||||
|
||||
def _create_ls(self):
|
||||
time.sleep(1) # Wait a bit to allow a first unsuccessful lookup().
|
||||
self.api.db_create('Logical_Switch', name=self.lsp_name).execute()
|
||||
|
||||
def test_lookup_with_timeout_and_notify_handler(self):
|
||||
notify_handler = event.RowEventHandler()
|
||||
self.api.idl.notify = notify_handler.notify
|
||||
t_create = threading.Thread(target=self._create_ls, args=())
|
||||
t_create.start()
|
||||
ret = self.api.lookup('Logical_Switch', self.lsp_name, timeout=3,
|
||||
notify_handler=notify_handler)
|
||||
self.assertEqual(self.lsp_name, ret.name)
|
||||
t_create.join()
|
||||
|
||||
def _test_lookup_exception(self, timeout, notify_handler):
|
||||
if notify_handler:
|
||||
self.api.idl.notify = notify_handler.notify
|
||||
t_create = threading.Thread(target=self._create_ls, args=())
|
||||
t_create.start()
|
||||
self.assertRaises(idlutils.RowNotFound, self.api.lookup,
|
||||
'Logical_Switch', self.lsp_name, timeout=timeout,
|
||||
notify_handler=notify_handler)
|
||||
t_create.join()
|
||||
|
||||
def test_lookup_without_timeout(self):
|
||||
self._test_lookup_exception(0, event.RowEventHandler())
|
||||
|
||||
def test_lookup_without_event_handler(self):
|
||||
self._test_lookup_exception(3, None)
|
||||
|
||||
def test_lookup_without_timeout_and_event_handler(self):
|
||||
self._test_lookup_exception(0, None)
|
Loading…
Reference in New Issue
Block a user