# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # 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. from oslo_log import log as logging from oslo_utils import encodeutils from taskflow import task from glance.i18n import _LW LOG = logging.getLogger(__name__) class OptionalTask(task.Task): def __init__(self, *args, **kwargs): super(OptionalTask, self).__init__(*args, **kwargs) self.execute = self._catch_all(self.execute) def _catch_all(self, func): # NOTE(flaper87): Read this comment before calling the MI6 # Here's the thing, there's no nice way to define "optional" # tasks. That is, tasks whose failure shouldn't affect the execution # of the flow. The only current "sane" way to do this, is by catching # everything and logging. This seems harmless from a taskflow # perspective but it is not. There are some issues related to this # "workaround": # # - Task's states will shamelessly lie to us saying the task succeeded. # # - No revert procedure will be triggered, which means optional tasks, # for now, mustn't cause any side-effects because they won't be able to # clean them up. If these tasks depend on other task that do cause side # effects, a task that cleans those side effects most be registered as # well. For example, _ImportToFS, _MyDumbTask, _DeleteFromFS. # # - Ideally, optional tasks shouldn't `provide` new values unless they # are part of an optional flow. Due to the decoration of the execute # method, these tasks will need to define the provided methods at # class level using `default_provides`. # # # The taskflow team is working on improving this and on something that # will provide the ability of defining optional tasks. For now, to lie # ourselves we must. # # NOTE(harlowja): The upstream change that is hopefully going to make # this easier/built-in is at: https://review.openstack.org/#/c/271116/ def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as exc: msg = (_LW("An optional task has failed, " "the failure was: %s") % encodeutils.exception_to_unicode(exc)) LOG.warn(msg) return wrapper