userbeacon/vscommunicator.py (60 lines of code) (raw):
import requests
from django.conf import settings
import logging
import time
import requests.exceptions
logger = logging.getLogger(__name__)
class HttpTimeoutError(Exception):
pass
class HttpError(Exception):
def __init__(self, target_url, response_code, response_body, response_headers):
self.target_url = target_url
self.response_code = response_code
self.response_body = response_body
self.response_headers = response_headers
def __str__(self):
return u"{0} error accessing {1}".format(self.response_code, self.target_url)
class VSCommunicator(object):
"""
simple class to handle communication with VS
"""
def __init__(self):
self.retry_wait = 5 #in seconds
self.max_retries = 10
self.verify = settings.SSL_VERIFY if hasattr(settings,"SSL_VERIFY") else True
def do_get(self, urlpath):
"""
perform a GET request to VS, with retries. Raises on error, for details see `do_generic`
:param urlpath:
:return:
"""
return self.do_generic(urlpath, lambda full_url:
requests.get(full_url,auth=(settings.VIDISPINE_ADMIN_USER,settings.VIDISPINE_ADMIN_PASSWORD),
headers={
"Accept": "application/json"
}, verify=self.verify))
def do_post(self, urlpath, body_content):
"""
perform a POST request, with retries. Raises on error, for details see `do_generic`
:param urlpath:
:param body_content:
:return:
"""
return self.do_generic(urlpath, lambda full_url:
requests.post(full_url, json=body_content,
auth=(settings.VIDISPINE_ADMIN_USER, settings.VIDISPINE_ADMIN_PASSWORD),
headers={"Accept":"application/json"}, verify=self.verify))
def do_put(self, urlpath, run_as=None):
"""
perform a bare PUT request with no body. With retries. Raises on error, for details see `do_generic`
:param urlpath: URL to put
:return:
"""
headers = {
"Accept": "application/json"
}
if run_as:
headers["RunAs"] = run_as
logger.info("Running PUT {0} as {1}".format(urlpath, run_as))
return self.do_generic(urlpath, lambda full_url:
requests.put(full_url, auth=(settings.VIDISPINE_ADMIN_USER,settings.VIDISPINE_ADMIN_PASSWORD),
headers=headers, verify=self.verify))
def do_generic(self, urlpath, requestlambda, attempt=0):
"""
error-catching wrapper for an http request. Recursively retries if a timeout is received
:param urlpath: API path to hit, with a leading /. /API is not necessary, it's added automatically if it does not exist.
:param requestlambda: lambda function that performs the actual request. This is passed the full_url as a parameter
and is expected to return a Requests response object
:param attempt: attempt counter, don't set this when calling externally
:return: the json content returned as a dictionary.
If the request errors then an HttpError is raised, if it succeeds but the content won't parse as json then
a ParseException is raised
"""
if urlpath.startswith("/API"):
api_url_path = urlpath
else:
api_url_path = "/API" + urlpath
full_url = settings.VIDISPINE_BASE_URL + api_url_path
result = requestlambda(full_url)
if result.status_code==503 or result.status_code==502 or result.status_code==501:
if attempt>=self.max_retries:
logger.error("Vidispine is still not available, giving up")
raise HttpTimeoutError("Timed out accessing {0}".format(full_url))
logger.warning("Vidispine not available on attempt {0}, received {1}".format(attempt, result.status_code))
time.sleep(self.retry_wait)
return self.do_generic(urlpath, requestlambda, attempt+1)
elif result.status_code!=200 and result.status_code!=201:
raise HttpError(full_url, result.status_code, result.text, result.headers)
else:
return result.json()