mirror of
https://github.com/unraid/webgui.git
synced 2026-05-03 16:29:45 -05:00
WIP Update.
This commit is contained in:
@@ -1,111 +0,0 @@
|
||||
Title="Clone VM"
|
||||
Tag="clipboard"
|
||||
Cond="(pgrep('libvirtd')!==false)"
|
||||
Markdown="false"
|
||||
---
|
||||
<?PHP
|
||||
/* Copyright 2005-2020, Lime Technology
|
||||
* Copyright 2015-2020, Derek Macias, Eric Schultz, Jon Panozzo.
|
||||
* Copyright 2012-2020, 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.
|
||||
*/
|
||||
?>
|
||||
<?
|
||||
// add vm translations (if needed)
|
||||
if (substr($_SERVER['REQUEST_URI'],0,4) != '/VMs') {
|
||||
$vms = "$docroot/languages/$locale/vms.dot";
|
||||
if (file_exists($vms)) $language = array_merge($language,unserialize(file_get_contents($vms)));
|
||||
}
|
||||
|
||||
?>
|
||||
<style>
|
||||
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>
|
||||
|
||||
<script>
|
||||
function getCloneName(name){
|
||||
var root = "" ;
|
||||
var match= ".iso" ;
|
||||
var box = $("#dialogWindow");
|
||||
box.html($("#templateClone").html());
|
||||
|
||||
box.find('#VMBeingCloned').html(name).change() ;
|
||||
|
||||
var height = 100;
|
||||
box.dialog({
|
||||
title: "Enter Clone Name",
|
||||
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'});
|
||||
}
|
||||
|
||||
getCloneName("Test") ;
|
||||
|
||||
</script>
|
||||
|
||||
<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>
|
||||
_(VM Being Cloned)_:
|
||||
<span id="VMBeingCloned"></span><br>
|
||||
|
||||
_(New VM)_:
|
||||
<input type="text" id="target" autocomplete="off" spellcheck="false" value="" ><br>
|
||||
<tabel>
|
||||
<tr><td>_(Overwrite)_:</td>
|
||||
<td><input type="checkbox" id="Overwrite" value="" ></td></tr><br>
|
||||
|
||||
<tr><td>_(Start Cloned VM)_:</td>
|
||||
<td><input type="checkbox" id="Start" value="" ></td></tr><br>
|
||||
|
||||
_(Edit VM after clone)_:
|
||||
<input type="checkbox" id="Edit" value="" ><br>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
@@ -305,4 +305,42 @@ $(function() {
|
||||
_(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>
|
||||
@@ -1,272 +0,0 @@
|
||||
<?PHP
|
||||
/* Copyright 2005-2021, Lime Technology
|
||||
* Copyright 2015-2021, 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';
|
||||
// add translations
|
||||
if (substr($_SERVER['REQUEST_URI'],0,4) != '/VMs') {
|
||||
$_SERVER['REQUEST_URI'] = 'vms';
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
}
|
||||
require_once "$docroot/webGui/include/Helpers.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
|
||||
switch ($display['theme']) {
|
||||
case 'gray' : $bgcolor = '#121510'; $border = '#606e7f'; $top = -44; break;
|
||||
case 'azure': $bgcolor = '#edeaef'; $border = '#606e7f'; $top = -44; break;
|
||||
case 'black': $bgcolor = '#212121'; $border = '#2b2b2b'; $top = -58; break;
|
||||
default : $bgcolor = '#ededed'; $border = '#e3e3e3'; $top = -58; break;
|
||||
}
|
||||
|
||||
$strSelectedTemplate = array_keys($arrAllTemplates)[1];
|
||||
if (isset($_GET['template']) && isset($arrAllTemplates[unscript($_GET['template'])])) {
|
||||
$strSelectedTemplate = unscript($_GET['template']);
|
||||
}
|
||||
|
||||
$arrLoad = [
|
||||
'name' => '',
|
||||
'icon' => $arrAllTemplates[$strSelectedTemplate]['icon'],
|
||||
'autostart' => false,
|
||||
'form' => $arrAllTemplates[$strSelectedTemplate]['form'],
|
||||
'state' => 'shutoff'
|
||||
];
|
||||
$strIconURL = '/plugins/dynamix.vm.manager/templates/images/'.$arrLoad['icon'];
|
||||
|
||||
if (isset($_GET['uuid'])) {
|
||||
// Edit VM mode
|
||||
$res = $lv->domain_get_domain_by_uuid(unscript($_GET['uuid']));
|
||||
|
||||
if ($res === false) {
|
||||
echo "<p class='notice'>"._('Invalid VM to edit').".</p><input type='button' value=\""._('Done')."\" onclick='done()'>";
|
||||
return;
|
||||
}
|
||||
|
||||
$strIconURL = $lv->domain_get_icon_url($res);
|
||||
$arrLoad = [
|
||||
'name' => $lv->domain_get_name($res),
|
||||
'icon' => basename($strIconURL),
|
||||
'autostart' => $lv->domain_get_autostart($res),
|
||||
'form' => $arrAllTemplates[$strSelectedTemplate]['form'],
|
||||
'state' => $lv->domain_get_state($res)
|
||||
];
|
||||
|
||||
if (empty($_GET['template'])) {
|
||||
// read vm-template attribute
|
||||
$strTemplateOS = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@os');
|
||||
$strLibreELEC = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@libreelec');
|
||||
$strOpenELEC = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@openelec');
|
||||
if ($strLibreELEC) $strSelectedTemplate = 'LibreELEC';
|
||||
elseif ($strOpenELEC) $strSelectedTemplate = 'OpenELEC';
|
||||
elseif ($strTemplateOS) {
|
||||
$strSelectedTemplate = $lv->_get_single_xpath_result($res, '//domain/metadata/*[local-name()=\'vmtemplate\']/@name');
|
||||
} else {
|
||||
// legacy VM support for <6.2 but need it going forward too
|
||||
foreach ($arrAllTemplates as $strName => $arrTemplate) {
|
||||
if (!empty($arrTemplate) && !empty($arrTemplate['os']) && $arrTemplate['os'] == $strTemplateOS) {
|
||||
$strSelectedTemplate = $strName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($strSelectedTemplate) || empty($arrAllTemplates[$strSelectedTemplate])) {
|
||||
$strSelectedTemplate = 'Windows 10'; //default to Windows 10
|
||||
}
|
||||
}
|
||||
$arrLoad['form'] = $arrAllTemplates[$strSelectedTemplate]['form'];
|
||||
}
|
||||
?>
|
||||
<link type="text/css" rel="stylesheet" href="<?autov('/plugins/dynamix.vm.manager/styles/dynamix.vm.manager.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.switchbutton.css')?>">
|
||||
|
||||
<style>
|
||||
body{-webkit-overflow-scrolling:touch}
|
||||
.fileTree{background:<?=$bgcolor?>;width:300px;max-height:150px;overflow-y:scroll;overflow-x:hidden;position:relative;z-index:100;display:none}
|
||||
#vmform table{margin-top:0}
|
||||
#vmform div#title + table{margin-top:0}
|
||||
#vmform table tr{vertical-align:top;line-height:40px}
|
||||
#vmform table tr td:nth-child(odd){width:300px;text-align:right;padding-right:10px}
|
||||
#vmform table tr td:nth-child(even){width:110px}
|
||||
#vmform table tr td:first-child{padding-right:30px}
|
||||
#vmform table tr td:last-child{width:inherit}
|
||||
#vmform .multiple{position:relative}
|
||||
#vmform .sectionbutton{position:absolute;left:2px;cursor:pointer;opacity:0.4;font-size:1.4rem;line-height:17px;z-index:10;transition-property:opacity,left;transition-duration:0.1s;transition-timing-function:linear}
|
||||
#vmform .sectionbutton.remove{top:0;opacity:0.3}
|
||||
#vmform .sectionbutton.add{bottom:0}
|
||||
#vmform .sectionbutton:hover{opacity:1.0}
|
||||
#vmform .sectiontab{position:absolute;top:2px;bottom:2px;left:0;width:6px;border-radius:3px;background-color:#DDDDDD;transition-property:background,width;transition-duration:0.1s;transition-timing-function:linear}
|
||||
#vmform .multiple:hover .sectionbutton{opacity:0.7;left:4px}
|
||||
#vmform .multiple:hover .sectionbutton.remove{opacity:0.6}
|
||||
#vmform .multiple:hover .sectiontab{background-color:#CCCCCC;width:8px}
|
||||
#vmform table.multiple{margin:10px 0;background:<?=$bgcolor?>;background-size:800px 100%;background-position:-800px;background-repeat:no-repeat;background-clip:content-box;transition:background 0.3s linear}
|
||||
#vmform table.multiple:hover{background-position:0 0;}
|
||||
#vmform table.multiple td{padding:5px 0}
|
||||
span.advancedview_panel{display:none;line-height:16px;margin-top:1px}
|
||||
.basic{display:none}
|
||||
.advanced{/*Empty placeholder*/}
|
||||
.switch-button-label.off{color:inherit}
|
||||
#template_img{cursor:pointer}
|
||||
#template_img:hover{opacity:0.5}
|
||||
#template_img:hover i{opacity:1.0}
|
||||
.template_img_chooser_inner{display:inline-block;width:80px;margin-bottom:15px;margin-right:10px;text-align:center;}
|
||||
.template_img_chooser_inner img{width:48px;height:48px}
|
||||
.template_img_chooser_inner p{text-align:center;line-height:8px;}
|
||||
#template_img_chooser{width:560px;height:300px;overflow-y:scroll;position:relative}
|
||||
#template_img_chooser div:hover{background-color:#eee;cursor:pointer;}
|
||||
#template_img_chooser_outer{position:absolute;display:none;border-radius:5px;border:1px solid <?=$border?>;background:<?=$bgcolor?>;z-index:10}
|
||||
#form_content{display:none}
|
||||
#vmform .four{overflow:hidden}
|
||||
#vmform .four label{float:left;display:table-cell;width:15%;}
|
||||
#vmform .four label:nth-child(4n+4){}
|
||||
#vmform .four label.cpu1{width:28%;height:16px;line-height:16px}
|
||||
#vmform .four label.cpu2{width:3%;height:16px;line-height:16px}
|
||||
#vmform .mac_generate{cursor:pointer;margin-left:-5px;color:#08C;font-size:1.3rem;transform:translate(0px, 2px)}
|
||||
#vmform .disk{display:none}
|
||||
#vmform .disk_preview{display:inline-block;color:#BBB;transform:translate(0px, 1px)}
|
||||
span#dropbox{border:1px solid <?=$border?>;background:<?=$bgcolor?>;padding:28px 12px;line-height:72px;margin-right:16px;}
|
||||
i.fa-plus-circle,i.fa-minus-circle{margin-left:8px}
|
||||
input[type=checkbox]{margin-left:0}
|
||||
</style>
|
||||
|
||||
<span class="status advancedview_panel" style="margin-top:<?=$top?>px"><input type="checkbox" class="advancedview"></span>
|
||||
<div class="domain">
|
||||
<form id="vmform" method="POST">
|
||||
<input type="hidden" name="domain[type]" value="kvm" />
|
||||
<input type="hidden" name="template[name]" value="<?=htmlspecialchars($strSelectedTemplate)?>" />
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>_(Icon)_:</td>
|
||||
<td>
|
||||
<input type="hidden" name="template[icon]" id="template_icon" value="<?=htmlspecialchars($arrLoad['icon'])?>" />
|
||||
<img id="template_img" src="<?=htmlspecialchars($strIconURL)?>" width="48" height="48" title="_(Change Icon)_..."/>
|
||||
<div id="template_img_chooser_outer">
|
||||
<div id="template_img_chooser">
|
||||
<?
|
||||
$arrImagePaths = [
|
||||
"$docroot/plugins/dynamix.vm.manager/templates/images/*.png" => '/plugins/dynamix.vm.manager/templates/images/',
|
||||
"$docroot/boot/config/plugins/dynamix.vm.manager/templates/images/*.png" => '/boot/config/plugins/dynamix.vm.manager/templates/images/'
|
||||
];
|
||||
foreach ($arrImagePaths as $strGlob => $strIconURLBase) {
|
||||
foreach (glob($strGlob) as $png_file) {
|
||||
echo '<div class="template_img_chooser_inner"><img src="'.$strIconURLBase.basename($png_file).'" basename="'.basename($png_file).'"><p>'.basename($png_file,'.png').'</p></div>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr style="line-height: 16px; vertical-align: middle;">
|
||||
<td>_(Autostart)_:</td>
|
||||
<td><div style="margin-left:-10px;padding-top:6px"><input type="checkbox" id="domain_autostart" name="domain[autostart]" style="display:none" class="autostart" value="1" <?if ($arrLoad['autostart']) echo 'checked'?>></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<blockquote class="inline_help">
|
||||
<p>If you want this VM to start with the array, set this to yes.</p>
|
||||
</blockquote>
|
||||
|
||||
<div id="form_content"><?eval('?>'.parse_file("$docroot/plugins/dynamix.vm.manager/templates/{$arrLoad['form']}",false))?></div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="<?autov('/webGui/javascript/jquery.filedrop.js')?>"></script>
|
||||
<script src="<?autov('/webGui/javascript/jquery.filetree.js')?>" charset="utf-8"></script>
|
||||
<script src="<?autov('/webGui/javascript/jquery.switchbutton.js')?>"></script>
|
||||
<script src="<?autov('/plugins/dynamix.vm.manager/javascript/dynamix.vm.manager.js')?>"></script>
|
||||
<script>
|
||||
function isVMAdvancedMode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function isVMXMLMode() {
|
||||
return ($.cookie('vmmanager_listview_mode') == 'xml');
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$('.autostart').switchButton({
|
||||
on_label: "_(Yes)_",
|
||||
off_label: "_(No)_",
|
||||
labels_placement: "right"
|
||||
});
|
||||
$('.autostart').change(function () {
|
||||
$('#domain_autostart').prop('checked', $(this).is(':checked'));
|
||||
});
|
||||
|
||||
$('.advancedview').switchButton({
|
||||
labels_placement: "left",
|
||||
on_label: "_(XML View)_",
|
||||
off_label: "_(Form View)_",
|
||||
checked: isVMXMLMode()
|
||||
});
|
||||
$('.advancedview').change(function () {
|
||||
toggleRows('xmlview', $(this).is(':checked'), 'formview');
|
||||
$.cookie('vmmanager_listview_mode', $(this).is(':checked') ? 'xml' : 'form', { expires: 3650 });
|
||||
});
|
||||
|
||||
$('#template_img').click(function (){
|
||||
var p = $(this).position();
|
||||
p.left -= 4;
|
||||
p.top -= 4;
|
||||
$('#template_img_chooser_outer').css(p);
|
||||
$('#template_img_chooser_outer').slideDown();
|
||||
});
|
||||
$('#template_img_chooser').on('click', 'div', function (){
|
||||
$('#template_img').attr('src', $(this).find('img').attr('src'));
|
||||
$('#template_icon').val($(this).find('img').attr('basename'));
|
||||
$('#template_img_chooser_outer').slideUp();
|
||||
});
|
||||
$(document).keyup(function(e) {
|
||||
if (e.which == 27) $('#template_img_chooser_outer').slideUp();
|
||||
});
|
||||
|
||||
$("#vmform table[data-category]").each(function () {
|
||||
var category = $(this).data('category');
|
||||
|
||||
updatePrefixLabels(category);
|
||||
<?if ($arrLoad['state'] == 'shutoff'):?> bindSectionEvents(category); <?endif;?>
|
||||
});
|
||||
|
||||
$("#vmform input[data-pickroot]").fileTreeAttach();
|
||||
|
||||
var $el = $('#form_content');
|
||||
var $xmlview = $el.find('.xmlview');
|
||||
var $formview = $el.find('.formview');
|
||||
|
||||
if ($xmlview.length || $formview.length) {
|
||||
$('.advancedview_panel').fadeIn('fast');
|
||||
if (isVMXMLMode()) {
|
||||
$('.formview').hide();
|
||||
$('.xmlview').filter(function() {
|
||||
return (($(this).prop('style').display + '') === '');
|
||||
}).show();
|
||||
} else {
|
||||
$('.xmlview').hide();
|
||||
$('.formview').filter(function() {
|
||||
return (($(this).prop('style').display + '') === '');
|
||||
}).show();
|
||||
}
|
||||
} else {
|
||||
$('.advancedview_panel').fadeOut('fast');
|
||||
}
|
||||
|
||||
$("#vmform #btnCancel").click(function (){
|
||||
done();
|
||||
});
|
||||
|
||||
$('#form_content').fadeIn('fast');
|
||||
});
|
||||
</script>
|
||||
@@ -1454,4 +1454,115 @@ private static $encoding = 'UTF-8';
|
||||
if ($spicevmc || $qemuvdaagent) $copypaste = true ; else $copypaste = false ;
|
||||
return $copypaste ;
|
||||
}
|
||||
|
||||
function vm_clone($vm, $clone ,$overwrite,$start,$edit, $free, $waitID) {
|
||||
global $lv,$domain_cfg ;
|
||||
/*
|
||||
Clone.
|
||||
|
||||
Stopped only.
|
||||
|
||||
Get new VM Name
|
||||
Extract XML for VM to be cloned.
|
||||
Check if directory exists.
|
||||
Check for disk space
|
||||
|
||||
Stop VM Starting until clone is finished or fails.
|
||||
|
||||
Create new directory for Clone.
|
||||
Update paths with new directory
|
||||
|
||||
Create new UUID
|
||||
Create new MAC Address for NICs
|
||||
|
||||
Create VM Disks from source. Options full or Sparce. Method of copy?
|
||||
|
||||
release orginal VM to start.
|
||||
|
||||
If option to edit, show VMUpdate
|
||||
*/
|
||||
|
||||
#VM must be shutdown.
|
||||
$res = $lv->get_domain_by_name($vm);
|
||||
$dom = $lv->domain_get_info($res);
|
||||
$state = $lv->domain_state_translate($dom['state']);
|
||||
# if VM running shutdown. Record was running.
|
||||
if ($state != 'shutdown') $arrResponse = $lv->domain_destroy($vm) ;
|
||||
# Wait for shutdown?
|
||||
|
||||
|
||||
|
||||
$disks =$lv->get_disk_stats($vm) ;
|
||||
|
||||
$capacity = 0 ;
|
||||
|
||||
foreach($disks as $disk) {
|
||||
$file = $disk["file"] ;
|
||||
$pathinfo = pathinfo($file) ;
|
||||
$filenew = $pathinfo["dirname"].'/'.$pathinfo["filename"].'.'.$name.'qcow2' ;
|
||||
$diskspec .= " --diskspec '".$disk["device"]."',snapshot=external,file='".$filenew."'" ;
|
||||
$capacity = $capacity + $disk["capacity"] ;
|
||||
}
|
||||
$dirpath = $pathinfo["dirname"] ;
|
||||
|
||||
#Check free space.
|
||||
write("addLog\0".htmlspecialchars("Checking for free space"));
|
||||
$dirfree = disk_free_space($pathinfo["dirname"]) ;
|
||||
|
||||
$capacity *= 1 ;
|
||||
|
||||
#if ($free == "yes" && $dirfree < $capacity) { $arrResponse = ['error' => _("Insufficent Storage for Clone")]; return $arrResponse ;}
|
||||
|
||||
#Clone XML
|
||||
$res = $lv->domain_get_domain_by_uuid($uuid);
|
||||
|
||||
$strXML = $lv->domain_get_xml($res);
|
||||
$uuid = $lv->domain_get_uuid($vm) ;
|
||||
var_dump($uuid) ;
|
||||
$config=domain_to_config($uuid) ;
|
||||
|
||||
|
||||
#$config = array_replace_recursive($arrConfigDefaults, domain_to_config($uuid));
|
||||
#$config = domain_to_config($uuid);
|
||||
$config["domain"]["name"] = $clone ;
|
||||
$config["domain"]["uuid"] = $lv->domain_generate_uuid() ;
|
||||
$config["nic"]["0"]["mac"] = $lv->generate_random_mac_addr() ;
|
||||
$config["domain"]["type"] = "kvm";
|
||||
|
||||
|
||||
$files_exist = false ;
|
||||
foreach ($config["disk"] as $diskid => $disk) {
|
||||
$config["disk"][$diskid]["new"] = str_replace($name,$clonename,$config["disk"][$diskid]["new"]) ;
|
||||
#var_dump(pathinfo($config["disk"][$diskid]["new"])) ;
|
||||
$pi = pathinfo($config["disk"][$diskid]["new"]) ;
|
||||
$isdir = is_dir($pi['dirname']) ;
|
||||
if (is_file($config["disk"][$diskid]["new"])) $file_exists = true ;
|
||||
#var_dump($isdir,$pi['dirname']) ;
|
||||
}
|
||||
|
||||
$clonedir = $domain_cfg['DOMAINDIR'].$clone ;
|
||||
#write("addLog\0".htmlspecialchars("Overwrite $overwrite Start $start Edit $edit Check Freespace $free"));
|
||||
write("addLog\0".htmlspecialchars("Checking for image files"));
|
||||
#if ($file_exists && $overwrite != "yes") { $arrResponse = ['error' => _("New image file names exist and Overwrite is no")]; return $arrResponse ;}
|
||||
|
||||
write("addLog\0".htmlspecialchars("Creating new XML $clone"));
|
||||
$xml = $lv->config_to_xml($config) ;
|
||||
file_put_contents("/tmp/xml" ,$xml) ;
|
||||
|
||||
foreach($disks as $disk) {
|
||||
$cmdstr = "ls" ;
|
||||
$error = execCommand_nchan($cmdstr,$path) ;
|
||||
if (!$error) {
|
||||
$arrResponse = ['error' => substr($output[0],6) ] ;
|
||||
return($arrResponse) ;
|
||||
} else {
|
||||
$arrResponse = ['success' => true] ;
|
||||
}
|
||||
|
||||
}
|
||||
$arrResponse = ['error' => _("Insufficent Storage for Clone")];
|
||||
return$arrResponse ;
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -2,41 +2,6 @@ function displayconsole(url) {
|
||||
window.open(url, '_blank', 'scrollbars=yes,resizable=yes');
|
||||
}
|
||||
|
||||
function getCloneName(name){
|
||||
var root = "" ;
|
||||
var match= ".iso" ;
|
||||
var box = $("#dialogWindow");
|
||||
box.html($("#templateClone").html());
|
||||
|
||||
box.find('#VMBeingCloned').html(name).change() ;
|
||||
|
||||
var height = 100;
|
||||
box.dialog({
|
||||
title: "Enter Clone Name",
|
||||
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 downloadFile(source) {
|
||||
var a = document.createElement('a');
|
||||
a.setAttribute('href',source);
|
||||
@@ -182,25 +147,12 @@ function addVMContext(name, uuid, template, state, vmrcurl, vmrcprotocol, log, c
|
||||
opts.push({text:_("Logs"), icon:"fa-navicon", action:function(e){e.preventDefault(); openTerminal('log',name,log);}});
|
||||
}
|
||||
opts.push({text:_("Edit"), icon:"fa-pencil", href:path+'/UpdateVM?uuid='+uuid});
|
||||
opts.push({text:_("Clone2"), icon:"fa-clone", href:path+'/CloneVM?uuid='+uuid});
|
||||
|
||||
if (state == "shutoff") {
|
||||
|
||||
opts.push({text:_("Clone"), icon:"fa-clone", action:function(e) {
|
||||
e.preventDefault();
|
||||
var clonename = getCloneName(name) ;
|
||||
/*swal({
|
||||
title:_("Enter"),
|
||||
text:_("From VM:")+name,
|
||||
type:"warning",
|
||||
showCancelButton:true,
|
||||
confirmButtonText:_('Proceed'),
|
||||
cancelButtonText:_('Cancel')
|
||||
},function(){
|
||||
$('#vm-'+uuid).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin');
|
||||
ajaxVMDispatch({action:"domain-clone", uuid:uuid }, "loadlist");
|
||||
}) ;*/
|
||||
|
||||
var clonename = VMClone(uuid,name) ;
|
||||
}});
|
||||
|
||||
opts.push({divider:true});
|
||||
@@ -280,3 +232,95 @@ function addVM() {
|
||||
if (x!=-1) path = path.substring(0,x);
|
||||
location = path+"/VMTemplates";
|
||||
}
|
||||
|
||||
|
||||
function getCloneName(uuid,name){
|
||||
var root = "" ;
|
||||
var match= ".iso" ;
|
||||
var box = $("#dialogWindow");
|
||||
box.html($("#templateClone").html());
|
||||
|
||||
box.find('#VMBeingCloned').html(name).change() ;
|
||||
|
||||
var height = 100;
|
||||
box.dialog({
|
||||
title: "Enter Clone Name",
|
||||
resizable: false,
|
||||
width: 600,
|
||||
height: 300,
|
||||
modal: true,
|
||||
show: {effect:'fade', duration:250},
|
||||
hide: {effect:'fade', duration:250},
|
||||
buttons: {
|
||||
"Clone": 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:"domain-clone", uuid:uuid , clone:target}, "loadlist"); ;
|
||||
box.dialog('close');
|
||||
},
|
||||
"Cancel": function(){
|
||||
box.dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
dialogStyle();
|
||||
}
|
||||
|
||||
function VMClone(uuid, name){
|
||||
|
||||
//var root = <?= '"'.$domain_cfg["MEDIADIR"].'"';?>;
|
||||
var match= ".iso";
|
||||
var box = $("#dialogWindow");
|
||||
box.html($("#templateblock").html());
|
||||
var height = 200;
|
||||
box.html($("#templateClone").html());
|
||||
|
||||
box.find('#VMBeingCloned').html(name).change() ;
|
||||
|
||||
//document.getElementById("targetsnaprmv").checked = true ;
|
||||
//document.getElementById("targetsnaprmvmeta").checked = true ;
|
||||
//document.getElementById("targetsnapkeep").checked = true ;
|
||||
//document.getElementById("targetsnapfspc").checked = true ;
|
||||
|
||||
box.dialog({
|
||||
title: "_(VM Clone)_",
|
||||
resizable: false,
|
||||
width: 600,
|
||||
height: 500,
|
||||
modal: true,
|
||||
show: {effect:'fade', duration:250},
|
||||
hide: {effect:'fade', duration:250},
|
||||
buttons: {
|
||||
"Clone" : function(){
|
||||
var target = box.find('#target');
|
||||
if (target.length) {
|
||||
target = target.val();
|
||||
if (!target ) {errorTarget(); return;}
|
||||
} else target = '';
|
||||
|
||||
var clone = box.find("#target").prop('value') ;
|
||||
|
||||
|
||||
x = box.find('#Start').prop('checked') ;
|
||||
if (x) start = 'yes' ; else start = 'no' ;
|
||||
x = box.find('#Edit').prop('checked') ;
|
||||
if (x) edit = 'yes' ; else edit = 'no' ;
|
||||
x = box.find('#Overwrite').prop('checked') ;
|
||||
if (x) overwrite = 'yes' ; else overwrite = 'no' ;
|
||||
x = box.find('#Free').prop('checked') ;
|
||||
if (x) free = 'yes' ; else free = 'no' ;
|
||||
scripturl = "VMClone.php " + encodeURIComponent("/usr/local/emhttp/plugins/dynamix.vm.manager/include/VMClone.php&" + $.param({action:"clone" , name:name ,clone:clone, overwrite:overwrite , edit:edit, start,start, free:free})) ;
|
||||
openVMAction((scripturl),"VM Clone", "dynamix.vm.manager", "loadlist") ;
|
||||
box.dialog('close');
|
||||
},
|
||||
"_(Cancel)_": function(){
|
||||
box.dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
dialogStyle();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/php -q
|
||||
<?PHP
|
||||
/* Copyright 2005-2023, Lime Technology
|
||||
*
|
||||
* 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/webGui/include/Wrappers.php";
|
||||
|
||||
// add translations
|
||||
$_SERVER['REQUEST_URI'] = '';
|
||||
$login_locale = _var($display,'locale');
|
||||
require_once "$docroot/plugins/dynamix.docker.manager/include/DockerClient.php";
|
||||
require_once "$docroot/webGui/include/Translations.php";
|
||||
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
|
||||
function write(...$messages){
|
||||
$com = curl_init();
|
||||
curl_setopt_array($com,[
|
||||
CURLOPT_URL => 'http://localhost/pub/vmaction?buffer_length=1',
|
||||
CURLOPT_UNIX_SOCKET_PATH => '/var/run/nginx.socket',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
foreach ($messages as $message) {
|
||||
curl_setopt($com, CURLOPT_POSTFIELDS, $message);
|
||||
curl_exec($com);
|
||||
}
|
||||
curl_close($com);
|
||||
}
|
||||
function execCommand_nchan($command,$idx) {
|
||||
$waitID = mt_rand();
|
||||
[$cmd,$args] = explode(' ',$command,2);
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._('Command execution')."</legend>".basename($cmd).' '.str_replace(" -","<br> -",htmlspecialchars($args))."<br><span id='wait-$waitID'>"._('Please wait')." </span><p class='logLine'></p></fieldset>","show_Wait\0$waitID");
|
||||
write("addLog\0<br>") ;
|
||||
write("addToID\0$idx\0 $action") ;
|
||||
$proc = popen("$command 2>&1",'r');
|
||||
while ($out = fgets($proc)) {
|
||||
$out = preg_replace("%[\t\n\x0B\f\r]+%", '',$out);
|
||||
if (substr($out,0,1) == "B") { ;
|
||||
write("progress\0$idx\0".htmlspecialchars(substr($out,strrpos($out,"Block Pull")))) ;
|
||||
} else echo write("addToID\0$idx\0 ".htmlspecialchars($out));
|
||||
}
|
||||
$retval = pclose($proc);
|
||||
$out = $retval ? _('The command failed').'.' : _('The command finished successfully').'!';
|
||||
write("stop_Wait\0$waitID","addLog\0<br><b>$out</b>");
|
||||
return $retval===0;
|
||||
}
|
||||
|
||||
#{action:"snap-", uuid:uuid , snapshotname:target , remove:remove, free:free ,removemeta:removemeta ,keep:keep, desc:desc}
|
||||
#VM ID [ 99]: pull. .Block Pull: [ 0 %]Block Pull: [100 %].Pull complete.
|
||||
$url = rawurldecode($argv[1]??'');
|
||||
$waitID = mt_rand();
|
||||
$style = ["<style>"];
|
||||
$style[] = ".logLine{font-family:bitstream!important;font-size:1.2rem!important;margin:0;padding:0}";
|
||||
$style[] = "fieldset.docker{border:solid thin;margin-top:8px}";
|
||||
$style[] = "legend{font-size:1.1rem!important;font-weight:bold}";
|
||||
$style[] = "</style>";
|
||||
|
||||
foreach (explode('&', $url) as $chunk) {
|
||||
$param = explode("=", $chunk);
|
||||
if ($param) {
|
||||
${urldecode($param[0])} = urldecode($param[1]) ;
|
||||
}
|
||||
}
|
||||
$id = 1 ;
|
||||
write(implode($style)."<p class='logLine'></p>");
|
||||
$process = " " ;
|
||||
write("<p class='logLine'></p>","addLog\0<fieldset class='docker'><legend>"._("Options for Block $action").": </legend><p class='logLine'></p><span id='wait-$waitID'>"._('Please wait')." </span></fieldset>");
|
||||
write("addLog\0".htmlspecialchars("VMName $name "));
|
||||
write("addLog\0".htmlspecialchars("Clone $clone "));
|
||||
write("addLog\0".htmlspecialchars("Overwrite $overwrite Start $start Edit $edit Check Freespace $free"));
|
||||
|
||||
switch ($action) {
|
||||
case "clone":
|
||||
vm_clone($name,$clone,$overwrite,$start,$edit,$free,$waitID) ;
|
||||
break ;
|
||||
}
|
||||
#execCommand_nchan("ls /") ;
|
||||
write("stop_Wait\0$waitID") ;
|
||||
write('_DONE_','');
|
||||
|
||||
?>
|
||||
@@ -342,6 +342,29 @@ function openDocker(cmd,title,plg,func,start=0,button=0) {
|
||||
$('button.confirm').prop('disabled',button==0);
|
||||
});
|
||||
}
|
||||
function openVMAction(cmd,title,plg,func,start=0,button=0) {
|
||||
// start = 0 : run command only when not already running (default)
|
||||
// start = 1 : run command unconditionally
|
||||
// button = 0 : hide CLOSE button (default)
|
||||
// button = 1 : show CLOSE button
|
||||
nchan_vmaction.start();
|
||||
$.post('/webGui/include/StartCommand.php',{cmd:cmd,start:start},function(pid) {
|
||||
if (pid==0) {
|
||||
nchan_vmaction.stop();
|
||||
$('div.spinner.fixed').hide();
|
||||
$(".upgrade_notice").addClass('alert');
|
||||
return;
|
||||
}
|
||||
swal({title:title,text:"<pre id='swaltext'></pre><hr>",html:true,animation:'none',showConfirmButton:button!=0,confirmButtonText:"<?=_('Close')?>"},function(close){
|
||||
nchan_vmaction.stop();
|
||||
$('div.spinner.fixed').hide();
|
||||
$('.sweet-alert').hide('fast').removeClass('nchan');
|
||||
setTimeout(function(){bannerAlert("<?=_('Attention - operation continues in background')?> ["+pid.toString().padStart(8,'0')+"]<i class='fa fa-bomb fa-fw abortOps' title=\"<?=_('Abort background process')?>\" onclick='abortOperation("+pid+")'></i>",cmd,plg,func,start);});
|
||||
});
|
||||
$('.sweet-alert').addClass('nchan');
|
||||
$('button.confirm').prop('disabled',button==0);
|
||||
});
|
||||
}
|
||||
function abortOperation(pid) {
|
||||
swal({title:"<?=_('Abort background operation')?>",text:"<?=_('This may leave an unknown state')?>",html:true,animation:'none',type:'warning',showCancelButton:true,confirmButtonText:"<?=_('Proceed')?>",cancelButtonText:"<?=_('Cancel')?>"},function(){
|
||||
$.post('/webGui/include/StartCommand.php',{kill:pid},function() {
|
||||
@@ -915,6 +938,54 @@ nchan_docker.on('message', function(data) {
|
||||
}
|
||||
box.scrollTop(box[0].scrollHeight);
|
||||
});
|
||||
var nchan_vmaction = new NchanSubscriber('/sub/vmaction',{subscriber:'websocket'});
|
||||
nchan_vmaction.on('message', function(data) {
|
||||
if (!data || openDone(data)) return;
|
||||
var box = $('pre#swaltext');
|
||||
data = data.split('\0');
|
||||
switch (data[0]) {
|
||||
case 'addLog':
|
||||
var rows = document.getElementsByClassName('logLine');
|
||||
if (rows.length) {
|
||||
var row = rows[rows.length-1];
|
||||
row.innerHTML += data[1]+'<br>';
|
||||
}
|
||||
break;
|
||||
case 'progress':
|
||||
var rows = document.getElementsByClassName('progress-'+data[1]);
|
||||
if (rows.length) {
|
||||
rows[rows.length-1].textContent = data[2];
|
||||
}
|
||||
break;
|
||||
case 'addToID':
|
||||
var rows = document.getElementById(data[1]);
|
||||
if (rows === null) {
|
||||
rows = document.getElementsByClassName('logLine');
|
||||
if (rows.length) {
|
||||
var row = rows[rows.length-1];
|
||||
row.innerHTML += '<span id="'+data[1]+'">'+data[1]+': <span class="content">'+data[2]+'</span><span class="progress-'+data[1]+'"></span>.</span><br>';
|
||||
}
|
||||
} else {
|
||||
var rows_content = rows.getElementsByClassName('content');
|
||||
if (!rows_content.length || rows_content[rows_content.length-1].textContent != data[2]) {
|
||||
rows.innerHTML += '<span class="content">'+data[2]+'</span><span class="progress-'+data[1]+'"></span>.';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'show_Wait':
|
||||
progress_span[data[1]] = document.getElementById('wait-'+data[1]);
|
||||
progress_dots[data[1]] = setInterval(function(){if (((progress_span[data[1]].innerHTML += '.').match(/\./g)||[]).length > 9) progress_span[data[1]].innerHTML = progress_span[data[1]].innerHTML.replace(/\.+$/,'');},500);
|
||||
break;
|
||||
case 'stop_Wait':
|
||||
clearInterval(progress_dots[data[1]]);
|
||||
progress_span[data[1]].innerHTML = '';
|
||||
break;
|
||||
default:
|
||||
box.html(box.html()+data[0]);
|
||||
break;
|
||||
}
|
||||
box.scrollTop(box[0].scrollHeight);
|
||||
});
|
||||
|
||||
var backtotopoffset = 250;
|
||||
var backtotopduration = 500;
|
||||
|
||||
Reference in New Issue
Block a user