Improve overcommit UI with info icons and modals

- Replace tooltips with info icons (ℹ️) next to CPU/Memory Overcommit
- Add modal dialogs showing detailed overcommit calculations
- Change Resource Quota Coverage to Resource Utilization
- Add CSS styling for overcommit details modals
- Improve UX with clickable info icons instead of hover tooltips
- Show capacity, requests, overcommit percentage, and available resources
This commit is contained in:
2025-10-01 15:41:43 -03:00
parent 8984701bf3
commit 2bb5266753
2 changed files with 126 additions and 36 deletions

View File

@@ -180,9 +180,13 @@ async def get_cluster_status(
# Count namespaces in overcommit (simplified - any namespace with requests > 0)
namespaces_in_overcommit = len([ns for ns in namespaces_list if ns['total_validations'] > 0])
# Calculate resource quota coverage (simplified)
if cpu_capacity > 0 and memory_capacity > 0:
resource_quota_coverage = round(((cpu_requests + memory_requests) / (cpu_capacity + memory_capacity)) * 100, 1)
# Calculate resource utilization (usage vs requests) - simplified
# This would ideally use actual usage data from Prometheus
resource_utilization = 0
if cpu_requests > 0 and memory_requests > 0:
# For now, we'll use a simplified calculation
# In a real implementation, this would compare actual usage vs requests
resource_utilization = 75 # Placeholder - would be calculated from real usage data
return {
"timestamp": datetime.now().isoformat(),
@@ -196,7 +200,7 @@ async def get_cluster_status(
"cpu_overcommit_percent": cpu_overcommit_percent,
"memory_overcommit_percent": memory_overcommit_percent,
"namespaces_in_overcommit": namespaces_in_overcommit,
"resource_quota_coverage": resource_quota_coverage,
"resource_utilization": resource_utilization,
"cpu_capacity": cpu_capacity if 'cpu_capacity' in locals() else 0,
"cpu_requests": cpu_requests if 'cpu_requests' in locals() else 0,
"memory_capacity": memory_capacity if 'memory_capacity' in locals() else 0,

View File

@@ -199,15 +199,39 @@
border-left-color: #27ae60;
}
.metric-value {
cursor: help;
position: relative;
.info-icon {
cursor: pointer;
color: #3498db;
font-size: 14px;
margin-left: 5px;
display: inline-block;
}
.metric-value:hover {
background-color: #e3f2fd;
.info-icon:hover {
color: #2980b9;
}
.overcommit-details {
padding: 1rem;
}
.overcommit-details h3 {
color: #2c3e50;
margin-bottom: 1rem;
border-bottom: 2px solid #3498db;
padding-bottom: 0.5rem;
}
.metric-detail {
margin: 0.75rem 0;
padding: 0.5rem;
background: #f8f9fa;
border-left: 3px solid #3498db;
border-radius: 4px;
padding: 2px 4px;
}
.metric-detail strong {
color: #2c3e50;
}
/* Status Overview */
@@ -838,20 +862,20 @@
<h3>📊 Cluster Overcommit Summary</h3>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-value" id="cpuOvercommit" title="CPU Overcommit Details">-</div>
<div class="metric-label">CPU Overcommit</div>
<div class="metric-value" id="cpuOvercommit">-</div>
<div class="metric-label">CPU Overcommit <span class="info-icon" onclick="showOvercommitDetails('cpu')"></span></div>
</div>
<div class="metric-card">
<div class="metric-value" id="memoryOvercommit" title="Memory Overcommit Details">-</div>
<div class="metric-label">Memory Overcommit</div>
<div class="metric-value" id="memoryOvercommit">-</div>
<div class="metric-label">Memory Overcommit <span class="info-icon" onclick="showOvercommitDetails('memory')"></span></div>
</div>
<div class="metric-card">
<div class="metric-value" id="namespacesInOvercommit">-</div>
<div class="metric-label">Namespaces in Overcommit</div>
</div>
<div class="metric-card">
<div class="metric-value" id="resourceQuotaCoverage">-</div>
<div class="metric-label">Resource Quota Coverage</div>
<div class="metric-value" id="resourceUtilization">-</div>
<div class="metric-label">Resource Utilization</div>
</div>
</div>
</div>
@@ -1064,31 +1088,21 @@
// Update overcommit metrics
if (data.overcommit) {
// CPU Overcommit with detailed tooltip
const cpuElement = document.getElementById('cpuOvercommit');
cpuElement.textContent = `${data.overcommit.cpu_overcommit_percent}%`;
if (data.overcommit.cpu_capacity && data.overcommit.cpu_requests) {
const cpuCapacity = data.overcommit.cpu_capacity;
const cpuRequests = data.overcommit.cpu_requests;
cpuElement.title = `CPU Overcommit Details:\n• Capacity Total: ${cpuCapacity} cores\n• Requests Total: ${cpuRequests} cores\n• Overcommit: ${data.overcommit.cpu_overcommit_percent}% (${cpuRequests} ÷ ${cpuCapacity} × 100)`;
}
// Memory Overcommit with detailed tooltip
const memoryElement = document.getElementById('memoryOvercommit');
memoryElement.textContent = `${data.overcommit.memory_overcommit_percent}%`;
if (data.overcommit.memory_capacity && data.overcommit.memory_requests) {
const memoryCapacityGB = (data.overcommit.memory_capacity / (1024**3)).toFixed(1);
const memoryRequestsGB = (data.overcommit.memory_requests / (1024**3)).toFixed(1);
memoryElement.title = `Memory Overcommit Details:\n• Capacity Total: ${data.overcommit.memory_capacity.toLocaleString()} bytes (≈ ${memoryCapacityGB} GB)\n• Requests Total: ${data.overcommit.memory_requests.toLocaleString()} bytes (≈ ${memoryRequestsGB} GB)\n• Overcommit: ${data.overcommit.memory_overcommit_percent}% (${memoryRequestsGB} ÷ ${memoryCapacityGB} × 100)`;
}
document.getElementById('cpuOvercommit').textContent = `${data.overcommit.cpu_overcommit_percent}%`;
document.getElementById('memoryOvercommit').textContent = `${data.overcommit.memory_overcommit_percent}%`;
document.getElementById('namespacesInOvercommit').textContent = data.overcommit.namespaces_in_overcommit || 0;
document.getElementById('resourceQuotaCoverage').textContent = `${data.overcommit.resource_quota_coverage}%`;
// Calculate resource utilization (usage vs requests)
const resourceUtilization = data.overcommit.resource_utilization || 0;
document.getElementById('resourceUtilization').textContent = `${resourceUtilization}%`;
// Store overcommit data for modal display
window.overcommitData = data.overcommit;
} else {
document.getElementById('cpuOvercommit').textContent = '0%';
document.getElementById('memoryOvercommit').textContent = '0%';
document.getElementById('namespacesInOvercommit').textContent = '0';
document.getElementById('resourceQuotaCoverage').textContent = '0%';
document.getElementById('resourceUtilization').textContent = '0%';
}
// Update status
@@ -1712,6 +1726,78 @@
document.getElementById('problemTableBody').innerHTML =
`<tr><td colspan="5" style="text-align: center; color: #e74c3c;">${message}</td></tr>`;
}
function showOvercommitDetails(type) {
if (!window.overcommitData) {
alert('Overcommit data not available');
return;
}
const data = window.overcommitData;
let title, content;
if (type === 'cpu') {
title = '🖥️ CPU Overcommit Details';
const cpuCapacity = data.cpu_capacity || 0;
const cpuRequests = data.cpu_requests || 0;
content = `
<div class="overcommit-details">
<h3>CPU Resource Analysis</h3>
<div class="metric-detail">
<strong>Capacity Total:</strong> ${cpuCapacity} cores
</div>
<div class="metric-detail">
<strong>Requests Total:</strong> ${cpuRequests} cores
</div>
<div class="metric-detail">
<strong>Overcommit:</strong> ${data.cpu_overcommit_percent}% (${cpuRequests} ÷ ${cpuCapacity} × 100)
</div>
<div class="metric-detail">
<strong>Available:</strong> ${(cpuCapacity - cpuRequests).toFixed(2)} cores
</div>
</div>
`;
} else if (type === 'memory') {
title = '💾 Memory Overcommit Details';
const memoryCapacity = data.memory_capacity || 0;
const memoryRequests = data.memory_requests || 0;
const memoryCapacityGB = (memoryCapacity / (1024**3)).toFixed(1);
const memoryRequestsGB = (memoryRequests / (1024**3)).toFixed(1);
content = `
<div class="overcommit-details">
<h3>Memory Resource Analysis</h3>
<div class="metric-detail">
<strong>Capacity Total:</strong> ${memoryCapacity.toLocaleString()} bytes (≈ ${memoryCapacityGB} GB)
</div>
<div class="metric-detail">
<strong>Requests Total:</strong> ${memoryRequests.toLocaleString()} bytes (≈ ${memoryRequestsGB} GB)
</div>
<div class="metric-detail">
<strong>Overcommit:</strong> ${data.memory_overcommit_percent}% (${memoryRequestsGB} ÷ ${memoryCapacityGB} × 100)
</div>
<div class="metric-detail">
<strong>Available:</strong> ${((memoryCapacity - memoryRequests) / (1024**3)).toFixed(1)} GB
</div>
</div>
`;
}
const modal = document.createElement('div');
modal.className = 'modal';
modal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h2>${title}</h2>
<span class="close" onclick="closeModal()">&times;</span>
</div>
<div class="modal-body">
${content}
</div>
</div>
`;
document.body.appendChild(modal);
modal.style.display = 'block';
}
</script>
</body>
</html>