Beispiel #1
0
    def check(cls, **body):
        """
        Post check statuses for use with monitors

        :param check: text for the message
        :type check: string

        :param host_name: name of the host submitting the check
        :type host_name: string

        :param status: integer for the status of the check
        :type status: Options: '0': OK, '1': WARNING, '2': CRITICAL, '3': UNKNOWN

        :param timestamp: timestamp of the event
        :type timestamp: POSIX timestamp

        :param message: description of why this status occurred
        :type message: string

        :param tags: list of tags for this check
        :type tags: string list

        :returns: Dictionary representing the API's JSON response
        """

        # Validate checks, include only non-null values
        for param, value in body.items():
            if param == "status" and body[param] not in CheckStatus.ALL:
                raise ApiError("Invalid status, expected one of: %s" % ", ".join(str(v) for v in CheckStatus.ALL))

        return super(ServiceCheck, cls)._trigger_action("POST", "check_run", **body)
Beispiel #2
0
    def query(cls, **params):
        """
        Query metrics from Datadog

        :param start: query start timestamp
        :type start: POSIX timestamp

        :param end: query end timestamp
        :type end: POSIX timestamp

        :param query: metric query
        :type query: string query

        :returns: Dictionary representing the API's JSON response

        *start* and *end* should be less than 24 hours apart.
        It is *not* meant to retrieve metric data in bulk.

        >>> api.Metric.query(start=int(time.time()) - 3600, end=int(time.time()),
                             query='avg:system.cpu.idle{*}')
        """
        # Set the right endpoint
        cls._resource_name = cls._METRIC_QUERY_ENDPOINT

        # `from` is a reserved keyword in Python, therefore
        # `api.Metric.query(from=...)` is not permited
        # -> map `start` to `from` and `end` to `to`
        try:
            params['from'] = params.pop('start')
            params['to'] = params.pop('end')
        except KeyError as e:
            raise ApiError("The parameter '{0}' is required".format(e.args[0]))

        return super(Metric, cls)._search(**params)
Beispiel #3
0
    def check(cls, **params):
        """
        Post check statuses for use with monitors

        :param check: text for the message
        :type check: string

        :param host_name: name of the host submitting the check
        :type host_name: string

        :param status: integer for the status of the check
        :type status: Options: '0': OK, '1': WARNING, '2': CRITICAL, '3': UNKNOWN

        :param timestamp: timestamp of the event
        :type timestamp: POSIX timestamp

        :param message: description of why this status occurred
        :type message: string

        :param tags: list of tags for this check
        :type tags: string list

        :returns: Dictionary representing the API's JSON response
        """
        if 'status' in params and params['status'] not in CheckStatus.ALL:
            raise ApiError('Invalid status, expected one of: %s'
                           % ', '.join(str(v) for v in CheckStatus.ALL))

        return super(ServiceCheck, cls)._trigger_action('POST', 'check_run', **params)
Beispiel #4
0
    def create(cls, attach_host_name=True, **params):
        """
        Post an event.

        :param title: title for the new event
        :type title: string

        :param text: event message
        :type text: string

        :param aggregation_key: key by which to group events in event stream
        :type aggregation_key: string

        :param alert_type: "error", "warning", "info" or "success".
        :type alert_type: string

        :param date_happened: when the event occurred. if unset defaults to the current time. \
        (POSIX timestamp)
        :type date_happened: integer

        :param handle: user to post the event as. defaults to owner of the application key used \
        to submit.
        :type handle: string

        :param priority: priority to post the event as. ("normal" or "low", defaults to "normal")
        :type priority: string

        :param related_event_id: post event as a child of the given event
        :type related_event_id: id

        :param tags: tags to post the event with
        :type tags: list of strings

        :param host: host to post the event with
        :type host: string

        :param device_name: device_name to post the event with
        :type device_name: list of strings

        :returns: Dictionary representing the API's JSON response

        >>> title = "Something big happened!"
        >>> text = 'And let me tell you all about it here!'
        >>> tags = ['version:1', 'application:web']

        >>> api.Event.create(title=title, text=text, tags=tags)
        """
        if params.get("alert_type"):
            if params["alert_type"] not in [
                    "error", "warning", "info", "success"
            ]:
                raise ApiError(
                    "Parameter alert_type must be either error, warning, info or success"
                )

        return super(Event, cls).create(attach_host_name=attach_host_name,
                                        **params)
Beispiel #5
0
    def delete_test(cls, **body):
        """
        Delete a test

        :param public_ids: list of public IDs to delete corresponding tests
        :type public_ids: list of strings

        :return: Dictionary representing the API's JSON response
        """

        if not isinstance(body["public_ids"], list):
            raise ApiError("Parameter 'public_ids' must be a list")

        # API path = "synthetics/tests/delete"

        return super(Synthetics, cls)._trigger_action("POST", name="synthetics", id="tests/delete", **body)
Beispiel #6
0
    def list(cls, from_epoch):
        """
        Get a list of active metrics since a given time (Unix Epoc)

        :param from_epoch: Start time in Unix Epoc (seconds)

        :returns: Dictionary containing a list of active metrics
        """

        cls._resource_name = cls._METRIC_LIST_ENDPOINT

        try:
            seconds = int(from_epoch)
            params = {'from': seconds}
        except ValueError:
            raise ApiError("Parameter 'from_epoch' must be an integer")

        return super(Metric, cls).get_all(**params)
Beispiel #7
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
Beispiel #8
0
 def raise_for_status(self):
     if not self._raise_for_status:
         return
     raise ApiError({'errors': ""})
Beispiel #9
0
    def submit(cls, method, path, api_version=None, body=None, attach_host_name=False,
               response_formatter=None, error_formatter=None, suppress_response_errors_on_codes=None,
               compress_payload=False, **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 api_version: The API version used

        :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 suppress_response_errors_on_codes: suppress ApiError on `errors` key in the response for the given HTTP
                                                  status codes
        :type suppress_response_errors_on_codes: None|list(int)

        :param compress_payload: compress the payload using zlib
        :type compress_payload: 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, _return_raw_response

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

            # Set api and app keys in headers
            headers = {}
            headers['DD-API-KEY'] = _api_key
            if _application_key:
                headers['DD-APPLICATION-KEY'] = _application_key

            # Check if the api_version is provided
            if not api_version:
                api_version = _api_version

            # set api and app keys in params only for some endpoints and thus remove keys from headers
            # as they cannot be set in both params and headers
            if cls._set_api_and_app_keys_in_params(api_version, path):
                params['api_key'] = _api_key
                del headers['DD-API-KEY']
                if _application_key:
                    params['application_key'] = _application_key
                    del headers['DD-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):
                tag_list = normalize_tags(params['tags'])
                params['tags'] = ','.join(tag_list)

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

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

            if compress_payload:
                body = zlib.compress(body.encode("utf-8"))
                headers["Content-Encoding"] = "deflate"

            # Construct the URL
            url = construct_url(_api_host, api_version, path)

            # 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))

                # response_obj can be a bool and not a dict
                if isinstance(response_obj, dict):
                    if response_obj and 'errors' in response_obj:
                        # suppress ApiError when specified and just return the response
                        if not (suppress_response_errors_on_codes and
                                result.status_code in suppress_response_errors_on_codes):
                            raise ApiError(response_obj)
            else:
                response_obj = None

            if response_formatter is not None:
                response_obj = response_formatter(response_obj)

            if _return_raw_response:
                return response_obj, result
            else:
                return 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].get('errors') or []):
                    log.error(error)
                if error_formatter is None:
                    return e.args[0]
                else:
                    return error_formatter(e.args[0])
            else:
                raise
Beispiel #10
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
Beispiel #11
0
 def test_apierror_exception(self):
     with pytest.raises(ApiError):
         raise ApiError()
     with pytest.raises(DatadogException):
         raise ApiError()