272 lines
14 KiB
PHP
272 lines
14 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
|
|
<!-- Page Title -->
|
|
<title>Web Infra Reports</title>
|
|
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
|
|
|
|
<!-- JQuery -->
|
|
<script src="/js/jquery-3.6.1.min.js"></script>
|
|
<!-- Bootstrap -->
|
|
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
|
<link rel="stylesheet" href="/css/bootstrap-icons/bootstrap-icons.css">
|
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
|
<!-- Bootstrap-tables -->
|
|
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
|
|
<script src="/js/bootstrap-table.min.js"></script>
|
|
<script src="/js/bootstrap-table-fr-FR.min.js"></script>
|
|
<!-- Chart -->
|
|
<script src="/js/chart.min.js"></script>
|
|
<script src="/js/apexcharts.min.js"></script>
|
|
<!-- Gauge -->
|
|
<link rel="stylesheet" href="/css/Gauge.css">
|
|
<script src="/js/jquery.AshAlom.gaugeMeter-2.0.0.min.js"></script>
|
|
|
|
</head>
|
|
|
|
<body class="bg-light text-dark">
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/include/all.php"; ?> <!-- Database connexion -->
|
|
<div class="container-fluid">
|
|
<div class="row flex-nowrap">
|
|
<!-- Left NAVBAR -->
|
|
<div class="col-auto col-md-2 col-xl-2 px-sm-2 px-0 bg-dark vh-100 position-sticky top-0" style="-ms-flex: 0 0 230px;flex: 0 0 230px;">
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/navbar.html"; ?> <!-- Left Navbar -->
|
|
</div>
|
|
<!-- Display -->
|
|
<div class="col py-3">
|
|
<!-- Page Title -->
|
|
<h1><span class="badge text-bg-secondary font-weight-bold" style="width:100%;">Hyper-V Clusters</span></h1>
|
|
<div class="container-fluid" style="zoom:90%">
|
|
<div class="row">
|
|
<?php
|
|
// =================================================================
|
|
// STEP 1: Fetch all required data with minimal database queries
|
|
// =================================================================
|
|
|
|
// Query 1: Get aggregated data for all clusters at once.
|
|
$cluster_query = "
|
|
SELECT
|
|
cluster,
|
|
COUNT(node) as node_count,
|
|
MIN(node_ram) as min_node_ram,
|
|
SUM(TRY_CAST(vm_count AS INT)) as total_vms,
|
|
SUM(TRY_CAST(vm_memory AS INT)) as total_vm_memory,
|
|
SUM(TRY_CAST(io_disk AS FLOAT)) as total_io_disk,
|
|
STRING_AGG(CAST(csvs AS VARCHAR(MAX)), '|') as all_csvs
|
|
FROM
|
|
cmdb_HyperV_Hosts
|
|
WHERE
|
|
cluster <> ''
|
|
AND cluster NOT LIKE '%-WM%'
|
|
AND cluster NOT LIKE '%-C1MAS%'
|
|
AND cluster NOT LIKE '%-vrs%'
|
|
AND (cluster LIKE 'DUN%' OR cluster LIKE 'DMV%')
|
|
GROUP BY
|
|
cluster
|
|
ORDER BY
|
|
cluster
|
|
";
|
|
$all_clusters_data = Invoke_Infra($cluster_query);
|
|
|
|
// Query 2: Get VM distribution for all relevant clusters at once.
|
|
$vm_repart_query = "
|
|
SELECT
|
|
v.Cluster,
|
|
v.Owner,
|
|
count(v.Owner) as vm_count
|
|
FROM
|
|
cmdb_vms v
|
|
INNER JOIN cmdb_HyperV_Hosts h ON v.Cluster = h.cluster
|
|
WHERE
|
|
v.decomtime IS NULL
|
|
AND h.cluster <> ''
|
|
AND h.cluster NOT LIKE '%-WM%'
|
|
AND h.cluster NOT LIKE '%-C1MAS%'
|
|
AND h.cluster NOT LIKE '%-vrs%'
|
|
AND (h.cluster LIKE 'DUN%' OR h.cluster LIKE 'DMV%')
|
|
GROUP BY
|
|
v.Cluster, v.Owner
|
|
ORDER BY
|
|
v.Cluster, v.Owner
|
|
";
|
|
$raw_vm_distribution = Invoke_Infra($vm_repart_query);
|
|
|
|
// Process the VM distribution data into an easy-to-use associative array
|
|
$vm_distribution = [];
|
|
if (is_array($raw_vm_distribution)) {
|
|
foreach ($raw_vm_distribution as $repart) {
|
|
$vm_distribution[$repart['Cluster']][] = [
|
|
'owner' => $repart['Owner'],
|
|
'vm_count' => $repart['vm_count']
|
|
];
|
|
}
|
|
}
|
|
// =================================================================
|
|
// STEP 2: Loop through the pre-fetched data and display it
|
|
// =================================================================
|
|
$total_vms_left = 0;
|
|
$cluster_count = 0;
|
|
|
|
if (is_array($all_clusters_data)) {
|
|
foreach ($all_clusters_data as $cluster_data) {
|
|
if ($cluster_count > 0 && $cluster_count % 4 == 0) {
|
|
echo "</div><div class='row'>";
|
|
}
|
|
|
|
// --- Calculations ---
|
|
$cluster_name = $cluster_data['cluster'];
|
|
$node_count = (int)$cluster_data['node_count'];
|
|
$total_vms = (int)$cluster_data['total_vms'];
|
|
$total_vm_memory = (int)$cluster_data['total_vm_memory'];
|
|
|
|
// Memory calculation
|
|
$cluster_usable_mem = (int)$cluster_data['min_node_ram'] - 32;
|
|
if ($node_count == 4) {
|
|
$cluster_usable_mem *= 2;
|
|
}
|
|
$free_mem = $cluster_usable_mem - $total_vm_memory;
|
|
$free_mem_percentage = ($cluster_usable_mem > 0) ? round($free_mem / $cluster_usable_mem * 100) : 0;
|
|
|
|
// Storage calculation
|
|
$capacity_gb = 0;
|
|
$free_gb = 0;
|
|
if (!empty($cluster_data['all_csvs'])) {
|
|
// Explode the big string into an array of all CSV entries (with duplicates)
|
|
$all_csv_entries = explode("|", $cluster_data['all_csvs']);
|
|
// Create a new array containing only the unique CSV entries
|
|
$unique_csv_entries = array_unique($all_csv_entries);
|
|
// Loop through the UNIQUE entries to perform the calculation
|
|
foreach ($unique_csv_entries as $csv_string) {
|
|
if (str_contains($csv_string, ';')) {
|
|
$parts = explode(";", $csv_string);
|
|
if(count($parts) >= 3) {
|
|
$capacity_gb += (int)$parts[1];
|
|
// Free space is Total Capacity (parts[1]) - Used Space (parts[2])
|
|
$free_gb += (int)$parts[1] - (int)$parts[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$free_gb = max(0, $free_gb);
|
|
$free_storage_percentage = ($capacity_gb > 0) ? round($free_gb / $capacity_gb * 100) : 0;
|
|
|
|
// IOPS Calculation
|
|
$avg_io = ($node_count > 0) ? round((float)$cluster_data['total_io_disk'] / $node_count * 1024) : 0;
|
|
$io_display = ($avg_io == 0) ? "?" : $avg_io;
|
|
|
|
// VMs Left Calculation
|
|
$vms_left_by_mem = ($free_mem > 0) ? floor($free_mem / 16) : 0;
|
|
$vms_left_by_storage = ($free_gb > 0) ? floor($free_gb / 110) : 0;
|
|
$vms_left = min($vms_left_by_mem, $vms_left_by_storage);
|
|
$total_vms_left += $vms_left;
|
|
|
|
// --- Display Logic ---
|
|
if ($vms_left > 2) {
|
|
$vleft_html = "<span class='text-success'><b>(" . $vms_left . "</b> VMs left)</span>";
|
|
} elseif ($vms_left >= 1) { // Handles 1 and 2
|
|
$vleft_html = "<span class='text-warning'><b>(" . $vms_left . "</b> VMs left)</span>";
|
|
} else {
|
|
$vleft_html = "<span class='text-danger'><b>(" . $vms_left . "</b> VMs left)</span>";
|
|
}
|
|
?>
|
|
|
|
<div class='col-3'>
|
|
<a href="cluster-detail.php?cluster=<?php echo htmlspecialchars($cluster_name); ?>" class="text-decoration-none" target="_blank">
|
|
<div class='card border-secondary mb-3'>
|
|
<div class='card-header text-white bg-dark border-secondary'>
|
|
<h4 class="text-center"><?php echo htmlspecialchars($cluster_name); ?></h4>
|
|
<h6 class="text-center">
|
|
<b><?php echo $node_count; ?></b> Nodes / <b><?php echo $total_vms; ?></b> VMs <?php echo $vleft_html; ?>
|
|
</h6>
|
|
|
|
<?php
|
|
$vm_dist_html = "<div class='progress' style='border: 2px solid grey; height: 20px; background-Color: transparent;'><div class='progress-bar' style='width: 0; background-color: transparent;'></div></div>";
|
|
if ($total_vms > 0 && isset($vm_distribution[$cluster_name])) {
|
|
$dist_data = $vm_distribution[$cluster_name];
|
|
if ($node_count == 2 && count($dist_data) > 0) {
|
|
$owner1_name = $dist_data[0]['owner'];
|
|
$owner1_count = $dist_data[0]['vm_count'];
|
|
$owner2_name = isset($dist_data[1]) ? $dist_data[1]['owner'] : 'N/A';
|
|
$owner2_count = isset($dist_data[1]) ? $dist_data[1]['vm_count'] : 0;
|
|
|
|
$p1 = ($total_vms > 0) ? ($owner1_count / $total_vms) * 100 : 0;
|
|
$p2 = ($total_vms > 0) ? ($owner2_count / $total_vms) * 100 : 0;
|
|
|
|
$vm_dist_html = "<div class='progress' style='border: 2px solid grey; height: 20px;'>
|
|
<div class='progress-bar text-white' role='progressbar' style='width:$p1%'><b>$owner1_name</b></div>
|
|
<div class='progress-bar bg-info text-dark' role='progressbar' style='width:$p2%'><b>$owner2_name</b></div>
|
|
</div>";
|
|
}
|
|
}
|
|
echo $vm_dist_html;
|
|
?>
|
|
</div>
|
|
<div class='card-body text-white bg-dark'>
|
|
<?php
|
|
$mem_color = ($free_mem_percentage <= 5) ? 'bg-danger' : (($free_mem_percentage <= 10) ? 'bg-warning' : 'bg-success');
|
|
$mem_border = ($free_mem_percentage <= 5) ? 'red' : (($free_mem_percentage <= 10) ? 'yellow' : 'green');
|
|
$mem_used_percentage = 100 - $free_mem_percentage;
|
|
?>
|
|
<div class='row mb-2 align-items-center'>
|
|
<div class='col-7'><b>Free Mem:</b> <?php echo $free_mem . " / " . $cluster_usable_mem; ?> GB</div>
|
|
<div class='col-5'>
|
|
<div class='progress' style='border: 2px solid <?php echo $mem_border; ?>; height: 20px;'>
|
|
<div class='progress-bar bg-dark progress-bar-striped' style='width: <?php echo $mem_used_percentage; ?>%;'></div>
|
|
<div class='progress-bar <?php echo $mem_color; ?>' style='width: <?php echo $free_mem_percentage; ?>%;'></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php
|
|
$disk_unit = "GB";
|
|
$display_capacity = $capacity_gb;
|
|
$display_free = $free_gb;
|
|
if ($capacity_gb > 10000) {
|
|
$disk_unit = "TB";
|
|
$display_capacity = round($capacity_gb / 1024, 1);
|
|
$display_free = round($free_gb / 1024, 1);
|
|
}
|
|
$disk_color = ($free_storage_percentage <= 5) ? 'bg-danger' : (($free_storage_percentage <= 10) ? 'bg-warning' : 'bg-success');
|
|
$disk_border = ($free_storage_percentage <= 5) ? 'red' : (($free_storage_percentage <= 10) ? 'yellow' : 'green');
|
|
$disk_used_percentage = 100 - $free_storage_percentage;
|
|
?>
|
|
<div class='row mb-2 align-items-center'>
|
|
<div class='col-7'><b>Free CSV:</b> <?php echo $display_free . " / " . $display_capacity . " " . $disk_unit; ?></div>
|
|
<div class='col-5'>
|
|
<div class='progress' style='border: 2px solid <?php echo $disk_border; ?>; height: 20px;'>
|
|
<div class='progress-bar bg-dark progress-bar-striped' style='width: <?php echo $disk_used_percentage; ?>%;'></div>
|
|
<div class='progress-bar <?php echo $disk_color; ?>' style='width: <?php echo $free_storage_percentage; ?>%;'></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class='row'><div class='col-7'><b>CSV I/O</b> (24h):</div><div class='col-5'><b><?php echo $io_display; ?> KB/s</b></div></div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<?php
|
|
$cluster_count++;
|
|
} // End foreach
|
|
} // End if is_array
|
|
?>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<br><br>
|
|
<h3 class="text-center">Total VMs Left: <?php echo $total_vms_left; ?></h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- End of main content -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="/js/switch.js"></script>
|
|
</body>
|
|
|