|
1 | 1 | """Client to retrieve data from a Netdata instance.""" |
2 | | -import asyncio |
3 | 2 | import logging |
4 | | -import socket |
| 3 | +from typing import Dict |
5 | 4 |
|
6 | | -import aiohttp |
7 | | -import async_timeout |
| 5 | +import httpx |
8 | 6 | from yarl import URL |
9 | 7 |
|
10 | 8 | from . import exceptions |
11 | 9 |
|
12 | 10 | _LOGGER = logging.getLogger(__name__) |
13 | | -_DATA_ENDPOINT = "data?chart={resource}&before=0&after=-1&options=seconds" |
14 | | -_ALARMS_ENDPOINT = "alarms?all&format=json" |
15 | | -_ALL_METRIC_ENDPOINT = ( |
| 11 | +DATA_ENDPOINT = "data?chart={resource}&before=0&after=-1&options=seconds" |
| 12 | +ALARMS_ENDPOINT = "alarms?all&format=json" |
| 13 | +ALL_METRIC_ENDPOINT = ( |
16 | 14 | "allmetrics?format=json&help=no&types=no&" "timestamps=yes&names=yes&data=average" |
17 | 15 | ) |
| 16 | +ALARM_COUNT = "alarm_count?context={resource}&status=RAISED" |
18 | 17 |
|
19 | 18 | API_VERSION = 1 |
20 | 19 |
|
21 | 20 |
|
22 | 21 | class Netdata(object): |
23 | 22 | """A class for handling connections with a Netdata instance.""" |
24 | 23 |
|
25 | | - def __init__(self, host, loop, session, port=19999, path=None): |
| 24 | + def __init__(self, host, port=19999, tls=None, path=None): |
26 | 25 | """Initialize the connection to the Netdata instance.""" |
27 | | - self._loop = loop |
28 | | - self._session = session |
29 | 26 | self.host = host |
30 | 27 | self.port = port |
31 | 28 | self.values = self.alarms = self.metrics = None |
32 | | - if path is None: |
33 | | - self.base_url = URL.build(scheme="http", host=host, port=port, path=f"/api/v{API_VERSION}/") |
34 | | - else: |
35 | | - self.base_url = URL.build(scheme="http", host=host, port=port, path=path) |
36 | 29 |
|
| 30 | + self.scheme = "http" if tls is None or not False else "https" |
37 | 31 |
|
38 | | - async def get_data(self, resource): |
39 | | - """Get detail for a resource from the data endpoint.""" |
40 | | - self.endpoint = _DATA_ENDPOINT |
41 | | - url = "{}{}".format(self.base_url, self.endpoint.format(resource=resource)) |
| 32 | + if path is None: |
| 33 | + self.base_url = URL.build( |
| 34 | + scheme=self.scheme, host=host, port=port, path=f"/api/v{API_VERSION}/" |
| 35 | + ) |
| 36 | + else: |
| 37 | + self.base_url = URL.build( |
| 38 | + scheme=self.scheme, host=host, port=port, path=path |
| 39 | + ) |
42 | 40 |
|
| 41 | + async def get_data(self, url) -> Dict: |
| 42 | + """Execute a request to a data endpoint.""" |
43 | 43 | try: |
44 | | - with async_timeout.timeout(5, loop=self._loop): |
45 | | - response = await self._session.get(url) |
| 44 | + async with httpx.AsyncClient() as client: |
| 45 | + response = await client.get(str(url)) |
| 46 | + except httpx.ConnectError: |
| 47 | + raise exceptions.NetdataConnectionError( |
| 48 | + f"Connection to {self.scheme}://{self.host}:{self.port} failed" |
| 49 | + ) |
| 50 | + |
| 51 | + if response.status_code == httpx.codes.OK: |
| 52 | + _LOGGER.debug(response.json()) |
| 53 | + try: |
| 54 | + return response.json() |
| 55 | + except TypeError: |
| 56 | + _LOGGER.error("Can not load data from Netdata") |
| 57 | + raise exceptions.NetdataError("Unable to get the data from Netdata") |
| 58 | + |
| 59 | + async def get_chart(self, resource): |
| 60 | + """Get the details about a chart.""" |
| 61 | + url = URL(self.base_url) / DATA_ENDPOINT.format(resource=resource) |
| 62 | + data = await self.get_data(url) |
46 | 63 |
|
47 | | - _LOGGER.info("Response from Netdata: %s", response.status) |
48 | | - data = await response.json() |
49 | | - _LOGGER.debug(data) |
| 64 | + try: |
50 | 65 | self.values = {k: v for k, v in zip(data["labels"], data["data"][0])} |
51 | | - |
52 | | - except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): |
53 | | - _LOGGER.error("Can not load data from Netdata") |
54 | | - raise exceptions.NetdataConnectionError() |
| 66 | + except TypeError: |
| 67 | + raise exceptions.NetdataError("Format of data doesn't match") |
55 | 68 |
|
56 | 69 | async def get_alarms(self): |
57 | 70 | """Get alarms for a Netdata instance.""" |
58 | | - self.endpoint = _ALARMS_ENDPOINT |
59 | | - url = "{}{}".format(self.base_url, self.endpoint) |
60 | | - |
61 | | - try: |
62 | | - with async_timeout.timeout(5, loop=self._loop): |
63 | | - response = await self._session.get(url) |
64 | | - |
65 | | - _LOGGER.debug("Response from Netdata: %s", response.status) |
66 | | - data = await response.json() |
67 | | - _LOGGER.debug(data) |
68 | | - self.alarms = data |
69 | | - |
70 | | - except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): |
71 | | - _LOGGER.error("Can not load data from Netdata") |
72 | | - raise exceptions.NetdataConnectionError() |
| 71 | + url = URL(self.base_url) / ALARMS_ENDPOINT |
| 72 | + self.alarms = await self.get_data(url) |
73 | 73 |
|
74 | 74 | async def get_allmetrics(self): |
75 | 75 | """Get all available metrics from a Netdata instance.""" |
76 | | - self.endpoint = _ALL_METRIC_ENDPOINT |
77 | | - url = "{}{}".format(self.base_url, self.endpoint) |
78 | | - |
79 | | - try: |
80 | | - with async_timeout.timeout(5, loop=self._loop): |
81 | | - response = await self._session.get(url) |
82 | | - |
83 | | - _LOGGER.debug("Response from Netdata: %s", response.status) |
84 | | - data = await response.json() |
85 | | - _LOGGER.debug(data) |
86 | | - self.metrics = data |
| 76 | + url = URL(self.base_url) / ALL_METRIC_ENDPOINT |
| 77 | + self.metrics = await self.get_data(url) |
87 | 78 |
|
88 | | - except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror): |
89 | | - _LOGGER.error("Can not load data from Netdata") |
90 | | - raise exceptions.NetdataConnectionError() |
| 79 | + async def get_info(self): |
| 80 | + """Get information about the Netdata instance.""" |
| 81 | + url = URL(self.base_url) / "info" |
| 82 | + self.info = await self.get_data(url) |
0 commit comments