fuel-qa/fuelweb_test/testrail/testrail.py
Artem Panchenko 0debb28161 Add retries to TestRail client
In order to workaround API limitations (such as
requests rate), adding mechanism of requests
retries based on predefined error codes.

Try to resend request 3 times if '429' code is
received from API.

Closes-bug: #1514797
Change-Id: I723ef7886c86b7e418627962a1ff8aa7a4281c0a
2015-11-11 21:52:01 +02:00

141 lines
4.2 KiB
Python

# Copyright 2015 Mirantis, Inc.
#
# 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.
#
# TestRail API binding for Python 2.x (API v2, available since
# TestRail 3.0)
#
# Learn more:
#
# http://docs.gurock.com/testrail-api2/start
# http://docs.gurock.com/testrail-api2/accessing
#
# Copyright Gurock Software GmbH. See license.md for details.
#
import base64
import json
import time
import urllib2
from settings import logger
def request_retry(codes):
log_msg = "Got {0} Error! Waiting {1} seconds and trying again..."
def retry_request(func):
def wrapper(*args, **kwargs):
iter_number = 0
while True:
try:
response = func(*args, **kwargs)
except urllib2.HTTPError as e:
if e.code in codes:
if iter_number < codes[e.code]:
wait = 5
if 'Retry-After' in e.hdrs:
wait = int(e.hdrs['Retry-after'])
logger.debug(log_msg.format(e.code, wait))
time.sleep(wait)
iter_number += 1
continue
raise e
else:
return response
return wrapper
return retry_request
class APIClient(object):
"""APIClient.""" # TODO documentation
def __init__(self, base_url):
self.user = ''
self.password = ''
if not base_url.endswith('/'):
base_url += '/'
self.__url = base_url + 'index.php?/api/v2/'
#
# Send Get
#
# Issues a GET request (read) against the API and returns the result
# (as Python dict).
#
# Arguments:
#
# uri The API method to call including parameters
# (e.g. get_case/1)
#
def send_get(self, uri):
return self.__send_request('GET', uri, None)
#
# Send POST
#
# Issues a POST request (write) against the API and returns the result
# (as Python dict).
#
# Arguments:
#
# uri The API method to call including parameters
# (e.g. add_case/1)
# data The data to submit as part of the request (as
# Python dict, strings must be UTF-8 encoded)
#
def send_post(self, uri, data):
return self.__send_request('POST', uri, data)
def __send_request(self, method, uri, data):
retry_codes = {429: 3}
@request_retry(codes=retry_codes)
def __get_response(_request):
return urllib2.urlopen(_request).read()
url = self.__url + uri
request = urllib2.Request(url)
if method == 'POST':
request.add_data(json.dumps(data))
auth = base64.encodestring(
'%s:%s' % (self.user, self.password)).strip()
request.add_header('Authorization', 'Basic %s' % auth)
request.add_header('Content-Type', 'application/json')
e = None
try:
response = __get_response(request)
except urllib2.HTTPError as e:
response = e.read()
if response:
result = json.loads(response)
else:
result = {}
if e is not None:
if result and 'error' in result:
error = '"' + result['error'] + '"'
else:
error = 'No additional error message received'
raise APIError('TestRail API returned HTTP %s (%s)' %
(e.code, error))
return result
class APIError(Exception):
"""APIError.""" # TODO documentation
pass