Custom XML settings saved in LibreELEC and OpenELEC forms

This commit is contained in:
bergware
2018-08-14 09:41:50 +02:00
parent a906e32263
commit 3330cc855e
3 changed files with 242 additions and 398 deletions

View File

@@ -107,7 +107,7 @@ $hdrXML = "<?xml version='1.0' encoding='UTF-8'?>\n"; // XML encoding declaratio
if ($_POST['createvm']) {
if ($_POST['xmldesc']) {
// XML view
$new = $lv->domain_define($_POST['xmldesc']);
$new = $lv->domain_define($_POST['xmldesc'], $_POST['domain']['xmlstartnow']==1);
if ($new){
$lv->domain_set_autostart($new, $_POST['domain']['autostart']==1);
$reply = ['success' => true];

View File

@@ -1,6 +1,7 @@
<?PHP
/* Copyright 2017, Lime Technology
* Copyright 2017, Derek Macias, Eric Schultz, Jon Panozzo.
/* Copyright 2018, Lime Technology
* Copyright 2018, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2018, 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,
@@ -13,6 +14,7 @@
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Custom.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$arrValidMachineTypes = getValidMachineTypes();
@@ -34,7 +36,6 @@
@mkdir(dirname($strLibreELECConfig), 0777, true);
}
// Compare libreelec.cfg and populate 'localpath' in $arrOEVersion
foreach ($arrLibreELECConfig as $strID => $strLocalpath) {
if (array_key_exists($strID, $arrLibreELECVersions)) {
@@ -45,17 +46,14 @@
}
}
if (array_key_exists('delete_version', $_POST)) {
if ($_POST['delete_version']) {
$arrDeleteLibreELEC = [];
if (array_key_exists($_POST['delete_version'], $arrLibreELECVersions)) {
$arrDeleteLibreELEC = $arrLibreELECVersions[$_POST['delete_version']];
}
$arrResponse = [];
$reply = [];
if (empty($arrDeleteLibreELEC)) {
$arrResponse = ['error' => 'Unknown version: ' . $_POST['delete_version']];
$reply = ['error' => 'Unknown version: ' . $_POST['delete_version']];
} else {
// delete img file
@unlink($arrDeleteLibreELEC['localpath']);
@@ -65,40 +63,36 @@
$text = '';
foreach ($arrLibreELECConfig as $key => $value) $text .= "$key=\"$value\"\n";
file_put_contents($strLibreELECConfig, $text);
$arrResponse = ['status' => 'ok'];
$reply = ['status' => 'ok'];
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if (array_key_exists('download_path', $_POST)) {
if ($_POST['download_path']) {
$arrDownloadLibreELEC = [];
if (array_key_exists($_POST['download_version'], $arrLibreELECVersions)) {
$arrDownloadLibreELEC = $arrLibreELECVersions[$_POST['download_version']];
}
if (empty($arrDownloadLibreELEC)) {
$arrResponse = ['error' => 'Unknown version: ' . $_POST['download_version']];
$reply = ['error' => 'Unknown version: ' . $_POST['download_version']];
} elseif (empty($_POST['download_path'])) {
$arrResponse = ['error' => 'Please choose a folder the LibreELEC image will download to'];
$reply = ['error' => 'Please choose a folder the LibreELEC image will download to'];
} else {
@mkdir($_POST['download_path'], 0777, true);
$_POST['download_path'] = realpath($_POST['download_path']) . '/';
// Check free space
if (disk_free_space($_POST['download_path']) < $arrDownloadLibreELEC['size']+10000) {
$arrResponse = [
$reply = [
'error' => 'Not enough free space, need at least ' . ceil($arrDownloadLibreELEC['size']/1000000).'MB'
];
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
$boolCheckOnly = !empty($_POST['checkonly']);
$strInstallScript = '/tmp/LibreELEC_' . $_POST['download_version'] . '_install.sh';
$strInstallScriptPgrep = '-f "LibreELEC_' . $_POST['download_version'] . '_install.sh"';
$strTempFile = $_POST['download_path'] . basename($arrDownloadLibreELEC['url']);
@@ -107,26 +101,20 @@
$strMD5StatusFile = $strTempFile . '.md5status';
$strExtractedFile = $_POST['download_path'] . basename($arrDownloadLibreELEC['url'], 'tar.xz') . 'img';
// Save to strLibreELECConfig
$arrLibreELECConfig[$_POST['download_version']] = $strExtractedFile;
$text = '';
foreach ($arrLibreELECConfig as $key => $value) $text .= "$key=\"$value\"\n";
file_put_contents($strLibreELECConfig, $text);
$strDownloadCmd = 'wget -nv -c -O ' . escapeshellarg($strTempFile) . ' ' . escapeshellarg($arrDownloadLibreELEC['url']);
$strDownloadPgrep = '-f "wget.*' . $strTempFile . '.*' . $arrDownloadLibreELEC['url'] . '"';
$strVerifyCmd = 'md5sum -c ' . escapeshellarg($strMD5File);
$strVerifyPgrep = '-f "md5sum.*' . $strMD5File . '"';
$strExtractCmd = 'tar Jxf ' . escapeshellarg($strTempFile) . ' -C ' . escapeshellarg(dirname($strTempFile));
$strExtractPgrep = '-f "tar.*' . $strTempFile . '.*' . dirname($strTempFile) . '"';
$strCleanCmd = '(chmod 777 ' . escapeshellarg($_POST['download_path']) . ' ' . escapeshellarg($strExtractedFile) . '; chown nobody:users ' . escapeshellarg($_POST['download_path']) . ' ' . escapeshellarg($strExtractedFile) . '; rm ' . escapeshellarg($strTempFile) . ' ' . escapeshellarg($strMD5File) . ' ' . escapeshellarg($strMD5StatusFile) . ')';
$strCleanPgrep = '-f "chmod.*chown.*rm.*' . $strMD5StatusFile . '"';
$strAllCmd = "#!/bin/bash\n\n";
$strAllCmd .= $strDownloadCmd . ' >>' . escapeshellarg($strLogFile) . ' 2>&1 && ';
$strAllCmd .= 'echo "' . $arrDownloadLibreELEC['md5'] . ' ' . $strTempFile . '" > ' . escapeshellarg($strMD5File) . ' && ';
@@ -136,103 +124,68 @@
$strAllCmd .= 'rm ' . escapeshellarg($strLogFile) . ' && ';
$strAllCmd .= 'rm ' . escapeshellarg($strInstallScript);
$arrResponse = [];
$reply = [];
if (file_exists($strExtractedFile)) {
if (!file_exists($strTempFile)) {
// Status = done
$arrResponse['status'] = 'Done';
$arrResponse['localpath'] = $strExtractedFile;
$arrResponse['localfolder'] = dirname($strExtractedFile);
$reply['status'] = 'Done';
$reply['localpath'] = $strExtractedFile;
$reply['localfolder'] = dirname($strExtractedFile);
} else {
if (pgrep($strExtractPgrep, false)) {
// Status = running extract
$arrResponse['status'] = 'Extracting ... ';
$reply['status'] = 'Extracting ... ';
} else {
// Status = cleanup
$arrResponse['status'] = 'Cleanup ... ';
$reply['status'] = 'Cleanup ... ';
}
}
} elseif (file_exists($strTempFile)) {
if (pgrep($strDownloadPgrep, false)) {
// Get Download percent completed
$intSize = filesize($strTempFile);
$strPercent = 0;
if ($intSize > 0) {
$strPercent = round(($intSize / $arrDownloadLibreELEC['size']) * 100);
}
$arrResponse['status'] = 'Downloading ... ' . $strPercent . '%';
$reply['status'] = 'Downloading ... ' . $strPercent . '%';
} elseif (pgrep($strVerifyPgrep, false)) {
// Status = running md5 check
$arrResponse['status'] = 'Verifying ... ';
$reply['status'] = 'Verifying ... ';
} elseif (file_exists($strMD5StatusFile)) {
// Status = running extract
$arrResponse['status'] = 'Extracting ... ';
$reply['status'] = 'Extracting ... ';
if (!pgrep($strExtractPgrep, false)) {
// Examine md5 status
$strMD5StatusContents = file_get_contents($strMD5StatusFile);
if (strpos($strMD5StatusContents, ': FAILED') !== false) {
// ERROR: MD5 check failed
unset($arrResponse['status']);
$arrResponse['error'] = 'MD5 verification failed, your download is incomplete or corrupted.';
unset($reply['status']);
$reply['error'] = 'MD5 verification failed, your download is incomplete or corrupted.';
}
}
} elseif (!file_exists($strMD5File)) {
// Status = running md5 check
$arrResponse['status'] = 'Downloading ... 100%';
$reply['status'] = 'Downloading ... 100%';
if (!pgrep($strInstallScriptPgrep, false) && !$boolCheckOnly) {
// Run all commands
file_put_contents($strInstallScript, $strAllCmd);
chmod($strInstallScript, 0777);
exec($strInstallScript . ' >/dev/null 2>&1 &');
}
}
} elseif (!$boolCheckOnly) {
if (!pgrep($strInstallScriptPgrep, false)) {
// Run all commands
file_put_contents($strInstallScript, $strAllCmd);
chmod($strInstallScript, 0777);
exec($strInstallScript . ' >/dev/null 2>&1 &');
}
$arrResponse['status'] = 'Downloading ... ';
$reply['status'] = 'Downloading ... ';
}
$arrResponse['pid'] = pgrep($strInstallScriptPgrep, false);
$reply['pid'] = pgrep($strInstallScriptPgrep, false);
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
@@ -305,89 +258,61 @@
]
];
$hdrXML = "<?xml version='1.0' encoding='UTF-8'?>\n"; // XML encoding declaration
// Merge in any default values from the VM template
if (!empty($arrAllTemplates[$strSelectedTemplate]) && !empty($arrAllTemplates[$strSelectedTemplate]['overrides'])) {
if ($arrAllTemplates[$strSelectedTemplate] && $arrAllTemplates[$strSelectedTemplate]['overrides']) {
$arrConfigDefaults = array_replace_recursive($arrConfigDefaults, $arrAllTemplates[$strSelectedTemplate]['overrides']);
}
if (array_key_exists('updatevm', $_POST) && !empty($_POST['domain']['uuid'])) {
$_GET['uuid'] = $_POST['domain']['uuid'];
}
// If we are editing a existing VM load it's existing configuration details
$boolNew = true;
$boolRunning = false;
$arrExistingConfig = [];
$strUUID = '';
$strXML = '';
if (!empty($_GET['uuid'])) {
$strUUID = $_GET['uuid'];
$res = $lv->domain_get_name_by_uuid($strUUID);
$dom = $lv->domain_get_info($res);
$boolNew = false;
$boolRunning = ($lv->domain_state_translate($dom['state']) != 'shutoff');
$arrExistingConfig = domain_to_config($strUUID);
$strXML = $lv->domain_get_xml($res);
}
// Active config for this page
$arrConfig = array_replace_recursive($arrConfigDefaults, $arrExistingConfig);
if (array_key_exists($arrConfig['template']['libreelec'], $arrLibreELECVersions)) {
$arrConfigDefaults['disk'][0]['image'] = $arrLibreELECVersions[$arrConfig['template']['libreelec']]['localpath'];
}
if (array_key_exists('createvm', $_POST)) {
$arrResponse = ['success' => true];
if (array_key_exists('xmldesc', $_POST)) {
$tmp = $lv->domain_define($_POST['xmldesc'], !empty($config['domain']['xmlstartnow']));
if (!$tmp){
$arrResponse = ['error' => $lv->get_last_error()];
// create new VM
if ($_POST['createvm']) {
if ($_POST['xmldesc']) {
// XML view
$new = $lv->domain_define($_POST['xmldesc'], $_POST['domain']['xmlstartnow']==1);
if ($new){
$lv->domain_set_autostart($new, $_POST['domain']['autostart']==1);
$reply = ['success' => true];
} else {
$lv->domain_set_autostart($tmp, $_POST['domain']['autostart'] == 1);
$reply = ['error' => $lv->get_last_error()];
}
} else {
if (!empty($_POST['shares'][0]['source'])) {
// form view
if ($_POST['shares'][0]['source']) {
@mkdir($_POST['shares'][0]['source'], 0777, true);
}
$tmp = $lv->domain_new($_POST);
if (!$tmp){
$arrResponse = ['error' => $lv->get_last_error()];
if ($lv->domain_new($_POST)){
$reply = ['success' => true];
} else {
$reply = ['error' => $lv->get_last_error()];
}
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if (array_key_exists('updatevm', $_POST)) {
$dom = $lv->domain_get_domain_by_uuid($_POST['domain']['uuid']);
// update existing VM
if ($_POST['updatevm']) {
$uuid = $_POST['domain']['uuid'];
$dom = $lv->domain_get_domain_by_uuid($uuid);
$oldAutoStart = $lv->domain_get_autostart($dom)==1;
$newAutoStart = $_POST['domain']['autostart']==1;
$strXML = $lv->domain_get_xml($dom);
if ($boolRunning) {
if ($lv->domain_get_state($dom)=='running') {
$arrErrors = [];
$arrExistingConfig = domain_to_config($_POST['domain']['uuid']);
$arrExistingConfig = domain_to_config($uuid);
$arrNewUSBIDs = $_POST['usb'];
// hot-attach any new usb devices
foreach ($arrNewUSBIDs as $strNewUSBID) {
foreach ($arrExistingConfig['usb'] as $arrExistingUSB) {
if ($strNewUSBID == $arrExistingUSB['id']) {
continue 2;
}
if ($strNewUSBID == $arrExistingUSB['id']) continue 2;
}
list($strVendor, $strProduct) = explode(':', $strNewUSBID);
list($strVendor,$strProduct) = explode(':', $strNewUSBID);
// hot-attach usb
file_put_contents('/tmp/hotattach.tmp', "<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x".$strVendor."'/>
<product id='0x".$strProduct."'/>
</source>
</hostdev>");
exec("virsh attach-device " . escapeshellarg($_POST['domain']['uuid']) . " /tmp/hotattach.tmp --live 2>&1", $arrOutput, $intReturnCode);
file_put_contents('/tmp/hotattach.tmp', "<hostdev mode='subsystem' type='usb'><source startupPolicy='optional'><vendor id='0x".$strVendor."'/><product id='0x".$strProduct."'/></source></hostdev>");
exec("virsh attach-device ".escapeshellarg($uuid)." /tmp/hotattach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) {
$arrErrors[] = implode(' ', $arrOutput);
}
@@ -397,92 +322,88 @@
foreach ($arrExistingConfig['usb'] as $arrExistingUSB) {
if (!in_array($arrExistingUSB['id'], $arrNewUSBIDs)) {
list($strVendor, $strProduct) = explode(':', $arrExistingUSB['id']);
file_put_contents('/tmp/hotdetach.tmp', "<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x".$strVendor."'/>
<product id='0x".$strProduct."'/>
</source>
</hostdev>");
exec("virsh detach-device " . escapeshellarg($_POST['domain']['uuid']) . " /tmp/hotdetach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) {
$arrErrors[] = implode(' ', $arrOutput);
}
file_put_contents('/tmp/hotdetach.tmp', "<hostdev mode='subsystem' type='usb'><source startupPolicy='optional'><vendor id='0x".$strVendor."'/><product id='0x".$strProduct."'/></source></hostdev>");
exec("virsh detach-device ".escapeshellarg($uuid)." /tmp/hotdetach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) $arrErrors[] = implode(' ',$arrOutput);
}
}
if (empty($arrErrors)) {
$arrResponse = ['success' => true];
} else {
$arrResponse = ['error' => implode(', ', $arrErrors)];
}
echo json_encode($arrResponse);
$reply = !$arrErrors ? ['success' => true] : ['error' => implode(', ',$arrErrors)];
echo json_encode($reply);
exit;
}
// Backup xml for existing domain in ram
$strOldXML = '';
$boolOldAutoStart = false;
// backup xml for existing domain in ram
if ($dom) {
$strOldXML = $lv->domain_get_xml($dom);
$boolOldAutoStart = $lv->domain_get_autostart($dom);
if (!array_key_exists('xmldesc', $_POST)) {
$strOldName = $lv->domain_get_name($dom);
$strNewName = $_POST['domain']['name'];
if (!empty($strOldName) &&
!empty($strNewName) &&
is_dir($domain_cfg['DOMAINDIR'].$strOldName.'/') &&
!is_dir($domain_cfg['DOMAINDIR'].$strNewName.'/')) {
$oldName = $lv->domain_get_name($dom);
$newName = $_POST['domain']['name'];
if (!$_POST['xmldesc']) {
$oldDir = $domain_cfg['DOMAINDIR'].$oldName;
$newDir = $domain_cfg['DOMAINDIR'].$newdName;
if ($oldName && $newName && is_dir($oldDir) && !is_dir($newDir)) {
// mv domain/vmname folder
if (rename($domain_cfg['DOMAINDIR'].$strOldName, $domain_cfg['DOMAINDIR'].$strNewName)) {
if (rename($oldDir, $newDir)) {
// replace all disk paths in xml
foreach ($_POST['disk'] as &$arrDisk) {
if (!empty($arrDisk['new'])) {
$arrDisk['new'] = str_replace($domain_cfg['DOMAINDIR'].$strOldName.'/', $domain_cfg['DOMAINDIR'].$strNewName.'/', $arrDisk['new']);
}
if (!empty($arrDisk['image'])) {
$arrDisk['image'] = str_replace($domain_cfg['DOMAINDIR'].$strOldName.'/', $domain_cfg['DOMAINDIR'].$strNewName.'/', $arrDisk['image']);
}
if ($arrDisk['new']) $arrDisk['new'] = str_replace($oldDir, $newDir, $arrDisk['new']);
if ($arrDisk['image']) $arrDisk['image'] = str_replace($oldDir, $newDir, $arrDisk['image']);
}
}
}
}
}
// Remove existing domain
$lv->nvram_backup($_POST['domain']['uuid']);
$lv->domain_undefine($dom);
$lv->nvram_restore($_POST['domain']['uuid']);
// Save new domain
if (array_key_exists('xmldesc', $_POST)) {
$tmp = $lv->domain_define($_POST['xmldesc']);
// construct updated config
if ($_POST['xmldesc']) {
// XML view
$xml = $_POST['xmldesc'];
} else {
if (!empty($_POST['shares'][0]['source'])) {
// form view
if ($_POST['shares'][0]['source']) {
@mkdir($_POST['shares'][0]['source'], 0777, true);
}
$tmp = $lv->domain_new($_POST);
$arrExistingConfig = custom::createArray('domain',$strXML);
$arrExistingConfig['metadata']['vmtemplate']['@attributes']['xmlns'] = 'unraid';
$arrExistingConfig['cputune']['vcpupin'] = [];
$arrUpdatedConfig = custom::createArray('domain',$lv->config_to_xml($_POST));
$arrConfig = array_replace_recursive($arrExistingConfig, $arrUpdatedConfig);
$xml = custom::createXML('domain',$arrConfig)->saveXML();
}
if (!$tmp){
$strLastError = $lv->get_last_error();
// Failure -- try to restore existing domain
$tmp = $lv->domain_define($strOldXML);
if ($tmp) $lv->domain_set_autostart($tmp, $boolOldAutoStart);
$arrResponse = ['error' => $strLastError];
// delete and create the VM
$lv->nvram_backup($uuid);
$lv->domain_undefine($dom);
$lv->nvram_restore($uuid);
$new = $lv->domain_define($xml);
if ($new) {
$lv->domain_set_autostart($new, $newAutoStart);
$reply = ['success' => true];
} else {
$lv->domain_set_autostart($tmp, $_POST['domain']['autostart'] == 1);
$arrResponse = ['success' => true];
// Failure -- try to restore existing domain
$old = $lv->domain_define($strXML);
if ($old) $lv->domain_set_autostart($old, $oldAutoStart);
$reply = ['error' => $lv->get_last_error()];
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if ($_GET['uuid']) {
// edit an existing VM
$dom = $lv->domain_get_domain_by_uuid($_GET['uuid']);
$boolRunning = $lv->domain_get_state($dom)=='running';
$strXML = $lv->domain_get_xml($dom);
$boolNew = false;
$arrConfig = domain_to_config($_GET['uuid']);
} else {
// edit new VM
$boolRunning = false;
$strXML = '';
$boolNew = true;
$arrConfig = $arrConfigDefaults;
}
if (array_key_exists($arrConfig['template']['libreelec'], $arrLibreELECVersions)) {
$arrConfigDefaults['disk'][0]['image'] = $arrLibreELECVersions[$arrConfig['template']['libreelec']]['localpath'];
}
?>
<link rel="stylesheet" href="/plugins/dynamix.vm.manager/scripts/codemirror/lib/codemirror.css">
@@ -1045,7 +966,7 @@
</div>
<div class="xmlview">
<textarea id="addcode" name="xmldesc" placeholder="Copy &amp; Paste Domain XML Configuration Here." autofocus><?= htmlspecialchars($strXML); ?></textarea>
<textarea id="addcode" name="xmldesc" placeholder="Copy &amp; Paste Domain XML Configuration Here." autofocus><?=htmlspecialchars($hdrXML).htmlspecialchars($strXML)?></textarea>
<table>
<tr>

View File

@@ -1,6 +1,7 @@
<?PHP
/* Copyright 2005-2017, Lime Technology
* Copyright 2015-2017, Derek Macias, Eric Schultz, Jon Panozzo.
/* Copyright 2005-2018, Lime Technology
* Copyright 2015-2018, Derek Macias, Eric Schultz, Jon Panozzo.
* Copyright 2012-2018, 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,
@@ -13,6 +14,7 @@
<?
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
require_once "$docroot/webGui/include/Helpers.php";
require_once "$docroot/webGui/include/Custom.php";
require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php";
$arrValidMachineTypes = getValidMachineTypes();
@@ -34,7 +36,6 @@
@mkdir(dirname($strOpenELECConfig), 0777, true);
}
// Compare openelec.cfg and populate 'localpath' in $arrOEVersion
foreach ($arrOpenELECConfig as $strID => $strLocalpath) {
if (array_key_exists($strID, $arrOpenELECVersions)) {
@@ -45,17 +46,15 @@
}
}
if (array_key_exists('delete_version', $_POST)) {
if ($_POST['delete_version']) {
$arrDeleteOpenELEC = [];
if (array_key_exists($_POST['delete_version'], $arrOpenELECVersions)) {
$arrDeleteOpenELEC = $arrOpenELECVersions[$_POST['delete_version']];
}
$arrResponse = [];
$reply = [];
if (empty($arrDeleteOpenELEC)) {
$arrResponse = ['error' => 'Unknown version: ' . $_POST['delete_version']];
$reply = ['error' => 'Unknown version: ' . $_POST['delete_version']];
} else {
// delete img file
@unlink($arrDeleteOpenELEC['localpath']);
@@ -65,40 +64,36 @@
$text = '';
foreach ($arrOpenELECConfig as $key => $value) $text .= "$key=\"$value\"\n";
file_put_contents($strOpenELECConfig, $text);
$arrResponse = ['status' => 'ok'];
$reply = ['status' => 'ok'];
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if (array_key_exists('download_path', $_POST)) {
if ($_POST['download_path']) {
$arrDownloadOpenELEC = [];
if (array_key_exists($_POST['download_version'], $arrOpenELECVersions)) {
$arrDownloadOpenELEC = $arrOpenELECVersions[$_POST['download_version']];
}
if (empty($arrDownloadOpenELEC)) {
$arrResponse = ['error' => 'Unknown version: ' . $_POST['download_version']];
$reply = ['error' => 'Unknown version: ' . $_POST['download_version']];
} elseif (empty($_POST['download_path'])) {
$arrResponse = ['error' => 'Please choose a folder the OpenELEC image will download to'];
$reply = ['error' => 'Please choose a folder the OpenELEC image will download to'];
} else {
@mkdir($_POST['download_path'], 0777, true);
$_POST['download_path'] = realpath($_POST['download_path']) . '/';
// Check free space
if (disk_free_space($_POST['download_path']) < $arrDownloadOpenELEC['size']+10000) {
$arrResponse = [
$reply = [
'error' => 'Not enough free space, need at least ' . ceil($arrDownloadOpenELEC['size']/1000000).'MB'
];
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
$boolCheckOnly = !empty($_POST['checkonly']);
$strInstallScript = '/tmp/OpenELEC_' . $_POST['download_version'] . '_install.sh';
$strInstallScriptPgrep = '-f "OpenELEC_' . $_POST['download_version'] . '_install.sh"';
$strTempFile = $_POST['download_path'] . basename($arrDownloadOpenELEC['url']);
@@ -107,26 +102,20 @@
$strMD5StatusFile = $strTempFile . '.md5status';
$strExtractedFile = $_POST['download_path'] . basename($arrDownloadOpenELEC['url'], 'tar.xz') . 'img';
// Save to strOpenELECConfig
$arrOpenELECConfig[$_POST['download_version']] = $strExtractedFile;
$text = '';
foreach ($arrOpenELECConfig as $key => $value) $text .= "$key=\"$value\"\n";
file_put_contents($strOpenELECConfig, $text);
$strDownloadCmd = 'wget -nv -c -O ' . escapeshellarg($strTempFile) . ' ' . escapeshellarg($arrDownloadOpenELEC['url']);
$strDownloadPgrep = '-f "wget.*' . $strTempFile . '.*' . $arrDownloadOpenELEC['url'] . '"';
$strVerifyCmd = 'md5sum -c ' . escapeshellarg($strMD5File);
$strVerifyPgrep = '-f "md5sum.*' . $strMD5File . '"';
$strExtractCmd = 'tar Jxf ' . escapeshellarg($strTempFile) . ' -C ' . escapeshellarg(dirname($strTempFile));
$strExtractPgrep = '-f "tar.*' . $strTempFile . '.*' . dirname($strTempFile) . '"';
$strCleanCmd = '(chmod 777 ' . escapeshellarg($_POST['download_path']) . ' ' . escapeshellarg($strExtractedFile) . '; chown nobody:users ' . escapeshellarg($_POST['download_path']) . ' ' . escapeshellarg($strExtractedFile) . '; rm ' . escapeshellarg($strTempFile) . ' ' . escapeshellarg($strMD5File) . ' ' . escapeshellarg($strMD5StatusFile) . ')';
$strCleanPgrep = '-f "chmod.*chown.*rm.*' . $strMD5StatusFile . '"';
$strAllCmd = "#!/bin/bash\n\n";
$strAllCmd .= $strDownloadCmd . ' >>' . escapeshellarg($strLogFile) . ' 2>&1 && ';
$strAllCmd .= 'echo "' . $arrDownloadOpenELEC['md5'] . ' ' . $strTempFile . '" > ' . escapeshellarg($strMD5File) . ' && ';
@@ -136,103 +125,69 @@
$strAllCmd .= 'rm ' . escapeshellarg($strLogFile) . ' && ';
$strAllCmd .= 'rm ' . escapeshellarg($strInstallScript);
$arrResponse = [];
$reply = [];
if (file_exists($strExtractedFile)) {
if (!file_exists($strTempFile)) {
// Status = done
$arrResponse['status'] = 'Done';
$arrResponse['localpath'] = $strExtractedFile;
$arrResponse['localfolder'] = dirname($strExtractedFile);
$reply['status'] = 'Done';
$reply['localpath'] = $strExtractedFile;
$reply['localfolder'] = dirname($strExtractedFile);
} else {
if (pgrep($strExtractPgrep, false)) {
// Status = running extract
$arrResponse['status'] = 'Extracting ... ';
$reply['status'] = 'Extracting ... ';
} else {
// Status = cleanup
$arrResponse['status'] = 'Cleanup ... ';
$reply['status'] = 'Cleanup ... ';
}
}
} elseif (file_exists($strTempFile)) {
if (pgrep($strDownloadPgrep, false)) {
// Get Download percent completed
$intSize = filesize($strTempFile);
$strPercent = 0;
if ($intSize > 0) {
$strPercent = round(($intSize / $arrDownloadOpenELEC['size']) * 100);
}
$arrResponse['status'] = 'Downloading ... ' . $strPercent . '%';
$reply['status'] = 'Downloading ... ' . $strPercent . '%';
} elseif (pgrep($strVerifyPgrep, false)) {
// Status = running md5 check
$arrResponse['status'] = 'Verifying ... ';
$reply['status'] = 'Verifying ... ';
} elseif (file_exists($strMD5StatusFile)) {
// Status = running extract
$arrResponse['status'] = 'Extracting ... ';
$reply['status'] = 'Extracting ... ';
if (!pgrep($strExtractPgrep, false)) {
// Examine md5 status
$strMD5StatusContents = file_get_contents($strMD5StatusFile);
if (strpos($strMD5StatusContents, ': FAILED') !== false) {
// ERROR: MD5 check failed
unset($arrResponse['status']);
$arrResponse['error'] = 'MD5 verification failed, your download is incomplete or corrupted.';
unset($reply['status']);
$reply['error'] = 'MD5 verification failed, your download is incomplete or corrupted.';
}
}
} elseif (!file_exists($strMD5File)) {
// Status = running md5 check
$arrResponse['status'] = 'Downloading ... 100%';
$reply['status'] = 'Downloading ... 100%';
if (!pgrep($strInstallScriptPgrep, false) && !$boolCheckOnly) {
// Run all commands
file_put_contents($strInstallScript, $strAllCmd);
chmod($strInstallScript, 0777);
exec($strInstallScript . ' >/dev/null 2>&1 &');
}
}
} elseif (!$boolCheckOnly) {
if (!pgrep($strInstallScriptPgrep, false)) {
// Run all commands
file_put_contents($strInstallScript, $strAllCmd);
chmod($strInstallScript, 0777);
exec($strInstallScript . ' >/dev/null 2>&1 &');
}
$arrResponse['status'] = 'Downloading ... ';
$reply['status'] = 'Downloading ... ';
}
$arrResponse['pid'] = pgrep($strInstallScriptPgrep, false);
$reply['pid'] = pgrep($strInstallScriptPgrep, false);
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
@@ -305,89 +260,61 @@
]
];
$hdrXML = "<?xml version='1.0' encoding='UTF-8'?>\n"; // XML encoding declaration
// Merge in any default values from the VM template
if (!empty($arrAllTemplates[$strSelectedTemplate]) && !empty($arrAllTemplates[$strSelectedTemplate]['overrides'])) {
if ($arrAllTemplates[$strSelectedTemplate] && $arrAllTemplates[$strSelectedTemplate]['overrides']) {
$arrConfigDefaults = array_replace_recursive($arrConfigDefaults, $arrAllTemplates[$strSelectedTemplate]['overrides']);
}
if (array_key_exists('updatevm', $_POST) && !empty($_POST['domain']['uuid'])) {
$_GET['uuid'] = $_POST['domain']['uuid'];
}
// If we are editing a existing VM load it's existing configuration details
$boolNew = true;
$boolRunning = false;
$arrExistingConfig = [];
$strUUID = '';
$strXML = '';
if (!empty($_GET['uuid'])) {
$strUUID = $_GET['uuid'];
$res = $lv->domain_get_name_by_uuid($strUUID);
$dom = $lv->domain_get_info($res);
$boolNew = false;
$boolRunning = ($lv->domain_state_translate($dom['state']) != 'shutoff');
$arrExistingConfig = domain_to_config($strUUID);
$strXML = $lv->domain_get_xml($res);
}
// Active config for this page
$arrConfig = array_replace_recursive($arrConfigDefaults, $arrExistingConfig);
if (array_key_exists($arrConfig['template']['openelec'], $arrOpenELECVersions)) {
$arrConfigDefaults['disk'][0]['image'] = $arrOpenELECVersions[$arrConfig['template']['openelec']]['localpath'];
}
if (array_key_exists('createvm', $_POST)) {
$arrResponse = ['success' => true];
if (array_key_exists('xmldesc', $_POST)) {
$tmp = $lv->domain_define($_POST['xmldesc'], !empty($config['domain']['xmlstartnow']));
if (!$tmp){
$arrResponse = ['error' => $lv->get_last_error()];
// create new VM
if ($_POST['createvm']) {
if ($_POST['xmldesc']) {
// XML view
$new = $lv->domain_define($_POST['xmldesc'], $_POST['domain']['xmlstartnow']==1);
if ($new){
$lv->domain_set_autostart($new, $_POST['domain']['autostart']==1);
$reply = ['success' => true];
} else {
$lv->domain_set_autostart($tmp, $_POST['domain']['autostart'] == 1);
$reply = ['error' => $lv->get_last_error()];
}
} else {
if (!empty($_POST['shares'][0]['source'])) {
// form view
if ($_POST['shares'][0]['source']) {
@mkdir($_POST['shares'][0]['source'], 0777, true);
}
$tmp = $lv->domain_new($_POST);
if (!$tmp){
$arrResponse = ['error' => $lv->get_last_error()];
if ($lv->domain_new($_POST)){
$reply = ['success' => true];
} else {
$reply = ['error' => $lv->get_last_error()];
}
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if (array_key_exists('updatevm', $_POST)) {
$dom = $lv->domain_get_domain_by_uuid($_POST['domain']['uuid']);
// update existing VM
if ($_POST['updatevm']) {
$uuid = $_POST['domain']['uuid'];
$dom = $lv->domain_get_domain_by_uuid($uuid);
$oldAutoStart = $lv->domain_get_autostart($dom)==1;
$newAutoStart = $_POST['domain']['autostart']==1;
$strXML = $lv->domain_get_xml($dom);
if ($boolRunning) {
if ($lv->domain_get_state($dom)=='running') {
$arrErrors = [];
$arrExistingConfig = domain_to_config($_POST['domain']['uuid']);
$arrExistingConfig = domain_to_config($uuid);
$arrNewUSBIDs = $_POST['usb'];
// hot-attach any new usb devices
foreach ($arrNewUSBIDs as $strNewUSBID) {
foreach ($arrExistingConfig['usb'] as $arrExistingUSB) {
if ($strNewUSBID == $arrExistingUSB['id']) {
continue 2;
}
if ($strNewUSBID == $arrExistingUSB['id']) continue 2;
}
list($strVendor, $strProduct) = explode(':', $strNewUSBID);
list($strVendor,$strProduct) = explode(':', $strNewUSBID);
// hot-attach usb
file_put_contents('/tmp/hotattach.tmp', "<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x".$strVendor."'/>
<product id='0x".$strProduct."'/>
</source>
</hostdev>");
exec("virsh attach-device " . escapeshellarg($_POST['domain']['uuid']) . " /tmp/hotattach.tmp --live 2>&1", $arrOutput, $intReturnCode);
file_put_contents('/tmp/hotattach.tmp', "<hostdev mode='subsystem' type='usb'><source startupPolicy='optional'><vendor id='0x".$strVendor."'/><product id='0x".$strProduct."'/></source></hostdev>");
exec("virsh attach-device ".escapeshellarg($uuid)." /tmp/hotattach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) {
$arrErrors[] = implode(' ', $arrOutput);
}
@@ -397,92 +324,88 @@
foreach ($arrExistingConfig['usb'] as $arrExistingUSB) {
if (!in_array($arrExistingUSB['id'], $arrNewUSBIDs)) {
list($strVendor, $strProduct) = explode(':', $arrExistingUSB['id']);
file_put_contents('/tmp/hotdetach.tmp', "<hostdev mode='subsystem' type='usb'>
<source startupPolicy='optional'>
<vendor id='0x".$strVendor."'/>
<product id='0x".$strProduct."'/>
</source>
</hostdev>");
exec("virsh detach-device " . escapeshellarg($_POST['domain']['uuid']) . " /tmp/hotdetach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) {
$arrErrors[] = implode(' ', $arrOutput);
}
file_put_contents('/tmp/hotdetach.tmp', "<hostdev mode='subsystem' type='usb'><source startupPolicy='optional'><vendor id='0x".$strVendor."'/><product id='0x".$strProduct."'/></source></hostdev>");
exec("virsh detach-device ".escapeshellarg($uuid)." /tmp/hotdetach.tmp --live 2>&1", $arrOutput, $intReturnCode);
if ($intReturnCode != 0) $arrErrors[] = implode(' ',$arrOutput);
}
}
if (empty($arrErrors)) {
$arrResponse = ['success' => true];
} else {
$arrResponse = ['error' => implode(', ', $arrErrors)];
}
echo json_encode($arrResponse);
$reply = !$arrErrors ? ['success' => true] : ['error' => implode(', ',$arrErrors)];
echo json_encode($reply);
exit;
}
// Backup xml for existing domain in ram
$strOldXML = '';
$boolOldAutoStart = false;
// backup xml for existing domain in ram
if ($dom) {
$strOldXML = $lv->domain_get_xml($dom);
$boolOldAutoStart = $lv->domain_get_autostart($dom);
if (!array_key_exists('xmldesc', $_POST)) {
$strOldName = $lv->domain_get_name($dom);
$strNewName = $_POST['domain']['name'];
if (!empty($strOldName) &&
!empty($strNewName) &&
is_dir($domain_cfg['DOMAINDIR'].$strOldName.'/') &&
!is_dir($domain_cfg['DOMAINDIR'].$strNewName.'/')) {
$oldName = $lv->domain_get_name($dom);
$newName = $_POST['domain']['name'];
if (!$_POST['xmldesc']) {
$oldDir = $domain_cfg['DOMAINDIR'].$oldName;
$newDir = $domain_cfg['DOMAINDIR'].$newdName;
if ($oldName && $newName && is_dir($oldDir) && !is_dir($newDir)) {
// mv domain/vmname folder
if (rename($domain_cfg['DOMAINDIR'].$strOldName, $domain_cfg['DOMAINDIR'].$strNewName)) {
if (rename($oldDir, $newDir)) {
// replace all disk paths in xml
foreach ($_POST['disk'] as &$arrDisk) {
if (!empty($arrDisk['new'])) {
$arrDisk['new'] = str_replace($domain_cfg['DOMAINDIR'].$strOldName.'/', $domain_cfg['DOMAINDIR'].$strNewName.'/', $arrDisk['new']);
}
if (!empty($arrDisk['image'])) {
$arrDisk['image'] = str_replace($domain_cfg['DOMAINDIR'].$strOldName.'/', $domain_cfg['DOMAINDIR'].$strNewName.'/', $arrDisk['image']);
}
if ($arrDisk['new']) $arrDisk['new'] = str_replace($oldDir, $newDir, $arrDisk['new']);
if ($arrDisk['image']) $arrDisk['image'] = str_replace($oldDir, $newDir, $arrDisk['image']);
}
}
}
}
}
// Remove existing domain
$lv->nvram_backup($_POST['domain']['uuid']);
$lv->domain_undefine($dom);
$lv->nvram_restore($_POST['domain']['uuid']);
// Save new domain
if (array_key_exists('xmldesc', $_POST)) {
$tmp = $lv->domain_define($_POST['xmldesc']);
// construct updated config
if ($_POST['xmldesc']) {
// XML view
$xml = $_POST['xmldesc'];
} else {
if (!empty($_POST['shares'][0]['source'])) {
// form view
if ($_POST['shares'][0]['source']) {
@mkdir($_POST['shares'][0]['source'], 0777, true);
}
$tmp = $lv->domain_new($_POST);
$arrExistingConfig = custom::createArray('domain',$strXML);
$arrExistingConfig['metadata']['vmtemplate']['@attributes']['xmlns'] = 'unraid';
$arrExistingConfig['cputune']['vcpupin'] = [];
$arrUpdatedConfig = custom::createArray('domain',$lv->config_to_xml($_POST));
$arrConfig = array_replace_recursive($arrExistingConfig, $arrUpdatedConfig);
$xml = custom::createXML('domain',$arrConfig)->saveXML();
}
if (!$tmp){
$strLastError = $lv->get_last_error();
// Failure -- try to restore existing domain
$tmp = $lv->domain_define($strOldXML);
if ($tmp) $lv->domain_set_autostart($tmp, $boolOldAutoStart);
$arrResponse = ['error' => $strLastError];
// delete and create the VM
$lv->nvram_backup($uuid);
$lv->domain_undefine($dom);
$lv->nvram_restore($uuid);
$new = $lv->domain_define($xml);
if ($new) {
$lv->domain_set_autostart($new, $newAutoStart);
$reply = ['success' => true];
} else {
$lv->domain_set_autostart($tmp, $_POST['domain']['autostart'] == 1);
$arrResponse = ['success' => true];
// Failure -- try to restore existing domain
$old = $lv->domain_define($strXML);
if ($old) $lv->domain_set_autostart($old, $oldAutoStart);
$reply = ['error' => $lv->get_last_error()];
}
echo json_encode($arrResponse);
echo json_encode($reply);
exit;
}
if ($_GET['uuid']) {
// edit an existing VM
$dom = $lv->domain_get_domain_by_uuid($_GET['uuid']);
$boolRunning = $lv->domain_get_state($dom)=='running';
$strXML = $lv->domain_get_xml($dom);
$boolNew = false;
$arrConfig = domain_to_config($_GET['uuid']);
} else {
// edit new VM
$boolRunning = false;
$strXML = '';
$boolNew = true;
$arrConfig = $arrConfigDefaults;
}
if (array_key_exists($arrConfig['template']['openelec'], $arrLibreELECVersions)) {
$arrConfigDefaults['disk'][0]['image'] = $arrLibreELECVersions[$arrConfig['template']['openelec']]['localpath'];
}
?>
<link rel="stylesheet" href="/plugins/dynamix.vm.manager/scripts/codemirror/lib/codemirror.css">