diff --git a/plugins/dynamix.vm.manager/include/libvirt_helpers.php b/plugins/dynamix.vm.manager/include/libvirt_helpers.php index 7de62180c..0dbba5ee2 100644 --- a/plugins/dynamix.vm.manager/include/libvirt_helpers.php +++ b/plugins/dynamix.vm.manager/include/libvirt_helpers.php @@ -1085,6 +1085,20 @@ ]; } + function create_vdisk(&$new) { + global $lv; + $index = 0; + foreach ($new['disk'] as $i => $disk) { + $index++; + if ($disk['new']) { + $disk = $lv->create_disk_image($disk, $new['domain']['name'], $index); + if ($disk['error']) return $disk['error']; + $new['disk'][$i] = $disk; + } + } + return false; + } + function array_update_recursive(&$old, &$new) { $hostold = $old['devices']['hostdev']; // existing devices including custom settings $hostnew = $new['devices']['hostdev']; // GUI generated devices @@ -1115,7 +1129,8 @@ // update parent arrays if (!$old['devices']['hostdev']) unset($old['devices']['hostdev']); if (!$new['devices']['hostdev']) unset($new['devices']['hostdev']); - unset($old['cputune']['vcpupin'],$old['devices']['graphics'],$old['devices']['video'],$old['devices']['disk']); + // remove existing auto-generated settings + unset($old['cputune']['vcpupin'],$old['devices']['graphics'],$old['devices']['video'],$old['devices']['disk'],$old['devices']['interface']); // set namespace $new['metadata']['vmtemplate']['@attributes']['xmlns'] = 'unraid'; } diff --git a/plugins/dynamix.vm.manager/templates/Custom.form.php b/plugins/dynamix.vm.manager/templates/Custom.form.php index 223035a08..523d72c85 100644 --- a/plugins/dynamix.vm.manager/templates/Custom.form.php +++ b/plugins/dynamix.vm.manager/templates/Custom.form.php @@ -201,11 +201,16 @@ $hdrXML = "\n"; // XML encoding declaratio $xml = $_POST['xmldesc']; } else { // form view - $arrExistingConfig = custom::createArray('domain',$strXML); - $arrUpdatedConfig = custom::createArray('domain',$lv->config_to_xml($_POST)); - array_update_recursive($arrExistingConfig, $arrUpdatedConfig); - $arrConfig = array_replace_recursive($arrExistingConfig, $arrUpdatedConfig); - $xml = custom::createXML('domain',$arrConfig)->saveXML(); + if ($error = create_vdisk($_POST) === false) { + $arrExistingConfig = custom::createArray('domain',$strXML); + $arrUpdatedConfig = custom::createArray('domain',$lv->config_to_xml($_POST)); + array_update_recursive($arrExistingConfig, $arrUpdatedConfig); + $arrConfig = array_replace_recursive($arrExistingConfig, $arrUpdatedConfig); + $xml = custom::createXML('domain',$arrConfig)->saveXML(); + } else { + echo json_encode(['error' => $error]); + exit; + } } // delete and create the VM $lv->nvram_backup($uuid); @@ -232,7 +237,7 @@ $hdrXML = "\n"; // XML encoding declaratio $boolRunning = $lv->domain_get_state($dom)=='running'; $strXML = $lv->domain_get_xml($dom); $boolNew = false; - $arrConfig = domain_to_config($uuid); + $arrConfig = array_replace_recursive($arrConfigDefaults, domain_to_config($uuid)); } else { // edit new VM $boolRunning = false; @@ -1418,6 +1423,7 @@ $(function() { $("#vmform .formview #btnSubmit").click(function frmSubmit() { var $button = $(this); var $panel = $('.formview'); + var form = $button.closest('form'); $("#vmform .disk_select option:selected").not("[value='manual']").closest('table').each(function () { var v = $(this).find('.disk_preview').html(); @@ -1428,24 +1434,34 @@ $(function() { // signal devices to be added or removed - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if (!$(this).prop('checked')) $(this).prop('checked',true).val($(this).val()+'#remove'); }); // remove unused graphic cards var gpus = [], i = 0; do { - var gpu = $button.closest('form').find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); + var gpu = form.find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); if (gpu) gpus.push(gpu); } while (gpu); - $button.closest('form').find('select[name="gpu[0][id]"] option').each(function(){ + form.find('select[name="gpu[0][id]"] option').each(function(){ var gpu = $(this).val(); - if (gpu != 'vnc' && !gpus.includes(gpu)) $('form#vmform').append(''); + if (gpu != 'vnc' && !gpus.includes(gpu)) form.append(''); + }); + // remove unused sound cards + var sound = [], i = 0; + do { + var audio = form.find('select[name="audio['+(i++)+'][id]"] option:selected').val(); + if (audio) sound.push(audio); + } while (audio); + form.find('select[name="audio[0][id]"] option').each(function(){ + var audio = $(this).val(); + if (audio && !sound.includes(audio)) form.append(''); }); var postdata = $button.closest('form').find('input,select').serialize().replace(/'/g,"%27"); // keep checkbox visually unchecked - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if ($(this).val().indexOf('#remove')>0) $(this).prop('checked',false); }); diff --git a/plugins/dynamix.vm.manager/templates/LibreELEC.form.php b/plugins/dynamix.vm.manager/templates/LibreELEC.form.php index c61639a25..a3cc2f8a0 100644 --- a/plugins/dynamix.vm.manager/templates/LibreELEC.form.php +++ b/plugins/dynamix.vm.manager/templates/LibreELEC.form.php @@ -392,7 +392,7 @@ $hdrXML = "\n"; // XML encoding declaratio $boolRunning = $lv->domain_get_state($dom)=='running'; $strXML = $lv->domain_get_xml($dom); $boolNew = false; - $arrConfig = domain_to_config($uuid); + $arrConfig = array_replace_recursive($arrConfigDefaults, domain_to_config($uuid)); } else { // edit new VM $boolRunning = false; @@ -1116,29 +1116,40 @@ $(function() { $("#vmform .formview #btnSubmit").click(function frmSubmit() { var $button = $(this); var $panel = $('.formview'); + var form = $button.closest('form'); $panel.find('input').prop('disabled', false); // enable all inputs otherwise they wont post // signal devices to be added or removed - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if (!$(this).prop('checked')) $(this).prop('checked',true).val($(this).val()+'#remove'); }); // remove unused graphic cards var gpus = [], i = 0; do { - var gpu = $button.closest('form').find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); + var gpu = form.find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); if (gpu) gpus.push(gpu); } while (gpu); - $button.closest('form').find('select[name="gpu[0][id]"] option').each(function(){ + form.find('select[name="gpu[0][id]"] option').each(function(){ var gpu = $(this).val(); - if (gpu != 'vnc' && !gpus.includes(gpu)) $('form#vmform').append(''); + if (gpu != 'vnc' && !gpus.includes(gpu)) form.append(''); + }); + // remove unused sound cards + var sound = [], i = 0; + do { + var audio = form.find('select[name="audio['+(i++)+'][id]"] option:selected').val(); + if (audio) sound.push(audio); + } while (audio); + form.find('select[name="audio[0][id]"] option').each(function(){ + var audio = $(this).val(); + if (audio && !sound.includes(audio)) form.append(''); }); - var postdata = $button.closest('form').find('input,select').serialize().replace(/'/g,"%27"); + var postdata = form.find('input,select').serialize().replace(/'/g,"%27"); // keep checkbox visually unchecked - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if ($(this).val().indexOf('#remove')>0) $(this).prop('checked',false); }); diff --git a/plugins/dynamix.vm.manager/templates/OpenELEC.form.php b/plugins/dynamix.vm.manager/templates/OpenELEC.form.php index c43e93c40..0a1bedfd0 100644 --- a/plugins/dynamix.vm.manager/templates/OpenELEC.form.php +++ b/plugins/dynamix.vm.manager/templates/OpenELEC.form.php @@ -394,7 +394,7 @@ $hdrXML = "\n"; // XML encoding declaratio $boolRunning = $lv->domain_get_state($dom)=='running'; $strXML = $lv->domain_get_xml($dom); $boolNew = false; - $arrConfig = domain_to_config($uuid); + $arrConfig = array_replace_recursive($arrConfigDefaults, domain_to_config($uuid)); } else { // edit new VM $boolRunning = false; @@ -1118,29 +1118,40 @@ $(function() { $("#vmform .formview #btnSubmit").click(function frmSubmit() { var $button = $(this); var $panel = $('.formview'); + var form = $button.closest('form'); $panel.find('input').prop('disabled', false); // enable all inputs otherwise they wont post // signal devices to be added or removed - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if (!$(this).prop('checked')) $(this).prop('checked',true).val($(this).val()+'#remove'); }); // remove unused graphic cards var gpus = [], i = 0; do { - var gpu = $button.closest('form').find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); + var gpu = form.find('select[name="gpu['+(i++)+'][id]"] option:selected').val(); if (gpu) gpus.push(gpu); } while (gpu); - $button.closest('form').find('select[name="gpu[0][id]"] option').each(function(){ + form.find('select[name="gpu[0][id]"] option').each(function(){ var gpu = $(this).val(); - if (gpu != 'vnc' && !gpus.includes(gpu)) $('form#vmform').append(''); + if (gpu != 'vnc' && !gpus.includes(gpu)) form.append(''); + }); + // remove unused sound cards + var sound = [], i = 0; + do { + var audio = form.find('select[name="audio['+(i++)+'][id]"] option:selected').val(); + if (audio) sound.push(audio); + } while (audio); + form.find('select[name="audio[0][id]"] option').each(function(){ + var audio = $(this).val(); + if (audio && !sound.includes(audio)) form.append(''); }); - var postdata = $button.closest('form').find('input,select').serialize().replace(/'/g,"%27"); + var postdata = form.find('input,select').serialize().replace(/'/g,"%27"); // keep checkbox visually unchecked - $button.closest('form').find('input[name="usb[]"],input[name="pci[]"]').each(function(){ + form.find('input[name="usb[]"],input[name="pci[]"]').each(function(){ if ($(this).val().indexOf('#remove')>0) $(this).prop('checked',false); }); diff --git a/plugins/dynamix/include/Custom.php b/plugins/dynamix/include/Custom.php index de58bc6df..0cedf51e6 100644 --- a/plugins/dynamix/include/Custom.php +++ b/plugins/dynamix/include/Custom.php @@ -34,7 +34,7 @@ class custom { * @param array $arr - array object to be converterd * @return XML object */ - public static function &createXML($root, $arr=array()) { + public static function &createXML($root, $arr) { $xml = self::getXMLRoot(); $xml->appendChild(self::Array2XML($root, $arr)); self::$xml = null; // clear the xml node in the class for 2nd time use @@ -55,7 +55,7 @@ class custom { /* * Recursive conversion Array to XML */ - private static function &Array2XML($node_name, $arr=array()) { + private static function &Array2XML($node_name, $arr) { $xml = self::getXMLRoot(); $node = $xml->createElement($node_name); if (is_array($arr)) { @@ -130,11 +130,11 @@ class custom { // store as single or multi array if (!isset($tags[$node])) { // specific for Unraid - if (in_array($node,['hostdev','controller','disk'])) $tags[$node][] = $data; else $tags[$node] = $data; + if (in_array($node,['hostdev','controller','disk','interface'])) $tags[$node][] = $data; else $tags[$node] = $data; } elseif (is_array($tags[$node]) && array_keys($tags[$node])===range(0, count($tags[$node])-1)) { $tags[$node][] = $data; } else { - $tags[$node] = array($tags[$node], $data); + $tags[$node] = [$tags[$node], $data]; } } $textContent = [];