From 753f1bc03fc5ae464d6a3c8266e025dce3cceccb Mon Sep 17 00:00:00 2001 From: Renat Akhmerov Date: Wed, 26 Feb 2020 11:38:51 +0700 Subject: [PATCH] Fix YAQL engine initialization * When Mistral is launched via launch.py script then YAQL engine class initialization logic doesn't use the right properties from the config file. This is because this initialization is caused by the chain of imports taking its start in launch.py, and this happens before the config file is parsed in the main() function of launch.py. Using lazy initialization of YAQL engine class (YAQL_ENGINE in yaql_expression.py module) solves this issue because now Mistral doesn't initialize it immediately before parsing the config and waits for the first usage of it. * Minor style changes per Mistral coding guidelines. Change-Id: If3367493803b57ef8bc281b1f64f2a223ac86f85 Closes-Bug: #1864785 --- mistral/cmd/launch.py | 21 ++++++++++-- mistral/expressions/yaql_expression.py | 44 ++++++++++++++++---------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/mistral/cmd/launch.py b/mistral/cmd/launch.py index 313452cc6..ebff6d218 100644 --- a/mistral/cmd/launch.py +++ b/mistral/cmd/launch.py @@ -32,9 +32,14 @@ import os # If ../mistral/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) +POSSIBLE_TOPDIR = os.path.normpath( + os.path.join( + os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir + ) +) + if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'mistral', '__init__.py')): sys.path.insert(0, POSSIBLE_TOPDIR) @@ -177,7 +182,9 @@ def get_properly_ordered_parameters(): else: conf_file_value = args[args.index(arg) + 1] args.remove(conf_file_value) + args.remove(arg) + args.insert(0, "--config-file") args.insert(1, conf_file_value) @@ -188,9 +195,11 @@ def override_keystone_options(): # TODO(wxy): This function is used for keeping backward compatibility. # Remove it in Stein. auth_opts = CONF['keystone_authtoken'] + for opt, value in auth_opts.items(): if opt in CONF['keystone']: default_value = auth_opts._group._opts[opt]['opt'].default + if default_value != value != CONF['keystone'][opt]: LOG.warning("The config option '%s' in section " "[keystone_authtoken] has the same copy in " @@ -202,10 +211,13 @@ def override_keystone_options(): def main(): try: CONF.register_cli_opts(config.CLI_OPTS) + config.parse_args(get_properly_ordered_parameters()) + print_server_info() logging.setup(CONF, 'Mistral') + override_keystone_options() # Please refer to the oslo.messaging documentation for transport @@ -246,6 +258,7 @@ def main(): def reset_server_managers(): global SERVER_THREAD_MANAGER global SERVER_PROCESS_MANAGER + SERVER_THREAD_MANAGER = None SERVER_PROCESS_MANAGER = None @@ -253,12 +266,14 @@ def reset_server_managers(): # Helper method used in unit tests to access the service launcher. def get_server_thread_manager(): global SERVER_THREAD_MANAGER + return SERVER_THREAD_MANAGER # Helper method used in unit tests to access the process launcher. def get_server_process_manager(): global SERVER_PROCESS_MANAGER + return SERVER_PROCESS_MANAGER diff --git a/mistral/expressions/yaql_expression.py b/mistral/expressions/yaql_expression.py index 2a8f75302..36ac19a64 100644 --- a/mistral/expressions/yaql_expression.py +++ b/mistral/expressions/yaql_expression.py @@ -34,6 +34,10 @@ LOG = logging.getLogger(__name__) _YAQL_CONF = cfg.CONF.yaql +INLINE_YAQL_REGEXP = '<%.*?%>' + +YAQL_ENGINE = None + def get_yaql_engine_options(): return { @@ -54,24 +58,30 @@ def create_yaql_engine_class(keyword_operator, allow_delegates, ).create(options=engine_options) -YAQL_ENGINE = create_yaql_engine_class( - _YAQL_CONF.keyword_operator, - _YAQL_CONF.allow_delegates, - get_yaql_engine_options() -) +def get_yaql_engine_class(): + global YAQL_ENGINE -LOG.info( - "YAQL engine has been initialized with the options: \n%s", - utils.merge_dicts( - get_yaql_engine_options(), - { - "keyword_operator": _YAQL_CONF.keyword_operator, - "allow_delegates": _YAQL_CONF.allow_delegates - } + if YAQL_ENGINE is not None: + return YAQL_ENGINE + + YAQL_ENGINE = create_yaql_engine_class( + _YAQL_CONF.keyword_operator, + _YAQL_CONF.allow_delegates, + get_yaql_engine_options() ) -) -INLINE_YAQL_REGEXP = '<%.*?%>' + LOG.info( + "YAQL engine has been initialized with the options: \n%s", + utils.merge_dicts( + get_yaql_engine_options(), + { + "keyword_operator": _YAQL_CONF.keyword_operator, + "allow_delegates": _YAQL_CONF.allow_delegates + } + ) + ) + + return YAQL_ENGINE def _sanitize_yaql_result(result): @@ -91,7 +101,7 @@ class YAQLEvaluator(Evaluator): @classmethod def validate(cls, expression): try: - YAQL_ENGINE(expression) + get_yaql_engine_class()(expression) except (yaql_exc.YaqlException, KeyError, ValueError, TypeError) as e: raise exc.YaqlGrammarException(getattr(e, 'message', e)) @@ -100,7 +110,7 @@ class YAQLEvaluator(Evaluator): expression = expression.strip() if expression else expression try: - result = YAQL_ENGINE(expression).evaluate( + result = get_yaql_engine_class()(expression).evaluate( context=expression_utils.get_yaql_context(data_context) ) except Exception as e: