File: //var/opt/nydus/ops/customer_local_ops/util/retry.py
# -*- coding: utf-8 -*-
"""\
© Copyright. All rights reserved.
"""
from __future__ import unicode_literals
from datetime import datetime, timedelta
from functools import wraps
import logging
LOG = logging.getLogger(__name__)
class Retry:
"""Ops decorated with @retry can return a Retry instance to request retry
with the interval and timeout as specified in the decorator. See also the
RETRY sentinel if there is no intermediate_result.
"""
def __init__(self, intermediate_result=None):
if not (intermediate_result is None or
isinstance(intermediate_result, dict)):
raise TypeError('Retry intermediate_result must be dictionary '
'but got %s' % type(intermediate_result))
self.intermediate_result = intermediate_result
def __str__(self):
return 'Retry ' + str(self.__dict__)
RETRY = Retry() # A Retry that can be returned from ops decorated with @retry
# when there is no intermediate_result.
def retry(interval=2, timeout=60):
"""Op decorator that returns a retry request to Nydus when the op returns
a Retry instance. Op will be retried by Nydus after `interval` seconds.
Returns failure at the specified timeout (seconds).
"""
def deco(original):
@wraps(original)
def wrapper(*args, **kw):
intermediate_result = kw.get('intermediate_result', None)
now = datetime.utcnow()
timeout_abs = (intermediate_result and
intermediate_result.get('timeout') or
now + timedelta(seconds=timeout))
if now >= timeout_abs:
msg = 'Timeout waiting for {} ({}s)'.format(getattr(original, '__name__', '?'), timeout)
LOG.error(msg)
return False, msg
result = original(*args, **kw)
if isinstance(result, Retry):
ir = result.intermediate_result or {}
ir.setdefault('timeout', timeout_abs)
return False, ir, interval
return result
return wrapper
return deco