소스 검색

Gráfica de consumo

Celestino Rey 6 달 전
부모
커밋
600332521b
3개의 변경된 파일104개의 추가작업 그리고 251개의 파일을 삭제
  1. 64 31
      app/main.py
  2. 40 219
      static/dashboard.js
  3. 0 1
      static/index.html

+ 64 - 31
app/main.py

@@ -7,6 +7,7 @@ import subprocess
 import random
 import xml.etree.ElementTree as ET
 import socket
+import time
 from pydantic import BaseModel
 
 app = FastAPI(title="VirtManager API", description="API for managing virtual machines")
@@ -14,6 +15,7 @@ app = FastAPI(title="VirtManager API", description="API for managing virtual mac
 app.mount("/static", StaticFiles(directory="static"), name="static")
 
 ws_processes = {}
+stats_cache = {}  # Cache for CPU stats to calculate deltas
 
 def get_free_port():
     with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
@@ -139,47 +141,78 @@ async def get_vm_stats(vm_name: str):
                 "memory_percent": 0
             }
         
-        # Get CPU stats
+        # Get domain info (memory is in MiB)
+        info = domain.info()
+        memory_total = info[1]  # Maximum memory in MiB
+        cpu_count = info[3]  # Number of virtual CPUs
+        
+        # Get CPU stats - calculate real CPU percentage
+        cpu_percent = 0
         try:
             cpu_stats = domain.getCPUStats(True)
-            if cpu_stats:
-                # CPU time in nanoseconds
-                cpu_time = cpu_stats[0].get('cpu_time', 0)
-                cpu_percent = cpu_time / 1e9  # Convert to percentage approximation
-                cpu_percent = min(100, cpu_percent % 100)  # Keep between 0-100
-            else:
-                cpu_percent = 0
-        except:
+            if cpu_stats and len(cpu_stats) > 0:
+                current_cpu_time_ns = cpu_stats[0].get('cpu_time', 0)
+                current_time = time.time()
+                
+                # Initialize or update cache
+                if vm_name not in stats_cache:
+                    stats_cache[vm_name] = {
+                        'cpu_time_ns': current_cpu_time_ns,
+                        'time': current_time,
+                        'memory': 0
+                    }
+                    cpu_percent = 0
+                else:
+                    prev_data = stats_cache[vm_name]
+                    prev_cpu_time_ns = prev_data['cpu_time_ns']
+                    prev_time = prev_data['time']
+                    
+                    # Calculate deltas
+                    cpu_time_delta_ns = current_cpu_time_ns - prev_cpu_time_ns
+                    time_delta_seconds = current_time - prev_time
+                    
+                    if time_delta_seconds > 0:
+                        # Convert CPU time delta to percentage
+                        # cpu_count vCPUs * 100% each = total possible CPU seconds in time_delta
+                        max_cpu_ns = (time_delta_seconds * cpu_count) * 1e9
+                        if max_cpu_ns > 0:
+                            cpu_percent = min(100, (cpu_time_delta_ns / max_cpu_ns) * 100)
+                    
+                    # Update cache
+                    stats_cache[vm_name] = {
+                        'cpu_time_ns': current_cpu_time_ns,
+                        'time': current_time,
+                        'memory': 0
+                    }
+        except Exception as e:
             cpu_percent = 0
         
         # Get memory stats
+        memory_used = 0
+        memory_percent = 0
         try:
             memory_stats = domain.memoryStats()
-            # Memory stats returns values in KB
-            memory_used = memory_stats.get('actual', 0) // 1024  # Convert to MiB
-            memory_total = memory_stats.get('swap_in', 0)  # Total memory in MiB
-            
-            # Get from domain info as fallback
-            if memory_total == 0:
-                info = domain.info()
-                memory_total = info[1]  # Memory in MiB
-            
-            memory_percent = (memory_used / memory_total * 100) if memory_total > 0 else 0
-        except:
+            if memory_stats:
+                # Try 'actual' first (actual memory used in KB)
+                if 'actual' in memory_stats:
+                    memory_used_kb = memory_stats.get('actual', 0)
+                    memory_used = memory_used_kb // 1024  # Convert to MiB
+                # Fallback to 'unused' to calculate used memory
+                elif 'unused' in memory_stats:
+                    unused_kb = memory_stats.get('unused', 0)
+                    total_kb = memory_total * 1024
+                    memory_used_kb = total_kb - unused_kb
+                    memory_used = memory_used_kb // 1024
+                else:
+                    memory_used = 0
+                
+                # Calculate percentage
+                if memory_total > 0:
+                    memory_percent = (memory_used / memory_total) * 100
+        except Exception as e:
             memory_used = 0
-            memory_total = 0
             memory_percent = 0
         
-        # Get domain memory from config
-        try:
-            xml_desc = domain.XMLDesc()
-            root = ET.fromstring(xml_desc)
-            memory_elem = root.find("memory")
-            if memory_elem is not None:
-                memory_total = int(memory_elem.text) // 1024  # Convert from KB to MiB
-        except:
-            pass
-        
         return {
             "name": vm_name,
             "state": "running",

+ 40 - 219
static/dashboard.js

@@ -197,23 +197,29 @@ function displayVMDetails(vm, vmName) {
                         </div>
                     </div>
 
-                    <!-- Performance Charts -->
+                    <!-- Performance Indicators -->
                     ${vm.state === 'running' ? `
                     <div class="card">
                         <div class="card-header">
                             <h6 class="card-title mb-0"><i class="bi bi-graph-up"></i> Rendimiento</h6>
                         </div>
                         <div class="card-body">
-                            <div class="row">
-                                <div class="col-md-6">
-                                    <div style="position: relative; height: 250px;">
-                                        <canvas id="cpuChart"></canvas>
-                                    </div>
+                            <div class="mb-3">
+                                <div class="d-flex justify-content-between mb-1">
+                                    <strong><i class="bi bi-cpu"></i> CPU</strong>
+                                    <span id="cpuPercent">0%</span>
                                 </div>
-                                <div class="col-md-6">
-                                    <div style="position: relative; height: 250px;">
-                                        <canvas id="memoryChart"></canvas>
-                                    </div>
+                                <div class="progress" style="height: 25px;">
+                                    <div id="cpuBar" class="progress-bar bg-primary" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
+                                </div>
+                            </div>
+                            <div>
+                                <div class="d-flex justify-content-between mb-1">
+                                    <strong><i class="bi bi-memory"></i> Memoria</strong>
+                                    <span id="memPercent">0%</span>
+                                </div>
+                                <div class="progress" style="height: 25px;">
+                                    <div id="memBar" class="progress-bar bg-warning" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
                                 </div>
                             </div>
                         </div>
@@ -988,22 +994,15 @@ async function deleteSnapshot(vmName, snapshotName) {
     }
 }
 
-// Chart instances storage
-let cpuChart = null;
-let memoryChart = null;
+// Performance monitoring
 let statsInterval = null;
-const statsHistory = {
-    cpu: [],
-    memory: [],
-    maxPoints: 20
-};
 
-// Initialize and update performance charts
+// Initialize and update performance indicators
 async function initializePerformanceCharts(vmName) {
     // Clear any existing interval
     if (statsInterval) clearInterval(statsInterval);
     
-    // Initialize charts immediately
+    // Update immediately
     await updatePerformanceStats(vmName);
     
     // Update every 2 seconds
@@ -1021,44 +1020,28 @@ async function updatePerformanceStats(vmName) {
             return;
         }
         
-        const timestamp = new Date().toLocaleTimeString('es-ES', { 
-            hour: '2-digit', 
-            minute: '2-digit',
-            second: '2-digit'
-        });
-        
-        // Update history
-        statsHistory.cpu.push({
-            time: timestamp,
-            value: data.cpu_percent
-        });
-        statsHistory.memory.push({
-            time: timestamp,
-            value: data.memory_percent
-        });
-        
-        // Keep only last maxPoints
-        if (statsHistory.cpu.length > statsHistory.maxPoints) {
-            statsHistory.cpu.shift();
-            statsHistory.memory.shift();
+        // Update CPU bar
+        const cpuPercent = data.cpu_percent;
+        const cpuBar = document.getElementById('cpuBar');
+        const cpuPercentEl = document.getElementById('cpuPercent');
+        if (cpuBar) {
+            cpuBar.style.width = cpuPercent + '%';
+            cpuBar.setAttribute('aria-valuenow', cpuPercent);
         }
-        
-        // Update CPU Chart
-        if (cpuChart) {
-            cpuChart.data.labels = statsHistory.cpu.map(s => s.time);
-            cpuChart.data.datasets[0].data = statsHistory.cpu.map(s => s.value);
-            cpuChart.update('none');
-        } else {
-            createCPUChart();
+        if (cpuPercentEl) {
+            cpuPercentEl.textContent = cpuPercent.toFixed(1) + '%';
         }
         
-        // Update Memory Chart
-        if (memoryChart) {
-            memoryChart.data.labels = statsHistory.memory.map(s => s.time);
-            memoryChart.data.datasets[0].data = statsHistory.memory.map(s => s.value);
-            memoryChart.update('none');
-        } else {
-            createMemoryChart();
+        // Update Memory bar
+        const memPercent = data.memory_percent;
+        const memBar = document.getElementById('memBar');
+        const memPercentEl = document.getElementById('memPercent');
+        if (memBar) {
+            memBar.style.width = memPercent + '%';
+            memBar.setAttribute('aria-valuenow', memPercent);
+        }
+        if (memPercentEl) {
+            memPercentEl.textContent = memPercent.toFixed(1) + '%';
         }
         
     } catch (error) {
@@ -1066,176 +1049,14 @@ async function updatePerformanceStats(vmName) {
     }
 }
 
-// Create CPU chart
-function createCPUChart() {
-    const ctx = document.getElementById('cpuChart');
-    if (!ctx) return;
-    
-    // Destroy existing chart if any
-    if (cpuChart) cpuChart.destroy();
-    
-    cpuChart = new Chart(ctx, {
-        type: 'line',
-        data: {
-            labels: statsHistory.cpu.map(s => s.time),
-            datasets: [{
-                label: 'CPU (%)',
-                data: statsHistory.cpu.map(s => s.value),
-                borderColor: '#667eea',
-                backgroundColor: 'rgba(102, 126, 234, 0.1)',
-                tension: 0.4,
-                fill: true,
-                borderWidth: 2,
-                pointRadius: 4,
-                pointBackgroundColor: '#667eea',
-                pointBorderColor: '#fff',
-                pointBorderWidth: 2,
-                pointHoverRadius: 6
-            }],
-        },
-        options: {
-            responsive: true,
-            maintainAspectRatio: false,
-            plugins: {
-                legend: {
-                    display: true,
-                    labels: {
-                        font: { size: 12 },
-                        padding: 15,
-                        color: '#666'
-                    }
-                },
-                tooltip: {
-                    backgroundColor: 'rgba(0,0,0,0.8)',
-                    padding: 12,
-                    titleFont: { size: 12 },
-                    bodyFont: { size: 12 },
-                    callbacks: {
-                        label: function(context) {
-                            return context.dataset.label + ': ' + context.parsed.y.toFixed(2) + '%';
-                        }
-                    }
-                }
-            },
-            scales: {
-                y: {
-                    beginAtZero: true,
-                    max: 100,
-                    ticks: {
-                        stepSize: 25,
-                        callback: function(value) {
-                            return value + '%';
-                        }
-                    },
-                    grid: {
-                        color: 'rgba(0,0,0,0.05)'
-                    }
-                },
-                x: {
-                    grid: {
-                        color: 'rgba(0,0,0,0.05)'
-                    }
-                }
-            }
-        }
-    });
-}
-
-// Create Memory chart
-function createMemoryChart() {
-    const ctx = document.getElementById('memoryChart');
-    if (!ctx) return;
-    
-    // Destroy existing chart if any
-    if (memoryChart) memoryChart.destroy();
-    
-    memoryChart = new Chart(ctx, {
-        type: 'line',
-        data: {
-            labels: statsHistory.memory.map(s => s.time),
-            datasets: [{
-                label: 'Memoria (%)',
-                data: statsHistory.memory.map(s => s.value),
-                borderColor: '#764ba2',
-                backgroundColor: 'rgba(118, 75, 162, 0.1)',
-                tension: 0.4,
-                fill: true,
-                borderWidth: 2,
-                pointRadius: 4,
-                pointBackgroundColor: '#764ba2',
-                pointBorderColor: '#fff',
-                pointBorderWidth: 2,
-                pointHoverRadius: 6
-            }],
-        },
-        options: {
-            responsive: true,
-            maintainAspectRatio: false,
-            plugins: {
-                legend: {
-                    display: true,
-                    labels: {
-                        font: { size: 12 },
-                        padding: 15,
-                        color: '#666'
-                    }
-                },
-                tooltip: {
-                    backgroundColor: 'rgba(0,0,0,0.8)',
-                    padding: 12,
-                    titleFont: { size: 12 },
-                    bodyFont: { size: 12 },
-                    callbacks: {
-                        label: function(context) {
-                            return context.dataset.label + ': ' + context.parsed.y.toFixed(2) + '%';
-                        }
-                    }
-                }
-            },
-            scales: {
-                y: {
-                    beginAtZero: true,
-                    max: 100,
-                    ticks: {
-                        stepSize: 25,
-                        callback: function(value) {
-                            return value + '%';
-                        }
-                    },
-                    grid: {
-                        color: 'rgba(0,0,0,0.05)'
-                    }
-                },
-                x: {
-                    grid: {
-                        color: 'rgba(0,0,0,0.05)'
-                    }
-                }
-            }
-        }
-    });
-}
 
-// Clean up charts when switching VMs
+// Clean up performance monitoring when switching VMs
 function cleanupCharts() {
     if (statsInterval) {
         clearInterval(statsInterval);
         statsInterval = null;
     }
-    
-    if (cpuChart) {
-        cpuChart.destroy();
-        cpuChart = null;
-    }
-    
-    if (memoryChart) {
-        memoryChart.destroy();
-        memoryChart = null;
-    }
-    
-    // Reset history
-    statsHistory.cpu = [];
-    statsHistory.memory = [];
 }
 
 
+

+ 0 - 1
static/index.html

@@ -6,7 +6,6 @@
     <title>VirtManager Dashboard</title>
     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
     <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
-    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
     <style>
         * {
             margin: 0;