Merge pull request #1988 from SimonFair/vmdashusage-fix-

7.x Various VM Updates
This commit is contained in:
tom mortensen
2025-02-06 12:38:31 -08:00
committed by GitHub
12 changed files with 528 additions and 16 deletions

View File

@@ -486,7 +486,7 @@ $(function() {
<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="addVM()" id="btnAddVM" value="_(Add VM)_/_(Templates)_" 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">

View File

@@ -38,6 +38,16 @@ Markdown="false"
if (strpos($strName,"User-") === false) $user = ""; else $user = ' class="user"';
?>
<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")?>">
<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')?>" charset="utf-8"></script>
<div class="vmtemplate">
<a href="/VMs/AddVM?template=<?=htmlspecialchars(urlencode($strName))?>">
<span name="<?=htmlspecialchars($strName)?>" <?=$user?>><img src="/plugins/dynamix.vm.manager/templates/images/<?=htmlspecialchars($arrTemplate['icon'])?>" title="<?=htmlspecialchars($strName)?>"></span>
@@ -46,7 +56,9 @@ Markdown="false"
</div>
<? endforeach; ?>
<br>
<center><button type='button' onclick='done()'>_(Cancel)_</button></center>
<center><button type='button' onclick='done()'>_(Cancel)_</button>
<button type='button' onclick='import_template()'>_(Import from file)_</button>
<button type='button' onclick='$("#fileupload").click();'>_(Upload)_</button></center>
<br>
<script>
@@ -59,15 +71,200 @@ function confirmRemoveUserTemplate(template) {
swal({title:"_(Proceed)_?",text:"Remove user template: " + template ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(Cancel)_"},function(p){if (p) removeUserTemplate(template); else refresh();});
}
function saveUserTemplateFile(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
});
}
function saveUserTemplateImport(name,template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-import',name:name,template:template,replace:"no"},function($return){
if ($return.success == false) {
swal({title:"_(File exists)_?",text:"Replace file: " + name.split(".")[0] ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(Proceed)_",cancelButtonText:"_(No)_"},function(p){
if (p) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-save',name:name,template:template,replace:"yes"},function($return){
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name.split(".")[0] + " " + $return.error ,type:'warning',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
if ($return.success == true) refresh();
});
}
else {
if ($return.success == false) swal({title:"_(Error occured)_?",text:"Action error " + name + " " + $return.error ,type:'error',html:true,showCancelButton:true,confirmButtonText:"_(OK)_"});
};
});
}
if ($return.success == true) refresh();
});
}
function downloadJSON(data, filename = 'data.json') {
// Convert the data to a JSON string
const jsonString = JSON.stringify(data, null, 2); // Beautify with 2 spaces
// Create a Blob from the JSON string
const blob = new Blob([jsonString], { type: 'application/json' });
// Generate a temporary URL for the Blob
const url = URL.createObjectURL(blob);
// Create a link element
const link = document.createElement('a');
link.href = url;
link.download = filename; // Specify the file name
// Trigger the download by programmatically clicking the link
link.click();
// Clean up by revoking the object URL
URL.revokeObjectURL(url);
}
function export_template(template){
const templatejson = <?=json_encode($ut)?>;
var json = templatejson[template];
var box = $("#dialogWindow");
box.html($("#templateexport").html());
box.find('#target').attr('value',template+".json");
box.find('#target2').attr('value',"/mnt/").fileTreeAttach(null,null,function(path){
box.find('#target2').val(path).change();
});
box.dialog({
title: "_(Select File)_",
height: 530,
width: 900,
resizable: false,
modal: true,
buttons: {
"_(Save)_": function(){
var target = box.find('#target');
if (target.length) target = target.val(); else target = '';
var target2 = box.find('#target2');
if (target2.length) target2 = target2.val(); else target2 = '';
box.find('#target').prop('disabled',true);
box.find('#target2').prop('disabled',true);
saveUserTemplateFile(target2+target,json);
box.dialog('close');
},
"_(Cancel)_": function(){
box.dialog('close');
},
"_(Download)_": function(){
downloadJSON(json,template+'.json');
box.dialog('close');
}
}
});
dialogStyle();
}
function import_template(){
var box = $("#dialogWindow");
box.html($("#templateimport").html());
box.find('#targetimp').attr('value',"/mnt").fileTreeAttach(null,null,function(path){
box.find('#targetimp').val(path).change();
});
box.dialog({
title: "_(Select File)_",
height: 530,
width: 900,
resizable: false,
modal: true,
buttons: {
"_(Import)_": function(){
var targetimp = box.find('#targetimp');
if (targetimp.length) targetimp = targetimp.val(); else targetimp = '';
box.find('#targetimp').prop('disabled',true);
saveUserTemplateImport(targetimp,"*file");
box.dialog('close');
},
"_(Cancel)_": function(){
box.dialog('close');
}
}
});
dialogStyle();
}
function dialogStyle() {
$('.ui-dialog-titlebar-close').css({'display':'none'});
$('.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 exportTemplate(template) {
$.post('/plugins/dynamix.vm.manager/include/VMajax.php',{action:'vm-template-export',template:template},function(){
refresh();});
}
function uploadFile(files,index,start,time) {
var file = files[0];
var blob = file;
reader.onloadend = function(e){
uploadedData = JSON.parse(e.target.result);
//alert('File uploaded successfully. Click "Process JSON" to proceed.');
saveUserTemplateImport(file.name,uploadedData);
};
reader.readAsText(blob);
}
function startUpload(files) {
if (files.length==0) return;
reader = new FileReader();
//window.onbeforeunload = function(e){return '';};
uploadFile(files);
}
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(){
$('div.vmtemplate').each(function(){
var templatename = $(this).find('span').attr('name');
$(this).find('span.user').append('<i class="fa fa-trash bin" title="_(Remove User Template)_" onclick="confirmRemoveUserTemplate(&quot;' + templatename + '&quot;);return false"></i>');
$(this).find('span.user').append('<br><i class="fa fa-external-link-square export" title="_(Export User Template)_" onclick="export_template(&quot;' + templatename + '&quot;);return false"></i>');
$(this).hover(function(){$(this).find('i.bin').show();},function(){$(this).find('i.bin').hide();});
$(this).hover(function(){$(this).find('i.export').show();},function(){$(this).find('i.export').hide();});
});
});
</script>
<style>
i.bin{display:none;font-size:1.8rem;position:absolute;margin-left:12px}
</style>
i.export{display:none;font-size:1.8rem;position:left;margin-left:1px}
</style>
<div id="dialogWindow"></div>
<div id="iframe-popup"></div>
<div id="templateexport" class="template">
<dl>
<dt>_(Save File Name)_:</dt>
<dd><input type="text" id="target" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="iso"></dd>
<dt>_(Save Path)_:</dt>
<dd><input type="text" id="target2" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="iso"></dd>
</dl>
</div>
<div id="templateimport" class="template">
<dl>
<dt>_(File for import)_:</dt>
<dd><input type="text" id="targetimp" autocomplete="off" spellcheck="false" value="" data-pickcloseonfile="true" data-pickfolders="true" data-pickfilter="json"></dd>
</dl>
</div>
<input hidden type="file" id="fileupload" value="" onchange="startUpload(this.files)"

View File

@@ -78,18 +78,22 @@ foreach ($vms as $vm) {
$vmrcurl = '';
$graphics = '';
$virtual = false ;
if (isset($arrConfig['gpu'][0]['model'])) {$vrtdriver=" "._("Driver").strtoupper(":{$arrConfig['gpu'][0]['model']} "); $vrtmodel =$arrConfig['gpu'][0]['model'];} else $vrtdriver = "";
if (isset($arrConfig['gpu'][0]['render']) && $vrtmodel == "virtio3d") {
if (isset($arrConfig['gpu'][0]['render']) && $arrConfig['gpu'][0]['render'] == "auto") $vrtdriver .= "<br>"._("RenderGPU").":"._("Auto"); else $vrtdriver .= "<br>"._("RenderGPU").":{$arrValidGPUDevices[$arrConfig['gpu'][0]['render']]['name']}";
}
if ($vmrcport > 0) {
$wsport = $lv->domain_get_ws_port($res);
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
if ($vmrcprotocol == "vnc") $vmrcscale = "&resize=scale"; else $vmrcscale = "";
$vmrcurl = autov('/plugins/dynamix.vm.manager/'.$vmrcprotocol.'.html',true).$vmrcscale.'&autoconnect=true&host='._var($_SERVER,'HTTP_HOST');
if ($vmrcprotocol == "spice") $vmrcurl .= '&vmname='. urlencode($vm) .'&port=/wsproxy/'.$vmrcport.'/'; else $vmrcurl .= '&port=&path=/wsproxy/'.$wsport.'/';
$graphics = strtoupper($vmrcprotocol).":".$vmrcport."\n";
$graphics = strtoupper($vmrcprotocol).':'._($auto)."$vrtdriver\n";
$virtual = true ;
} elseif ($vmrcport == -1 || $autoport) {
$vmrcprotocol = $lv->domain_get_vmrc_protocol($res);
if ($autoport == "yes") $auto = "auto"; else $auto="manual";
$graphics = strtoupper($vmrcprotocol).':'._($auto)."\n";
$graphics = strtoupper($vmrcprotocol).':'._($auto)."$vrtdriver\n";
$virtual = true ;
}
if (!empty($arrConfig['gpu'])) {

View File

@@ -872,6 +872,50 @@ case 'vm-template-remove':
$arrResponse = ['success' => true];
break;
case 'vm-template-save':
$template = $_REQUEST['template'];
$name = $_REQUEST['name'];
$replace = $_REQUEST['replace'];
if (is_file($name) && $replace == "no"){
$arrResponse = ['success' => false, 'error' => _("File exists.")];
} else {
$error = file_put_contents($name,json_encode($template));
if ($error !== false) $arrResponse = ['success' => true];
else {
$arrResponse = ['success' => false, 'error' => _("File write failed.")];
}
}
break;
case 'vm-template-import':
$template = $_REQUEST['template'];
$name = $_REQUEST['name'];
$replace = $_REQUEST['replace'];
$templateslocation = "/boot/config/plugins/dynamix.vm.manager/savedtemplates.json";
if ($template==="*file") {
$template=json_decode(file_get_contents($name));
}
$namepathinfo = pathinfo($name);
$template_name = $namepathinfo['filename'];
if (is_file($templateslocation)){
$ut = json_decode(file_get_contents($templateslocation),true) ;
if (isset($ut[$template_name]) && $replace == "no"){
$arrResponse = ['success' => false, 'error' => _("Template exists.")];
} else {
$ut[$template_name] = $template;
$error = file_put_contents($templateslocation,json_encode($ut,JSON_PRETTY_PRINT));;
if ($error !== false) $arrResponse = ['success' => true];
else {
$arrResponse = ['success' => false, 'error' => _("Tempalte file write failed.")];
}
}
}
break;
default:
$arrResponse = ['error' => _('Unknown action')." '$action'"];
break;

View File

@@ -55,6 +55,22 @@
return ($tmp) ? $tmp : $this->_set_last_error();
}
function get_domain_capabilities($emulatorbin, $arch, $machine, $virttype, $xpath) {
#@conn [resource]: resource for connection
#@emulatorbin [string]: optional path to emulator
#@arch [string]: optional domain architecture
#@machine [string]: optional machine type
#@virttype [string]: optional virtualization type
#@flags [int] : extra flags; not used yet, so callers should always pass 0
#@xpath [string]: optional xPath query to be applied on the result
#Returns: : domain capabilities XML from the connection or FALSE for error
$tmp = libvirt_connect_get_domain_capabilities($this->conn, $emulatorbin, $arch, $machine, $virttype, 0, $xpath);
return ($tmp) ? $tmp : $this->_set_last_error();
}
function get_machine_types($arch = 'x86_64' /* or 'i686' */) {
$tmp = libvirt_connect_get_machine_types($this->conn);
@@ -383,6 +399,10 @@
$cpucache = '';
$cpufeatures = '';
$cpumigrate = '';
$cpucheck = '';
$cpumatch = '' ;
$cpucustom = '' ;
$cpufallback = '' ;
if (!empty($domain['cpumode']) && $domain['cpumode'] == 'host-passthrough') {
$cpumode .= "mode='host-passthrough'";
$cpucache = "<cache mode='passthrough'/>";
@@ -406,6 +426,8 @@
if (!empty($domain['cpumigrate'])) $cpumigrate = " migratable='".$domain['cpumigrate']."'" ;
}
#<cpu mode='custom' match='exact' check='partial'>
#<model fallback='allow'>Skylake-Client-noTSX-IBRS</model>
$cpustr = "<cpu $cpumode $cpumigrate>
<topology sockets='1' cores='{$intCores}' threads='{$intThreads}'/>
@@ -847,6 +869,25 @@
if (($gpu['copypaste'] == "yes") && ($strProtocol == "spice")) $vmrcmousemode = "<mouse mode='server'/>" ; else $vmrcmousemode = "" ;
if ($strProtocol == "spice") $virtualaudio = "spice" ; else $virtualaudio = "none" ;
$strEGLHeadless = "";
$strAccel3d ="";
if ($strModelType == "virtio3d") {
$strModelType = "virtio";
if (!isset($gpu['render'])) $gpu['render'] = "auto";
if ($gpu['render'] == "auto") {
$strEGLHeadless = '<graphics type="egl-headless"><gl enable="yes"/></graphics>';
$strAccel3d = "<acceleration accel3d='yes'/>";
} else {
$strEGLHeadless = '<graphics type="egl-headless"><gl enable="yes" rendernode="/dev/dri/by-path/pci-0000:'.$gpu['render'].'-render"/></graphics>';
$strAccel3d ="<acceleration accel3d='yes'/>";
}}
$strDisplayOptions = "";
if ($strModelType == "qxl") {
if (empty($gpu['DisplayOptions'])) $gpu['DisplayOptions'] ="ram='65536' vram='16384' vgamem='16384' heads='1' primary='yes'";
$strDisplayOptions = $gpu['DisplayOptions'];
}
$vmrc = "<input type='tablet' bus='usb'/>
<input type='mouse' bus='ps2'/>
<input type='keyboard' bus='ps2'/>
@@ -854,8 +895,11 @@
<listen type='address' address='0.0.0.0'/>
$vmrcmousemode
</graphics>
$strEGLHeadless
<video>
<model type='$strModelType'/>
<model type='$strModelType' $strDisplayOptions>
$strAccel3d
</model>
<address type='pci' domain='0x0000' bus='0x00' slot='0x1e' function='0x0'/>
</video>
<audio id='1' type='$virtualaudio'/>";
@@ -2158,7 +2202,7 @@
}
function domain_get_vnc_port($domain) {
$tmp = $this->get_xpath($domain, '//domain/devices/graphics/@port', false);
$tmp = $this->get_xpath($domain, '//domain/devices/graphics[@type="spice" or @type="vnc"]/@port', false);
$var = (int)$tmp[0];
unset($tmp);
@@ -2166,7 +2210,7 @@
}
function domain_get_vmrc_autoport($domain) {
$tmp = $this->get_xpath($domain, '//domain/devices/graphics/@autoport', false);
$tmp = $this->get_xpath($domain, '//domain/devices/graphics[@type="spice" or @type="vnc"]/@autoport', false);
$var = $tmp[0];
unset($tmp);
@@ -2189,6 +2233,44 @@
$var = $tmp[0];
unset($tmp);
if ($var=="virtio") {
$tmp = $this->get_xpath($domain, '//domain/devices/video/model/acceleration/@accel3d', false);
if ($tmp[0] == "yes") $var = "virtio3d";
unset($tmp);
}
return $var;
}
function domain_get_vnc_render($domain) {
$tmp = $this->get_xpath($domain, '//domain/devices/graphics[@type="egl-headless"]/gl/@rendernode', false);
if (!$tmp)
return 'auto';
$var = $tmp[0];
unset($tmp);
if (!str_contains($var,"pci")) $var = trim(shell_exec("udevadm info -q symlink -r $var"));
$var = str_replace(['/dev/dri/by-path/pci-0000:','-render'],['',''],$var);
return $var;
}
function domain_get_vnc_display_options($domain) {
$tmp = $this->get_xpath($domain, '//domain/devices/video/model/@heads', false);
if (!$tmp)
$heads=1;
$heads = $tmp[0];
unset($tmp);
$tmp = $this->get_xpath($domain, '//domain/devices/video/model/@vram', false);
if (!$tmp)
$vram=16384/1024;
$vram = $tmp[0]/1024;
unset($tmp);
$var = "H$heads.{$vram}M";
return $var;
}
@@ -2267,6 +2349,40 @@
return $var;
}
# <cpu mode='custom' match='exact' check='partial'>
# <model fallback='allow'>Skylake-Client-noTSX-IBRS</model>
function domain_get_cpu_custom($domain) {
$tmp = $this->get_xpath($domain, '//domain/cpu/@match', false);
if (!$tmp)
$tmp[0] = '';
$var['match'] = trim($tmp[0]);
unset($tmp);
$tmp = $this->get_xpath($domain, '//domain/cpu/@check', false);
if (!$tmp)
$tmp[0] = '';
$var['check'] = trim($tmp[0]);
unset($tmp);
$tmp = $this->get_xpath($domain, '//domain/cpu/model/@fallback', false);
if (!$tmp)
$tmp[0] = '';
$var['fallback'] = trim($tmp[0]);
unset($tmp);
$tmp = $this->get_xpath($domain, '//domain/cpu/model', false);
if (!$tmp)
$tmp[0] = '';
$var['model'] = trim($tmp[0]);
unset($tmp);
return $var;
}
function domain_get_cpu_migrate($domain) {
$tmp = $this->get_xpath($domain, '//domain/cpu/@migratable', false);

View File

@@ -708,6 +708,55 @@ private static $encoding = 'UTF-8';
] ;
#<model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
$arrDisplayOptions = [
"H1.16M" => [
"text" => "1 Display 16Mb Memory",
"qxlxml" => "ram='65536' vram='16384' vgamem='16384' heads='1' primary='yes'",
],
"H1.32M" => [
"text" => "1 Display 32Mb Memory",
"qxlxml" => "ram='65536' vram='32768' vgamem='32768' heads='1' primary='yes'",
],
"H1.64M" => [
"text" => "1 Display 64Mb Memory",
"qxlxml" => "ram='65536' vram='65536' vram64='65535' vgamem='65536' heads='1' primary='yes'",
],
"H1.128M" => [
"text" => "1 Display 128Mb Memory",
"qxlxml"=> "ram='65536' vram='131072' vram64='131072' vgamem='65536' heads='1' primary='yes'",
],
"H1.256M" => [
"text" => "1 Display 256Mb Memory",
"qxlxml" => "ram='65536' vram='262144' vram64='262144' vgamem='65536' heads='1' primary='yes'",
],
"H2.64M" => [
"text" => "2 Displays 64Mb Memory",
"qxlxml" => "ram='65536' vram='65536' vram64='65535' vgamem='65536' heads='2' primary='yes'",
],
"H2.128M" => [
"text" => "2 Displays 128Mb Memory",
"qxlxml" => "ram='65536' vram='131072'vram64='131072' vgamem='65536' heads='2' primary='yes'",
],
"H2.256M" => [
"text" => "2 Displays 256Mb Memory",
"qxlxml" => "ram='65536' vram='262144'vram64='262144' vgamem='65536' heads='2' primary='yes'",
],
"H4.64M" => [
"text" => "4 Displays 64Mb Memory",
"qxlxml" => "ram='65536' vram='65536' vram64='65535' vgamem='65536' heads='4' primary='yes'",
],
"H4.128M" => [
"text" => "4 Displays 128Mb Memory",
"qxlxml" => "ram='65536' vram='131072'vram64='131072' vgamem='65536' heads='4' primary='yes'",
],
"H4.256M" => [
"text" => "4 Displays 256Mb Memory",
"qxlxml"=> "ram='65536' vram='262144' vram64='262144' vgamem='65536' heads='4' primary='yes'",
],
];
// Read configuration file (guaranteed to exist)
$domain_cfgfile = "/boot/config/domain.cfg";
$domain_cfg = parse_ini_file($domain_cfgfile);
@@ -866,6 +915,15 @@ private static $encoding = 'UTF-8';
}
}
// Attempt to get the boot_vga driver for this pci device
$strBootVGA = '';
if (is_file('/sys/bus/pci/devices/0000:' . $arrMatch['id'] . '/boot_vga') && $strClass == 'vga') {
$strFileVal = file_get_contents('/sys/bus/pci/devices/0000:' . $arrMatch['id'] . '/boot_vga');
if (!empty($strFileVal)) {
$strBootVGA = trim($strFileVal);
}
}
// Clean up the vendor and product name
$arrMatch['vendorname'] = sanitizeVendor($arrMatch['vendorname']);
$arrMatch['productname'] = sanitizeProduct($arrMatch['productname']);
@@ -880,6 +938,7 @@ private static $encoding = 'UTF-8';
'productname' => $arrMatch['productname'],
'class' => $strClass,
'driver' => $strDriver,
'bootvga' => $strBootVGA,
'name' => $arrMatch['vendorname'] . ' ' . $arrMatch['productname'],
'blacklisted' => $boolBlacklisted
];
@@ -1116,6 +1175,7 @@ private static $encoding = 'UTF-8';
'cirrus' => 'Cirrus',
'qxl' => 'QXL (best)',
'virtio' => 'Virtio(2d)',
'virtio3d' => 'Virtio(3d)',
'vmvga' => 'vmvga'
];
@@ -1269,6 +1329,8 @@ private static $encoding = 'UTF-8';
'autoport' => $autoport,
'copypaste' => $getcopypaste,
'guest' => ['multi' => 'off' ],
'render' => $lv->domain_get_vnc_render($res),
'DisplayOptions' => $lv->domain_get_vnc_display_options($res),
];
}
@@ -1481,7 +1543,7 @@ private static $encoding = 'UTF-8';
unset($old['devices']['input']);
// preserve vnc/spice port settings
// unset($new['devices']['graphics']['@attributes']['port'],$new['devices']['graphics']['@attributes']['autoport']);
if (!$new['devices']['graphics']) unset($old['devices']['graphics']);
unset($old['devices']['graphics']);
if (!isset($new['devices']['graphics']['@attributes']['keymap']) && isset($old['devices']['graphics']['@attributes']['keymap'])) unset($old['devices']['graphics']['@attributes']['keymap']) ;
// update parent arrays
if (!$old['devices']['hostdev']) unset($old['devices']['hostdev']);

View File

@@ -0,0 +1 @@
.fileTree{background:#f2f2f2;width:300px;max-height:150px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}

View File

@@ -0,0 +1,2 @@
.fileTree{background:#1c1c1c;width:300px;max-height:150px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}

View File

@@ -0,0 +1,2 @@
.fileTree{background:#1c1c1c;width:300px;max-height:150px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}

View File

@@ -0,0 +1,2 @@
.fileTree{background:#f2f2f2;width:300px;max-height:150px;overflow-y:scroll;overflow-x:hidden;position:absolute;z-index:100;display:none}

View File

@@ -2,3 +2,4 @@
.vmtemplate{display:inline-block;width:80px;height:90px;margin-bottom:15px;margin-left:10px;text-align:center;vertical-align:top}
.vmtemplate img{width:48px;height:48px}
.vmtemplate p{text-align:center;margin-top:8px;line-height:12px}
div.template,div#dialogWindow,input#upload{display:none}

View File

@@ -102,7 +102,9 @@
'keymap' => 'none',
'port' => -1 ,
'wsport' => -1,
'copypaste' => 'no'
'copypaste' => 'no',
'render' => 'auto',
'DisplayOptions' => ""
]
],
'audio' => [
@@ -464,9 +466,9 @@
<select id="cpu" name="domain[cpumode]" class="cpu" title="_(define type of cpu presented to this vm)_">
<?mk_dropdown_options(['host-passthrough' => _('Host Passthrough').' (' . $strCPUModel . ')', 'custom' => _('Emulated').' ('._('QEMU64').')'], $arrConfig['domain']['cpumode']);?>
</select>
<span class="advanced" id="domain_cpumigrate_text"<?=$migratehidden?>>_(Migratable)_:</span>
<span class="advanced" id="domain_cpumigrate_text" <?=$migratehidden?>>_(Migratable)_:</span>
<select name="domain[cpumigrate]" id="domain_cpumigrate" <?=$migratehidden?> class="narrow" title="_(define if migratable)_">
<select name="domain[cpumigrate]" id="domain_cpumigrate" <?=$migratehidden?> hidden class="narrow" title="_(define if migratable)_">
<?
echo mk_option($arrConfig['domain']['cpumigrate'], 'on', 'On');
echo mk_option($arrConfig['domain']['cpumigrate'], 'off', 'Off') ;
@@ -1159,8 +1161,8 @@
<?foreach ($arrConfig['gpu'] as $i => $arrGPU) {
$strLabel = ($i > 0) ? appendOrdinalSuffix($i + 1) : '';
?>
$bootgpuhidden = " hidden ";
?>
<table data-category="Graphics_Card" data-multiple="true" data-minimum="1" data-maximum="<?=count($arrValidGPUDevices)+1?>" data-index="<?=$i?>" data-prefix="<?=$strLabel?>">
<tr>
<td>_(Graphics Card)_:</td>
@@ -1249,9 +1251,28 @@
<tr class="<?if ($arrGPU['id'] != 'virtual') echo 'was';?>advanced vncmodel">
<td>_(VM Console Video Driver)_:</td>
<td>
<select id="vncmodel" name="gpu[<?=$i?>][model]" class="narrow" title="_(video for VM Console)_">
<select id="vncmodel" name="gpu[<?=$i?>][model]" class="narrow" title="_(video for VM Console)_" onchange="VMConsoleDriverChange(this)">
<?mk_dropdown_options($arrValidVNCModels, $arrGPU['model']);?>
</select>
<?if ($arrGPU['model'] == "virtio3d") $vmcrender = "" ; else $vmcrender = " hidden ";?>
<span id="vncrendertext" <?=$vncrender?>>_(Render GPU)_:</span>
<select id="vncrender" name="gpu[<?=$i?>][render]">
<?
echo mk_option($arrGPU['render'], 'auto', _('Auto'));
foreach($arrValidGPUDevices as $arrDev) {
echo mk_option($arrGPU['render'], $arrDev['id'], $arrDev['name'].' ('.$arrDev['id'].')');
}
?>
</select>
<?$arrGPU['DisplayOptions'] = htmlentities($arrDisplayOptions[$arrGPU['DisplayOptions']]['qxlxml'],ENT_QUOTES);
if ($arrGPU['model'] == "qxl") $vncdspopt = "" ; else $vncdspopt = " hidden ";?>
<span id="vncdspopttext" <?=$vncdspopt?>>_(Display(s) and RAM)_:</span>
<select id="vncdspopt" name="gpu[<?=$i?>][DisplayOptions]">
<?
foreach($arrDisplayOptions as $key => $value) {
echo mk_option($arrGPU['DisplayOptions'], htmlentities($value['qxlxml'],ENT_QUOTES), _($value['text']));
}?>
</select>
</td>
</tr>
@@ -1274,6 +1295,10 @@
<input type="text" name="gpu[<?=$i?>][rom]" autocomplete="off" spellcheck="false" data-pickcloseonfile="true" data-pickfilter="rom,bin" data-pickmatch="^[^.].*" data-pickroot="/mnt/" value="<?=htmlspecialchars($arrGPU['rom'])?>" placeholder="_(Path to ROM BIOS file)_ (_(optional)_)" title="_(Path to ROM BIOS file)_ (_(optional)_)" />
</td>
</tr>
<?
if ($arrValidGPUDevices[$arrGPU['id']]['bootvga'] == "1") $bootgpuhidden = "";
?>
<tr id="gpubootvga<?=$i?>" <?=$bootgpuhidden?>><td>_(Graphics ROM Needed?)_:</td><td><span class="orange-text"><i class="fa fa-warning"></i> _(GPU is primary adapater, vbios may be required.)_</span></td></tr>
</table>
<?if ($i == 0 || $i == 1) {?>
<blockquote class="inline_help">
@@ -1300,6 +1325,8 @@
<p class="<?if ($arrGPU['id'] != 'virtual') echo 'was';?>advanced vncmodel">
<b>Virtual Video Driver</b><br>
If you wish to assign a different video driver to use for a VM Console connection, specify one here.
QXL has an option of setting number of screens and vram.
Virtio3d allows render device to be specified or auto.(This allow GPU to be used in a VM without passthru for 3D acceleration no screen output)
</p>
<p class="vncpassword">
@@ -1350,6 +1377,7 @@
<input type="text" name="gpu[{{INDEX}}][rom]" autocomplete="off" spellcheck="false" data-pickcloseonfile="true" data-pickfilter="rom,bin" data-pickmatch="^[^.].*" data-pickroot="/mnt/" value="" placeholder="_(Path to ROM BIOS file)_ (_(optional)_)" title="_(Path to ROM BIOS file)_ (_(optional)_)" />
</td>
</tr>
<tr id="gpubootvga{{INDEX}}" hidden><td>_(Graphics ROM Needed?)_:</td><td><span class="orange-text"><i class="fa fa-warning"></i> _(GPU is primary adapater, vbios may be required.)_</span></td></tr>
</table>
</script>
@@ -2084,6 +2112,31 @@ function AutoportChange(autoport) {
}
}
function VMConsoleDriverChange(driver) {
if (driver.value != "virtio3d") {
document.getElementById("vncrender").style.visibility="hidden";
document.getElementById("vncrendertext").style.visibility="hidden";
document.getElementById("vncrender").style.display="none";
document.getElementById("vncrendertext").style.display="none";
} else {
document.getElementById("vncrender").style.display="inline";
document.getElementById("vncrender").style.visibility="visible";
document.getElementById("vncrendertext").style.display="inline";
document.getElementById("vncrendertext").style.visibility="visible";
}
if (driver.value != "qxl") {
document.getElementById("vncdspopt").style.visibility="hidden";
document.getElementById("vncdspopttext").style.visibility="hidden";
} else {
document.getElementById("vncdspopt").style.display="inline";
document.getElementById("vncdspopt").style.visibility="visible";
document.getElementById("vncdspopttext").style.display="inline";
document.getElementById("vncdspopttext").style.visibility="visible";
}
}
function ProtocolChange(protocol) {
var autoport = document.getElementById("autoport").value ;
if (autoport == "yes") {
@@ -2392,6 +2445,7 @@ $(function() {
}) ;
$("#vmform").on("change", ".gpu", function changeGPUEvent() {
const ValidGPUs = <?=json_encode($arrValidGPUDevices);?>;
var myvalue = $(this).val();
var mylabel = $(this).children('option:selected').text();
var myindex = $(this).closest('table').data('index');
@@ -2403,6 +2457,28 @@ $(function() {
slideDownRows($vnc_sections.not(isVMAdvancedMode() ? '.basic' : '.advanced'));
var MultiSel = document.getElementById("GPUMultiSel0") ;
MultiSel.disabled = true ;
if (document.getElementById("vncmodel").value == "virtio3d") {
$("#vncrender").show();
$("#vncrendertext").show();
} else {
$("#vncrender").hide();
$("#vncrendertext").hide();
document.getElementById("vncrender").style.display="none";
document.getElementById("vncrendertext").style.display="none";
}
if (document.getElementById("vncmodel").value == "qxl") {
$("#vncdspopt").show();
$("#vncdspopttext").show();
document.getElementById("vncdspopt").style.display="inline";
document.getElementById("vncdspopt").style.visibility="visible";
document.getElementById("vncdspopttext").style.display="inline";
document.getElementById("vncdspopttext").style.visibility="visible";
} else {
$("#vncdspopt").hide();
$("#vncdspopttext").hide();
document.getElementById("vncdspopt").style.display="none";
document.getElementById("vncdspopttext").style.display="none";
}
} else {
slideUpRows($vnc_sections);
$vnc_sections.filter('.advanced').removeClass('advanced').addClass('wasadvanced');
@@ -2411,8 +2487,13 @@ $(function() {
}
}
if (mylabel == "_(None)_") $("#gpubootvga"+myindex).hide();
if (myvalue != "_(virtual)_" && myvalue != '' && myvalue != "_(nogpu)_") {
if (ValidGPUs[myvalue].bootvga == "1") $("#gpubootvga"+myindex).show(); else $("#gpubootvga"+myindex).hide();
}
$romfile = $(this).closest('table').find('.romfile');
if (myvalue == 'virtual' || myvalue == '' || myvalue =="nogpu") {
if (myvalue == '_(virtual)_' || myvalue == '' || myvalue =="_(nogpu)_") {
slideUpRows($romfile.not(isVMAdvancedMode() ? '.basic' : '.advanced'));
$romfile.filter('.advanced').removeClass('advanced').addClass('wasadvanced');
} else {