Add source status API and enhance dashboard with data source checks

- Introduced a new API endpoint `/api/source-status/` to return the status of Prometheus and OpenStack data sources.
- Implemented lightweight health check functions for both Prometheus and OpenStack.
- Updated the dashboard template to display the status of data sources dynamically.
- Added tests for the new API endpoint to ensure correct functionality and response handling.
- Configured a cache timeout for source status checks to improve performance.
This commit is contained in:
2026-02-07 17:12:25 +03:00
parent 917a7758bc
commit fd03c22042
7 changed files with 207 additions and 3 deletions

View File

@@ -29,6 +29,10 @@
Save as PDF
</button>
<span id="regionBadge" class="badge badge-primary badge-lg">{{ region.name }}</span>
<div id="source-status" class="flex items-center gap-2 no-print" aria-label="Data source status">
<span id="source-status-prometheus" class="badge badge-ghost badge-sm" title="Prometheus">Prometheus: …</span>
<span id="source-status-openstack" class="badge badge-ghost badge-sm" title="OpenStack">OpenStack: …</span>
</div>
<label class="swap swap-rotate theme-toggle no-print">
<input type="checkbox" class="theme-controller" value="dark" />
<svg class="swap-off fill-current w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -71,6 +75,46 @@
});
</script>
<script src="{% static 'js/export-pdf.js' %}"></script>
<script>
(function() {
function updateSourceStatus(el, label, data) {
if (!el) return;
var status = data && data.status;
var msg = data && data.message;
var title = msg ? (label + ': ' + msg) : label;
el.setAttribute('title', title);
el.setAttribute('aria-label', title);
if (status === 'ok') {
el.textContent = label + ': OK';
el.classList.remove('badge-error', 'badge-warning');
el.classList.add('badge-success');
} else if (status === 'error') {
el.textContent = label + ': Error';
el.classList.remove('badge-success', 'badge-warning');
el.classList.add('badge-error');
} else if (status === 'mock') {
el.textContent = label + ': Mock';
el.classList.remove('badge-error', 'badge-success');
el.classList.add('badge-warning');
} else {
el.textContent = label + ': …';
el.classList.remove('badge-success', 'badge-error', 'badge-warning');
}
}
document.addEventListener('DOMContentLoaded', function() {
var promEl = document.getElementById('source-status-prometheus');
var osEl = document.getElementById('source-status-openstack');
if (!promEl || !osEl) return;
fetch('/api/source-status/').then(function(r) { return r.ok ? r.json() : {}; }).then(function(data) {
updateSourceStatus(promEl, 'Prometheus', data.prometheus);
updateSourceStatus(osEl, 'OpenStack', data.openstack);
}).catch(function() {
updateSourceStatus(promEl, 'Prometheus', { status: 'error', message: 'Failed to fetch status' });
updateSourceStatus(osEl, 'OpenStack', { status: 'error', message: 'Failed to fetch status' });
});
});
})();
</script>
{% block script %}
{% endblock %}
</body>