Add a retry when downloading a file

When downloading of new file fails due to short and temporary network
issue then wait a few seconds and retry to download the file again.

There were situations when small network issues caused python-tempestconf
to end with an error.

Story: 2006231
Task: 35832

Change-Id: Ie3f1d8b412dccecac2b05f6b5f540e36824777a4
This commit is contained in:
Lukas Piwowarski 2019-07-24 11:26:31 +00:00
parent 5cf1410618
commit 041dac5acb
2 changed files with 59 additions and 1 deletions

View File

@ -13,9 +13,11 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from functools import wraps
import os import os
import shutil import shutil
import subprocess import subprocess
import time
from six.moves import urllib from six.moves import urllib
from tempest.lib import exceptions from tempest.lib import exceptions
@ -222,6 +224,55 @@ class ImageService(VersionedService):
with open(path, 'wb') as out: with open(path, 'wb') as out:
out.write(body.data) out.write(body.data)
def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):
"""Retry calling the decorated function using exponential backoff
http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
Licensed under the BSD 3-Clause "New" or "Revised" License
(https://github.com/saltycrane/retry-decorator/blob/master/LICENSE)
:param ExceptionToCheck: the exception to check
:type ExceptionToCheck: Exception or tuple
:param tries: number of times before giving up
:type type: int
:param delay: initial delay between retries in seconds
:type type: int
:param backoff: backoff multiplier e.g. value of 2 will double the
delay each retry
:type backoff: int
:param logger: logger to use. If None, print
:type logger: logging. Logger instance
"""
def deco_retry(f):
@wraps(f)
def f_retry(*args, **kwargs):
mtries, mdelay = tries, delay
while mtries > 1:
try:
return f(*args, **kwargs)
except ExceptionToCheck as e:
msg = "%s, Retrying in %d seconds." % (str(e), mdelay)
if logger:
logger.warning(msg)
else:
print(msg)
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
return f(*args, **kwargs)
return f_retry
return deco_retry
@retry(urllib.error.URLError, logger=C.LOG)
def retry_urlopen(self, url):
"""Opens url using urlopen. If it fails, it will try again.
:type url: string
"""
return urllib.request.urlopen(url)
def _download_file(self, url, destination): def _download_file(self, url, destination):
"""Downloads a file specified by `url` to `destination`. """Downloads a file specified by `url` to `destination`.
@ -232,7 +283,7 @@ class ImageService(VersionedService):
C.LOG.info("Image '%s' already fetched to '%s'.", url, destination) C.LOG.info("Image '%s' already fetched to '%s'.", url, destination)
return return
C.LOG.info("Downloading '%s' and saving as '%s'", url, destination) C.LOG.info("Downloading '%s' and saving as '%s'", url, destination)
f = urllib.request.urlopen(url) f = self.retry_urlopen(url)
data = f.read() data = f.read()
with open(destination, "wb") as dest: with open(destination, "wb") as dest:
dest.write(data) dest.write(data)

View File

@ -0,0 +1,7 @@
---
features:
- |
When downloading of new file fails due to short and temporary
network issue then python-tempestconf does not end immediately
with an error but rather waits a few seconds and retries to
download the file again.