Skip to content

Commit d1bed07

Browse files
authored
Migration poetry (home-assistant-ecosystem#10)
* Bump version * Refactoring (httpx, new endpoints, code removal, etc.) * Add basic unittests * Migrate to pyproject * Update example * Move tests * Update worksflow
1 parent b8b07cd commit d1bed07

File tree

12 files changed

+874
-124
lines changed

12 files changed

+874
-124
lines changed

.github/workflows/python-package.yml

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Python package and lint
1+
name: Testing
22

33
on:
44
push:
@@ -15,15 +15,36 @@ jobs:
1515
python-version: [ 3.8, 3.9 ]
1616

1717
steps:
18-
- uses: actions/checkout@v2
18+
- name: Checkout
19+
uses: actions/checkout@v2
20+
1921
- name: Set up Python ${{ matrix.python-version }}
2022
uses: actions/setup-python@v2
2123
with:
2224
python-version: ${{ matrix.python-version }}
25+
26+
- name: Install Poetry
27+
uses: snok/install-poetry@v1
28+
with:
29+
virtualenvs-create: true
30+
virtualenvs-in-project: true
31+
installer-parallel: true
32+
33+
- name: Load cached venv
34+
id: cached-poetry-dependencies
35+
uses: actions/cache@v2
36+
with:
37+
path: .venv
38+
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
39+
2340
- name: Install dependencies
41+
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
42+
run: poetry install --no-interaction --no-root
43+
44+
- name: Install library
45+
run: poetry install --no-interaction
46+
47+
- name: Run tests
2448
run: |
25-
python -m pip install --upgrade pip
26-
pip install flake8 pytest
27-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
28-
- name: Black Code Formatter
29-
uses: lgeiger/black-action@v1.0.1
49+
source .venv/bin/activate
50+
pytest tests/

MANIFEST.in

Lines changed: 0 additions & 1 deletion
This file was deleted.

example.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
"""Sample code to interact with a Netdata instance."""
22
import asyncio
3-
import aiohttp
43
import json
54

65
from netdata import Netdata
76

87

98
async def main():
109
"""Get the data from a Netdata instance."""
11-
async with aiohttp.ClientSession() as session:
12-
data = Netdata("localhost", loop, session)
13-
# Get data for the CPU
14-
await data.get_data("system.cpu")
15-
print(json.dumps(data.values, indent=4, sort_keys=True))
16-
17-
# Print the current value of the system's CPU
18-
print("CPU System:", round(data.values["system"], 2))
19-
20-
# Get the alarms which are present
21-
await data.get_alarms()
22-
print(data.alarms)
10+
client = Netdata("localhost")
11+
12+
# Get all metrics
13+
await client.get_info()
14+
print(client.info)
15+
16+
# Get details of a available chart
17+
await client.get_chart("system.cpu")
18+
print(json.dumps(client.values, indent=4, sort_keys=True))
19+
20+
# Print the current value of the system's CPU
21+
print("CPU System:", round(client.values["system"], 2))
22+
23+
# Get the alarms which are present
24+
await client.get_alarms()
25+
print(client.alarms)
26+
27+
# Get all metrics
28+
await client.get_allmetrics()
29+
print(client.metrics)
30+
2331

2432
loop = asyncio.get_event_loop()
2533
loop.run_until_complete(main())

netdata/__init__.py

Lines changed: 48 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,82 @@
11
"""Client to retrieve data from a Netdata instance."""
2-
import asyncio
32
import logging
4-
import socket
3+
from typing import Dict
54

6-
import aiohttp
7-
import async_timeout
5+
import httpx
86
from yarl import URL
97

108
from . import exceptions
119

1210
_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 = (
1614
"allmetrics?format=json&help=no&types=no&" "timestamps=yes&names=yes&data=average"
1715
)
16+
ALARM_COUNT = "alarm_count?context={resource}&status=RAISED"
1817

1918
API_VERSION = 1
2019

2120

2221
class Netdata(object):
2322
"""A class for handling connections with a Netdata instance."""
2423

25-
def __init__(self, host, loop, session, port=19999, path=None):
24+
def __init__(self, host, port=19999, tls=None, path=None):
2625
"""Initialize the connection to the Netdata instance."""
27-
self._loop = loop
28-
self._session = session
2926
self.host = host
3027
self.port = port
3128
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)
3629

30+
self.scheme = "http" if tls is None or not False else "https"
3731

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+
)
4240

41+
async def get_data(self, url) -> Dict:
42+
"""Execute a request to a data endpoint."""
4343
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)
4663

47-
_LOGGER.info("Response from Netdata: %s", response.status)
48-
data = await response.json()
49-
_LOGGER.debug(data)
64+
try:
5065
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")
5568

5669
async def get_alarms(self):
5770
"""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)
7373

7474
async def get_allmetrics(self):
7575
"""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)
8778

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

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy