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
 |