Beispiel #1
0
    def __init__(self, host=None, port=None):
        """
        Initializer

        :param host: host to connect to
        :param port: port to use for connection
        """

        # Create the logger object
        self.logger = logging.getLogger(__name__)

        # Configuration file
        self.config_reader = ConfigReader()

        # Host to connect to (IP address or known host name)
        self._host = None
        if host is not None:
            self.host = host
        else:
            self.host = self.config_reader.get('cluster', 'tdm.ip')

        # Port Number to connect to
        self._port = None
        if port is not None:
            self.port = port
        else:
            self.port = int(self.config_reader.get('cluster', 'tdm.port'))
Beispiel #2
0
    def _get_metric_tags_from_tsuid(cls, tsuid):
        """
        Get the metric and tags of a TSUID provided from tsdb-uid table

        :param tsuid: TSUID to get info from
        :type tsuid: str

        :return: the metric and tags
        :rtype: tuple (metric, tags)

        :raises ValueError: if TSUID is unknown
        :raises ValueError: if OpenTSDB result can't be parsed
        """

        # get Ikats information
        config_reader = ConfigReader()

        if tsuid:
            # extracting uids by cutting tsuid in slices of 6 characters
            uids = [tsuid[i:i + 6] for i in range(0, len(tsuid), 6)]
        else:
            raise ValueError("TSUID incorrect (got:%s)" % tsuid)

        metric = None
        tags = {}
        for i, uid in enumerate(uids):
            if i == 0:
                item_type = 'metric'
            elif i % 2 == 0:
                item_type = 'tagv'
            else:
                item_type = 'tagk'

            url = "http://%s:%s/api/uid/uidmeta?uid=%s&type=%s" % (
                config_reader.get('cluster', 'opentsdb.read.ip'),
                int(config_reader.get('cluster',
                                      'opentsdb.read.port')), uid, item_type)

            results = requests.get(url=url)
            if 200 <= results.status_code < 300:
                try:
                    result = results.json()['name']
                except:
                    raise ValueError("OpenTSDB result not parsable (got:%s)" %
                                     results.status_code)
            else:
                raise ValueError("UID unknown (got:%s)" % results.status_code)

            if item_type == 'metric':
                metric = result
                tag_key = ''
            elif item_type == 'tagk':
                tag_key = result
                tags[tag_key] = None
            else:
                if tag_key in tags:
                    tags[tag_key] = result

        return metric, tags
Beispiel #3
0
    def get_nb_points_from_tsuid(cls, tsuid, ed=None, timeout=300):
        """
        return the effective imported number of points for a given tsuid

        :param tsuid: name of the metric
        :param ed: end date of the ts to get (EPOCH ms)
        :param timeout: timeout for the request (in seconds)

        :type tsuid: str
        :type ed: int
        :type timeout: int

        :return: the imported number of points
        :rtype: int

        :raises ValueError: if no TS with tsuid were found
        :raises SystemError: if openTSDB triggers an error
        """

        # Get Ikats information
        config_reader = ConfigReader()

        # Send the request to get the TSUID information
        if ed is None:
            # Retrieve end date from metadata
            tdm = TemporalDataMgr()
            metadata = tdm.get_meta_data([tsuid])[tsuid]
            # Create or update the metadata
            ed = int(metadata['ikats_end_date'])

        q_ed = "end=%s&" % int(ed)

        url = "http://%s:%s/api/query?start=0&%s&ms=true&tsuid=sum:%sms-count:%s" % (
            config_reader.get('cluster', 'opentsdb.read.ip'),
            int(config_reader.get(
                'cluster', 'opentsdb.read.port')), q_ed, int(ed + 1), tsuid)
        results = requests.get(url=url, timeout=timeout).json()
        if 'error' in results:
            if 'No such name for' in results['error']['message']:
                raise ValueError("OpenTSDB Error : %s (url: %s)" %
                                 (results['error']['message'], url))
            else:
                raise SystemError("OpenTSDB Error : %s (url: %s)" %
                                  (results['error']['message'], url))

        # Extract Nb points successfully imported
        # The for loop may have at most 2 loops because of the way openTSDB output is built
        nb_points = 0
        for point in results[0]['dps']:
            nb_points += int(results[0]['dps'][point])

        return nb_points
Beispiel #4
0
    def __init__(self, host=None, port=None, qsize=1000, threads_count=1):
        """
        Main OpenTSDB client.

        :param host: Host to connect to (overrides configuration file)
        :param port: port to connect to (overrides configuration file)
        :param qsize: Size of the send_queue to use (bigger implies big memory usage) (default:1k)
        :param threads_count: set the initial threads count (default:1 meaning, no multi-threaded)

        :type host: str
        :type port: int
        :type qsize: int
        :type threads_count: int
        """
        self.thr_list = []

        # Prepare events detecting the end of an import
        self._event_done = Event()
        self._event_abort = Event()

        # Get the cluster configuration
        config = ConfigReader()

        # Host and port to connect to
        self.host = host or config.get('cluster', 'opentsdb.write.ip')
        self.port = port or int(config.get('cluster', 'opentsdb.write.port'))

        self.use_threads = threads_count > 1
        if self.use_threads:
            self.send_queue = Queue(maxsize=qsize)
            self.result_queue = Queue()
            self.queue_max_size = qsize
            self.threads_count = threads_count
            # Start threads
            for _ in range(min(threads_count, os.cpu_count())):
                self.__add_thr()
Beispiel #5
0
    def create_tsuid(cls, fid, show_details=False):
        """
        Create a reference of timeseries in openTSDB without creating any data

        :param fid: Functional Identifier of the TS in Ikats
        :param show_details: Show metric and tags if set to True

        :type fid: str
        :type show_details: bool

        :return: the timeseries reference in database (tsuid)
        :rtype: str

        :raises IkatsConflictError: if TSUID already exist
        """
        tdm = TemporalDataMgr()
        try:
            # check if fid already associated to an existing tsuid
            tsuid = tdm.get_tsuid_from_func_id(func_id=fid)
            # if fid already exist in database, raise a conflict exception
            raise IkatsConflictError(
                "%s already associated to an existing tsuid: %s" %
                (fid, tsuid))

        except ValueError:
            # here creation of a new tsuid
            metric, tags = cls._gen_metric_tags()

            # get Ikats config information
            config_reader = ConfigReader()

            # formatting request
            url = "http://%s:%s/api/uid/assign?metric=%s&tagk=%s&tagv=%s" \
                  % (config_reader.get('cluster', 'opentsdb.read.ip'),
                     int(config_reader.get('cluster', 'opentsdb.read.port')),
                     metric,
                     ','.join([str(k) for k, v in tags.items()]),
                     ','.join([str(v) for k, v in tags.items()]))

            results = requests.get(url=url).json()

            # initializing tsuid with metric uid retrieved from opentsdb json response
            tsuid = cls._extract_uid_from_json(item_type='metric',
                                               value=metric,
                                               json=results)

            # retrieving and concatenating by pair [ tagk + tagv ] uids from opentsdb json response
            tagkv_items = [
                cls._extract_uid_from_json(
                    item_type='tagk', value=str(k), json=results) +
                cls._extract_uid_from_json(
                    item_type='tagv', value=str(v), json=results)
                for k, v in tags.items()
            ]

            # concatenating [tagk + tagv] uids to previously initialized tsuid, after having sorted them in
            # increasing order
            tsuid += ''.join(item for item in sorted(tagkv_items))

            # finally importing tsuid/fid pair in non temporal database
            tdm.import_fid(tsuid=tsuid, fid=fid)

            if show_details:
                return tsuid, metric, tags

            return tsuid
Beispiel #6
0
    def _get_tsuid_from_metric_tags(cls, metric, ed=None, timeout=120, **tags):
        """
        return the TSUID  (and effective imported number of points) from a metric name and a list of tags

        :param metric: name of the metric
        :param ed: end date of the ts to get (EPOCH ms)
        :param tags: dict of tags key and tags values

        :type metric: str
        :type ed: int
        :type tags: dict

        :return: the TSUID and the imported number of points
        :rtype: tuple

        :raises ValueError: if more than 1 TS matching the criteria exists in openTSDB
        :raises ValueError: if no TS with metric and tags were found
        :raises SystemError: if openTSDB triggers an error
        """

        # get Ikats information
        config_reader = ConfigReader()

        # Build a string like "{tagk:tagv,tagk2:tagv2,tagk3:tagv3}"
        tag_string = "{%s}" % ','.join(
            ["%s=%s" % (k, v) for k, v in tags.items()])

        # Send the request to get the TSUID information

        q_ed = ""
        if ed is not None:
            q_ed = "end=%s&" % int(ed)

        # The "random" query parameter is a trick to not hit opentsdb cache
        url = "http://%s:%s/api/query?start=0&%sshow_tsuids=true&ms=true&m=sum:%sms-count:%s%s&_=%s" % (
            config_reader.get('cluster', 'opentsdb.read.ip'),
            int(config_reader.get('cluster', 'opentsdb.read.port')), q_ed,
            int(ed + 1), metric, tag_string, random.random())

        results = requests.get(url=url, timeout=timeout).json()
        if 'error' in results:
            if 'No such name for' in results['error']['message']:
                raise ValueError("OpenTSDB Error : %s (url: %s)" %
                                 (results['error']['message'], url))
            else:
                raise SystemError("OpenTSDB Error : %s (url: %s)" %
                                  (results['error']['message'], url))

        # Extract TSUID
        try:
            tsuid = results[0]['tsuids'][0]
        except IndexError:
            cls.logger.error("No results for url : %s", url)
            raise ValueError("No results for url : %s" % url)

        # Extract Nb points successfully imported
        # The for loop may have at most 2 loops because of the way openTSDB output is built
        nb_points = 0
        for point in results[0]['dps']:
            nb_points += int(results[0]['dps'][point])

        # Do some checks
        if len(results[0]['tsuids']) > 1:
            cls.logger.error("Too many results: %s", len(results['results']))
            raise ValueError("Too many results: %s" % len(results['results']))

        return tsuid, nb_points
Beispiel #7
0
import logging

from django.test import TestCase
import mock

from apps.algo.catalogue.models.orm.implem import ImplementationDao
from apps.algo.execute.models.business.scripts.execalgo import run
from ikats.core.config.ConfigReader import ConfigReader
from ikats.core.library.status import State
from ikats.core.resource.client.temporal_data_mgr import DTYPE
from ikats_processing.core.resource_config import ResourceClientSingleton
import numpy as np

LOGGER = logging.getLogger(__name__)

CONFIG_READER = ConfigReader()
HOST = CONFIG_READER.get('cluster', 'tdm.ip')
PORT = int(CONFIG_READER.get('cluster', 'tdm.port'))


# noinspection PyUnusedLocal
def get_fid_from_tsuid_mock(self, tsuid):
    """
    Mock of TemporalDataMgr.get_data_set method

    Same parameters and types as the original function

    """
    return {'funcId': 'funcId' + tsuid, 'tsuid': tsuid}

Beispiel #8
0
from unittest import TestCase

import httpretty
import mock
import numpy as np

from ikats.core.config.ConfigReader import ConfigReader
from ikats.core.resource.api import IkatsApi
from ikats.core.resource.client.temporal_data_mgr import DTYPE
from ikats.core.resource.opentsdb.HttpClient import HttpClient
from ikats.core.resource.opentsdb.wrapper import Wrapper

USE_REAL_SERVER = False

# Configuration file
CONFIG_READER = ConfigReader()

# Address of the real server to use for tests
TEST_HOST = CONFIG_READER.get('cluster', 'tdm.ip')
TEST_PORT = int(CONFIG_READER.get('cluster', 'tdm.port'))

TEST_OPENTSDB_HOST = CONFIG_READER.get('cluster', 'opentsdb.read.ip')
TEST_OPENTSDB_PORT = int(CONFIG_READER.get('cluster', 'opentsdb.read.port'))
ROOT_URL = 'http://%s:%s/TemporalDataManagerWebApp/webapi' % (TEST_HOST,
                                                              TEST_PORT)
DIRECT_ROOT_URL = 'http://%s:%s/api' % (TEST_OPENTSDB_HOST, TEST_OPENTSDB_PORT)


def log_to_stdout(logger_to_use):
    """
    Allow to print some loggers to stdout
Beispiel #9
0
class RestClient(object):
    """
    Generic class to communicate using REST API
    """
    class VERB(Enum):
        """
        Definition of possibilities for HTTP verb
        Only the following 4 are managed because they are the only allowed verbs for CRUD interface
        * CREATE -> POST
        * READ -> GET
        * UPDATE -> PUT
        * DELETE -> DELETE
        """
        POST = 0
        GET = 1
        PUT = 2
        DELETE = 3

    def __init__(self, host=None, port=None):
        """
        Initializer

        :param host: host to connect to
        :param port: port to use for connection
        """

        # Create the logger object
        self.logger = logging.getLogger(__name__)

        # Configuration file
        self.config_reader = ConfigReader()

        # Host to connect to (IP address or known host name)
        self._host = None
        if host is not None:
            self.host = host
        else:
            self.host = self.config_reader.get('cluster', 'tdm.ip')

        # Port Number to connect to
        self._port = None
        if port is not None:
            self.port = port
        else:
            self.port = int(self.config_reader.get('cluster', 'tdm.port'))

    @property
    def host(self):
        """
        Host address
        :getter: provide the stored host address
        :setter: check host validity

        :raises TypeError: if host has a wrong type (in setter)
        :raises ValueError: if host is not an understandable URL (in setter)
        """
        return self._host

    @host.setter
    def host(self, value):
        """
        See getter
        """
        if type(value) is not None:
            if type(value) is not str:
                raise TypeError("Host must be a string (got %s)" % type(value))
            if value == "":
                raise ValueError("Host must be filled")
            if not is_url_valid(value):
                raise ValueError("Host not a valid URL : %s" % value)
        self._host = value

    @property
    def port(self):
        """
        port number
        :getter: provide the stored port address
        :setter: check port validity

        :raises TypeError: if port is not a number
        :raises ValueError: if port is not a correct port number
        """
        return self._port

    @port.setter
    def port(self, value):
        """
        See getter
        """
        if type(value) is not int:
            raise TypeError("Port must be a number (got %s)" % type(value))
        if value <= 0 or value >= 65535:
            raise ValueError("Port must be within ]0:65535] (got %s)" % value)
        self._port = value

    def _send(self,
              verb=None,
              template="",
              uri_params=None,
              q_params=None,
              files=None,
              data=None,
              json_data=None,
              headers=None):
        """
        Generic call command that should not be called directly

        It performs the following actions:
        * checks the input type validity
        * calls the correct verb method from the library (get, post, put, delete)
        * formats the output (utf-8)
        * Handles the status from the server
        * decode the output
        * return the data

        :param template: optional, default "": key matching the template to use for url building:
          see dict ikats.core.resource.client.utils.TEMPLATES
        :param uri_params:  optional, default None: parameters applied to the template
        :param verb:  optional, default None: HTTP method to call
        :type verb: IkatsRest.VERB
        :param q_params: optional, default None: list of query parameters
        :type q_params: dict or None
        :param files: optional, default None: files full path to attach to request
        :type files: str or list or None
        :param data: optional, default None: data input consumed by request
            -note: when data is not None, json must be None
        :type data: object
        :param json_data: optional, default None: json input consumed by request
            -note: when json is not None, data must be None
        :type json_data: object
        :return: the response as a anonymous class containing the following attributes:
            class Result:
                url = *url of the request performed*
                json = *body content parsed from json*
                text = *body content parsed as text*
                raw = *raw response content*
                status = *HTTP status code*
                reason = *reason (useful in case of HTTP status code 4xx or 5xx)
            This way to return results improve readability of the code.
            Example:
                r = self.send(...)
                if r.status == 200:
                    print(r.text)
        :rtype: anonymous class

        .. note:
           Timeout set to following values:
              - 120 seconds for GET and POST
              - 120 seconds for PUT and DELETE

        :raises TypeError: if VERB is incorrect
        :raises TypeError: if FORMAT is incorrect
        :raises ValueError: if a parameter of uri_param contains spaces
                            if there are unexpected argument values
        :raises ServerError: if receiving HTTP error code 5xx
        """

        # Check the validity of inputs
        assert (template
                in TEMPLATES), "Template is not defined: %s" % template

        if not isinstance(verb, RestClient.VERB):
            self.logger.error(
                'Verb type is %s whereas IkatsRest.VERB is expected',
                type(verb))
            raise TypeError(
                "Verb type is %s whereas IkatsRest.VERB is expected",
                type(verb))

        if (data is not None) and (json_data is not None):
            raise ValueError(
                "Integrity error: arguments data and json_data are mutually exclusive."
            )

        # Build the URL
        if uri_params is None:
            uri_params = {}
        if 'host' not in uri_params:
            uri_params['host'] = self.host
        if 'port' not in uri_params:
            uri_params['port'] = self.port
        url = TEMPLATES[template]['pattern'] % uri_params

        # Converts file to 'requests' module format
        json_file = build_json_files(files)

        # Dispatch method
        try:
            if verb == RestClient.VERB.POST:
                result = requests.post(url,
                                       data=data,
                                       json=json_data,
                                       files=json_file,
                                       params=q_params,
                                       timeout=600,
                                       headers=headers)
            elif verb == RestClient.VERB.GET:
                result = requests.get(url,
                                      params=q_params,
                                      timeout=600,
                                      headers=headers)
            elif verb == RestClient.VERB.PUT:
                result = requests.put(url,
                                      params=q_params,
                                      timeout=600,
                                      headers=headers)
            elif verb == RestClient.VERB.DELETE:
                result = requests.delete(url,
                                         params=q_params,
                                         timeout=600,
                                         headers=headers)
            else:
                self.logger.error(
                    "Verb [%s] is unknown, shall be one defined by VERB Enumerate",
                    verb)
                raise RuntimeError(
                    "Verb [%s] is unknown, shall be one defined by VERB Enumerate"
                    % verb)

            # Format output encoding
            result.encoding = 'utf-8'

            # Debug information
            if result.status_code == 400 or result.status_code >= 500:
                self.logger.debug("Sending request:")
                self.logger.debug("  %-6s: %s", str(verb)[5:], result.url)
                self.logger.debug("  Status: %s", result.status_code)
                self.logger.debug("  Data: %s", data)
        except Exception as exception:
            self.logger.error(
                "ERROR OCCURRED DURING THE HTTP SEND ACTION (details below)")
            self.logger.error(exception)
            raise
        finally:
            # Close potential opened files
            close_files(json_file)

        # Error handling
        if 500 <= result.status_code < 600:
            self.logger.error('%s Server Error: %s', result.status_code,
                              result.reason)
            raise ServerError('%s Server Error: %s %s %s' %
                              (result.status_code, verb, url, result.text))

        return RestClientResponse(result)
Beispiel #10
0
import requests

from ikats.core.config.ConfigReader import ConfigReader
from ikats.core.resource.opentsdb.HttpClient import HttpClient

LOGGER = HttpClient.LOGGER
LOGGER.setLevel(logging.DEBUG)
FORMATTER = logging.Formatter('%(asctime)s:%(levelname)s:%(funcName)s:%(message)s')
# Create another handler that will redirect log entries to STDOUT
STREAM_HANDLER = logging.StreamHandler()
STREAM_HANDLER.setLevel(logging.DEBUG)
STREAM_HANDLER.setFormatter(FORMATTER)
LOGGER.addHandler(STREAM_HANDLER)

# Configuration file
CONFIG_READER = ConfigReader()


def delete_ts(metric, **tags):
    """
    Delete the TS from OpenTSDB manually
    """

    tag_string = ','.join(['%s=%s' % (k, v) for k, v in tags.items()])

    # Address of the real server to use for tests
    test_opentsdb_host = CONFIG_READER.get('cluster', 'opentsdb.write.ip')
    test_opentsdb_port = int(CONFIG_READER.get('cluster', 'opentsdb.write.port'))
    direct_root_url = 'http://%s:%s//api/query?start=0&m=sum:%s{%s}&ms=true&delete=true' % (
        test_opentsdb_host, test_opentsdb_port, metric, tag_string)