Exemple #1
0
    def _get_metadata(agentConfig):
        if GCE.metadata is not None:
            return GCE.metadata

        if not agentConfig['collect_instance_metadata']:
            log.info(
                "Instance metadata collection is disabled. Not collecting it.")
            GCE.metadata = {}
            return GCE.metadata

        socket_to = None
        try:
            socket_to = socket.getdefaulttimeout()
            socket.setdefaulttimeout(GCE.TIMEOUT)
        except Exception:
            pass

        try:
            opener = url_lib.build_opener()
            opener.addheaders = [('X-Google-Metadata-Request', 'True')]
            GCE.metadata = json.loads(opener.open(GCE.URL).read().strip())

        except Exception:
            GCE.metadata = {}

        try:
            if socket_to is None:
                socket_to = 3
            socket.setdefaulttimeout(socket_to)
        except Exception:
            pass
        return GCE.metadata
Exemple #2
0
    def _update(cls, args):
        api._timeout = args.timeout
        format = args.format
        graphs = args.graphs
        if args.graphs is None:
            graphs = sys.stdin.read()
        try:
            graphs = json.loads(graphs)
        except:
            raise Exception('bad json parameter')

        res = api.Screenboard.update(
            args.screenboard_id,
            title=args.title,
            description=args.description,
            graphs=graphs,
            template_variables=args.template_variables,
            width=args.width,
            height=args.height)
        report_warnings(res)
        report_errors(res)
        if format == 'pretty':
            print(cls._pretty_json(res))
        else:
            print(json.dumps(res))
Exemple #3
0
    def _get_metadata(agentConfig):
        if GCE.metadata is not None:
            return GCE.metadata

        if not agentConfig['collect_instance_metadata']:
            log.info("Instance metadata collection is disabled. Not collecting it.")
            GCE.metadata = {}
            return GCE.metadata

        socket_to = None
        try:
            socket_to = socket.getdefaulttimeout()
            socket.setdefaulttimeout(GCE.TIMEOUT)
        except Exception:
            pass

        try:
            opener = url_lib.build_opener()
            opener.addheaders = [('X-Google-Metadata-Request', 'True')]
            GCE.metadata = json.loads(opener.open(GCE.URL).read().strip())

        except Exception:
            GCE.metadata = {}

        try:
            if socket_to is None:
                socket_to = 3
            socket.setdefaulttimeout(socket_to)
        except Exception:
            pass
        return GCE.metadata
Exemple #4
0
def _template_variables(tpl_var_input):
    if '[' not in tpl_var_input:
        return [v.strip() for v in tpl_var_input.split(',')]
    else:
        try:
            return json.loads(tpl_var_input)
        except Exception:
            raise argparse.ArgumentTypeError('bad template_variable json parameter')
Exemple #5
0
def _template_variables(tpl_var_input):
    if '[' not in tpl_var_input:
        return [v.strip() for v in tpl_var_input.split(',')]
    else:
        try:
            return json.loads(tpl_var_input)
        except Exception:
            raise argparse.ArgumentTypeError('bad template_variable json parameter')
    def _delete_dashboards(cls, args):
        api._timeout = args.timeout
        format = args.format
        dashboard_list_id = args.dashboard_list_id
        dashboards = json.loads(args.dashboards)

        res = api.DashboardList.delete_items(dashboard_list_id, dashboards=dashboards)
        report_warnings(res)
        report_errors(res)

        if format == 'pretty':
            print(pretty_json(res))
        else:
            print(json.dumps(res))
Exemple #7
0
 def _update(cls, args):
     api._timeout = args.timeout
     format = args.format
     options = None
     if args.options is not None:
         try:
             options = json.loads(args.options)
         except:
             raise Exception('bad json parameter')
     res = api.Monitor.update(args.monitor_id, type=args.type, query=args.query,
                              name=args.name, message=args.message, options=options)
     report_warnings(res)
     report_errors(res)
     if format == 'pretty':
         print(cls._pretty_json(res))
     else:
         print(json.dumps(res))
Exemple #8
0
 def _update(cls, args):
     api._timeout = args.timeout
     format = args.format
     options = None
     if args.options is not None:
         try:
             options = json.loads(args.options)
         except:
             raise Exception('bad json parameter')
     res = api.Monitor.update(args.monitor_id, type=args.type, query=args.query,
                              name=args.name, message=args.message, options=options)
     report_warnings(res)
     report_errors(res)
     if format == 'pretty':
         print(pretty_json(res))
     else:
         print(json.dumps(res))
Exemple #9
0
 def _post(cls, args):
     api._timeout = args.timeout
     format = args.format
     graphs = args.graphs
     if args.graphs is None:
         graphs = sys.stdin.read()
     try:
         graphs = json.loads(graphs)
     except:
         raise Exception('bad json parameter')
     res = api.Timeboard.create(title=args.title, description=args.description, graphs=[graphs],
                                template_variables=args.template_variables)
     report_warnings(res)
     report_errors(res)
     if format == 'pretty':
         print(pretty_json(res))
     else:
         print(json.dumps(res))
Exemple #10
0
    def get_tags(agentConfig):
        if not agentConfig['collect_instance_metadata']:
            log.info(
                "Instance metadata collection is disabled. Not collecting it.")
            return []

        socket_to = None
        try:
            socket_to = socket.getdefaulttimeout()
            socket.setdefaulttimeout(EC2.TIMEOUT)
        except Exception:
            pass

        try:
            iam_role = url_lib.urlopen(
                EC2.URL + "/iam/security-credentials").read().strip()
            iam_params = json.loads(
                url_lib.urlopen(EC2.URL + "/iam/security-credentials" + "/" +
                                unicode(iam_role)).read().strip())
            from boto.ec2.connection import EC2Connection
            connection = EC2Connection(
                aws_access_key_id=iam_params['AccessKeyId'],
                aws_secret_access_key=iam_params['SecretAccessKey'],
                security_token=iam_params['Token'])
            instance_object = connection.get_only_instances(
                [EC2.metadata['instance-id']])[0]

            EC2_tags = [
                u"%s:%s" % (tag_key, tag_value)
                for tag_key, tag_value in iteritems(instance_object.tags)
            ]

        except Exception:
            log.exception("Problem retrieving custom EC2 tags")
            EC2_tags = []

        try:
            if socket_to is None:
                socket_to = 3
            socket.setdefaulttimeout(socket_to)
        except Exception:
            pass

        return EC2_tags
Exemple #11
0
 def _post(cls, args):
     api._timeout = args.timeout
     format = args.format
     graphs = args.graphs
     if args.graphs is None:
         graphs = sys.stdin.read()
     try:
         graphs = json.loads(graphs)
     except:
         raise Exception('bad json parameter')
     res = api.Timeboard.create(title=args.title,
                                description=args.description,
                                graphs=[graphs],
                                template_variables=args.template_variables)
     report_warnings(res)
     report_errors(res)
     if format == 'pretty':
         print(pretty_json(res))
     else:
         print(json.dumps(res))
Exemple #12
0
    def _update(cls, args):
        api._timeout = args.timeout
        format = args.format
        graphs = args.graphs
        if args.graphs is None:
            graphs = sys.stdin.read()
        try:
            graphs = json.loads(graphs)
        except:
            raise Exception('bad json parameter')

        res = api.Screenboard.update(
            args.screenboard_id, title=args.title, description=args.description,
            graphs=graphs, template_variables=args.template_variables,
            width=args.width, height=args.height)
        report_warnings(res)
        report_errors(res)
        if format == 'pretty':
            print(pretty_json(res))
        else:
            print(json.dumps(res))
Exemple #13
0
    def _new_file(cls, args):
        api._timeout = args.timeout
        format = args.format
        graphs = args.graphs
        if args.graphs is None:
            graphs = sys.stdin.read()
        try:
            graphs = json.loads(graphs)
        except:
            raise Exception('bad json parameter')
        res = api.Screenboard.create(title=args.filename,
                                     description="Description for {0}".format(args.filename),
                                     graphs=[graphs])
        report_warnings(res)
        report_errors(res)

        cls._write_screen_to_file(res['id'], args.filename, args.timeout, format, args.string_ids)

        if format == 'pretty':
            print(pretty_json(res))
        else:
            print(json.dumps(res))
Exemple #14
0
    def get_tags(agentConfig):
        if not agentConfig['collect_instance_metadata']:
            log.info("Instance metadata collection is disabled. Not collecting it.")
            return []

        socket_to = None
        try:
            socket_to = socket.getdefaulttimeout()
            socket.setdefaulttimeout(EC2.TIMEOUT)
        except Exception:
            pass

        try:
            iam_role = url_lib.urlopen(EC2.URL + "/iam/security-credentials").read().strip()
            iam_params = json.loads(url_lib.urlopen(EC2.URL + "/iam/security-credentials" + "/" +
                                    unicode(iam_role)).read().strip())
            from boto.ec2.connection import EC2Connection
            connection = EC2Connection(aws_access_key_id=iam_params['AccessKeyId'],
                                       aws_secret_access_key=iam_params['SecretAccessKey'],
                                       security_token=iam_params['Token'])
            instance_object = connection.get_only_instances([EC2.metadata['instance-id']])[0]

            EC2_tags = [u"%s:%s" % (tag_key, tag_value) for tag_key, tag_value
                        in iteritems(instance_object.tags)]

        except Exception:
            log.exception("Problem retrieving custom EC2 tags")
            EC2_tags = []

        try:
            if socket_to is None:
                socket_to = 3
            socket.setdefaulttimeout(socket_to)
        except Exception:
            pass

        return EC2_tags
Exemple #15
0
 def get_request_data(self):
     """
     Returns JSON formatted data from the submitted `requests`.
     """
     _, kwargs = self.request_mock.request.call_args
     return json.loads(kwargs['data'])
Exemple #16
0
    def request(cls,
                method,
                path,
                body=None,
                attach_host_name=False,
                response_formatter=None,
                error_formatter=None,
                **params):
        """
        Make an HTTP API request

        :param method: HTTP method to use to contact API endpoint
        :type method: HTTP method string

        :param path: API endpoint url
        :type path: url

        :param body: dictionnary to be sent in the body of the request
        :type body: dictionary

        :param response_formatter: function to format JSON response from HTTP API request
        :type response_formatter: JSON input function

        :param error_formatter: function to format JSON error response from HTTP API request
        :type error_formatter: JSON input function

        :param attach_host_name: link the new resource object to the host name
        :type attach_host_name: bool

        :param params: dictionnary to be sent in the query string of the request
        :type params: dictionary

        :returns: JSON or formated response from HTTP API request
        """

        try:
            # Check if it's ok to submit
            if not cls._should_submit():
                raise HttpBackoff(
                    "Too many timeouts. Won't try again for {1} seconds.".
                    format(*cls._backoff_status()))

            # Import API, User and HTTP settings
            from datadog.api import _api_key, _application_key, _api_host, \
                _swallow, _host_name, _proxies, _max_retries, _timeout

            # Check keys and add then to params
            if _api_key is None:
                raise ApiNotInitialized(
                    "API key is not set."
                    " Please run 'initialize' method first.")
            params['api_key'] = _api_key
            if _application_key:
                params['application_key'] = _application_key

            # Construct the url
            url = "%s/api/%s/%s" % (_api_host, cls._api_version,
                                    path.lstrip("/"))

            # Attach host name to body
            if attach_host_name and body:
                # Is it a 'series' list of objects ?
                if 'series' in body:
                    # Adding the host name to all objects
                    for obj_params in body['series']:
                        if 'host' not in obj_params:
                            obj_params['host'] = _host_name
                else:
                    if 'host' not in body:
                        body['host'] = _host_name

            # If defined, make sure tags are defined as a comma-separated string
            if 'tags' in params and isinstance(params['tags'], list):
                params['tags'] = ','.join(params['tags'])

            # Process the body, if necessary
            headers = {}
            if isinstance(body, dict):
                body = json.dumps(body)
                headers['Content-Type'] = 'application/json'

            # Process requesting
            start_time = time.time()
            try:
                # Use a session to set a max_retries parameters
                s = requests.Session()
                http_adapter = requests.adapters.HTTPAdapter(
                    max_retries=_max_retries)
                s.mount('https://', http_adapter)

                # Request
                result = s.request(method,
                                   url,
                                   headers=headers,
                                   params=params,
                                   data=body,
                                   timeout=_timeout,
                                   proxies=_proxies)

                result.raise_for_status()
            except requests.ConnectionError as e:
                raise ClientError("Could not request %s %s%s: %s" %
                                  (method, _api_host, url, e))
            except requests.exceptions.Timeout as e:
                cls._timeout_counter += 1
                raise HttpTimeout('%s %s timed out after %d seconds.' %
                                  (method, url, _timeout))
            except requests.exceptions.HTTPError as e:
                if e.response.status_code == 404 or e.response.status_code == 400:
                    pass
                else:
                    raise
            except TypeError as e:
                raise TypeError(
                    "Your installed version of 'requests' library seems not compatible with"
                    "Datadog's usage. We recommand upgrading it ('pip install -U requests')."
                    "If you need help or have any question, please contact [email protected]"
                )

            # Request succeeded: log it and reset the timeout counter
            duration = round((time.time() - start_time) * 1000., 4)
            log.info("%s %s %s (%sms)" %
                     (result.status_code, method, url, duration))
            cls._timeout_counter = 0

            # Format response content
            content = result.content

            if content:
                try:
                    if is_p3k():
                        response_obj = json.loads(content.decode('utf-8'))
                    else:
                        response_obj = json.loads(content)
                except ValueError:
                    raise ValueError(
                        'Invalid JSON response: {0}'.format(content))

                if response_obj and 'errors' in response_obj:
                    raise ApiError(response_obj)
            else:
                response_obj = None
            if response_formatter is None:
                return response_obj
            else:
                return response_formatter(response_obj)

        except ClientError as e:
            if _swallow:
                log.error(str(e))
                if error_formatter is None:
                    return {'errors': e.args[0]}
                else:
                    return error_formatter({'errors': e.args[0]})
            else:
                raise
        except ApiError as e:
            if _swallow:
                for error in e.args[0]['errors']:
                    log.error(str(error))
                if error_formatter is None:
                    return e.args[0]
                else:
                    return error_formatter(e.args[0])
            else:
                raise
Exemple #17
0
    def submit(cls, method, path, body=None, attach_host_name=False, response_formatter=None,
               error_formatter=None, **params):
        """
        Make an HTTP API request

        :param method: HTTP method to use to contact API endpoint
        :type method: HTTP method string

        :param path: API endpoint url
        :type path: url

        :param body: dictionary to be sent in the body of the request
        :type body: dictionary

        :param response_formatter: function to format JSON response from HTTP API request
        :type response_formatter: JSON input function

        :param error_formatter: function to format JSON error response from HTTP API request
        :type error_formatter: JSON input function

        :param attach_host_name: link the new resource object to the host name
        :type attach_host_name: bool

        :param params: dictionary to be sent in the query string of the request
        :type params: dictionary

        :returns: JSON or formated response from HTTP API request
        """
        try:
            # Check if it's ok to submit
            if not cls._should_submit():
                _, backoff_time_left = cls._backoff_status()
                raise HttpBackoff(backoff_time_left)

            # Import API, User and HTTP settings
            from datadog.api import _api_key, _application_key, _api_host, \
                _mute, _host_name, _proxies, _max_retries, _timeout, \
                _cacert

            # Check keys and add then to params
            if _api_key is None:
                raise ApiNotInitialized("API key is not set."
                                        " Please run 'initialize' method first.")
            params['api_key'] = _api_key
            if _application_key:
                params['application_key'] = _application_key

            # Attach host name to body
            if attach_host_name and body:
                # Is it a 'series' list of objects ?
                if 'series' in body:
                    # Adding the host name to all objects
                    for obj_params in body['series']:
                        if obj_params.get('host', "") == "":
                            obj_params['host'] = _host_name
                else:
                    if body.get('host', "") == "":
                        body['host'] = _host_name

            # If defined, make sure tags are defined as a comma-separated string
            if 'tags' in params and isinstance(params['tags'], list):
                params['tags'] = ','.join(params['tags'])

            # Process the body, if necessary
            headers = {}
            if isinstance(body, dict):
                body = json.dumps(body)
                headers['Content-Type'] = 'application/json'

            # Construct the URL
            url = "{api_host}/api/{api_version}/{path}".format(
                  api_host=_api_host,
                  api_version=cls._api_version,
                  path=path.lstrip("/"),
            )

            # Process requesting
            start_time = time.time()

            result = cls._get_http_client().request(
                method=method, url=url,
                headers=headers, params=params, data=body,
                timeout=_timeout, max_retries=_max_retries,
                proxies=_proxies, verify=_cacert
            )

            # Request succeeded: log it and reset the timeout counter
            duration = round((time.time() - start_time) * 1000., 4)
            log.info("%s %s %s (%sms)" % (result.status_code, method, url, duration))
            cls._timeout_counter = 0

            # Format response content
            content = result.content

            if content:
                try:
                    if is_p3k():
                        response_obj = json.loads(content.decode('utf-8'))
                    else:
                        response_obj = json.loads(content)
                except ValueError:
                    raise ValueError('Invalid JSON response: {0}'.format(content))

                if response_obj and 'errors' in response_obj:
                    raise ApiError(response_obj)
            else:
                response_obj = None
            if response_formatter is None:
                return response_obj
            else:
                return response_formatter(response_obj)

        except HttpTimeout:
            cls._timeout_counter += 1
            raise
        except ClientError as e:
            if _mute:
                log.error(str(e))
                if error_formatter is None:
                    return {'errors': e.args[0]}
                else:
                    return error_formatter({'errors': e.args[0]})
            else:
                raise
        except ApiError as e:
            if _mute:
                for error in e.args[0]['errors']:
                    log.error(str(error))
                if error_formatter is None:
                    return e.args[0]
                else:
                    return error_formatter(e.args[0])
            else:
                raise
Exemple #18
0
 def get_request_data(self):
     """
     Returns JSON formatted data from the submitted `requests`.
     """
     _, kwargs = self.request_mock.request.call_args
     return json.loads(kwargs['data'])
Exemple #19
0
    def get_request_data(self):
        """

        """
        _, kwargs = self.request_mock.request.call_args
        return json.loads(kwargs['data'])
Exemple #20
0
    def request(cls, method, path, body=None, attach_host_name=False, response_formatter=None,
                error_formatter=None, **params):
        """
        Make an HTTP API request

        :param method: HTTP method to use to contact API endpoint
        :type method: HTTP method string

        :param path: API endpoint url
        :type path: url

        :param body: dictionnary to be sent in the body of the request
        :type body: dictionary

        :param response_formatter: function to format JSON response from HTTP API request
        :type response_formatter: JSON input function

        :param error_formatter: function to format JSON error response from HTTP API request
        :type error_formatter: JSON input function

        :param attach_host_name: link the new resource object to the host name
        :type attach_host_name: bool

        :param params: dictionnary to be sent in the query string of the request
        :type params: dictionary

        :returns: JSON or formated response from HTTP API request
        """

        try:
            # Check if it's ok to submit
            if not cls._should_submit():
                raise HttpBackoff("Too many timeouts. Won't try again for {1} seconds."
                                  .format(*cls._backoff_status()))

            # Import API, User and HTTP settings
            from datadog.api import _api_key, _application_key, _api_host, \
                _swallow, _host_name, _proxies, _max_retries, _timeout, \
                _cacert

            # Check keys and add then to params
            if _api_key is None:
                raise ApiNotInitialized("API key is not set."
                                        " Please run 'initialize' method first.")
            params['api_key'] = _api_key
            if _application_key:
                params['application_key'] = _application_key

            # Construct the url
            url = "%s/api/%s/%s" % (_api_host, cls._api_version, path.lstrip("/"))

            # Attach host name to body
            if attach_host_name and body:
                # Is it a 'series' list of objects ?
                if 'series' in body:
                    # Adding the host name to all objects
                    for obj_params in body['series']:
                        if 'host' not in obj_params:
                            obj_params['host'] = _host_name
                else:
                    if 'host' not in body:
                        body['host'] = _host_name

            # If defined, make sure tags are defined as a comma-separated string
            if 'tags' in params and isinstance(params['tags'], list):
                params['tags'] = ','.join(params['tags'])

            # Process the body, if necessary
            headers = {}
            if isinstance(body, dict):
                body = json.dumps(body)
                headers['Content-Type'] = 'application/json'

            # Process requesting
            start_time = time.time()
            try:
                # Use a session to set a max_retries parameters
                s = requests.Session()
                http_adapter = requests.adapters.HTTPAdapter(max_retries=_max_retries)
                s.mount('https://', http_adapter)

                # Request
                result = s.request(
                    method,
                    url,
                    headers=headers,
                    params=params,
                    data=body,
                    timeout=_timeout,
                    proxies=_proxies,
                    verify=_cacert)

                result.raise_for_status()
            except requests.ConnectionError as e:
                raise ClientError("Could not request %s %s%s: %s" % (method, _api_host, url, e))
            except requests.exceptions.Timeout as e:
                cls._timeout_counter += 1
                raise HttpTimeout('%s %s timed out after %d seconds.' % (method, url, _timeout))
            except requests.exceptions.HTTPError as e:
                if e.response.status_code in (400, 403, 404):
                    pass
                else:
                    raise
            except TypeError as e:
                raise TypeError(
                    "Your installed version of 'requests' library seems not compatible with"
                    "Datadog's usage. We recommand upgrading it ('pip install -U requests')."
                    "If you need help or have any question, please contact [email protected]")

            # Request succeeded: log it and reset the timeout counter
            duration = round((time.time() - start_time) * 1000., 4)
            log.info("%s %s %s (%sms)" % (result.status_code, method, url, duration))
            cls._timeout_counter = 0

            # Format response content
            content = result.content

            if content:
                try:
                    if is_p3k():
                        response_obj = json.loads(content.decode('utf-8'))
                    else:
                        response_obj = json.loads(content)
                except ValueError:
                    raise ValueError('Invalid JSON response: {0}'.format(content))

                if response_obj and 'errors' in response_obj:
                    raise ApiError(response_obj)
            else:
                response_obj = None
            if response_formatter is None:
                return response_obj
            else:
                return response_formatter(response_obj)

        except ClientError as e:
            if _swallow:
                log.error(str(e))
                if error_formatter is None:
                    return {'errors': e.args[0]}
                else:
                    return error_formatter({'errors': e.args[0]})
            else:
                raise
        except ApiError as e:
            if _swallow:
                for error in e.args[0]['errors']:
                    log.error(str(error))
                if error_formatter is None:
                    return e.args[0]
                else:
                    return error_formatter(e.args[0])
            else:
                raise
Exemple #21
0
    def submit(cls,
               method,
               path,
               body=None,
               attach_host_name=False,
               response_formatter=None,
               error_formatter=None,
               **params):
        """
        Make an HTTP API request

        :param method: HTTP method to use to contact API endpoint
        :type method: HTTP method string

        :param path: API endpoint url
        :type path: url

        :param body: dictionary to be sent in the body of the request
        :type body: dictionary

        :param response_formatter: function to format JSON response from HTTP API request
        :type response_formatter: JSON input function

        :param error_formatter: function to format JSON error response from HTTP API request
        :type error_formatter: JSON input function

        :param attach_host_name: link the new resource object to the host name
        :type attach_host_name: bool

        :param params: dictionary to be sent in the query string of the request
        :type params: dictionary

        :returns: JSON or formated response from HTTP API request
        """
        try:
            # Check if it's ok to submit
            if not cls._should_submit():
                _, backoff_time_left = cls._backoff_status()
                raise HttpBackoff(backoff_time_left)

            # Import API, User and HTTP settings
            from datadog.api import _api_key, _application_key, _api_host, \
                _mute, _host_name, _proxies, _max_retries, _timeout, \
                _cacert

            # Check keys and add then to params
            if _api_key is None:
                raise ApiNotInitialized(
                    "API key is not set."
                    " Please run 'initialize' method first.")
            params['api_key'] = _api_key
            if _application_key:
                params['application_key'] = _application_key

            # Attach host name to body
            if attach_host_name and body:
                # Is it a 'series' list of objects ?
                if 'series' in body:
                    # Adding the host name to all objects
                    for obj_params in body['series']:
                        if obj_params.get('host', "") == "":
                            obj_params['host'] = _host_name
                else:
                    if body.get('host', "") == "":
                        body['host'] = _host_name

            # If defined, make sure tags are defined as a comma-separated string
            if 'tags' in params and isinstance(params['tags'], list):
                params['tags'] = ','.join(params['tags'])

            # Process the body, if necessary
            headers = {}
            if isinstance(body, dict):
                body = json.dumps(body)
                headers['Content-Type'] = 'application/json'

            # Construct the URL
            url = "{api_host}/api/{api_version}/{path}".format(
                api_host=_api_host,
                api_version=cls._api_version,
                path=path.lstrip("/"),
            )

            # Process requesting
            start_time = time.time()

            result = cls._get_http_client().request(method=method,
                                                    url=url,
                                                    headers=headers,
                                                    params=params,
                                                    data=body,
                                                    timeout=_timeout,
                                                    max_retries=_max_retries,
                                                    proxies=_proxies,
                                                    verify=_cacert)

            # Request succeeded: log it and reset the timeout counter
            duration = round((time.time() - start_time) * 1000., 4)
            log.info("%s %s %s (%sms)" %
                     (result.status_code, method, url, duration))
            cls._timeout_counter = 0

            # Format response content
            content = result.content

            if content:
                try:
                    if is_p3k():
                        response_obj = json.loads(content.decode('utf-8'))
                    else:
                        response_obj = json.loads(content)
                except ValueError:
                    raise ValueError(
                        'Invalid JSON response: {0}'.format(content))

                if response_obj and 'errors' in response_obj:
                    raise ApiError(response_obj)
            else:
                response_obj = None
            if response_formatter is None:
                return response_obj
            else:
                return response_formatter(response_obj)

        except HttpTimeout:
            cls._timeout_counter += 1
            raise
        except ClientError as e:
            if _mute:
                log.error(str(e))
                if error_formatter is None:
                    return {'errors': e.args[0]}
                else:
                    return error_formatter({'errors': e.args[0]})
            else:
                raise
        except ApiError as e:
            if _mute:
                for error in e.args[0]['errors']:
                    log.error(str(error))
                if error_formatter is None:
                    return e.args[0]
                else:
                    return error_formatter(e.args[0])
            else:
                raise