Skip to content

Commit 6f9e288

Browse files
authored
Merge pull request #8 from imrehg/tuning
Changes since the first release
2 parents 8dd4277 + 37b4640 commit 6f9e288

12 files changed

+186
-16
lines changed

.github/workflows/python.yaml

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Python
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
build:
11+
strategy:
12+
matrix:
13+
python-version: ['3.9', '3.10']
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v2
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Switch to Current Branch
23+
run: git checkout ${{ env.BRANCH }}
24+
25+
- name: Set up Python ${{ matrix.python-version }}
26+
uses: actions/setup-python@v1
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Install dependencies
31+
run: |
32+
python -m pip install --upgrade pip
33+
python -m pip install 'poetry>=1.2.0b1'
34+
poetry plugin add poetry-dynamic-versioning-plugin
35+
poetry install
36+
37+
- name: Run linting
38+
run: |
39+
poetry run isort --check src/ tests/
40+
poetry run black --check src/ tests/
41+
poetry run flake8 src/ tests/
42+
poetry run mypy src
43+
poetry run bandit src
44+
45+
- name: Run unit tests
46+
run: |
47+
poetry run coverage run -m pytest
48+
poetry run coverage report -m

.gitignore

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
2-
# Ignore dynaconf secret files
1+
# Ignore various secret files
32
.secrets.*
3+
.env
44

5-
# Coverage.py generated data
6-
.coverage
5+
# Built packages
6+
dist/

Dockerfile

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
FROM python:3.9
2+
3+
ENV PYTHONUNBUFFERED=1 \
4+
# prevents python creating .pyc files
5+
PYTHONDONTWRITEBYTECODE=1 \
6+
\
7+
# pip
8+
PIP_NO_CACHE_DIR=off \
9+
PIP_DISABLE_PIP_VERSION_CHECK=on \
10+
PIP_DEFAULT_TIMEOUT=100 \
11+
\
12+
# poetry minimal version
13+
POETRY_VERSION=1.2.0b1 \
14+
# make poetry create the virtual environment in the project's root
15+
# it gets named `.venv`
16+
POETRY_VIRTUALENVS_IN_PROJECT=true \
17+
# do not ask any interactive question
18+
POETRY_NO_INTERACTION=1
19+
20+
RUN pip install --upgrade pip \
21+
&& pip install 'poetry>=$POETRY_VERSION'
22+
23+
WORKDIR /opt/app
24+
25+
# Install dependencies
26+
COPY pyproject.toml poetry.lock ./
27+
28+
# Install the dependencies first, then add the versioning
29+
# plugin to remove the need to mount the `.git` folder, and
30+
# thus caching better.
31+
RUN poetry install --without=dev --no-root \
32+
&& poetry plugin add poetry-dynamic-versioning-plugin
33+
34+
# Install the project
35+
COPY src ./src
36+
RUN --mount=source=.git,target=.git,type=bind \
37+
poetry dynamic-versioning \
38+
&& poetry install --without=dev
39+
40+
CMD ["poetry", "run", "linkhub_exporter"]

README.md

+60-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,66 @@ Tested with an Alcatel HH41 4G LTE hotspot WiFi router.
66

77
## Usage
88

9-
Detailed usage TBC.
9+
Install Poetry for you system (need `>=1.2.0b1` currently if using
10+
the dynamic versioning, and have to add the relevant plugin with
11+
`poetry plugin add poetry-dynamic-versioning-plugin`). Then install the
12+
package with:
13+
14+
```shell
15+
poetry install
16+
```
17+
18+
You'll need a Request Key to run exporter, which is derived from the
19+
login password of router box admin interface. See below how to
20+
obtain it.
21+
22+
Once you have a key, you can set it in multiple ways:
23+
24+
* In `.secrets.toml`, see the template shipped at `secrets.toml.template`
25+
for the format (note the `.` for the non-template filename), OR
26+
* Set an environment variable `DYNACONF_REQUEST_KEY` with the value, e.g.
27+
`export DYNACONF_REQUEST_KEY=...` in your shell where `...` is replaced with
28+
the actual value.
29+
30+
Then start up the exporter:
31+
32+
```shell
33+
poetry run exporter
34+
```
35+
36+
### Running in Docker
37+
38+
Build the image with the included Dockerfile from the cloned repository,
39+
let's say:
40+
41+
```shell
42+
docker build -t linkhub_exporter
43+
```
44+
45+
and then run the resulting image as:
46+
47+
```shell
48+
docker run -ti --rm -e "DYNACONF_REQUEST_KEY=...." -p 9877:9877 linkhub_exporter
49+
```
50+
51+
which exposes the Prometheus metrics on `http://localhost:9877`. Don't forget
52+
to set the `DYNACONF_REQUEST_KEY` value, or add it in an `.env` file and
53+
run things as:
54+
55+
```shell
56+
docker run -ti --rm --env-file .env -p 9877:9877 linkhub_exporter
57+
```
58+
59+
### Getting the request key
60+
61+
Currently the easiest way to get it is to:
62+
63+
* Open a browser and navigate to your router admin interface
64+
* Open the debug console, and ensure that network requests are logged there
65+
* Log in to the admin interface
66+
* Check requests going to `webapi`, look for the requests headers, and the
67+
value of the `_TclRequestVerificationKey` is what you should use for the
68+
request key setting of this exporter.
1069

1170
## License
1271

env.template

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copy this to `.env` and fill in your key
2+
DYNACONF_REQUEST_KEY=''

pyproject.toml

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "linkhub_prometheus_exporter"
3-
version = "0.1.0"
3+
version = "0.0.0"
44
description = "A Prometheus metrics exporter for Alcatel Linkhub 4G router boxes"
55
authors = ["Gergely Imreh <[email protected]>"]
66

@@ -35,13 +35,16 @@ module = [
3535
ignore_missing_imports = true
3636

3737
[tool.poetry.scripts]
38-
exporter = "linkhub_prometheus_exporter.exporter:main"
38+
linkhub_exporter = "linkhub_prometheus_exporter.exporter:main"
3939

4040
[tool.poetry-dynamic-versioning]
4141
enable = true
4242
vcs = "git"
4343
style = "pep440"
4444

45+
[tool.poetry-dynamic-versioning.substitution]
46+
files = ["*/__init__.py", "*/*/__init__.py"]
47+
4548
[build-system]
4649
requires = ["poetry>=1.2.0b1", "poetry-dynamic-versioning-plugin"]
4750
build-backend = "poetry.masonry.api"

secrets.toml.template

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
# Copy this file to `.secrets.toml` and fill in the values
2-
[default]
32
request_key = ''

settings.toml settings.toml.template

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
[default]
1+
# Copy this file to `settings.toml` and update the values
2+
# for easy change of settings
23
polling_interval_seconds = 5
34
box_address = '192.168.1.1'
45
exporter_address = '0.0.0.0'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Will be dynamically filled
2+
__version__ = "0.0.0"

src/linkhub_prometheus_exporter/config.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
from dynaconf import Dynaconf
1+
from dynaconf import Dynaconf, Validator
22

33
settings = Dynaconf(
44
envvar_prefix="DYNACONF",
55
settings_files=["settings.toml", ".secrets.toml"],
6-
environments=True,
6+
# Validating and setting defaults
7+
validators=[
8+
Validator("REQUEST_KEY", is_type_of=str),
9+
Validator("POLLING_INTERVAL_SECONDS", is_type_of=int, default=5),
10+
Validator("BOX_ADDRESS", is_type_of=str, default="192.168.1.1"),
11+
Validator("EXPORTER_PORT", is_type_of=int, default=9877),
12+
Validator("EXPORTER_ADDRESS", is_type_of=str, default="0.0.0.0"),
13+
],
714
)
815

916
# `envvar_prefix` = export envvars with `export DYNACONF_FOO=bar`.

src/linkhub_prometheus_exporter/exporter.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import requests
55
from prometheus_client import Gauge, Info, start_http_server
66

7+
from . import __version__
78
from .config import settings
89

910
logging.basicConfig(level=settings.get("LOG_LEVEL", default="INFO"))
@@ -142,12 +143,19 @@ def fetch_new(self):
142143

143144
def main():
144145
"""Main entry point for the exporter"""
146+
logging.info("Linkhub Prometheus Exporter, version %s", __version__)
147+
148+
try:
149+
router_metrics = RouterMetrics(
150+
request_key=settings.REQUEST_KEY,
151+
box_addr=settings.BOX_ADDRESS,
152+
polling_interval_seconds=settings.POLLING_INTERVAL_SECONDS,
153+
)
154+
except AttributeError as exc:
155+
# Every other setting besides REQUEST_KEY has defaults
156+
logging.error("Missing REQUEST_KEY configuration.")
157+
raise RuntimeError("Missing REQUEST_KEY configuration.") from exc
145158

146-
router_metrics = RouterMetrics(
147-
request_key=settings.REQUEST_KEY,
148-
box_addr=settings.BOX_ADDRESS,
149-
polling_interval_seconds=settings.POLLING_INTERVAL_SECONDS,
150-
)
151159
start_http_server(
152160
port=settings.EXPORTER_PORT, addr=settings.EXPORTER_ADDRESS
153161
)

tests/test_basic.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from linkhub_prometheus_exporter.exporter import RouterMetrics
55

66
# Test payload data
7+
# Exported & modified from real requests
78
NETWORK_INFO = {
89
"PLMN": "12345",
910
"NetworkType": 8,

0 commit comments

Comments
 (0)