Add dashboard statistics module and refactor views for metrics handling
All checks were successful
CI / ci (push) Successful in 14s

- Introduced a new `stats.py` module to encapsulate dashboard statistics building and cache key constants.
- Refactored `views.py` to utilize the new `build_stats` function for constructing metrics context, improving code organization and readability.
- Updated Prometheus query handling to streamline metrics fetching with a new `fetch_dashboard_metrics` function.
- Enhanced test cases to reflect changes in metrics fetching and context building, ensuring accurate functionality.
- Added new HTML templates for displaying detailed resource allocation and flavor statistics on the dashboard.
This commit is contained in:
2026-02-07 18:39:28 +03:00
parent 9fa0a78eb6
commit 6a27fecb13
11 changed files with 925 additions and 1083 deletions

View File

@@ -1,9 +1,37 @@
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
from watcher_visio.settings import PROMETHEUS_URL
# Timeout for lightweight health check (seconds)
CHECK_TIMEOUT = 5
# Dashboard Prometheus queries (query_key -> query string), run in parallel
DASHBOARD_QUERIES = {
"hosts_total": "count(node_exporter_build_info{job='node_exporter_compute'})",
"pcpu_total": (
"sum(count(node_cpu_seconds_total{job='node_exporter_compute', mode='idle'}) "
"without (cpu,mode))"
),
"pcpu_usage": "sum(node_load5{job='node_exporter_compute'})",
"vcpu_allocated": "sum(libvirt_domain_info_virtual_cpus)",
"vcpu_overcommit_max": (
"avg(openstack_placement_resource_allocation_ratio{resourcetype='VCPU'})"
),
"pram_total": "sum(node_memory_MemTotal_bytes{job='node_exporter_compute'})",
"pram_usage": "sum(node_memory_Active_bytes{job='node_exporter_compute'})",
"vram_allocated": "sum(libvirt_domain_info_maximum_memory_bytes)",
"vram_overcommit_max": (
"avg(avg_over_time("
"openstack_placement_resource_allocation_ratio{resourcetype='MEMORY_MB'}[5m]))"
),
"vm_count": "sum(libvirt_domain_state_code)",
"vm_active": "sum(libvirt_domain_state_code{stateDesc='the domain is running'})",
}
# Keys that should be parsed as float (rest as int)
DASHBOARD_FLOAT_KEYS = frozenset(("pcpu_usage", "vcpu_overcommit_max", "vram_overcommit_max"))
def check_prometheus() -> dict:
"""
@@ -36,3 +64,23 @@ def query_prometheus(query: str) -> str | list[str]:
return result
else:
return result[0]["value"][1]
def fetch_dashboard_metrics() -> dict:
"""Run all dashboard Prometheus queries in parallel and return a dict of name -> value."""
result = {}
with ThreadPoolExecutor(max_workers=len(DASHBOARD_QUERIES)) as executor:
future_to_key = {
executor.submit(query_prometheus, query=q): key for key, q in DASHBOARD_QUERIES.items()
}
for future in as_completed(future_to_key):
key = future_to_key[future]
try:
raw = future.result()
if key in DASHBOARD_FLOAT_KEYS:
result[key] = float(raw)
else:
result[key] = int(raw)
except (ValueError, TypeError):
result[key] = float(0) if key in DASHBOARD_FLOAT_KEYS else 0
return result