Source code for heat.common.lifecycle_plugin_utils


#
#    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.

"""Utility for fetching and running plug point implementation classes."""

from oslo_log import log as logging

from heat.engine import resources

LOG = logging.getLogger(__name__)
pp_class_instances = None


[docs]def get_plug_point_class_instances(): """Instances of classes that implements pre/post stack operation methods. Get list of instances of classes that (may) implement pre and post stack operation methods. The list of class instances is sorted using get_ordinal methods on the plug point classes. If class1.ordinal() < class2.ordinal(), then class1 will be before class2 in the list. """ global pp_class_instances if pp_class_instances is None: pp_class_instances = [] pp_classes = [] try: slps = resources.global_env().get_stack_lifecycle_plugins() pp_classes = [cls for name, cls in slps] except Exception: LOG.exception("failed to get lifecycle plug point classes") for ppc in pp_classes: try: pp_class_instances.append(ppc()) except Exception: LOG.exception( "failed to instantiate stack lifecycle class %s", ppc) try: pp_class_instances = sorted(pp_class_instances, key=lambda ppci: ppci.get_ordinal()) except Exception: LOG.exception("failed to sort lifecycle plug point classes") return pp_class_instances
[docs]def do_pre_ops(cnxt, stack, current_stack=None, action=None): """Call available pre-op methods sequentially. In order determined with get_ordinal(), with parameters context, stack, current_stack, action. On failure of any pre_op method, will call post-op methods corresponding to successful calls of pre-op methods. """ cinstances = get_plug_point_class_instances() if action is None: action = stack.action failure, failure_exception_message, success_count = _do_ops( cinstances, 'do_pre_op', cnxt, stack, current_stack, action, None) if failure: cinstances = cinstances[0:success_count] _do_ops(cinstances, 'do_post_op', cnxt, stack, current_stack, action, True) raise Exception(failure_exception_message)
[docs]def do_post_ops(cnxt, stack, current_stack=None, action=None, is_stack_failure=False): """Call available post-op methods sequentially. In order determined with get_ordinal(), with parameters context, stack, current_stack, action, is_stack_failure. """ cinstances = get_plug_point_class_instances() if action is None: action = stack.action _do_ops(cinstances, 'do_post_op', cnxt, stack, current_stack, action, None)
def _do_ops(cinstances, opname, cnxt, stack, current_stack=None, action=None, is_stack_failure=None): success_count = 0 failure = False failure_exception_message = None for ci in cinstances: op = getattr(ci, opname, None) if callable(op): try: if is_stack_failure is not None: op(cnxt, stack, current_stack, action, is_stack_failure) else: op(cnxt, stack, current_stack, action) success_count += 1 except Exception as ex: LOG.exception( "%(opname)s %(ci)s failed for %(a)s on %(sid)s", {'opname': opname, 'ci': type(ci), 'a': action, 'sid': stack.id}) failure = True failure_exception_message = ex.args[0] if ex.args else str(ex) break LOG.info("done with class=%(c)s, stackid=%(sid)s, action=%(a)s", {'c': type(ci), 'sid': stack.id, 'a': action}) return (failure, failure_exception_message, success_count)