Source code for oio.common.client

# Copyright (C) 2015-2020 OpenIO SAS, as part of OpenIO SDS
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.


from oio.common.green import sleep

from oio.common.constants import HEADER_PREFIX
from oio.common.logger import get_logger
from oio.common.configuration import load_namespace_conf, validate_service_conf
from oio.api.base import HttpApi
from oio.common.exceptions import Conflict, OioException, ServiceBusy
from random import randrange


REQUEST_ATTEMPTS = 1


[docs]class ProxyClient(HttpApi): """ Client directed towards oio-proxy, with logging facility """ _slot_time = 0.5 def __init__(self, conf, request_prefix="", no_ns_in_url=False, endpoint=None, request_attempts=REQUEST_ATTEMPTS, logger=None, **kwargs): """ :param request_prefix: text to insert in between endpoint and requested URL :type request_prefix: `str` :param no_ns_in_url: do not insert namespace name between endpoint and `request_prefix` :type no_ns_in_url: `bool` :param request_attempts: number of attempts for the request in case of error 503 (defaults to 1) :raise oio.common.exceptions.ServiceBusy: if all attempts fail """ assert request_attempts > 0 validate_service_conf(conf) self.ns = conf.get('namespace') self.conf = conf self.logger = logger or get_logger(conf) # Look for an endpoint in the application configuration if not endpoint: endpoint = self.conf.get('proxyd_url', None) # Look for an endpoint in the namespace configuration if not endpoint: ns_conf = load_namespace_conf(self.ns) endpoint = ns_conf.get('proxy') # Historically, the endpoint did not contain any scheme self.proxy_scheme = 'http' split_endpoint = endpoint.split('://', 1) if len(split_endpoint) > 1: self.proxy_scheme = split_endpoint[0] self.proxy_netloc = split_endpoint[-1] ep_parts = list() ep_parts.append(self.proxy_scheme + ':/') ep_parts.append(self.proxy_netloc) ep_parts.append("v3.0") if not no_ns_in_url: ep_parts.append(self.ns) if request_prefix: ep_parts.append(request_prefix.lstrip('/')) self._request_attempts = request_attempts super(ProxyClient, self).__init__( endpoint='/'.join(ep_parts), service_type='proxy', **kwargs) def _direct_request(self, method, url, headers=None, request_attempts=None, **kwargs): if not request_attempts: request_attempts = self._request_attempts if request_attempts <= 0: raise OioException("Negative request attempts: %d" % request_attempts) if kwargs.get("autocreate"): if not headers: headers = dict() headers[HEADER_PREFIX + "action-mode"] = "autocreate" kwargs.pop("autocreate") if kwargs.get("tls"): headers = headers or dict() headers[HEADER_PREFIX + "upgrade-to-tls"] = kwargs.pop("tls") for i in range(request_attempts): try: return super(ProxyClient, self)._direct_request( method, url, headers=headers, **kwargs) except ServiceBusy: if i >= request_attempts - 1: raise # retry with exponential backoff ProxyClient._exp_sleep(i + 1) except Conflict: if i > 0 and method == 'POST': # We were retrying a POST operation, it's highly probable # that the original operation succeeded after we timed # out. So we consider this a success and don't raise # the exception. return None, None raise @staticmethod def _exp_sleep(attempts): """Sleep an exponential amount of time derived from `attempts`.""" limit = pow(2, attempts) k = randrange(limit) sleep(k * ProxyClient._slot_time)