def user_lock_acquire(context, user_id, action_id, engine=None, forced=False): """Try to lock the specified user. :param context: the context used for DB operations; :param user_id: ID of the user to be locked. :param action_id: ID of the action that attempts to lock the user. :param engine: ID of the engine that attempts to lock the user. :param forced: set to True to cancel current action that owns the lock, if any. :returns: True if lock is acquired, or False otherwise. """ owner = db_api.user_lock_acquire(user_id, action_id) if action_id == owner: return True retries = cfg.CONF.lock_retry_times retry_interval = cfg.CONF.lock_retry_interval while retries > 0: sleep(retry_interval) LOG.debug(_('Acquire lock for user %s again'), user_id) owner = db_api.user_lock_acquire(user_id, action_id) if action_id == owner: return True retries = retries - 1 if forced: owner = db_api.user_lock_steal(user_id, action_id) return action_id == owner action = db_api.action_get(context, owner) if (action and action.owner and action.owner != engine and is_engine_dead(context, action.owner)): LOG.info(_LI('The user %(u)s is locked by dead action %(a)s, ' 'try to steal the lock.'), { 'u': user_id, 'a': owner }) reason = _('Engine died when executing this action.') db_api.action_mark_failed(context, action.id, time.time(), reason=reason) db_api.user_lock_steal(user_id, action_id) return True LOG.error(_LE('User is already locked by action %(old)s, ' 'action %(new)s failed grabbing the lock'), {'old': owner, 'new': action_id}) return False
def set_status(self, result, reason=None): """Set action status based on return value from execute.""" timestamp = wallclock() if result == self.RES_OK: status = self.SUCCEEDED db_api.action_mark_succeeded(self.context, self.id, timestamp) elif result == self.RES_ERROR: status = self.FAILED db_api.action_mark_failed(self.context, self.id, timestamp, reason=reason or 'ERROR') elif result == self.RES_TIMEOUT: status = self.FAILED db_api.action_mark_failed(self.context, self.id, timestamp, reason=reason or 'TIMEOUT') elif result == self.RES_CANCEL: status = self.CANCELLED db_api.action_mark_cancelled(self.context, self.id, timestamp) else: # result == self.RES_RETRY: status = self.READY # Action failed at the moment, but can be retried # We abandon it and then notify other dispatchers to execute it db_api.action_abandon(self.context, self.id) if status == self.SUCCEEDED: EVENT.info(self.context, self, self.action, status, reason) elif status == self.READY: EVENT.warning(self.context, self, self.action, status, reason) else: EVENT.error(self.context, self, self.action, status, reason) self.status = status self.status_reason = reason