Skip to content

Commit 5e84db7

Browse files
authoredMay 8, 2022
Merge pull request #15 from imrehg/jsonrpc
Switch to using JSON-RPC library for request creation and response parsing
2 parents 8640814 + 2576676 commit 5e84db7

File tree

7 files changed

+52
-43
lines changed

7 files changed

+52
-43
lines changed
 

‎.github/workflows/python.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
name: "Lint & test"
1414
strategy:
1515
matrix:
16-
python-version: ['3.9', '3.10']
16+
python-version: ['3.10']
1717
runs-on: ubuntu-latest
1818

1919
steps:
@@ -61,10 +61,10 @@ jobs:
6161
with:
6262
fetch-depth: 0
6363

64-
- name: Set up Python 3.9
64+
- name: Set up Python 3.10
6565
uses: actions/setup-python@v1
6666
with:
67-
python-version: 3.9
67+
python-version: 3.10
6868

6969
- name: Publish
7070
run: |

‎.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
# Ignore various secret files
1+
# Ignore various secret & settings files
22
.secrets.*
33
.env
4+
settings.toml
45

56
# Built packages
67
dist/

‎Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.9
1+
FROM python:3.10
22

33
ENV PYTHONUNBUFFERED=1 \
44
# prevents python creating .pyc files

‎poetry.lock

+12-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ prometheus-client = "^0.14.1"
1818
requests = "^2.27.1"
1919
dynaconf = "^3.1.8"
2020
types-requests = "^2.27.25"
21+
jsonrpcclient = "^4.0.2"
2122

2223
[tool.poetry.dev-dependencies]
2324
pytest = "*"

‎src/linkhub_prometheus_exporter/exporter.py

+29-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import logging
22
import time
3+
from typing import Any
34

45
import requests
6+
from jsonrpcclient import Error, Ok, parse, request_hex
57
from prometheus_client import Gauge, Info, start_http_server
68

79
from . import __version__
@@ -34,27 +36,6 @@ class RouterMetrics:
3436
"Total transferred data this month (bytes)",
3537
)
3638

37-
payload = {
38-
"network_info": {
39-
"id": "1",
40-
"jsonrpc": "2.0",
41-
"method": "GetNetworkInfo",
42-
"params": {},
43-
},
44-
"system_status": {
45-
"id": "1",
46-
"jsonrpc": "2.0",
47-
"method": "GetSystemStatus",
48-
"params": {},
49-
},
50-
"usage_record": {
51-
"id": "1",
52-
"jsonrpc": "2.0",
53-
"method": "GetUsageRecord",
54-
"params": {},
55-
},
56-
}
57-
5839
def __init__(
5940
self,
6041
request_key: str,
@@ -72,24 +53,36 @@ def __init__(
7253
"Referer": f"http://{self.box_addr}/index.html",
7354
}
7455

75-
def run_metrics_loop(self):
56+
def run_metrics_loop(self) -> None:
7657
"""Metrics fetching loop"""
7758

7859
while True:
7960
logging.debug("Fetching metrics.")
80-
# self.fetch()
81-
self.fetch_new()
61+
self.fetch_metrics()
8262
time.sleep(self.polling_interval_seconds)
8363

84-
def _box_api_request(self, json: dict) -> dict:
64+
def _box_api_request(self, method: str) -> dict[str, Any]:
8565
response = requests.post(
86-
self.url, json=json, headers=self.headers, timeout=self.timeout
66+
self.url,
67+
json=request_hex(method),
68+
headers=self.headers,
69+
timeout=self.timeout,
8770
)
88-
logging.debug("Response JSON: %s", response.json())
89-
return response.json().get("result", {})
71+
logging.debug("Method: %s; response: %s", method, response.json())
72+
match parse(response.json()):
73+
case Ok(result, _):
74+
return result
75+
case Error(_, message, _, _):
76+
logging.error(
77+
"API error: method: %s; message: %s", method, message
78+
)
79+
raise RuntimeError(message)
80+
case _:
81+
assert False, "Impossible parsed response received."
9082

9183
def _read_network_info(self) -> None:
92-
results = self._box_api_request(json=self.payload["network_info"])
84+
"""Requesting, parsing, and updating network info metrics."""
85+
results = self._box_api_request("GetNetworkInfo")
9386
logging.debug("Network info: %s", results)
9487

9588
# Set Prometheus metrics
@@ -114,15 +107,17 @@ def _read_network_info(self) -> None:
114107
)
115108

116109
def _read_system_status(self) -> None:
117-
results = self._box_api_request(json=self.payload["system_status"])
110+
"""Requesting, parsing, and updating system status metrics."""
111+
results = self._box_api_request("GetSystemStatus")
118112
logging.debug("System status: %s", results)
119113

120114
# Set Prometheus metrics
121115
if value := results.get("TotalConnNum"):
122116
self.connected_devices.set(value)
123117

124118
def _read_usage_record(self) -> None:
125-
results = self._box_api_request(json=self.payload["usage_record"])
119+
"""Requesting, parsing, and updating usage record metrics."""
120+
results = self._box_api_request("GetUsageRecord")
126121
logging.debug("Usage record: %s", results)
127122

128123
# Set Prometheus metrics
@@ -135,13 +130,14 @@ def _read_usage_record(self) -> None:
135130
if value := results.get("HUseData"):
136131
self.total_transfer_this_month.set(value)
137132

138-
def fetch_new(self):
133+
def fetch_metrics(self) -> None:
134+
"""Fetch all relevant metrics."""
139135
self._read_network_info()
140136
self._read_system_status()
141137
self._read_usage_record()
142138

143139

144-
def main():
140+
def main() -> None:
145141
"""Main entry point for the exporter"""
146142
logging.info("Linkhub Prometheus Exporter, version %s", __version__)
147143

‎tests/test_basic.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,23 @@ def matcher(pattern):
7474

7575
requests_mock.post(
7676
box_api_url,
77-
json={"result": NETWORK_INFO},
77+
json={"jsonrpc": "2.0", "result": NETWORK_INFO, "id": "1"},
7878
additional_matcher=matcher("GetNetworkInfo"),
7979
)
8080
requests_mock.post(
8181
box_api_url,
82-
json={"result": SYSTEM_STATUS},
82+
json={"jsonrpc": "2.0", "result": SYSTEM_STATUS, "id": "2"},
8383
additional_matcher=matcher("GetSystemStatus"),
8484
)
8585
requests_mock.post(
8686
box_api_url,
87-
json={"result": USAGE_RECORD},
87+
json={"jsonrpc": "2.0", "result": USAGE_RECORD, "id": "3"},
8888
additional_matcher=matcher("GetUsageRecord"),
8989
)
9090

9191
# Do a metrics update
9292
router_metrics = RouterMetrics("", box_addr, 10)
93-
router_metrics.fetch_new()
93+
router_metrics.fetch_metrics()
9494

9595
# Compare with expectations
9696
assert REGISTRY.get_sample_value("sinr") == pytest.approx(

0 commit comments

Comments
 (0)
Please sign in to comment.