121 lines
4.6 KiB
Python
121 lines
4.6 KiB
Python
"""Elasticsearch + Kibana health API for Command Center."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, Query
|
|
from fastapi.responses import JSONResponse
|
|
|
|
ELASTICSEARCH_URL = os.getenv("ELASTICSEARCH_URL", "https://10.0.21.46:9200").rstrip("/")
|
|
KIBANA_URL = os.getenv("KIBANA_URL", "http://10.0.21.46:5601").rstrip("/")
|
|
ELASTIC_USER = os.getenv("ELASTIC_USER", "elastic")
|
|
ELASTIC_PASSWORD = os.getenv("ELASTIC_PASSWORD", "")
|
|
|
|
router = APIRouter(prefix="/api/search", tags=["search"])
|
|
|
|
|
|
async def _probe_es() -> dict[str, Any]:
|
|
auth = (ELASTIC_USER, ELASTIC_PASSWORD) if ELASTIC_PASSWORD else None
|
|
try:
|
|
async with httpx.AsyncClient(timeout=6.0, verify=False) as client:
|
|
r = await client.get(f"{ELASTICSEARCH_URL}/", auth=auth)
|
|
if r.status_code >= 400:
|
|
return {"ok": False, "status_code": r.status_code, "error": r.text[:200]}
|
|
info = r.json()
|
|
health_r = await client.get(f"{ELASTICSEARCH_URL}/_cluster/health", auth=auth)
|
|
health = health_r.json() if health_r.status_code < 400 else {}
|
|
stats_r = await client.get(f"{ELASTICSEARCH_URL}/_cat/indices?format=json&bytes=b", auth=auth)
|
|
indices = stats_r.json() if stats_r.status_code < 400 else []
|
|
return {
|
|
"ok": True,
|
|
"url": ELASTICSEARCH_URL,
|
|
"cluster_name": info.get("cluster_name"),
|
|
"version": info.get("version", {}).get("number"),
|
|
"health": health.get("status", "unknown"),
|
|
"nodes": health.get("number_of_nodes"),
|
|
"indices_count": len(indices) if isinstance(indices, list) else 0,
|
|
"indices": [
|
|
{
|
|
"name": i.get("index"),
|
|
"docs": i.get("docs.count"),
|
|
"size": i.get("store.size"),
|
|
"health": i.get("health"),
|
|
}
|
|
for i in (indices[:50] if isinstance(indices, list) else [])
|
|
],
|
|
}
|
|
except Exception as exc:
|
|
return {"ok": False, "url": ELASTICSEARCH_URL, "error": str(exc)}
|
|
|
|
|
|
async def _probe_kibana() -> dict[str, Any]:
|
|
try:
|
|
async with httpx.AsyncClient(timeout=6.0, verify=False) as client:
|
|
r = await client.get(f"{KIBANA_URL}/api/status")
|
|
if r.status_code >= 400:
|
|
return {"ok": False, "status_code": r.status_code}
|
|
data = r.json()
|
|
overall = data.get("status", {}).get("overall", {})
|
|
return {
|
|
"ok": overall.get("level") in ("available", "green", "yellow"),
|
|
"url": KIBANA_URL,
|
|
"ui_url": KIBANA_URL,
|
|
"level": overall.get("level", "unknown"),
|
|
}
|
|
except Exception as exc:
|
|
return {"ok": False, "url": KIBANA_URL, "error": str(exc)}
|
|
|
|
|
|
@router.get("/health")
|
|
async def search_health():
|
|
es, kb = await _probe_es(), await _probe_kibana()
|
|
return {
|
|
"ok": es.get("ok") or kb.get("ok"),
|
|
"elasticsearch": es,
|
|
"kibana": kb,
|
|
}
|
|
|
|
|
|
@router.get("/elasticsearch")
|
|
async def get_elasticsearch():
|
|
result = await _probe_es()
|
|
if not result.get("ok"):
|
|
return JSONResponse(result, status_code=502)
|
|
return result
|
|
|
|
|
|
@router.get("/kibana")
|
|
async def get_kibana():
|
|
return await _probe_kibana()
|
|
|
|
|
|
@router.get("/elasticsearch/query")
|
|
async def es_search(q: str = Query(..., min_length=1), size: int = Query(10, le=50)):
|
|
if not ELASTIC_PASSWORD:
|
|
return JSONResponse({"ok": False, "error": "ELASTIC_PASSWORD not configured"}, status_code=503)
|
|
try:
|
|
async with httpx.AsyncClient(timeout=15.0, verify=False) as client:
|
|
r = await client.post(
|
|
f"{ELASTICSEARCH_URL}/_search",
|
|
auth=(ELASTIC_USER, ELASTIC_PASSWORD),
|
|
json={"query": {"query_string": {"query": q}}, "size": size},
|
|
)
|
|
if r.status_code >= 400:
|
|
return JSONResponse({"ok": False, "error": r.text[:300]}, status_code=r.status_code)
|
|
data = r.json()
|
|
hits = [
|
|
{
|
|
"index": h.get("_index"),
|
|
"id": h.get("_id"),
|
|
"score": h.get("_score"),
|
|
"source": h.get("_source"),
|
|
}
|
|
for h in data.get("hits", {}).get("hits", [])
|
|
]
|
|
return {"ok": True, "total": data.get("hits", {}).get("total", {}), "hits": hits}
|
|
except Exception as exc:
|
|
return JSONResponse({"ok": False, "error": str(exc)}, status_code=502)
|