Files
webgui/plugins/dynamix.vm.manager/VMMachines.page
T

542 lines
21 KiB
Plaintext

Menu="VMs:1"
Title="Virtual Machines"
Tag="columns"
Cond="(pgrep('libvirtd')!==false)"
Markdown="false"
---
<?PHP
/* Copyright 2005-2017, Lime Technology
* Copyright 2015-2017, Derek Macias, Eric Schultz, Jon Panozzo.
*
* 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 = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/plugins/dynamix.vm.manager/classes/libvirt_helpers.php";
exec('cat /sys/devices/system/cpu/*/topology/thread_siblings_list|sort -nu', $cpus);
function showCPUs($uuid) {
global $cpus;
$arrConfig = domain_to_config($uuid);
echo "<div class='four'>";
foreach ($cpus as $pair) {
unset($cpu1,$cpu2);
list($cpu1, $cpu2) = preg_split('/[,-]/',$pair);
$check = in_array($cpu1, $arrConfig['domain']['vcpu']) ? 'fa-check-square-o' : 'fa-square-o';
if (!$cpu2) {
echo "<label><i class='fa $check'></i>&nbsp;&nbsp;cpu $cpu1</label>";
} else {
echo "<label class='cpu1'><i class='fa $check'></i>&nbsp;&nbsp;cpu $cpu1 / $cpu2</label>";
$check = in_array($cpu2, $arrConfig['domain']['vcpu']) ? 'fa-check-square-o' : 'fa-square-o';
echo "<label class='cpu2'><i class='fa $check'></i></label>";
}
}
echo "</div>";
}
$action = array_key_exists('action', $_GET) ? $_GET['action'] : '';
$subaction = array_key_exists('subaction', $_GET) ? $_GET['subaction'] : '';
$uuid = $_GET['uuid'];
if ($_GET['refresh']) {
$name = $_GET['name'];
if($lv->domain_is_active($name)){
echo "<meta http-equiv='refresh' content='5; url=/VMs?name=$name&amp;refresh=true'>";
$msg = "Waiting for $name to shutdown...";
}else{
echo "<script>clearHistory();</script>";
$msg = "$name has been shutdown";
}
}
if ($action) {
}else{
if ($subaction) {
$domName = $lv->domain_get_name_by_uuid($uuid);
if ($subaction == 'domain-diskdev') {
$msg = $lv->domain_set_disk_dev($domName, $_GET['olddev'], $_POST['diskdev']) ?
$domName.' disk dev has been changed from '.$_GET['olddev'].' to '.$_POST['diskdev']:
'Error: '.$lv->get_last_error();
}
elseif ($subaction == 'disk-resize') {
$capacity = str_replace(["KB","MB","GB","TB","PB", " ", ","], ["K","M","G","T","P", "", ""], strtoupper($_POST['cap']));
$oldcap = str_replace(["KB","MB","GB","TB","PB", " ", ","], ["K","M","G","T","P", "", ""], strtoupper($_GET['oldcap']));
if (substr($oldcap,0,-1) < substr($capacity,0,-1)){
shell_exec("qemu-img resize -q ".escapeshellarg($_GET['disk'])." ".escapeshellarg($capacity));
$msg = $domName." disk capacity has been changed to $capacity";
}else {
$msg = "Error: disk capacity has to be greater than {$_GET['oldcap']}";
}
}
elseif ($subaction == 'cdrom-change') {
$msg = $lv->domain_change_cdrom($domName, $_POST['cdrom'], $_GET['dev'], $_GET['bus']) ?
"$domName cdrom has been changed" :
"Error: ".$lv->get_last_error();
}
elseif ($subaction == 'memory-change') {
$msg = $lv->domain_set_memory($domName, $_GET['memory']*1024) ?
"$domName memory has been changed to ".$_GET['memory']." MiB" :
"Error: ".$lv->get_last_error();
}
elseif ($subaction == 'vcpu-change') {
$vcpu = $_GET['vcpu'];
$msg = $lv->domain_set_vcpu($domName, $vcpu) ?
"$domName vcpu number has been changed to $vcpu" :
"Error: ".$lv->get_last_error();
}
elseif ($subaction == 'bootdev-change') {
$bootdev = $_GET['bootdev'];
$msg = $lv->domain_set_boot_device($domName, $bootdev) ?
"$domName boot device has been changed to $bootdev" :
"Error: ".$lv->get_last_error();
}
elseif ($subaction == 'disk-remove') {
$msg = $lv->domain_disk_remove($domName, $_GET['dev']) ?
"$domName disk has been removed" :
'Error: '.$lv->get_last_error();
}
elseif ($subaction == 'snap-create') {
$msg = $lv->domain_snapshot_create($domName) ?
"Snapshot for $domName has been created" :
'Error: '.$lv->get_last_error();
}
elseif ($subaction == 'snap-delete') {
$msg = $lv->domain_snapshot_delete($domName, $_GET['snap']) ?
"Snapshot for $domName has been deleted" :
'Error: '.$lv->get_last_error();
}
elseif ($subaction == 'snap-revert') {
$msg = $lv->domain_snapshot_revert($domName, $_GET['snap']) ?
"$domName has been reverted" :
'Error: '.$lv->get_last_error();
}
elseif ($subaction == 'snap-desc') {
$msg = $lv->snapshot_set_metadata($domName, $_GET['snap'], $_POST['snapdesc']) ?
"Snapshot description for $domName has been saved":
'Error: '.$lv->get_last_error();
}
echo "<script>clearHistory();</script>";
}
echo "<table class='tablesorter shift kvm' id='kvm_table'>
<thead>
<tr>
<th class='header'><i class='fa fa-th-list'></i></th>
<th class='header'>Name</th>
<th class='header'>Description</th>
<th class='header'>CPUs</th>
<th class='header'>Memory</th>
<th class='header'>vDisks</th>
<th class='header'>Graphics</th>
<th class='header'>Autostart</th>
</tr>
</thead>
<tbody id='kvm_list'>";
//Get domain variables for each domain
if ($libvirt_running == "yes")
$doms = $lv->get_domains();
if(!is_array($doms)){
if ($libvirt_running == "yes") {
echo "<tr><td></td><td colspan=\"7\">No Virtual Machines Installed</td></tr>";
$msg = "No VMs defined. Create from template or add XML.";
} else {
$msg = "Libvirt is not running. Goto Settings tab then click Start.";
}
} else {
sort($doms);
$contextMenus = [];
for ($i = 0; $i < sizeof($doms); $i++) {
$name = $doms[$i];
$res = $lv->get_domain_by_name($name);
$desc = $lv->domain_get_description($res);
$uuid = $lv->domain_get_uuid($res);
$dom = $lv->domain_get_info($res);
$id = $lv->domain_get_id($res);
$is_autostart = $lv->domain_get_autostart($res);
$state = $lv->domain_state_translate($dom['state']);
$arrConfig = domain_to_config($uuid);
if ($state == 'running') {
$mem = $dom['memory'] / 1024;
}else{
$mem = $lv->domain_get_memory($res)/1024;
}
$mem = round($mem).'M';
$vcpu = $dom['nrVirtCpu'];
$auto = $is_autostart ? 'checked="checked"':"";
$template = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@name');
if (empty($template)) {
$template = 'Custom';
}
$log = (is_file('/var/log/libvirt/qemu/' . $name . '.log') ? 'libvirt/qemu/' . $name . '.log' : '');
if (($diskcnt = $lv->get_disk_count($res)) > 0) {
$disks = $diskcnt.' / '.$lv->get_disk_capacity($res);
$diskdesc = 'Current physical size: '.$lv->get_disk_capacity($res, true);
}else{
$disks = '-';
$diskdesc = '';
}
$vncport = $lv->domain_get_vnc_port($res);
$arrValidDiskBuses = getValidDiskBuses();
$vnc = '';
$graphics = '';
if ($vncport > 0) {
$wsport = $lv->domain_get_ws_port($res);
$vnc = '/plugins/dynamix.vm.manager/vnc.html?autoconnect=true&host=' . $_SERVER['HTTP_HOST'] . '&port=' . $wsport . '&path=';
$graphics = 'VNC:'.$vncport;
} else if ($vncport == -1) {
$graphics = 'VNC:auto';
} else if (!empty($arrConfig['gpu'])) {
$arrValidGPUDevices = getValidGPUDevices();
foreach($arrConfig['gpu'] as $arrGPU) {
foreach($arrValidGPUDevices as $arrDev) {
if ($arrGPU['id'] == $arrDev['id']) {
$graphics .= $arrDev['name']."\n";
}
}
}
$graphics = str_replace("\n", "<br>", trim($graphics));
}
unset($tmp);
if (!$id)
$id = '-';
unset($dom);
$contextMenus[] = sprintf("addVMContext('%s', '%s', '%s', '%s', '%s', '%s');", addslashes($name), addslashes($uuid), addslashes($template), $state, addslashes($vnc), addslashes($log));
$vmicon = $lv->domain_get_icon_url($res);
//Domain information
echo "<tr style='background-color:".bcolor($i)."'>
<td style=\"width: 48px; padding: 4px\">".renderVMContentIcon($uuid, $name, $vmicon, $state)."</td>
<td><a href='#' onclick=\"return toggle_id('name$i');\" title='click for more VM info'>$name</a></td>
<td>$desc</td>
<td><a class='vcpu$i' style='cursor:pointer'>$vcpu</a></td>
<td>$mem</td>
<td title='$diskdesc'>$disks</td>
<td>$graphics</td>
<td><input class='autostart' type='checkbox' name='auto_$name' title='Toggle VM auostart' $auto uuid='$uuid'></td>";
echo "</tr>
<tr id='name$i' style='display: none'>";
/* Disk device information */
echo "<td colspan='7' style='overflow: hidden'>
<table class='tablesorter domdisk' id='domdisk_table'>
<thead>
<tr>
<th class='header'><i class='fa fa-hdd-o'></i><b> Disk devices &nbsp;</b></th>
<th class='header'>Bus</th>
<th class='header'>Capacity</th>
<th class='header'>Allocation</th>
<th class='header'>Actions</th><th class='header'></th>
</tr>
</thead>
<tbody id='domdisk_list'>";
/* Display domain disks */
$tmp = $lv->get_disk_stats($res);
if (!empty($tmp)) {
for ($ii = 0; $ii < sizeof($tmp); $ii++) {
$capacity = $lv->format_size($tmp[$ii]['capacity'], 0);
$allocation = $lv->format_size($tmp[$ii]['allocation'], 0);
$disk = (array_key_exists('file', $tmp[$ii])) ? $tmp[$ii]['file'] : $tmp[$ii]['partition'];
$dev = $tmp[$ii]['device'];
$bus = $tmp[$ii]['bus'];
echo "<tr>
<td>$disk</td>
<td>{$arrValidDiskBuses[$bus]}</td>
<td title='Click to increase Disk Size'>
<form method='post' action='?subaction=disk-resize&amp;uuid=$uuid&amp;disk=$disk&amp;oldcap=$capacity' /><span class='diskresize' style='width:30px'>
<span class='text' ><a href='#'> $capacity </a></span>
<input class='input' type='text' style='width:46px' name='cap' value='$capacity' val='diskresize' hidden />
</span>
</form>
</td>
<td>$allocation</td>
<td>";
/* add detach button if shutoff */
if ($state == 'shutoff')
echo "detach <a href='#' onclick=\"swal({title:'Are you sure?',text:'Detach ".basename($disk)." from VM: ".$name."',type:'warning',showCancelButton:true},function(){ajaxVMDispatch('attached',{action:'disk-remove',uuid:'".$uuid."',dev:'".$dev."'});});return false;\" title='detach disk from VM'>
<i class='fa fa-eject blue'></i></a>";
else
echo "N/A";
echo '</td>
</tr>';
}
}
/*end display disk display*/
/* Display domain cdroms */
$tmp = $lv->get_cdrom_stats($res);
if (!empty($tmp)) {
for ($ii = 0; $ii < sizeof($tmp); $ii++) {
$capacity = $lv->format_size($tmp[$ii]['capacity'], 0);
$allocation = $lv->format_size($tmp[$ii]['allocation'], 0);
$disk = (array_key_exists('file', $tmp[$ii])) ? $tmp[$ii]['file'] : $tmp[$ii]['partition'];
$dev = $tmp[$ii]['device'];
$bus = $tmp[$ii]['bus'];
echo "<tr>
<td>$disk</td>
<td>{$arrValidDiskBuses[$bus]}</td>
<td>$capacity</td>
<td>$allocation</td>
<td>";
/* add detach button if shutoff */
if ($state == 'shutoff')
echo "detach <a href='#' onclick=\"swal({title:'Are you sure?',text:'Detach ".basename($disk)." from VM: ".$name."',type:'warning',showCancelButton:true},function(){ajaxVMDispatch('attached',{action:'disk-remove',uuid:'".$uuid."',dev:'".$dev."'});});return false;\" title='detach disk from VM'>
<i class='fa fa-eject blue'></i></a>";
else
echo "N/A";
echo ' </td>
</tr>';
}
}
/*end display cdrom display*/
echo "</tbody></table>";
unset($capacity, $dev, $disk, $type, $vcpu);
/* Backup information */
/* echo "<table class='tablesorter domsnap' id='backup_table'>
<tr>
<thead>
<th class='header'><i class='fa fa-floppy-o'></i><b> Backups &nbsp;&nbsp;&nbsp;</b>";
if ($state == 'shutoff')
echo "<a href='?subaction=backup-create&amp;uuid=$uuid' title='create a backup of current domain'><i class='fa fa-plus green'></i></a>";
echo "</th>
<th class='header'>Name</th>
<th class='header'>Date</th>
<th class='header'>Time</th>
<th class='header'>Description</th>
<th class='header'>Actions ";
if ($state == 'running')
echo " <small>(stop to enable)</small>";
echo "</th><th class='header'></th><th class='header'></th>
</thead>
</tr>";
if (false) {
sort($tmp);
for ($ii = 0; $ii < sizeof($tmp); $ii++) {
$backup = $tmp[$ii];
$date = date("D d M Y",$name);
$time = date("H:i:s",$backup);
$info = $lv->domain_backup_get_info($name, $backup);
if(empty($info)){
$info = "Click to change description";
$val = "";}
else
$val = $info;
echo "<tr style='background-color:".bcolor($ii)."'>
<td>".($ii+1)."</td>
<td>$backup</td>
<td>$date</td>
<td>$time</td>
<td><form method='post' action='?subaction=backup-desc&amp;uuid=$uuid&amp;backup=$backup' /><span class='backdesc'>
<span class='text'><a href='#'> $info </a></span>
<input class='input' type='text' name='backdesc' value='$val' val='backdesc' hidden placeholder='Click to change description' title='Click to change description'/>
</span></form>
</td>
<td>
revert &nbsp;<a href='?subaction=backup-revert&amp;uuid=$uuid&amp;backup=$backup'><i class='fa fa-refresh lightblue'></i></a>
</td>
<td>
delete&nbsp;<a href='?subaction=backup-delete&amp;uuid=$uuid&amp;backup=$backup'><i class='fa fa-remove red'></i></a>
</td>
</tr>";
}
}
else
echo "<tr><td>no backups</td>
<td>none</td>
<td>N/A</td>
<td>N/A</td>
<td>N/A</td>
<td>N/A</td>
</tr>";
echo '</table>';*/
/* End Backup information*/
/* Snapshot information */
/*
echo "<table class='tablesorter domsnap' id='domsnap_table'>
<thead>
<tr>
<th class='header'><i class='fa fa-camera-retro'></i><b> Snapshots </b>";
if ($state == 'shutoff')
echo "<a href='?subaction=snap-create&amp;uuid=$uuid' title='create a snapshot of current domain state'><i class='fa fa-plus green'></i></a>";
echo "</th>
<th class='header'>Name</th>
<th class='header'>Date</th>
<th class='header'>Time</th>
<th class='header'>Description</th>
<th class='header'>Actions ";
if ($state == 'running')
echo " <small>(stop to enable)</small>";
echo "</th><th class='header'></th><th class='header'></th>
</tr>
</thead>
<tbody id=''domsnap_list>";
$tmp = $lv->domain_snapshots_list($res);
if (!empty($tmp)) {
sort($tmp);
for ($ii = 0; $ii < sizeof($tmp); $ii++) {
$snap = $tmp[$ii];
$date = date("D d M Y",$snap);
$time = date("H:i:s",$snap);
$info = $lv->domain_snapshot_get_info($res, $snap);
if(empty($info)){
$info = "Click to change description";
$val = "";}
else
$val = $info;
echo "<tr style='background-color:".bcolor($ii)."'>
<td>".($ii+1)."</td>
<td>$snap</td>
<td>$date</td>
<td>$time</td>
<td><form method='post' action='?subaction=snap-desc&amp;uuid=$uuid&amp;snap=$snap' /><span class='snapdesc'>
<span class='text'><a href='#'> $info </a></span>
<input class='input' type='text' name='snapdesc' value='$val' val='snapdesc' hidden placeholder='Click to change description' title='Click to change description'/>
</span></form>
</td>
<td>
revert &nbsp;<a href='?subaction=snap-revert&amp;uuid=$uuid&amp;snap=$snap'><i class='fa fa-refresh lightblue'></i></a>
</td>
<td>
delete&nbsp;<a href='?subaction=snap-delete&amp;uuid=$uuid&amp;snap=$snap'><i class='fa fa-trash red'></i></a>
</td>
</tr>";
}
}
else
echo "<tr><td>no snapshots</td>
<td>none</td>
<td>N/A</td>
<td>N/A</td>
<td>N/A</td>
<td>N/A</td>
</tr>";
echo '</tbody></table>';
*/
echo '</td></tr>';
}
}
echo '</tbody></table>';
}
/* End Snapshot information */
unset($name, $val);
if($msg){
if(strpos($msg, "rror:"))
$color = 'red-text';
else
$color = 'green-text';
echo "<script type='text/javascript'>$(function() { $('#countdown').html('<span class=\"".$color."\">".$msg."</span>');}); </script>";
}
?>
<? if ($display['tabs']==0): ?>
<input type="button" id="btnAddVM" value="Add VM"/>
<? endif ?>
<style>
div.four{font-family:arimo;font-size:12px;color:#A0A0A0;width:300px}
div.four label{float:left;display:table-cell;width:25%}
div.four label:nth-child(4n+4){float:none;clear:both}
div.four label.cpu1{width:27%}
div.four label.cpu2{width:33%}
div.Panel{float:unset;margin:4px;height:inherit;border:none;}
div.Panel:hover{background-color:unset;}
div.PanelText{display:none;}
</style>
<script src="/plugins/dynamix.vm.manager/javascript/vmmanager.js"></script>
<script>
function vncOpen() {
$.post('/plugins/dynamix.vm.manager/classes/vnc.php',{cmd:'open',root:'<?=$docroot?>',file:'<?=$docroot?>/plugins/dynamix.vm.manager/vncconnect.vnc'},function(data) {
window.location.href = data;
});
}
function toggle_id(itemID){
if ((document.getElementById(itemID).style.display == 'none')) {
slideDownRows($('#'+itemID));
} else {
slideUpRows($('#'+itemID));
}
return false;
}
function showInput(){
$(this).off('click');
$(this).siblings('input').each(function(){$(this).show();});
$(this).siblings('input').focus();
$(this).hide();
}
function hideInput(){
$(this).hide();
$(this).siblings('span').show();
$(this).siblings('span').click(showInput);
}
<?if ($display['resize']):?>
function resize(bind) {
var width = [];
var h = $('#kvm_list').height();
var s = Math.max(window.innerHeight-300,370);
if (h>s || bind) {
$('#kvm_list').height(s);
$('#kvm_table tbody tr:first-child td').each(function(){width.push($(this).width());});
$('#kvm_table thead tr th').each(function(i){$(this).width(width[i]);});
if (!bind) $('#kvm_table thead,#kvm_table tbody').addClass('fixed');
}
}
<?endif;?>
$(function() {
<?for ($i=0; $i < sizeof($doms); $i++):?>
<? $name = $doms[$i];?>
<? $res = $lv->get_domain_by_name($name);?>
<? $uuid = $lv->domain_get_uuid($res);?>
$('.vcpu<?=$i?>').tooltipster({
trigger:'custom',
triggerOpen:{mouseenter:true,click:true,touchstart:true},
contentAsHTML:true,
animation:'grow',
triggerClose:{click:true,scroll:true,mouseleave:true,delay:1},
interactive:true,
viewportAware:true,
functionBefore:function(instance,helper){instance.content("<?=showCPUs($uuid)?>");}
});
<?endfor;?>
$('.text').click(showInput);
$('.input').blur(hideInput);
$('#btnAddVM').click(function AddVMEvent() {
$('.tab>input#tab2').click();
});
$('.autostart').switchButton({
labels_placement: "right"
});
$('.autostart').change(function () {
$.post( "/plugins/dynamix.vm.manager/VMajax.php", { action: "domain-autostart", uuid: $(this).attr('uuid'), autostart: $(this).prop('checked'), response: "json" }, function( data ) {
$(this).prop('checked', data.autostart );
}, "json");
});
<?if ($display['resize']):?>
resize();
$(window).bind('resize',function(){resize(true);});
<?endif;?>
context.init({ preventDoubleContext: false });
<?=implode("\n\t", $contextMenus)?>
});
</script>