Files
watcher-visio/dashboard/views.py
Nikolay Tatarinov 6a27fecb13
All checks were successful
CI / ci (push) Successful in 14s
Add dashboard statistics module and refactor views for metrics handling
- 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.
2026-02-07 18:39:28 +03:00

144 lines
4.8 KiB
Python

import json
from django.conf import settings
from django.core.cache import cache
from django.http import JsonResponse
from django.shortcuts import render
from dashboard.mock_data import get_mock_context
from dashboard.openstack_utils.audits import get_audits, get_current_cluster_cpu
from dashboard.openstack_utils.connect import check_openstack, get_connection
from dashboard.openstack_utils.flavor import get_flavor_list
from dashboard.prometheus_utils.query import check_prometheus, fetch_dashboard_metrics
from dashboard.stats import (
CACHE_KEY_AUDITS,
CACHE_KEY_CURRENT_CLUSTER,
CACHE_KEY_SOURCE_STATUS,
CACHE_KEY_STATS,
EMPTY_FLAVORS,
build_stats,
)
def _empty_metrics():
"""Metrics dict with zero/default values for skeleton context."""
return {
"hosts_total": 0,
"pcpu_total": 0,
"pcpu_usage": 0,
"vcpu_allocated": 0,
"vcpu_overcommit_max": 0,
"pram_total": 0,
"pram_usage": 0,
"vram_allocated": 0,
"vram_overcommit_max": 0,
"vm_count": 0,
"vm_active": 0,
}
def collect_context():
connection = get_connection()
region_name = connection._compute_region
flavors = get_flavor_list(connection=connection)
audits = get_audits(connection=connection)
metrics = fetch_dashboard_metrics()
context = build_stats(metrics, region_name, flavors)
context["audits"] = audits
current_cluster = get_current_cluster_cpu(connection)
context["current_cluster"] = {
"host_labels": json.dumps(current_cluster["host_labels"]),
"cpu_current": json.dumps(current_cluster["cpu_current"]),
}
for audit in context["audits"]:
audit["migrations"] = json.dumps(audit["migrations"])
audit["host_labels"] = json.dumps(audit["host_labels"])
audit["cpu_current"] = json.dumps(audit["cpu_current"])
audit["cpu_projected"] = json.dumps(audit["cpu_projected"])
return context
def collect_stats():
"""Build stats dict: region, pcpu, pram, vcpu, vram, vm, flavors (no audits)."""
connection = get_connection()
region_name = connection._compute_region
flavors = get_flavor_list(connection=connection)
metrics = fetch_dashboard_metrics()
return build_stats(metrics, region_name, flavors)
def collect_audits():
"""Build audits list with serialized fields for frontend."""
connection = get_connection()
audits = get_audits(connection=connection)
for audit in audits:
audit["migrations"] = json.dumps(audit["migrations"])
audit["host_labels"] = json.dumps(audit["host_labels"])
audit["cpu_current"] = json.dumps(audit["cpu_current"])
audit["cpu_projected"] = json.dumps(audit["cpu_projected"])
return audits
def _skeleton_context():
"""Minimal context for skeleton-only index render."""
context = build_stats(_empty_metrics(), "", EMPTY_FLAVORS)
context["skeleton"] = True
context["audits"] = []
context["current_cluster"] = {
"host_labels": "[]",
"cpu_current": "[]",
}
return context
def index(request):
if getattr(settings, "USE_MOCK_DATA", False):
context = get_mock_context()
return render(request, "index.html", context)
context = _skeleton_context()
return render(request, "index.html", context)
def api_stats(request):
cache_ttl = getattr(settings, "DASHBOARD_CACHE_TTL", 120)
data = cache.get(CACHE_KEY_STATS)
if data is None:
data = collect_stats()
cache.set(CACHE_KEY_STATS, data, timeout=cache_ttl)
return JsonResponse(data)
def api_audits(request):
cache_ttl = getattr(settings, "DASHBOARD_CACHE_TTL", 120)
audits = cache.get(CACHE_KEY_AUDITS)
current_cluster = cache.get(CACHE_KEY_CURRENT_CLUSTER)
if audits is None:
audits = collect_audits()
cache.set(CACHE_KEY_AUDITS, audits, timeout=cache_ttl)
if current_cluster is None:
connection = get_connection()
current_cluster = get_current_cluster_cpu(connection)
cache.set(CACHE_KEY_CURRENT_CLUSTER, current_cluster, timeout=cache_ttl)
return JsonResponse({"audits": audits, "current_cluster": current_cluster})
def api_source_status(request):
"""Return status of Prometheus and OpenStack data sources (ok / error / mock)."""
if getattr(settings, "USE_MOCK_DATA", False):
return JsonResponse(
{
"prometheus": {"status": "mock"},
"openstack": {"status": "mock"},
}
)
cache_ttl = getattr(settings, "SOURCE_STATUS_CACHE_TTL", 30)
data = cache.get(CACHE_KEY_SOURCE_STATUS)
if data is None:
data = {
"prometheus": check_prometheus(),
"openstack": check_openstack(),
}
cache.set(CACHE_KEY_SOURCE_STATUS, data, timeout=cache_ttl)
return JsonResponse(data)