Add "Host" column to the inventory table in /X/Inventory.php and introduce VM issue metrics (Unclustered, Wrong Owner) in Hyper-V clusters report with refined query logic.
This commit is contained in:
@@ -6,40 +6,29 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
<!-- Page Title -->
|
|
||||||
<title>Web Infra Reports</title>
|
<title>Web Infra Reports</title>
|
||||||
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
|
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
|
||||||
|
|
||||||
<!-- JQuery -->
|
|
||||||
<script src="/js/jquery-3.6.1.min.js"></script>
|
<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.min.css">
|
||||||
<link rel="stylesheet" href="/css/bootstrap-icons/bootstrap-icons.css">
|
<link rel="stylesheet" href="/css/bootstrap-icons/bootstrap-icons.css">
|
||||||
<script src="/js/bootstrap.bundle.min.js"></script>
|
<script src="/js/bootstrap.bundle.min.js"></script>
|
||||||
<!-- Bootstrap-tables -->
|
|
||||||
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
|
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
|
||||||
<script src="/js/bootstrap-table.min.js"></script>
|
<script src="/js/bootstrap-table.min.js"></script>
|
||||||
<script src="/js/bootstrap-table-fr-FR.min.js"></script>
|
<script src="/js/bootstrap-table-fr-FR.min.js"></script>
|
||||||
<!-- Chart -->
|
|
||||||
<script src="/js/chart.min.js"></script>
|
<script src="/js/chart.min.js"></script>
|
||||||
<script src="/js/apexcharts.min.js"></script>
|
<script src="/js/apexcharts.min.js"></script>
|
||||||
<!-- Gauge -->
|
|
||||||
<link rel="stylesheet" href="/css/Gauge.css">
|
<link rel="stylesheet" href="/css/Gauge.css">
|
||||||
<script src="/js/jquery.AshAlom.gaugeMeter-2.0.0.min.js"></script>
|
<script src="/js/jquery.AshAlom.gaugeMeter-2.0.0.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-light text-dark">
|
<body class="bg-light text-dark">
|
||||||
<?php include $_SERVER['DOCUMENT_ROOT'] . "/include/all.php"; ?> <!-- Database connexion -->
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/include/all.php"; ?> <div class="container-fluid">
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row flex-nowrap">
|
<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;">
|
<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 -->
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/navbar.html"; ?> </div>
|
||||||
</div>
|
|
||||||
<!-- Display -->
|
|
||||||
<div class="col py-3">
|
<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>
|
<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="container-fluid" style="zoom:90%">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -48,7 +37,7 @@
|
|||||||
// STEP 1: Fetch all required data with minimal database queries
|
// STEP 1: Fetch all required data with minimal database queries
|
||||||
// =================================================================
|
// =================================================================
|
||||||
|
|
||||||
// Query 1: Get aggregated data for all clusters at once.
|
// Query 1: Get aggregated data for all clusters at once. (Unchanged)
|
||||||
$cluster_query = "
|
$cluster_query = "
|
||||||
SELECT
|
SELECT
|
||||||
cluster,
|
cluster,
|
||||||
@@ -73,7 +62,7 @@
|
|||||||
";
|
";
|
||||||
$all_clusters_data = Invoke_Infra($cluster_query);
|
$all_clusters_data = Invoke_Infra($cluster_query);
|
||||||
|
|
||||||
// Query 2: Get VM distribution for all relevant clusters at once.
|
// Query 2: Get VM distribution for all relevant clusters at once. (Unchanged)
|
||||||
$vm_repart_query = "
|
$vm_repart_query = "
|
||||||
SELECT
|
SELECT
|
||||||
v.Cluster,
|
v.Cluster,
|
||||||
@@ -96,7 +85,6 @@
|
|||||||
";
|
";
|
||||||
$raw_vm_distribution = Invoke_Infra($vm_repart_query);
|
$raw_vm_distribution = Invoke_Infra($vm_repart_query);
|
||||||
|
|
||||||
// Process the VM distribution data into an easy-to-use associative array
|
|
||||||
$vm_distribution = [];
|
$vm_distribution = [];
|
||||||
if (is_array($raw_vm_distribution)) {
|
if (is_array($raw_vm_distribution)) {
|
||||||
foreach ($raw_vm_distribution as $repart) {
|
foreach ($raw_vm_distribution as $repart) {
|
||||||
@@ -106,6 +94,37 @@
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MODIFICATION : Requête finale sans la jointure pour éviter les doublons
|
||||||
|
$vm_issues_query = "
|
||||||
|
SELECT
|
||||||
|
v.Cluster,
|
||||||
|
SUM(CASE WHEN v.IsClustered = 'False' THEN 1 ELSE 0 END) as unclustered_count,
|
||||||
|
SUM(CASE WHEN v.IsClustered = 'True' AND v.Owner <> v.PreferredOwner THEN 1 ELSE 0 END) as wrong_owner_count
|
||||||
|
FROM
|
||||||
|
cmdb_vms v
|
||||||
|
WHERE
|
||||||
|
v.decomtime IS NULL
|
||||||
|
AND v.Cluster <> ''
|
||||||
|
AND v.Cluster NOT LIKE '%-WM%'
|
||||||
|
AND v.Cluster NOT LIKE '%-C1MAS%'
|
||||||
|
AND v.Cluster NOT LIKE '%-vrs%'
|
||||||
|
AND (v.Cluster LIKE 'DUN%' OR v.Cluster LIKE 'DMV%')
|
||||||
|
GROUP BY
|
||||||
|
v.Cluster
|
||||||
|
";
|
||||||
|
$raw_vm_issues = Invoke_Infra($vm_issues_query);
|
||||||
|
|
||||||
|
$vm_issues = [];
|
||||||
|
if (is_array($raw_vm_issues)) {
|
||||||
|
foreach ($raw_vm_issues as $issue) {
|
||||||
|
$vm_issues[$issue['Cluster']] = [
|
||||||
|
'unclustered' => $issue['unclustered_count'],
|
||||||
|
'wrong_owner' => $issue['wrong_owner_count']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
// STEP 2: Loop through the pre-fetched data and display it
|
// STEP 2: Loop through the pre-fetched data and display it
|
||||||
// =================================================================
|
// =================================================================
|
||||||
@@ -136,17 +155,13 @@
|
|||||||
$capacity_gb = 0;
|
$capacity_gb = 0;
|
||||||
$free_gb = 0;
|
$free_gb = 0;
|
||||||
if (!empty($cluster_data['all_csvs'])) {
|
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']);
|
$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);
|
$unique_csv_entries = array_unique($all_csv_entries);
|
||||||
// Loop through the UNIQUE entries to perform the calculation
|
|
||||||
foreach ($unique_csv_entries as $csv_string) {
|
foreach ($unique_csv_entries as $csv_string) {
|
||||||
if (str_contains($csv_string, ';')) {
|
if (str_contains($csv_string, ';')) {
|
||||||
$parts = explode(";", $csv_string);
|
$parts = explode(";", $csv_string);
|
||||||
if(count($parts) >= 3) {
|
if(count($parts) >= 3) {
|
||||||
$capacity_gb += (int)$parts[1];
|
$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 += (int)$parts[1] - (int)$parts[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,10 +170,6 @@
|
|||||||
$free_gb = max(0, $free_gb);
|
$free_gb = max(0, $free_gb);
|
||||||
$free_storage_percentage = ($capacity_gb > 0) ? round($free_gb / $capacity_gb * 100) : 0;
|
$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 Calculation
|
||||||
$vms_left_by_mem = ($free_mem > 0) ? floor($free_mem / 16) : 0;
|
$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_by_storage = ($free_gb > 0) ? floor($free_gb / 110) : 0;
|
||||||
@@ -168,7 +179,7 @@
|
|||||||
// --- Display Logic ---
|
// --- Display Logic ---
|
||||||
if ($vms_left > 2) {
|
if ($vms_left > 2) {
|
||||||
$vleft_html = "<span class='text-success'><b>(" . $vms_left . "</b> VMs left)</span>";
|
$vleft_html = "<span class='text-success'><b>(" . $vms_left . "</b> VMs left)</span>";
|
||||||
} elseif ($vms_left >= 1) { // Handles 1 and 2
|
} elseif ($vms_left >= 1) {
|
||||||
$vleft_html = "<span class='text-warning'><b>(" . $vms_left . "</b> VMs left)</span>";
|
$vleft_html = "<span class='text-warning'><b>(" . $vms_left . "</b> VMs left)</span>";
|
||||||
} else {
|
} else {
|
||||||
$vleft_html = "<span class='text-danger'><b>(" . $vms_left . "</b> VMs left)</span>";
|
$vleft_html = "<span class='text-danger'><b>(" . $vms_left . "</b> VMs left)</span>";
|
||||||
@@ -193,10 +204,8 @@
|
|||||||
$owner1_count = $dist_data[0]['vm_count'];
|
$owner1_count = $dist_data[0]['vm_count'];
|
||||||
$owner2_name = isset($dist_data[1]) ? $dist_data[1]['owner'] : 'N/A';
|
$owner2_name = isset($dist_data[1]) ? $dist_data[1]['owner'] : 'N/A';
|
||||||
$owner2_count = isset($dist_data[1]) ? $dist_data[1]['vm_count'] : 0;
|
$owner2_count = isset($dist_data[1]) ? $dist_data[1]['vm_count'] : 0;
|
||||||
|
|
||||||
$p1 = ($total_vms > 0) ? ($owner1_count / $total_vms) * 100 : 0;
|
$p1 = ($total_vms > 0) ? ($owner1_count / $total_vms) * 100 : 0;
|
||||||
$p2 = ($total_vms > 0) ? ($owner2_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;'>
|
$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 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 class='progress-bar bg-info text-dark' role='progressbar' style='width:$p2%'><b>$owner2_name</b></div>
|
||||||
@@ -244,7 +253,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 class='row mt-2'>
|
||||||
|
<?php
|
||||||
|
$unclustered_count = $vm_issues[$cluster_name]['unclustered'] ?? 0;
|
||||||
|
$wrong_owner_count = $vm_issues[$cluster_name]['wrong_owner'] ?? 0;
|
||||||
|
|
||||||
|
if ($unclustered_count > 0) {
|
||||||
|
echo "<div class='col-7'><span class='badge text-bg-danger'>Unclustered: $unclustered_count</span></div>";
|
||||||
|
}else{
|
||||||
|
echo "<div class='col-7'><span class='badge text-bg-success'>Unclustered: $unclustered_count</span></div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($wrong_owner_count > 0) {
|
||||||
|
echo "<div class='col-5'><span class='badge text-bg-warning'>Wrong Owner: {$wrong_owner_count}</span></div>";
|
||||||
|
}else{
|
||||||
|
echo "<div class='col-5'><span class='badge text-bg-success'>Wrong Owner: {$wrong_owner_count}</span></div>";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@@ -262,10 +289,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End of main content -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/js/switch.js"></script>
|
<script src="/js/switch.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
<thead> <!-- Header -->
|
<thead> <!-- Header -->
|
||||||
<tr>
|
<tr>
|
||||||
<th data-field="Name" data-sortable="true">Name</th>
|
<th data-field="Name" data-sortable="true">Name</th>
|
||||||
|
<th data-field="Host" data-sortable="true">Host</th>
|
||||||
<th data-field="OS" data-sortable="true">OS Type</th>
|
<th data-field="OS" data-sortable="true">OS Type</th>
|
||||||
<th data-field="distrib" data-sortable="true">Distrib</th>
|
<th data-field="distrib" data-sortable="true">Distrib</th>
|
||||||
<th data-field="Last Boot" data-sortable="true">Last reboot</th>
|
<th data-field="Last Boot" data-sortable="true">Last reboot</th>
|
||||||
@@ -77,6 +78,8 @@
|
|||||||
$name = $s['hostname'];
|
$name = $s['hostname'];
|
||||||
echo "<td><a href='/inventory/server-detail.php?s=".$name."' target='_blank'>$name</a></td>";
|
echo "<td><a href='/inventory/server-detail.php?s=".$name."' target='_blank'>$name</a></td>";
|
||||||
|
|
||||||
|
//Host
|
||||||
|
echo "<td>".$s['Owner']."</td>";
|
||||||
//OS Type
|
//OS Type
|
||||||
$typ = match ($s['type'] ?? null) {
|
$typ = match ($s['type'] ?? null) {
|
||||||
'AIX' => 'AIX',
|
'AIX' => 'AIX',
|
||||||
|
|||||||
Reference in New Issue
Block a user