-
Current CPU Distribution
+
CPU Distribution (Current vs Projected)
-
+
-
-
-
-
-
Projected CPU Distribution
-
-
-
-
-
-
@@ -490,8 +469,7 @@
document.getElementById('previewStrategy').textContent = option.dataset.strategy || 'Balanced';
});
- let cpuHostChart = null;
- let cpuProjectedChart = null;
+ let cpuDistributionChart = null;
function setStat(key, text) {
var el = document.querySelector('[data-stats="' + key + '"]');
@@ -660,29 +638,20 @@
migrationCount.textContent = `${data.migrations.length} action${data.migrations.length !== 1 ? 's' : ''}`;
}
- // Update CPU charts
+ // Update CPU chart (combined current vs projected)
function updateCPUCharts(auditId) {
const data = auditData[auditId];
if (!data || !data.hostData) return;
- const currentCtx = document.getElementById('cpuHostChart').getContext('2d');
- const projectedCtx = document.getElementById('cpuProjectedChart').getContext('2d');
+ const ctx = document.getElementById('cpuDistributionChart').getContext('2d');
- // Calculate statistics
const currentStats = calculateStats(data.hostData.current);
- const projectedStats = calculateStats(data.hostData.projected);
- // Update stats displays
document.getElementById('currentCpuMean').textContent = currentStats.mean.toFixed(1);
- document.getElementById('projectedCpuMean').textContent = projectedStats.mean.toFixed(1);
- document.getElementById('projectedCpuStd').textContent = (currentStats.std * 0.5).toFixed(1);
document.getElementById('currentCpuStd').textContent = (currentStats.std * 0.5).toFixed(1);
- // Destroy existing charts
- if (cpuHostChart) cpuHostChart.destroy();
- if (cpuProjectedChart) cpuProjectedChart.destroy();
+ if (cpuDistributionChart) cpuDistributionChart.destroy();
- // Chart colors
const colors = {
primary: getCSSVar('--color-primary'),
secondary: getCSSVar('--color-secondary'),
@@ -694,28 +663,90 @@
error: getCSSVar('--color-error')
};
- // Create current CPU chart
- cpuHostChart = new Chart(currentCtx, {
+ cpuDistributionChart = new Chart(ctx, {
type: 'bar',
data: {
labels: data.hostData.labels,
- datasets: [{
- label: 'CPU %',
- data: data.hostData.current,
- backgroundColor: colors.info + '40',
- borderColor: colors.info,
- borderWidth: 1,
- borderRadius: 3
- }]
+ datasets: [
+ {
+ label: 'Current',
+ data: data.hostData.current.slice(),
+ backgroundColor: colors.info + '40',
+ borderColor: colors.info,
+ borderWidth: 1,
+ borderRadius: 3
+ },
+ {
+ label: 'Projected',
+ data: data.hostData.projected.slice(),
+ backgroundColor: colors.warning + '40',
+ borderColor: colors.warning,
+ borderWidth: 1,
+ borderRadius: 3
+ }
+ ]
},
options: {
responsive: true,
maintainAspectRatio: false,
+ animation: {
+ onComplete: function() {
+ var chart = this;
+ if (typeof chart.getDatasetMeta !== 'function') chart = chart.chart;
+ if (!chart || chart._hidingDataset === undefined) return;
+ var i = chart._hidingDataset;
+ chart.getDatasetMeta(i).hidden = true;
+ chart.data.datasets[i].data = chart._cpuOriginalData[i].slice();
+ delete chart._hidingDataset;
+ chart.update('none');
+ }
+ },
plugins: {
- legend: { display: false },
+ legend: {
+ display: true,
+ position: 'top',
+ align: 'center',
+ onClick: function(e, legendItem, legend) {
+ const i = legendItem.datasetIndex;
+ const chart = legend.chart;
+ const len = chart.data.labels.length;
+ if (chart.isDatasetVisible(i)) {
+ chart._hidingDataset = i;
+ chart.data.datasets[i].data = Array(len).fill(0);
+ chart.update();
+ } else {
+ chart.data.datasets[i].data = Array(len).fill(0);
+ chart.show(i);
+ chart.update('none');
+ chart.data.datasets[i].data = chart._cpuOriginalData[i].slice();
+ chart.update();
+ }
+ },
+ labels: {
+ usePointStyle: true,
+ pointStyle: 'rect',
+ boxWidth: 14,
+ boxHeight: 14,
+ padding: 12,
+ color: 'inherit',
+ generateLabels: function(chart) {
+ const datasets = chart.data.datasets;
+ return datasets.map(function(ds, i) {
+ return {
+ text: ds.label,
+ fillStyle: ds.borderColor,
+ strokeStyle: ds.borderColor,
+ lineWidth: 1,
+ hidden: !chart.isDatasetVisible(i),
+ datasetIndex: i
+ };
+ });
+ }
+ }
+ },
tooltip: {
callbacks: {
- label: (ctx) => `${Number(ctx.parsed.y).toFixed(2)}% CPU`
+ label: (ctx) => `${ctx.dataset.label}: ${Number(ctx.parsed.y).toFixed(2)}% CPU`
}
},
annotation: {
@@ -747,80 +778,6 @@
}
}
},
- scales: {
- y: {
- beginAtZero: true,
- max: 100,
- grid: {
- drawBorder: false,
- },
- ticks: {
- callback: value => value + '%'
- }
- },
- x: {
- grid: { display: false },
- ticks: {
- maxRotation: 45
- }
- }
- }
- }
- });
-
- // Create projected CPU chart
- cpuProjectedChart = new Chart(projectedCtx, {
- type: 'bar',
- data: {
- labels: data.hostData.labels,
- datasets: [{
- label: 'Projected CPU %',
- data: data.hostData.projected,
- backgroundColor: colors.warning + '40',
- borderColor: colors.warning,
- borderWidth: 1,
- borderRadius: 3
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: { display: false },
- tooltip: {
- callbacks: {
- label: (ctx) => `${Number(ctx.parsed.y).toFixed(2)}% CPU`
- }
- },
- annotation: {
- annotations: {
- MeanLine: {
- type: 'line',
- yMin: projectedStats.mean.toFixed(1),
- yMax: projectedStats.mean.toFixed(1),
- borderColor: colors.success,
- borderWidth: 2,
- borderDash: []
- },
- upperStdLine: {
- type: 'line',
- yMin: (currentStats.mean + currentStats.std * 0.5).toFixed(1),
- yMax: (currentStats.mean + currentStats.std * 0.5).toFixed(1),
- borderColor: colors.error,
- borderWidth: 1,
- borderDash: [5, 5]
- },
- lowerStdLine: {
- type: 'line',
- yMin: currentStats.mean > currentStats.std * 0.5 ? (currentStats.mean - currentStats.std * 0.5).toFixed(1) : 0,
- yMax: currentStats.mean > currentStats.std * 0.5 ? (currentStats.mean - currentStats.std * 0.5).toFixed(1) : 0,
- borderColor: colors.error,
- borderWidth: 1,
- borderDash: [5, 5]
- }
- }
- }
- },
scales: {
y: {
beginAtZero: true,
@@ -833,12 +790,18 @@
x: {
grid: { display: false },
ticks: {
- maxRotation: 45
- }
+ display: false
+ },
+ barPercentage: 1,
+ categoryPercentage: 0.85
}
}
}
});
+ cpuDistributionChart._cpuOriginalData = [
+ data.hostData.current.slice(),
+ data.hostData.projected.slice()
+ ];
}
// Utility functions