started log ingestion and analysis

This commit is contained in:
Jake Kasper
2026-04-24 14:15:58 -04:00
parent c2537dd955
commit 9ac96cee9a
27 changed files with 1368 additions and 179 deletions

View File

@@ -1,4 +1,4 @@
"""PLS API client for cluster and per-node discovery."""
"""PLS API client for cluster, per-node discovery, and site-wide config."""
from __future__ import annotations
@@ -11,6 +11,10 @@ from app.config import PLS_AUTH_BACKEND, PLS_BASE_URL, PLS_PASSWORD, PLS_USERNAM
_token: str | None = None
class PlsRequestError(RuntimeError):
pass
def _base_url_for_host(host: str | None = None) -> str:
if not host:
return PLS_BASE_URL.rstrip("/")
@@ -18,9 +22,9 @@ def _base_url_for_host(host: str | None = None) -> str:
return urlunsplit((parts.scheme, host, parts.path.rstrip("/"), "", ""))
async def _login() -> str | None:
async def _login(force: bool = False) -> str | None:
global _token
if _token:
if _token and not force:
return _token
if not PLS_USERNAME or not PLS_PASSWORD:
return None
@@ -48,17 +52,45 @@ async def _get(path: str, host: str | None = None) -> dict | list | None:
if not token:
return None
headers = {"Authorization": f"Bearer {token}"}
url = f"{_base_url_for_host(host)}/{path.lstrip('/')}"
try:
async with httpx.AsyncClient(timeout=5, verify=PLS_VERIFY_TLS) as client:
response = await client.get(url, headers=headers)
response = await client.get(url, headers={"Authorization": f"Bearer {token}"})
if response.status_code in {401, 403}:
refreshed = await _login(force=True)
if not refreshed:
return None
response = await client.get(url, headers={"Authorization": f"Bearer {refreshed}"})
response.raise_for_status()
return response.json()
except Exception:
return None
async def _put(path: str, payload: dict, host: str | None = None) -> dict | list | None:
token = await _login()
if not token:
raise PlsRequestError("PLS authentication is not configured or login failed")
url = f"{_base_url_for_host(host)}/{path.lstrip('/')}"
try:
async with httpx.AsyncClient(timeout=8, verify=PLS_VERIFY_TLS) as client:
response = await client.put(url, headers={"Authorization": f"Bearer {token}"}, json=payload)
if response.status_code in {401, 403}:
refreshed = await _login(force=True)
if not refreshed:
raise PlsRequestError("PLS token expired and re-login failed")
response = await client.put(url, headers={"Authorization": f"Bearer {refreshed}"}, json=payload)
if response.is_error:
detail = response.text.strip()
raise PlsRequestError(f"HTTP {response.status_code}: {detail or 'unknown PLS validation error'}")
return response.json()
except PlsRequestError:
raise
except Exception as exc:
raise PlsRequestError(str(exc)) from exc
def node_host(node_name: str) -> str:
return node_name.split("@", 1)[1] if "@" in node_name else node_name
@@ -76,3 +108,13 @@ async def get_system_info(host: str | None = None) -> dict | None:
async def get_services(host: str | None = None) -> list[dict]:
data = await _get("services", host=host)
return data if isinstance(data, list) else []
async def get_fluentbit_config() -> dict | None:
data = await _get("fluent-bit/config")
return data if isinstance(data, dict) else None
async def put_fluentbit_config(config: dict) -> dict | None:
data = await _put("fluent-bit/config", config)
return data if isinstance(data, dict) else None