shim model query hooks to use neutron-lib

This patch shims the model query hook functionality to use neutron-lib.
A shim is used as the query hooks are cached in a shared dict and by
shimming them to use neutron-lib we can let consumers move over to
neutron-lib's version at will. Once all current consumers are moved
over to lib, we can remove the shim and consume from lib directly.

The DBQueryHooksFixture is also used in this patch to replace the
existing cleanup function in the unit tests.

Change-Id: I18a4e836ce89b607b5452527e5f8dd172cad6b4f
This commit is contained in:
Boden R 2018-08-14 15:24:13 -06:00
parent 858a7ff42d
commit 5c208a313a
2 changed files with 7 additions and 111 deletions

View File

@ -17,6 +17,7 @@ NOTE: This module shall not be used by external projects. It will be moved
"""
from neutron_lib.api import attributes
from neutron_lib.db import model_query
from neutron_lib.db import utils as db_utils
from neutron_lib.objects import utils as obj_utils
from neutron_lib.utils import helpers
@ -25,111 +26,10 @@ from sqlalchemy import sql, or_, and_
from sqlalchemy.ext import associationproxy
# Classes implementing extensions will register hooks into this dictionary
# for "augmenting" the "core way" of building a query for retrieving objects
# from a model class. Hooks are registered by invoking register_hook().
_model_query_hooks = {
# model1 : {
# hook1: {
# 'query': query_hook,
# 'filter': filter_hook,
# 'result_filters': result_filters
# },
# hook2: {
# 'query': query_hook,
# 'filter': filter_hook,
# 'result_filters': result_filters
# },
# ...
# },
# model2 : {
# hook1: {
# 'query': query_hook,
# 'filter': filter_hook,
# 'result_filters': result_filters
# },
# hook2: {
# 'query': query_hook,
# 'filter': filter_hook,
# 'result_filters': result_filters
# },
# ...
# },
# ...
}
def register_hook(model, name, query_hook, filter_hook,
result_filters=None):
"""Register a hook to be invoked when a query is executed.
:param model: The DB Model that the hook applies to.
:type model: sqlalchemy orm model
:param name: A name for the hook.
:type name: str
:param query_hook: The method to be called to augment the query.
:type query_hook: callable or None
:param filter_hook: A method to be called to augment the query filter.
:type filter_hook: callable or None
:param result_filters: A Method to be called to filter the query result.
:type result_filters: callable or None
Adds the hook components to the _model_query_hooks dict. Models are the
keys of this dict, whereas the value is another dict mapping hook names
to callables performing the hook.
Each hook has three components:
"query", used to build the query expression
"filter", used to build the filter expression
"result_filters", used for final filtering on the query result
Query hooks take as input the query being built and return a
transformed query expression.
def mymodel_query_hook(context, original_model, query):
augmented_query = ...
return augmented_query
Filter hooks take as input the filter expression being built and return
a transformed filter expression
def mymodel_filter_hook(context, original_model, filters):
refined_filters = ...
return refined_filters
Result filter hooks take as input the query expression and the filter
expression, and return a final transformed query expression.
def mymodel_result_filter_hook(query, filters):
final_filters = ...
return query.filter(final_filters)
"""
if callable(query_hook):
query_hook = helpers.make_weak_ref(query_hook)
if callable(filter_hook):
filter_hook = helpers.make_weak_ref(filter_hook)
if callable(result_filters):
result_filters = helpers.make_weak_ref(result_filters)
_model_query_hooks.setdefault(model, {})[name] = {
'query': query_hook,
'filter': filter_hook,
'result_filters': result_filters
}
def get_hooks(model):
"""Retrieve the model query hooks for a model.
:param model: The DB Model to look up for query hooks.
:type model: sqlalchemy orm model
:return: list of hooks
:rtype: list of dict of callable
"""
return _model_query_hooks.get(model, {}).values()
# TODO(boden): remove shims
_model_query_hooks = model_query._model_query_hooks
register_hook = model_query.register_hook
get_hooks = model_query.get_hooks
def query_with_hooks(context, model):

View File

@ -47,7 +47,6 @@ from neutron.api.rpc.callbacks.producer import registry as rpc_producer_reg
from neutron.common import config
from neutron.common import rpc as n_rpc
from neutron.conf.agent import common as agent_config
from neutron.db import _model_query as model_query
from neutron.db import _resource_extend as resource_extend
from neutron.db import agentschedulers_db
from neutron import manager
@ -184,6 +183,8 @@ class DietTestCase(base.BaseTestCase):
# Make sure we see all relevant deprecation warnings when running tests
self.useFixture(tools.WarningsFixture())
self.useFixture(fixture.DBQueryHooksFixture())
# NOTE(ihrachys): oslotest already sets stopall for cleanup, but it
# does it using six.moves.mock (the library was moved into
# unittest.mock in Python 3.4). So until we switch to six.moves.mock
@ -192,7 +193,6 @@ class DietTestCase(base.BaseTestCase):
# six before removing the cleanup callback from here.
self.addCleanup(mock.patch.stopall)
self.addCleanup(self.reset_model_query_hooks)
self.addCleanup(self.reset_resource_extend_functions)
self.addOnException(self.check_for_systemexit)
@ -200,10 +200,6 @@ class DietTestCase(base.BaseTestCase):
tools.reset_random_seed()
@staticmethod
def reset_model_query_hooks():
model_query._model_query_hooks = {}
@staticmethod
def reset_resource_extend_functions():
resource_extend._resource_extend_functions = {}