1. make metrics in commands_typed be agnostic of fastapi, django; 2. implement ping wrapper for rest.py in checks; 3. use env settings to specify hosts to ping; 4. add pyright, ruff into Makefile; 5. test in production;
96 lines
1.7 KiB
Python
96 lines
1.7 KiB
Python
import fastapi
|
|
import re
|
|
import numpy
|
|
import subprocess
|
|
import fastapi.responses
|
|
import pydantic_settings
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
from online.fxreader.pr34.commands_typed import metrics as pr34_metrics
|
|
|
|
from typing import Optional, Any, ClassVar
|
|
|
|
|
|
class Settings(pydantic_settings.BaseSettings):
|
|
checks_hosts: list[str]
|
|
|
|
_singleton: ClassVar[Optional['Settings']] = None
|
|
|
|
@classmethod
|
|
def singleton(cls) -> 'Settings':
|
|
if cls._singleton is None:
|
|
cls._singleton = Settings.model_validate({})
|
|
|
|
return cls._singleton
|
|
|
|
|
|
def ping_stats(host: str) -> Optional[float]:
|
|
try:
|
|
ping_output = subprocess.check_output(
|
|
[
|
|
'ping',
|
|
'-i',
|
|
'0.1',
|
|
'-c',
|
|
'3',
|
|
'-w',
|
|
'1',
|
|
host,
|
|
]
|
|
).decode('utf-8')
|
|
except:
|
|
logger.exception('')
|
|
|
|
ping_output = ''
|
|
|
|
r1 = re.compile(r'time=(\d+\.\d+)\sms')
|
|
|
|
spend_time = [float(o[1]) for o in r1.finditer(ping_output)]
|
|
|
|
if len(spend_time) == 0:
|
|
return None
|
|
else:
|
|
return float(numpy.mean(spend_time))
|
|
|
|
|
|
async def metrics_get() -> fastapi.responses.Response:
|
|
ping_res = {h: ping_stats(h) for h in Settings.singleton().checks_hosts}
|
|
|
|
metrics = [
|
|
pr34_metrics.Metric.model_validate(
|
|
dict(
|
|
name='ping_mean',
|
|
type='gauge',
|
|
help='ping to host, 3 counts, up to 1 second',
|
|
samples=[
|
|
dict(
|
|
value=str(v),
|
|
parameters=dict(
|
|
host=k,
|
|
),
|
|
)
|
|
],
|
|
)
|
|
)
|
|
for k, v in ping_res.items()
|
|
if not v is None
|
|
]
|
|
serialize_res = pr34_metrics.serialize(metrics)
|
|
|
|
return fastapi.responses.Response(
|
|
content=serialize_res.json2,
|
|
headers={
|
|
'Content-Type': serialize_res.content_type,
|
|
},
|
|
)
|
|
|
|
|
|
def get_router() -> fastapi.APIRouter:
|
|
router = fastapi.APIRouter()
|
|
|
|
router.get('/metrics')(metrics_get)
|
|
|
|
return router
|