Merge pull request #1613 from SimonFair/VM-Metrics

Add  VM usage data
This commit is contained in:
tom mortensen
2024-02-14 08:40:25 -08:00
committed by GitHub
6 changed files with 243 additions and 2 deletions

View File

@@ -1675,6 +1675,14 @@ For setting the console options to show on context menus. Web will show only inb
Virtual Manager Remote Viewer will only show the Remote Viewer option. Both will show both Web and Remote Viewer.
:end
:vms_usage_help:
Show metrics for CPU both guest and host percentage, memory, disk io and network io.
:end
:vms_usage_timer_help:
Setting in seconds for metrics refresh time.
:end
:vms_acs_override_help:
*PCIe ACS override* allows various hardware components to expose themselves as isolated devices.
Typically it is sufficient to isolate *Downstream* ports.

View File

@@ -183,6 +183,19 @@ _(Console Options)_:
:vms_console_help:
_(Show VM Usage)_:
: <select id="vmusage" name="USAGE">
<?=mk_option($domain_cfg['USAGE'], 'N', _('Disabled'))?>
<?=mk_option($domain_cfg['USAGE'], 'Y', _('Enabled'))?>
</select>
:vms_usage_help:
_(VM Usage refresh timer(seconds))_:
: <input type="number" id="vm_usage_timer" name="USAGETIMER" value="<?=htmlspecialchars($domain_cfg['USAGETIMER'] ?? 3) ?>" class="narrow">
:vms_usage_timer_help:
_(PCIe ACS override)_:
: <select id="pcie_acs_override"<?=$safemode?' disabled':''?>>
<?= mk_option($pcie_acs_override, '', _('Disabled'))?>

View File

@@ -0,0 +1,41 @@
Menu="VMs:0"
Title="VM Usage Statisics"
Nchan="vm_usage:stop"
Cond="exec(\"grep -o '^USAGE=.Y' /boot/config/domain.cfg 2>/dev/null\") && is_file('/var/run/libvirt/libvirtd.pid')"
---
<?PHP
/* Copyright 2005-2024, Lime Technology
* Copyright 2012-2024, Simon Fairweather.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp');
?>
<table id="vmstats" class="tablesorter four shift">
<thead class='child'><tr><th class="th1">_(Name)_</th><th class="th2">_(Guest CPU)_</th><th>_(Host CPU)_</th><th>_(Memory)_</th><th>_(Disk IO)_</th><th>_(Network IO)_</th></tr></thead>
<tbody id ="vmstatsbody" class='child'>
</tbody>
</table>
<script>
var vmusage = new NchanSubscriber('/sub/vm_usage',{subscriber:'websocket'});
vmusage.on('message', function(msg){
var data = JSON.parse(msg);
$('#vmstatsbody').html(data);
});
$(function(){
vmusage.start();
});
window.onunload = function(){
vmusage.stop();
}
</script>

View File

@@ -1694,8 +1694,8 @@
else {
$doms = libvirt_list_domains($this->conn);
foreach ($doms as $dom) {
$tmp = $this->domain_get_name($dom);
$ret[$tmp] = libvirt_domain_get_info($dom);
$tmp = $this->get_domain_object($dom);
$ret[$dom] = libvirt_domain_get_info($tmp);
}
}
@@ -1771,6 +1771,11 @@
return ($tmp) ? $tmp : $this->_set_last_error();
}
function domain_get_all_domain_stats() {
$tmp = libvirt_connect_get_all_domain_stats($this->conn);
return ($tmp) ? $tmp : $this->_set_last_error();
}
function domain_start($dom) {
$dom=$this->get_domain_object($dom);
if ($dom) {

View File

@@ -2529,4 +2529,81 @@ function addtemplatexml($post) {
return $reply;
}
function get_vm_usage_stats($collectcpustats = true,$collectdiskstats = true,$collectnicstats = true, $collectmemstats = true) {
global $lv, $vmusagestats;
$hostcpus = $lv->host_get_node_info();
$timestamp = time();
$allstats=$lv->domain_get_all_domain_stats();
foreach ($allstats as $vm => $data) {
$state = $data["state.state"];
# CPU Metrics
$cpuTime = 0;
$cpuHostPercent = 0;
$cpuGuestPercent = 0;
$cpuTimeAbs = $data["cpu.time"];
if ($state == 1 && $collectcpustats == true) {
$guestcpus = $data["vcpu.current"];
$cpuTime = $cpuTimeAbs - $vmusagestats[$vm]["cpuTimeAbs"];
$pcentbase = ((($cpuTime) * 100.0) / ((($timestamp) - $vmusagestats[$vm]["timestamp"] ) * 1000.0 * 1000.0 * 1000.0));
$cpuHostPercent = round($pcentbase / $hostcpus['cpus'],1);
$cpuGuestPercent = round($pcentbase / $guestcpus, 1) ;
$cpuHostPercent = max(0.0, min(100.0, $cpuHostPercent));
$cpuGuestPercent = max(0.0, min(100.0, $cpuGuestPercent));
}
# Memory Metrics
if ($state == 1 && $collectmemstats) {
$currentmem = $data["balloon.current"];
$unusedmem = $data["balloon.unused"];
$meminuse = $currentmem - $unusedmem;
} else $currentmem = $meminuse = 0;
# Disk
if ($state == 1 && $collectdiskstats) {
$disknum = $data["block.count"];
$rd=$wr=$i=0;
for ($i = 0; $i < $disknum; $i++) {
if ($data["block.$i.name"] == "hda" || $data["block.$i.name"] == "hdb") continue;
$rd += $data["block.$i.rd.bytes"] ;
$wr += $data["block.$i.wr.bytes"] ;
}
$rdrate = ($rd - $vmusagestats[$vm]['rdp']);
$wrrate = ($wr - $vmusagestats[$vm]['wrp']);
} else $rdrate=$wrrate=0;
# Net
if ($state == 1 && $collectnicstats) {
$nicnum = $data["net.count"];
$rx=$tx=$i=0;
for ($i = 0; $i < $nicnum; $i++) {
$rx += $data["net.$i.rx.bytes"] ;
$tx += $data["net.$i.tx.bytes"] ;
}
$rxrate = round(($rx - $vmusagestats[$vm]['rxp']),0);
$txrate = round(($tx - $vmusagestats[$vm]['txp']),0);
} else $rxrate=$txrate=0;
$vmusagestats[$vm] = [
"cpuTime" => $cpuTime,
"cpuTimeAbs" => $cpuTimeAbs,
"cpuhost" => $cpuHostPercent,
"cpuguest" => $cpuGuestPercent,
"timestamp" => $timestamp,
"mem" => $meminuse,
"maxmem" => $currentmem,
"rxrate" => $rxrate,
"rxp" => $rx,
"txrate" => $txrate,
"txp" => $tx,
"rdp" => $rd,
"rdrate" => $rdrate,
"wrp" => $wr,
"wrrate" => $wrrate,
"state" => $state,
];
}
}
?>

View File

@@ -0,0 +1,97 @@
#!/usr/bin/php -q
<?PHP
/* Copyright 2005-2024, Lime Technology
* Copyright 2024-2024, Simon Fairweather.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
?>
<?
$docroot = '/usr/local/emhttp';
$varroot = '/var/local/emhttp';
$md5_old = -1;
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/publish.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
global $vmusagestats;
extract(parse_plugin_cfg('dynamix',true));
get_vm_usage_stats();
sleep(1);
// add translations
$_SERVER['REQUEST_URI'] = 'dashboard';
$login_locale = _var($display,'locale');
require_once "$docroot/webGui/include/Translations.php";
// remember current language
$locale_init = $locale;
function update_translation($locale) {
global $docroot,$language;
$language = [];
if ($locale) {
$text = "$docroot/languages/$locale/translations.txt";
if (file_exists($text)) {
$store = "$docroot/languages/$locale/translations.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = unserialize(file_get_contents($store));
}
$text = "$docroot/languages/$locale/dashboard.txt";
if (file_exists($text)) {
$store = "$docroot/languages/$locale/dashboard.dot";
if (!file_exists($store)) file_put_contents($store,serialize(parse_lang_file($text)));
$language = array_merge($language,unserialize(file_get_contents($store)));
}
}
}
$domain_cfgfile = "/boot/config/domain.cfg";
$domain_cfg = parse_ini_file($domain_cfgfile);
if (!isset($domain_cfg['USAGETIMER'])) $timer = 3 ; else $timer = $domain_cfg['USAGETIMER'];
while (true) {
extract(parse_plugin_cfg('dynamix',true));
if (_var($display,'locale') != $locale_init) {
$locale_init = _var($display,'locale');
update_translation($locale_init);
}
get_vm_usage_stats();
$echo = [];
$echo = [];
$ts = $time1 - $time0;
$echodata = "";
$running = 0;
foreach ($vmusagestats as $vm => $vmdata) {
if ($vmdata['state'] == 1) {
$running++;
$echodata .= "<tr><td>$vm</td>" ;
$echodata .= "<td class='advanced'><span class='cpug-".$vm."'>".$vmdata['cpuguest']."%</span><div class='usage-disk mm'><span id='cpug-".$vm."' style='width:".$vmdata['cpuguest']."%;'></span><span></span></div></td>";
$echodata .= "<td class='advanced'><span class='cpuh-".$vm."'>".$vmdata['cpuhost']."%</span><div class='usage-disk mm'><span id='cpuh-".$vm."' style='width:".$vmdata['cpuhost']."%;'></span><span></span></div></td><td>";
$echodata .= my_scale($vmdata['mem'],$unit)."$unit / ".my_scale($vmdata['maxmem'],$unit)."$unit</td><td>";
$echodata .= _("Read").": ".my_scale($vmdata['rdrate'],$unit)."$unit/s<br>"._("Write").": ".my_scale($vmdata['wrrate'],$unit)."$unit/s</td><td>";
$echodata .= _("RX").": ".my_scale($vmdata['rxrate'],$unit)."$unit/s<br>"._("TX").": ".my_scale($vmdata['txrate'],$unit)."$unit/s</td></tr>";
}
$echo = $echodata ;
}
if ($running < 1) $echo = "<tr><td colspan='7' style='text-align:center;padding-top:12px'>"._('No VMs running')."</td></tr>";
$echo = json_encode($echo);
$md5_new = md5($echo,true);
if ($md5_new !== $md5_old) {
$md5_old = publish('vm_usage',$echo)!==false ? $md5_new : -1;
$time0 = $time1;
}
sleep($timer);
}
?>