mirror of
https://github.com/Smaug123/PulumiConfig
synced 2025-10-05 08:38:41 +00:00
Add PureGym Prometheus stats (#28)
This commit is contained in:
40
PulumiWebServer/Nix/flake.lock
generated
40
PulumiWebServer/Nix/flake.lock
generated
@@ -156,11 +156,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1706473109,
|
||||
"narHash": "sha256-iyuAvpKTsq2u23Cr07RcV5XlfKExrG8gRpF75hf1uVc=",
|
||||
"lastModified": 1707683400,
|
||||
"narHash": "sha256-Zc+J3UO1Xpx+NL8UB6woPHyttEy9cXXtm+0uWwzuYDc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "d634c3abafa454551f2083b054cd95c3f287be61",
|
||||
"rev": "21b078306a2ab68748abf72650db313d646cf2ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -226,11 +226,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1706373441,
|
||||
"narHash": "sha256-S1hbgNbVYhuY2L05OANWqmRzj4cElcbLuIkXTb69xkk=",
|
||||
"lastModified": 1707650010,
|
||||
"narHash": "sha256-dOhphIA4MGrH4ElNCy/OlwmN24MsnEqFjRR6+RY7jZw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "56911ef3403a9318b7621ce745f5452fb9ef6867",
|
||||
"rev": "809cca784b9f72a5ad4b991e0e7bcf8890f9c3a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -242,16 +242,16 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1705957679,
|
||||
"narHash": "sha256-Q8LJaVZGJ9wo33wBafvZSzapYsjOaNjP/pOnSiKVGHY=",
|
||||
"lastModified": 1707603439,
|
||||
"narHash": "sha256-LodBVZ3+ehJP2azM5oj+JrhfNAAzmTJ/OwAIOn0RfZ0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9a333eaa80901efe01df07eade2c16d183761fa3",
|
||||
"rev": "d8cd80616c8800feec0cab64331d7c3d5a1a6d98",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-23.05",
|
||||
"ref": "release-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -273,11 +273,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1706173671,
|
||||
"narHash": "sha256-lciR7kQUK2FCAYuszyd7zyRRmTaXVeoZsCyK6QFpGdk=",
|
||||
"lastModified": 1707451808,
|
||||
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4fddc9be4eaf195d631333908f2a454b03628ee5",
|
||||
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -380,11 +380,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1706573865,
|
||||
"narHash": "sha256-Gfk5mMz6gRLVwljXrPTfqAX7Bp8UQK4vKtXu9kYkzQM=",
|
||||
"lastModified": 1707775682,
|
||||
"narHash": "sha256-cLIgwrkNAkJpTgKdzU0qaWwy8rClqIBYYjOm/UHprcg=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "e96ae78665db13cbf4745c04d634a8a878549768",
|
||||
"revCount": 10,
|
||||
"rev": "419f27053f92ad0f0e42874cdc584fb0cca534e3",
|
||||
"revCount": 12,
|
||||
"type": "git",
|
||||
"url": "https://gitea.patrickstevens.co.uk/patrick/puregym-unofficial-dotnet"
|
||||
},
|
||||
@@ -469,11 +469,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1706410821,
|
||||
"narHash": "sha256-iCfXspqUOPLwRobqQNAQeKzprEyVowLMn17QaRPQc+M=",
|
||||
"lastModified": 1707748232,
|
||||
"narHash": "sha256-o9L8jrOemQl/5cYp++0cWdfMLzVljCdHwPFF4N0KZeQ=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "73bf36912e31a6b21af6e0f39218e067283c67ef",
|
||||
"rev": "695275c349bb27f91b2b06cb742510899c887b81",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@@ -41,6 +41,13 @@
|
||||
mode = "0440";
|
||||
};
|
||||
|
||||
environment.etc."grafana-dashboards/puregym.json" = {
|
||||
source = ./puregym.json;
|
||||
group = "grafana";
|
||||
user = "grafana";
|
||||
mode = "0440";
|
||||
};
|
||||
|
||||
services.grafana = {
|
||||
enable = true;
|
||||
settings = {
|
||||
|
144
PulumiWebServer/Nix/grafana/puregym.json
Normal file
144
PulumiWebServer/Nix/grafana/puregym.json
Normal file
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P40645DF18AF953B4"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"axisSoftMin": 0,
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"timezone": [
|
||||
"Europe/London"
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "P40645DF18AF953B4"
|
||||
},
|
||||
"disableTextWrap": false,
|
||||
"editorMode": "builder",
|
||||
"expr": "fullness",
|
||||
"fullMetaSearch": false,
|
||||
"includeNullMetadata": true,
|
||||
"instant": false,
|
||||
"legendFormat": "{{label}}",
|
||||
"range": true,
|
||||
"refId": "A",
|
||||
"useBackend": false
|
||||
}
|
||||
],
|
||||
"title": "Gym occupancy",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 38,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "PureGym",
|
||||
"uid": "f1399663-bebe-42d8-9162-b046af9fb0c2",
|
||||
"version": 3,
|
||||
"weekStart": ""
|
||||
}
|
@@ -52,6 +52,18 @@
|
||||
};
|
||||
|
||||
scrapeConfigs = [
|
||||
{
|
||||
job_name = "gym-fullness";
|
||||
static_configs = [
|
||||
{
|
||||
# Gym 19 is London Oval
|
||||
targets = ["localhost:${toString config.services.puregym-config.port}"];
|
||||
}
|
||||
];
|
||||
params = { gym_id = ["19"]; };
|
||||
metrics_path = "/fullness-prometheus";
|
||||
scrape_interval = "5m";
|
||||
}
|
||||
{
|
||||
job_name = "node";
|
||||
static_configs = [
|
||||
|
@@ -13,12 +13,49 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
_last_accessed_by_id = defaultdict(lambda: datetime.min)
|
||||
_last_accessed_by_name = defaultdict(lambda: datetime.min)
|
||||
|
||||
_all_gyms = {}
|
||||
_last_refreshed_gyms = datetime.min
|
||||
|
||||
def _bad_request(self, text: str, code: int = 400) -> None:
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'text/plain; charset=utf-8')
|
||||
self.end_headers()
|
||||
self.wfile.write(text.encode('utf-8'))
|
||||
|
||||
def _get_fullness(self, gym_id: int) -> bytes:
|
||||
if abs(datetime.now() - self._last_accessed_by_id[gym_id]) > timedelta(seconds=30):
|
||||
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
||||
output = subprocess.check_output(
|
||||
[puregym, 'fullness', '--bearer-token', token, '--gym-id', str(gym_id)], text=True,
|
||||
encoding='utf-8')
|
||||
output = output.encode('utf-8')
|
||||
self._cache_result_by_id[gym_id] = output
|
||||
self._last_accessed_by_id[gym_id] = datetime.now()
|
||||
else:
|
||||
output = self._cache_result_by_id[gym_id]
|
||||
return output
|
||||
|
||||
def _refresh_gyms(self) -> None:
|
||||
if self._last_refreshed_gyms < datetime.now() - timedelta(days=1):
|
||||
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
||||
output = subprocess.check_output(
|
||||
[puregym, 'all-gyms', '--bearer-token', token, '--terse', 'true'], text=True,
|
||||
encoding='utf-8')
|
||||
new_gyms = {}
|
||||
for line in output.splitlines():
|
||||
gym_id, gym_name = line.split(',')
|
||||
new_gyms[int(gym_id)] = gym_name
|
||||
self._all_gyms = new_gyms
|
||||
self._last_refreshed_gyms = datetime.now()
|
||||
|
||||
def get_all_gyms(self, _query: dict[AnyStr, list[AnyStr]]) -> None:
|
||||
self._refresh_gyms()
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/plain')
|
||||
self.end_headers()
|
||||
for gym_id, gym_name in self._all_gyms.items():
|
||||
self.wfile.write(f'{gym_id}: {gym_name}\n'.encode())
|
||||
|
||||
def get_fullness(self, query: dict[AnyStr, list[AnyStr]]) -> None:
|
||||
desired_gym_name = None
|
||||
query_gym = query.get("gym_name", None)
|
||||
@@ -48,16 +85,7 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
desired_gym_id = None
|
||||
|
||||
if desired_gym_id is not None:
|
||||
if abs(datetime.now() - self._last_accessed_by_id[desired_gym_id]) > timedelta(seconds=30):
|
||||
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
||||
output = subprocess.check_output(
|
||||
[puregym, 'fullness', '--bearer-token', token, '--gym-id', str(desired_gym_id)], text=True,
|
||||
encoding='utf-8')
|
||||
output = output.encode('utf-8')
|
||||
self._cache_result_by_id[desired_gym_id] = output
|
||||
self._last_accessed_by_id[desired_gym_id] = datetime.now()
|
||||
else:
|
||||
output = self._cache_result_by_id[desired_gym_id]
|
||||
output = self._get_fullness(desired_gym_id)
|
||||
elif desired_gym_name is not None:
|
||||
if abs(datetime.now() - self._last_accessed_by_name[desired_gym_name]) > timedelta(seconds=30):
|
||||
token = subprocess.check_output(['cat', '/tmp/puregym_token']).strip()
|
||||
@@ -79,8 +107,39 @@ class MyHandler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(output)
|
||||
|
||||
def get_prometheus(self, query: dict[AnyStr, list[AnyStr]]) -> None:
|
||||
query_gym = query.get("gym_id", None)
|
||||
if query_gym is None:
|
||||
self._bad_request('Must supply gym_id')
|
||||
return
|
||||
try:
|
||||
gym_id = [int(i) for i in query_gym]
|
||||
except ValueError:
|
||||
self._bad_request('at least one gym_id did not parse as an int')
|
||||
return
|
||||
|
||||
if not gym_id:
|
||||
self._bad_request('supply at least one gym_id')
|
||||
return
|
||||
|
||||
try:
|
||||
fullness = [(i, int(self._get_fullness(i).split(b' ')[0])) for i in gym_id]
|
||||
except ValueError:
|
||||
self._bad_request('at least one fullness did not yield an int', 500)
|
||||
return
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/plain')
|
||||
self.end_headers()
|
||||
self._refresh_gyms()
|
||||
for gym_id, fullness in fullness:
|
||||
gym_name = ''.join(c for c in self._all_gyms[gym_id] if c == ' ' or str.isalnum(c))
|
||||
self.wfile.write(f'fullness{{label="{gym_name}"}} {fullness}\n'.encode())
|
||||
|
||||
_handlers: dict[str, Callable[["MyHandler", dict[AnyStr, list[AnyStr]]], None]] = {
|
||||
"/fullness": get_fullness
|
||||
"/fullness": get_fullness,
|
||||
"/fullness-prometheus": get_prometheus,
|
||||
"/gym-mapping": get_all_gyms,
|
||||
}
|
||||
|
||||
def do_GET(self):
|
||||
|
12
flake.lock
generated
12
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1703637592,
|
||||
"narHash": "sha256-8MXjxU0RfFfzl57Zy3OfXCITS0qWDNLzlBAdwxGZwfY=",
|
||||
"lastModified": 1706732774,
|
||||
"narHash": "sha256-hqJlyJk4MRpcItGYMF+3uHe8HvxNETWvlGtLuVpqLU0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cfc3698c31b1fb9cdcf10f36c9643460264d0ca8",
|
||||
"rev": "b8b232ae7b8b144397fdb12d20f592e5e7c1a64d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
Reference in New Issue
Block a user