glances_api 0.3.6__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {glances_api-0.3.6 → glances_api-0.4.0}/PKG-INFO +1 -1
- glances_api-0.4.0/glances_api/__init__.py +153 -0
- {glances_api-0.3.6 → glances_api-0.4.0}/pyproject.toml +24 -1
- {glances_api-0.3.6 → glances_api-0.4.0}/setup.py +1 -1
- glances_api-0.3.6/glances_api/__init__.py +0 -86
- {glances_api-0.3.6 → glances_api-0.4.0}/LICENSE +0 -0
- {glances_api-0.3.6 → glances_api-0.4.0}/README.rst +0 -0
- {glances_api-0.3.6 → glances_api-0.4.0}/glances_api/exceptions.py +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Client to interact with the Glances API."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from . import exceptions
|
|
10
|
+
|
|
11
|
+
_LOGGER = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Glances:
|
|
15
|
+
"""A class for handling the data retrieval."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
host: str = "localhost",
|
|
20
|
+
port: int = 61208,
|
|
21
|
+
version: int = 2,
|
|
22
|
+
ssl: bool = False,
|
|
23
|
+
verify_ssl: bool = True,
|
|
24
|
+
username: str | None = None,
|
|
25
|
+
password: str | None = None,
|
|
26
|
+
httpx_client: httpx.AsyncClient | None = None,
|
|
27
|
+
):
|
|
28
|
+
"""Initialize the connection."""
|
|
29
|
+
schema = "https" if ssl else "http"
|
|
30
|
+
self.url = f"{schema}://{host}:{port}/api/{version}"
|
|
31
|
+
self.data: dict[str, Any] = {}
|
|
32
|
+
self.plugins: list[str] = []
|
|
33
|
+
self.values: Any | None = None
|
|
34
|
+
self.username = username
|
|
35
|
+
self.password = password
|
|
36
|
+
self.verify_ssl = verify_ssl
|
|
37
|
+
self.httpx_client = httpx_client
|
|
38
|
+
|
|
39
|
+
async def get_data(self, endpoint: str) -> None:
|
|
40
|
+
"""Retrieve the data."""
|
|
41
|
+
url = f"{self.url}/{endpoint}"
|
|
42
|
+
|
|
43
|
+
httpx_client: Any = (
|
|
44
|
+
self.httpx_client
|
|
45
|
+
if self.httpx_client
|
|
46
|
+
else httpx.AsyncClient(verify=self.verify_ssl)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
async with httpx_client as client:
|
|
51
|
+
if self.password is None:
|
|
52
|
+
response = await client.get(url)
|
|
53
|
+
else:
|
|
54
|
+
response = await client.get(
|
|
55
|
+
url, auth=(self.username, self.password)
|
|
56
|
+
)
|
|
57
|
+
except (httpx.ConnectError, httpx.TimeoutException):
|
|
58
|
+
raise exceptions.GlancesApiConnectionError(f"Connection to {url} failed")
|
|
59
|
+
|
|
60
|
+
if response.status_code == httpx.codes.UNAUTHORIZED:
|
|
61
|
+
raise exceptions.GlancesApiAuthorizationError(
|
|
62
|
+
"Please check your credentials"
|
|
63
|
+
)
|
|
64
|
+
if response.status_code == httpx.codes.BAD_REQUEST:
|
|
65
|
+
raise exceptions.GlancesApiNoDataAvailable(
|
|
66
|
+
f"endpoint: '{endpoint}' is not valid"
|
|
67
|
+
)
|
|
68
|
+
if response.status_code == httpx.codes.OK:
|
|
69
|
+
try:
|
|
70
|
+
_LOGGER.debug(response.json())
|
|
71
|
+
if endpoint == "all":
|
|
72
|
+
self.data = response.json()
|
|
73
|
+
elif endpoint == "pluginslist":
|
|
74
|
+
self.plugins = response.json()
|
|
75
|
+
except TypeError:
|
|
76
|
+
_LOGGER.error("Can not load data from Glances")
|
|
77
|
+
raise exceptions.GlancesApiConnectionError(
|
|
78
|
+
"Unable to get the data from Glances"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
async def get_metrics(self, element: str) -> None:
|
|
82
|
+
"""Get all the metrics for a monitored element."""
|
|
83
|
+
await self.get_data("all")
|
|
84
|
+
await self.get_data("pluginslist")
|
|
85
|
+
|
|
86
|
+
if element in self.plugins:
|
|
87
|
+
self.values = self.data[element]
|
|
88
|
+
else:
|
|
89
|
+
raise exceptions.GlancesApiError("Element data not available")
|
|
90
|
+
|
|
91
|
+
async def get_ha_sensor_data(self) -> dict[str, Any] | None:
|
|
92
|
+
"""Create a dictionary with data for Home Assistant sensors."""
|
|
93
|
+
await self.get_data("all")
|
|
94
|
+
|
|
95
|
+
sensor_data: dict[str, Any] = {}
|
|
96
|
+
|
|
97
|
+
if disks := self.data.get("fs"):
|
|
98
|
+
sensor_data["fs"] = {}
|
|
99
|
+
for disk in disks:
|
|
100
|
+
disk_free = disk.get("free") or (disk["size"] - disk["used"])
|
|
101
|
+
sensor_data["fs"][disk["mnt_point"]] = {
|
|
102
|
+
"disk_use": round(disk["used"] / 1024**3, 1),
|
|
103
|
+
"disk_use_percent": disk["percent"],
|
|
104
|
+
"disk_free": round(disk_free / 1024**3, 1),
|
|
105
|
+
}
|
|
106
|
+
if data := self.data.get("sensors"):
|
|
107
|
+
sensor_data["sensors"] = {}
|
|
108
|
+
for sensor in data:
|
|
109
|
+
sensor_data["sensors"][sensor["label"]] = {
|
|
110
|
+
sensor["type"]: sensor["value"]
|
|
111
|
+
}
|
|
112
|
+
if data := self.data.get("mem"):
|
|
113
|
+
sensor_data["mem"] = {
|
|
114
|
+
"memory_use_percent": data["percent"],
|
|
115
|
+
"memory_use": round(data["used"] / 1024**2, 1),
|
|
116
|
+
"memory_free": round(data["free"] / 1024**2, 1),
|
|
117
|
+
}
|
|
118
|
+
if data := self.data.get("memswap"):
|
|
119
|
+
sensor_data["memswap"] = {
|
|
120
|
+
"swap_use_percent": data["percent"],
|
|
121
|
+
"swap_use": round(data["used"] / 1024**3, 1),
|
|
122
|
+
"swap_free": round(data["free"] / 1024**3, 1),
|
|
123
|
+
}
|
|
124
|
+
if data := self.data.get("load"):
|
|
125
|
+
sensor_data["load"] = {
|
|
126
|
+
"processor_load": data.get("min15")
|
|
127
|
+
or self.data["cpu"]["total"] # to be checked
|
|
128
|
+
}
|
|
129
|
+
if data := self.data.get("processcount"):
|
|
130
|
+
sensor_data["processcount"] = {
|
|
131
|
+
"process_running": data["running"],
|
|
132
|
+
"process_total": data["total"],
|
|
133
|
+
"process_thread": data["thread"],
|
|
134
|
+
"process_sleeping": data["sleeping"],
|
|
135
|
+
}
|
|
136
|
+
if data := self.data.get("quicklook"):
|
|
137
|
+
sensor_data["cpu"] = {"cpu_use_percent": data["cpu"]}
|
|
138
|
+
if "docker" in self.data and (data := self.data["docker"].get("containers")):
|
|
139
|
+
active_containers = [
|
|
140
|
+
container for container in data if container["Status"] == "running"
|
|
141
|
+
]
|
|
142
|
+
sensor_data["docker"] = {"docker_active": len(active_containers)}
|
|
143
|
+
cpu_use = 0.0
|
|
144
|
+
for container in active_containers:
|
|
145
|
+
cpu_use += container["cpu"]["total"]
|
|
146
|
+
sensor_data["docker"]["docker_cpu_use"] = round(cpu_use, 1)
|
|
147
|
+
mem_use = 0.0
|
|
148
|
+
for container in active_containers:
|
|
149
|
+
mem_use += container["memory"]["usage"]
|
|
150
|
+
sensor_data["docker"]["docker_memory_use"] = round(mem_use / 1024**2, 1)
|
|
151
|
+
if data := self.data.get("raid"):
|
|
152
|
+
sensor_data["raid"] = data
|
|
153
|
+
return sensor_data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "glances_api"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Python API for interacting with Glances"
|
|
5
5
|
authors = ["Fabian Affolter <fabian@affolter-engineering.ch>"]
|
|
6
6
|
homepage = "https://github.com/home-assistant-ecosystem/python-glances-api"
|
|
@@ -27,6 +27,29 @@ pytest = "^6.2.5"
|
|
|
27
27
|
pytest-httpx = ">0.15,<1"
|
|
28
28
|
pytest-asyncio = "^0.16.0"
|
|
29
29
|
isort = "^5.10.0"
|
|
30
|
+
mypy = "^0.971"
|
|
31
|
+
|
|
32
|
+
[tool.mypy]
|
|
33
|
+
check_untyped_defs = true
|
|
34
|
+
disallow_any_generics = true
|
|
35
|
+
disallow_incomplete_defs = true
|
|
36
|
+
disallow_untyped_defs = true
|
|
37
|
+
mypy_path = "glances_api/"
|
|
38
|
+
no_implicit_optional = true
|
|
39
|
+
show_error_codes = true
|
|
40
|
+
warn_unreachable = true
|
|
41
|
+
warn_unused_ignores = true
|
|
42
|
+
exclude = [
|
|
43
|
+
'pyproject.toml',
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[[tool.mypy.overrides]]
|
|
47
|
+
module = "tests.*"
|
|
48
|
+
allow_untyped_defs = true
|
|
49
|
+
|
|
50
|
+
[[tool.mypy.overrides]]
|
|
51
|
+
module = "docs.*"
|
|
52
|
+
ignore_errors = true
|
|
30
53
|
|
|
31
54
|
[build-system]
|
|
32
55
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -12,7 +12,7 @@ install_requires = \
|
|
|
12
12
|
|
|
13
13
|
setup_kwargs = {
|
|
14
14
|
'name': 'glances-api',
|
|
15
|
-
'version': '0.
|
|
15
|
+
'version': '0.4.0',
|
|
16
16
|
'description': 'Python API for interacting with Glances',
|
|
17
17
|
'long_description': 'python-glances-api\n==================\n\nA Python client for interacting with `Glances <https://nicolargo.github.io/glances/>`_.\n\nThis module is not official, developed, supported or endorsed by Glances.\n\nInstallation\n------------\n\nThe module is available from the `Python Package Index <https://pypi.python.org/pypi>`_.\n\n.. code:: bash\n\n $ pip3 install glances_api\n\nOr on a Fedora-based system or on a CentOS/RHEL machine with has EPEL enabled.\n\n.. code:: bash\n\n $ sudo dnf -y install python3-glances-api\n\n\nFor Nix or NixOS is `pre-packed module <https://search.nixos.org/packages?channel=unstable&query=glances-api>`_\navailable. The lastest release is usually present in the ``unstable`` channel.\n\n.. code:: bash\n\n $ nix-env -iA nixos.python3Packages.glances-api\n\nUsage\n-----\n\nThe file ``example.py`` contains an example about how to use this module.\n\nLicense\n-------\n\n``python-glances-api`` is licensed under MIT, for more details check LICENSE.\n',
|
|
18
18
|
'author': 'Fabian Affolter',
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""Client to interact with the Glances API."""
|
|
2
|
-
import asyncio
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
import httpx
|
|
6
|
-
|
|
7
|
-
from . import exceptions
|
|
8
|
-
|
|
9
|
-
_LOGGER = logging.getLogger(__name__)
|
|
10
|
-
_RESOURCE = "{schema}://{host}:{port}/api/{version}"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Glances(object):
|
|
14
|
-
"""A class for handling the data retrieval."""
|
|
15
|
-
|
|
16
|
-
def __init__(
|
|
17
|
-
self,
|
|
18
|
-
host="localhost",
|
|
19
|
-
port=61208,
|
|
20
|
-
version=2,
|
|
21
|
-
ssl=False,
|
|
22
|
-
verify_ssl=True,
|
|
23
|
-
username=None,
|
|
24
|
-
password=None,
|
|
25
|
-
httpx_client=None,
|
|
26
|
-
):
|
|
27
|
-
"""Initialize the connection."""
|
|
28
|
-
schema = "https" if ssl else "http"
|
|
29
|
-
self.url = _RESOURCE.format(
|
|
30
|
-
schema=schema, host=host, port=port, version=version
|
|
31
|
-
)
|
|
32
|
-
self.plugins = None
|
|
33
|
-
self.values = None
|
|
34
|
-
self.username = username
|
|
35
|
-
self.password = password
|
|
36
|
-
self.verify_ssl = verify_ssl
|
|
37
|
-
self.httpx_client = httpx_client
|
|
38
|
-
|
|
39
|
-
async def get_data(self, endpoint):
|
|
40
|
-
"""Retrieve the data."""
|
|
41
|
-
url = "{}/{}".format(self.url, endpoint)
|
|
42
|
-
|
|
43
|
-
httpx_client = (
|
|
44
|
-
self.httpx_client
|
|
45
|
-
if self.httpx_client
|
|
46
|
-
else httpx.AsyncClient(verify=self.verify_ssl)
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
async with httpx_client as client:
|
|
51
|
-
if self.password is None:
|
|
52
|
-
response = await client.get(str(url))
|
|
53
|
-
else:
|
|
54
|
-
response = await client.get(
|
|
55
|
-
str(url), auth=(self.username, self.password)
|
|
56
|
-
)
|
|
57
|
-
except (httpx.ConnectError, httpx.TimeoutException):
|
|
58
|
-
raise exceptions.GlancesApiConnectionError(f"Connection to {url} failed")
|
|
59
|
-
|
|
60
|
-
if response.status_code == httpx.codes.UNAUTHORIZED:
|
|
61
|
-
raise exceptions.GlancesApiAuthorizationError(
|
|
62
|
-
"Please check your credentials"
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
if response.status_code == httpx.codes.OK:
|
|
66
|
-
try:
|
|
67
|
-
_LOGGER.debug(response.json())
|
|
68
|
-
if endpoint == "all":
|
|
69
|
-
self.data = response.json()
|
|
70
|
-
if endpoint == "pluginslist":
|
|
71
|
-
self.plugins = response.json()
|
|
72
|
-
except TypeError:
|
|
73
|
-
_LOGGER.error("Can not load data from Glances")
|
|
74
|
-
raise exceptions.GlancesApiConnectionError(
|
|
75
|
-
"Unable to get the data from Glances"
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
async def get_metrics(self, element):
|
|
79
|
-
"""Get all the metrics for a monitored element."""
|
|
80
|
-
await self.get_data("all")
|
|
81
|
-
await self.get_data("pluginslist")
|
|
82
|
-
|
|
83
|
-
if element in self.plugins:
|
|
84
|
-
self.values = self.data[element]
|
|
85
|
-
else:
|
|
86
|
-
raise exceptions.GlancesApiError("Element data not available")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|