mirror of
https://github.com/unraid/webgui.git
synced 2026-04-28 13:59:21 -05:00
b3fe8659fe
Add option to eject and insert CD ISOs on VM Machines page Add boot order on VM Machines page
301 lines
13 KiB
Plaintext
301 lines
13 KiB
Plaintext
Menu="VMs:1"
|
|
Title="Virtual Machines"
|
|
Tag="columns"
|
|
Cond="is_file('/var/run/libvirt/libvirtd.pid')"
|
|
Markdown="false"
|
|
---
|
|
<?PHP
|
|
/* Copyright 2005-2021, Lime Technology
|
|
* Copyright 2015-2021, Derek Macias, Eric Schultz, Jon Panozzo.
|
|
* Copyright 2012-2021, 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 = 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 = 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($_GET['uuid']??'');
|
|
$subaction = $_GET['subaction'] ?? false;
|
|
if (isset($_GET['refresh'])) {
|
|
$vm = unscript($_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 {
|
|
echo "<script>clearHistory();</script>";
|
|
$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, $_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, $_GET['snap'])
|
|
? "Snapshot for $vm has been deleted"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-revert') {
|
|
$msg = $lv->domain_snapshot_revert($vm, $_GET['snap'])
|
|
? "$vm has been reverted"
|
|
: "Error: ".$lv->get_last_error();
|
|
} elseif ($subaction == 'snap-desc') {
|
|
$msg = $lv->snapshot_set_metadata($vm, $_GET['snap'], $_POST['snapdesc'])
|
|
? "Snapshot description for $vm has been saved"
|
|
: "Error: ".$lv->get_last_error();
|
|
}
|
|
echo "<script>clearHistory();</script>";
|
|
}
|
|
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%}
|
|
table.domdisk thead th:nth-child(1){width:76%;}
|
|
table.domdisk thead th:nth-child(2){width:8%;}
|
|
table.domdisk thead th:nth-child(3){width:8%;}
|
|
table.domdisk thead th:nth-child(3){width:8%;}
|
|
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}
|
|
.fileTree{background:<?=$bgcolor?>;width:500px;max-height:320px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}
|
|
div.template,div#dialogWindow,input#upload{display:none}
|
|
.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 href="#" style="cursor:hand;margin-left:12px;display:inline-block;width:32px" 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 ($display['resize']):?>
|
|
function resize(bind) {
|
|
var width = [];
|
|
var h = $('#kvm_list').height();
|
|
var s = Math.max(window.innerHeight-340,330);
|
|
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 resetSorting() {
|
|
$('input[type=button]').prop('disabled',true);
|
|
$.post('/plugins/dynamix.vm.manager/include/UserPrefs.php',{reset:true},function(){loadlist();});
|
|
}
|
|
var sortableHelper = function(e,i){
|
|
i.children().each(function(){
|
|
$(this).width($(this).width());
|
|
});
|
|
return i;
|
|
};
|
|
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 = "/mnt/user/isos" ;
|
|
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',"_(Close)_").prop('onclick',null).off('click').click(function(){box.dialog('close');});
|
|
$('.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'});
|
|
}
|
|
|
|
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]).sortable({helper:sortableHelper,items:'tr.sortable',cursor:'move',axis:'y',containment:'parent',delay:100,opacity:0.5,zIndex:9999,
|
|
update:function(e,ui){
|
|
$('#kvm_list').find('tr.sortable').each(function(){
|
|
var parent = $(this).attr('parent-id');
|
|
var child = $('tr[child-id="'+parent+'"]');
|
|
child.detach().insertAfter($(this));
|
|
});
|
|
var row = $('#kvm_list').find('tr:first');
|
|
var names = ''; var 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});
|
|
}});
|
|
$('head').append('<script>'+data[1]+'<\/script>');
|
|
<?if ($display['resize']):?>
|
|
resize();
|
|
$(window).bind('resize',function(){resize(true);});
|
|
<?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');
|
|
});
|
|
context.init({preventDoubleContext:false,left:true,above:false});
|
|
$('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();});
|
|
loadlist();
|
|
});
|
|
</script>
|
|
|
|
<div id="dialogWindow"></div>
|
|
<div markdown="1" id="templateISO" 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>
|
|
_(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="">
|
|
|
|
</body>
|
|
</html>
|
|
</div> |