'Return a context manager to record the progress of an action.
Upon entering the context manager, the state is set to IN_PROGRESS.
Upon exiting, the state will be set to COMPLETE if no exception was
raised, or FAILED otherwise. Non-exit exceptions will be translated
to ResourceFailure exceptions.
Expected exceptions are re-raised, with the Resource moved to the
COMPLETE state.'

@contextlib.contextmanager
def _action_recorder(self, action, expected_exceptions=tuple()):

    attempts = 1
    first_iter = [True]
    if self.stack.convergence:
        lock_acquire = self.LOCK_ACQUIRE
        lock_release = self.LOCK_RELEASE
        if (action != self.CREATE):
            attempts += max(cfg.CONF.client_retry_limit, 0)
    else:
        lock_acquire = lock_release = self.LOCK_NONE
    @tenacity.retry(stop=tenacity.stop_after_attempt(attempts), retry=tenacity.retry_if_exception_type(exception.UpdateInProgress), wait=tenacity.wait_random(max=2), reraise=True)
    def set_in_progress():
        if (not first_iter[0]):
            res_obj = resource_objects.Resource.get_obj(self.context, self.id)
            self._atomic_key = res_obj.atomic_key
        else:
            first_iter[0] = False
        self.state_set(action, self.IN_PROGRESS, lock=lock_acquire)
    try:
        set_in_progress()
        (yield)
    except exception.UpdateInProgress as ex:
        with excutils.save_and_reraise_exception():
            LOG.info('Update    in    progress    for    %s', self.name)
    except expected_exceptions as ex:
        with excutils.save_and_reraise_exception():
            self.state_set(action, self.COMPLETE, six.text_type(ex), lock=lock_release)
            LOG.debug('%s', six.text_type(ex))
    except Exception as ex:
        LOG.info('%(action)s:    %(info)s', {'action': action, 'info': six.text_type(self)}, exc_info=True)
        failure = exception.ResourceFailure(ex, self, action)
        self.state_set(action, self.FAILED, six.text_type(failure), lock=lock_release)
        raise failure
    except BaseException as exc:
        with excutils.save_and_reraise_exception():
            try:
                reason = six.text_type(exc)
                msg = ('%s    aborted' % action)
                if reason:
                    msg += ('    (%s)' % reason)
                self.state_set(action, self.FAILED, msg, lock=lock_release)
            except Exception:
                LOG.exception('Error    marking    resource    as    failed')
    else:
        self.state_set(action, self.COMPLETE, lock=lock_release)
