mirror of
https://github.com/unraid/webgui.git
synced 2026-01-06 09:39:58 -06:00
346 lines
15 KiB
Plaintext
346 lines
15 KiB
Plaintext
Menu="VMs:1"
|
|
Title="Virtual Machines"
|
|
Tag="columns"
|
|
Cond="is_file('/var/run/libvirt/libvirtd.pid')"
|
|
Markdown="false"
|
|
---
|
|
<?PHP
|
|
/* Copyright 2005-2023, Lime Technology
|
|
* Copyright 2015-2023, Derek Macias, Eric Schultz, Jon Panozzo.
|
|
* Copyright 2012-2023, Bergware International.
|
|
*
|
|
* 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.
|
|
*/
|
|
?>
|
|
<?
|
|
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
|
|
|
$cpus = cpu_list();
|
|
$hover = in_array($theme,['white','azure']) ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.1)';
|
|
$bgcolor = in_array($theme,['white','azure']) ? '#f2f2f2' : '#1c1c1c';
|
|
$fgcolor = in_array($theme,['white','azure']) ? '#1c1c1c' : '#f2f2f2';
|
|
$incolor = $theme!='gray' ? $bgcolor : '#121510';
|
|
|
|
function showCPUs($uuid) {
|
|
global $cpus;
|
|
$vm = domain_to_config($uuid);
|
|
$vcpu = $vm['domain']['vcpu'];
|
|
echo "<div class='four'>";
|
|
foreach ($cpus as $pair) {
|
|
unset($cpu1,$cpu2);
|
|
[$cpu1, $cpu2] = my_preg_split('/[,-]/',$pair);
|
|
$check = ($vcpu && in_array($cpu1, $vcpu)) ? 'fa-circle orange-text':'fa-circle-o';
|
|
if (!$cpu2) {
|
|
echo "<label><i class='fa fa-fw $check'></i> cpu $cpu1</label>";
|
|
} else {
|
|
echo "<label class='cpu1'><i class='fa fa-fw $check'></i> cpu $cpu1 / $cpu2</label>";
|
|
$check = ($vcpu && in_array($cpu2, $vcpu)) ? 'fa-circle orange-text':'fa-circle-o';
|
|
echo "<label class='cpu2'><i class='fa fa-fw $check'></i></label>";
|
|
}
|
|
}
|
|
echo "</div>";
|
|
}
|
|
function vsize($size,$expand=true) {
|
|
$units = ['','K','M','G','T','P','E','Z','Y'];
|
|
if ($expand) {
|
|
$size = str_replace(['B',' ',',', '.'],'',strtoupper($size));
|
|
[$c1,$c2] = my_preg_split('/(?<=[0-9])(?=[A-Z])/',$size);
|
|
return $c1 * pow(1024,array_search($c2,$units)?:0);
|
|
} else {
|
|
$base = $size ? floor(log($size,1024)) : 0;
|
|
return $size/pow(1024,$base).$units[$base];
|
|
}
|
|
}
|
|
|
|
$uuid = unscript(_var($_GET,'uuid'));
|
|
$subaction = _var($_GET,'subaction');
|
|
if (isset($_GET['refresh'])) {
|
|
$vm = unscript(_var($_GET,'name'));
|
|
if ($lv->domain_is_active($vm)) {
|
|
echo "<meta http-equiv='refresh' content='5; url=/VMs?name=$vm&refresh=true'>";
|
|
$msg = "Waiting for $vm to shutdown...";
|
|
} else {
|
|
$msg = "$vm has been shutdown";
|
|
}
|
|
}
|
|
if ($subaction) {
|
|
$vm = $lv->domain_get_name_by_uuid($uuid);
|
|
if ($subaction == 'disk-resize') {
|
|
$capacity = vsize($_GET['cap']);
|
|
if ($capacity > vsize($_GET['oldcap'])) {
|
|
shell_exec("qemu-img resize -q ".escapeshellarg(unscript($_GET['disk']??''))." ".vsize($capacity,0));
|
|
$msg = $vm." disk capacity has been changed to {$_GET['cap']}";
|
|
} else {
|
|
$msg = "Error: disk capacity has to be greater than {$_GET['oldcap']}";
|
|
}
|
|
} elseif ($subaction == 'disk-remove') {
|
|
$msg = $lv->domain_disk_remove($vm,_var($_GET,'dev'))
|
|
? "$vm disk has been removed"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-create') {
|
|
$msg = $lv->domain_snapshot_create($vm)
|
|
? "Snapshot for $vm has been created"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-delete') {
|
|
$msg = $lv->domain_snapshot_delete($vm,_var($_GET,'snap'))
|
|
? "Snapshot for $vm has been deleted"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-revert') {
|
|
$msg = $lv->domain_snapshot_revert($vm,_var($_GET,'snap'))
|
|
? "$vm has been reverted"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-desc') {
|
|
$msg = $lv->snapshot_set_metadata($vm,_var($_GET,'snap'),_var($_POST,'snapdesc'))
|
|
? "Snapshot description for $vm has been saved"
|
|
: "Error: ".$lv->get_last_error();
|
|
}
|
|
}
|
|
if ($libvirt_running=='yes') $vms = $lv->get_domains() ?: [];
|
|
if (empty($vms)) {
|
|
$msg = $libvirt_running=='yes'
|
|
? 'No VMs defined. Create from template or add XML.'
|
|
: 'Libvirt is not running. Goto Settings tab then click Start.';
|
|
}
|
|
?>
|
|
<link type="text/css" rel="stylesheet" href="<?autov('/webGui/styles/jquery.switchbutton.css')?>">
|
|
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.filetree.css")?>">
|
|
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/jquery.ui.css")?>">
|
|
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
|
|
<style>
|
|
th.th1{width:25%}
|
|
th.th2{width:15%}
|
|
th.th3{width:80px}
|
|
div.four{font-size:1.1rem;width:260px}
|
|
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:32%}
|
|
div.four label.cpu2{width:26%}
|
|
div.template,div#dialogWindow,input#upload{display:none}
|
|
table.domdisk thead tr th:nth-child(1){width:56%!important}
|
|
table.domdisk thead tr th:nth-child(n+2){width:8%!important}
|
|
table.domdisk thead tr th:nth-child(1){padding-left:72px}
|
|
table.domdisk tbody tr td:nth-child(1){padding-left:72px}
|
|
table.domdisk tbody tr:nth-child(even){background-color:transparent!important}
|
|
table.domdisk tbody tr:nth-child(4n-1){background-color:transparent!important}
|
|
i.mover{margin-right:8px;display:none}
|
|
#resetsort{margin-left:12px;display:inline-block;width:32px}
|
|
.fileTree{background:<?=$bgcolor?>;width:500px;max-height:320px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}
|
|
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button[disabled]{cursor:default;color:#808080;background:-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 0 no-repeat,-webkit-gradient(linear,left top,right top,from(#404040),to(#808080)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#404040),to(#404040)) 0 100% no-repeat,-webkit-gradient(linear,left bottom,left top,from(#808080),to(#808080)) 100% 100% no-repeat;background:linear-gradient(90deg,#404040 0,#808080) 0 0 no-repeat,linear-gradient(90deg,#404040 0,#808080) 0 100% no-repeat,linear-gradient(0deg,#404040 0,#404040) 0 100% no-repeat,linear-gradient(0deg,#808080 0,#808080) 100% 100% no-repeat;background-size:100% 2px,100% 2px,2px 100%,2px 100%}
|
|
.dropdown-menu{z-index:10001}
|
|
</style>
|
|
<table id="kvm_table" class="tablesorter four shift">
|
|
<thead><tr><th class="th1"><a id="resetsort" class="nohand" onclick="resetSorting()" title="Reset sorting"><i class="fa fa-th-list"></i></a>_(Name)_</th><th class="th2">_(Description)_</th><th>_(CPUs)_</th><th>_(Memory)_</th><th>_(vDisks)_</th><th>_(Graphics)_</th><th class="th3">_(Autostart)_</th></tr></thead>
|
|
<tbody id="kvm_list"><tr><td colspan='8'></td></tr></tbody>
|
|
</table>
|
|
<input type="button" onclick="addVM()" id="btnAddVM" value="_(Add VM)_" style="display:none">
|
|
<input type="button" onclick="startAll()" value="_(Start All)_" style="display:none">
|
|
<input type="button" onclick="stopAll()" value="_(Stop All)_" style="display:none">
|
|
|
|
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
|
|
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/dynamix.vm.manager.js')?>"></script>
|
|
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/vmmanager.js')?>"></script>
|
|
<script src="<?autov("/webGui/javascript/jquery.filetree.js")?>"></script>
|
|
<script>
|
|
<?if (_var($display,'resize')):?>
|
|
function resize() {
|
|
$('#kvm_list').height(Math.max(window.innerHeight-340,330));
|
|
$('#kvm_table thead,#kvm_table tbody').removeClass('fixed');
|
|
$('#kvm_table thead tr th').each(function(){$(this).width($(this).width());});
|
|
$('#kvm_table tbody tr td').each(function(){$(this).width($(this).width());});
|
|
$('#kvm_table thead,#kvm_table tbody').not('.child').addClass('fixed');
|
|
}
|
|
<?endif;?>
|
|
function resetSorting() {
|
|
if ($.cookie('lockbutton')==null) return;
|
|
$('input[type=button]').prop('disabled',true);
|
|
$.post('/plugins/dynamix.vm.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
|
|
}
|
|
function changemedia(uuid,dev,bus,file) {
|
|
if (file === "--select") getisoimage(uuid,dev,bus,file);
|
|
if (file === "--eject") ajaxVMDispatch({action:"change-media", uuid:uuid , cdrom:"" , dev:dev , bus:bus , file:file}, "loadlist");
|
|
|
|
}
|
|
function getisoimage(uuid,dev,bus,file){
|
|
var root = <?= '"'.$domain_cfg["MEDIADIR"].'"';?>;
|
|
var match= ".iso";
|
|
var box = $("#dialogWindow");
|
|
box.html($("#templateISO").html());
|
|
|
|
box.find('#target').attr('data-pickroot',root).attr('data-picktop',root).attr('data-pickmatch',match).fileTreeAttach(null,null,function(path){
|
|
var bits = path.substr(1).split('/');
|
|
var auto = bits.length>3 ? '' : share;
|
|
box.find('#target').val(path+auto).change();
|
|
});
|
|
var height = 100;
|
|
box.dialog({
|
|
title: "Select ISO",
|
|
resizable: false,
|
|
width: 600,
|
|
height: 300,
|
|
modal: true,
|
|
show: {effect:'fade', duration:250},
|
|
hide: {effect:'fade', duration:250},
|
|
buttons: {
|
|
"_(Insert)_": function(){
|
|
var target = box.find('#target');
|
|
if (target.length) {
|
|
target = target.val();
|
|
if (!target ) {errorTarget(); return;}
|
|
} else target = '';
|
|
box.find('#target').prop('disabled',true);
|
|
ajaxVMDispatch({action:"change-media", uuid:uuid , cdrom:"" , dev:dev , bus:bus , file:target}, "loadlist");
|
|
box.dialog('close');
|
|
},
|
|
"_(Cancel)_": function(){
|
|
box.dialog('close');
|
|
}
|
|
}
|
|
});
|
|
dialogStyle();
|
|
}
|
|
function dialogStyle() {
|
|
$('.ui-dialog-titlebar-close').css({'background':'transparent','border':'none','font-size':'1.8rem','margin-top':'-14px','margin-right':'-18px'}).html('<i class="fa fa-times"></i>').prop('title') ;
|
|
$('.ui-dialog-title').css({'text-align':'center','width':'100%','font-size':'1.8rem'});
|
|
$('.ui-dialog-content').css({'padding-top':'15px','vertical-align':'bottom'});
|
|
$('.ui-button-text').css({'padding':'0px 5px'});
|
|
}
|
|
var sortableHelper = function(e,ui){
|
|
var child = ui.next();
|
|
if (child.is(':visible')) child.addClass('unhide').hide();
|
|
ui.children().each(function(){$(this).width($(this).width());});
|
|
return ui;
|
|
};
|
|
function LockButton() {
|
|
if ($.cookie('lockbutton')==null) {
|
|
$.cookie('lockbutton','lockbutton');
|
|
$('#resetsort').removeClass('nohand').addClass('hand');
|
|
$('i.mover').show();
|
|
$('#kvm_list .sortable').css({'cursor':'move'});
|
|
<?if ($themes1):?>
|
|
$('div.nav-item.LockButton a').prop('title',"_(Lock sortable items)_");
|
|
$('div.nav-item.LockButton b').removeClass('icon-u-lock green-text').addClass('icon-u-lock-open red-text');
|
|
<?endif;?>
|
|
$('div.nav-item.LockButton span').text("_(Lock sortable items)_");
|
|
$('#kvm_list').sortable({helper:sortableHelper,items:'.sortable',cursor:'grab',axis:'y',containment:'parent',delay:100,opacity:0.5,zIndex:9999,forcePlaceholderSize:true,
|
|
update:function(e,ui){
|
|
$('#kvm_list .sortable').each(function(){
|
|
var parent = $(this).attr('parent-id');
|
|
var child = $('tr[child-id="'+parent+'"]');
|
|
child.detach().insertAfter($(this));
|
|
if (child.hasClass('unhide')) child.removeClass('unhide').show();
|
|
});
|
|
var row = $('#kvm_list tr:first');
|
|
var names = '', index = '';
|
|
row.parent().children().find('td.vm-name').each(function(){names+=$(this).find('a').text()+';';index+=$(this).parent().parent().children().index($(this).parent())+';';});
|
|
$.post('/plugins/dynamix.vm.manager/include/UserPrefs.php',{names:names,index:index});
|
|
}});
|
|
} else {
|
|
$.removeCookie('lockbutton');
|
|
$('#resetsort').removeClass('hand').addClass('nohand');
|
|
$('i.mover').hide();
|
|
$('#kvm_list .sortable').css({'cursor':'default'});
|
|
<?if ($themes1):?>
|
|
$('div.nav-item.LockButton a').prop('title',"_(Unlock sortable items)_");
|
|
$('div.nav-item.LockButton b').removeClass('icon-u-lock-open red-text').addClass('icon-u-lock green-text');
|
|
<?endif;?>
|
|
$('div.nav-item.LockButton span').text("_(Unlock sortable items)_");
|
|
$('#kvm_list').sortable('destroy');
|
|
}
|
|
}
|
|
function loadlist() {
|
|
timers.vm = setTimeout(function(){$('div.spinner.fixed').show('slow');},500);
|
|
$.get('/plugins/dynamix.vm.manager/include/VMMachines.php',{show:$.cookie('vmshow')},function(d) {
|
|
clearTimeout(timers.vm);
|
|
var data = d.split(/\0/);
|
|
$('#kvm_list').html(data[0]);
|
|
$('head').append('<script>'+data[1]+'<\/script>');
|
|
<?if (_var($display,'resize')):?>
|
|
resize();
|
|
$(window).bind('resize',function(){resize();});
|
|
<?endif;?>
|
|
<?foreach ($vms as $vm) {
|
|
$res = $lv->get_domain_by_name($vm);
|
|
$uuid = $lv->domain_get_uuid($res);
|
|
?> $('.vcpu-<?=$uuid?>').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)?>");}
|
|
});
|
|
<?}?>
|
|
$('.autostart').switchButton({labels_placement:'right', on_label:"_(On)_", off_label:"_(Off)_"});
|
|
$('.autostart').change(function() {
|
|
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'domain-autostart',uuid:$(this).attr('uuid'),autostart:$(this).prop('checked'),response:'json'},function(data){
|
|
$(this).prop('checked', data.autostart);
|
|
},'json');
|
|
});
|
|
$('div.spinner.fixed').hide('slow');
|
|
$('input[type=button]').prop('disabled',false).show('slow');
|
|
$('.text').click(showInput);
|
|
$('.input').blur(hideInput);
|
|
});
|
|
}
|
|
$(function() {
|
|
<?if ($msg):?>
|
|
<?$color = strpos($msg, "rror:")!==false ? 'red-text':'green-text'?>
|
|
$('#countdown').html("<span class='<?=$color?>'><?=_($msg)?></span>");
|
|
<?endif;?>
|
|
$('#btnAddVM').click(function AddVMEvent(){$('.tab>input#tab2').click();});
|
|
$.removeCookie('lockbutton');
|
|
loadlist();
|
|
});
|
|
</script>
|
|
|
|
<div id="dialogWindow"></div>
|
|
<div markdown="1" id="templateISO" class="template">
|
|
_(ISO Image)_:
|
|
: <input type="text" id="target" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="" data-pickmatch="" data-pickroot="" data-picktop="">
|
|
|
|
</div>
|
|
|
|
<div id="dialogWindow"></div>
|
|
<div markdown="1" id="templateClone" class="template">
|
|
<html <?=$display['rtl']?>lang="<?=strtok($locale,'_')?:'en'?>">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="format-detection" content="telephone=no">
|
|
<meta name="viewport" content="width=1600">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
<meta name="referrer" content="same-origin">
|
|
<link type="text/css" rel="stylesheet" href="<?autov("/webGui/styles/default-fonts.css")?>">
|
|
</head>
|
|
<body>
|
|
<br>
|
|
<table>
|
|
<tr><td>_(VM Being Cloned)_:</td>
|
|
<td><span id="VMBeingCloned"></span></td></tr>
|
|
|
|
<tr><td>_(New VM)_:</td>
|
|
<td><input type="text" id="target" autocomplete="off" spellcheck="false" value="" ></td></tr>
|
|
|
|
<tr><td>_(Overwrite)_:</td>
|
|
<td><input type="checkbox" id="Overwrite" value="" ></td></tr>
|
|
|
|
<tr><td>_(Start Cloned VM)_:</td>
|
|
<td><input type="checkbox" id="Start" value="" ></td></tr>
|
|
|
|
<tr><td>_(Edit VM after clone)_:</td>
|
|
<td><input type="checkbox" id="Edit" value="" ></td></tr>
|
|
|
|
<tr><td>_(Check Free Space)_:</td>
|
|
<td><input type="checkbox" id="Free" value="" ></td></tr>
|
|
|
|
</table>
|
|
</body>
|
|
</html>
|
|
</div> |