diff options
Diffstat (limited to 'modules/serversetup')
-rw-r--r-- | modules/serversetup/config.json | 4 | ||||
-rw-r--r-- | modules/serversetup/module.inc.php | 186 | ||||
-rw-r--r-- | modules/serversetup/templates/ipaddress.html | 34 | ||||
-rw-r--r-- | modules/serversetup/templates/ipxe.html | 149 | ||||
-rw-r--r-- | modules/serversetup/templates/ipxe_update.html | 20 |
5 files changed, 393 insertions, 0 deletions
diff --git a/modules/serversetup/config.json b/modules/serversetup/config.json new file mode 100644 index 00000000..93209f62 --- /dev/null +++ b/modules/serversetup/config.json @@ -0,0 +1,4 @@ +{ + "category":"settings", + "enabled":"true" +} diff --git a/modules/serversetup/module.inc.php b/modules/serversetup/module.inc.php new file mode 100644 index 00000000..e37d7d40 --- /dev/null +++ b/modules/serversetup/module.inc.php @@ -0,0 +1,186 @@ +<?php + +class Page_ServerSetup extends Page +{ + + private $mountIpxeTask; + private $taskStatus; + private $currentAddress; + private $currentMenu; + + protected function doPreprocess() + { + User::load(); + + if (!User::hasPermission('superadmin')) { + Message::addError('no-permission'); + Util::redirect('?do=Main'); + } + + $this->currentMenu = Property::getBootMenu(); + + if(Request::get('download') !== false){ + $this->downloadIpxe(Request::get('download')); + } + + if(Request::get('defaultIpxe') !== false){ + $this->defaultIpxe(Request::get('defaultIpxe')); + } + + $action = Request::post('action'); + + if ($action === false) { + $this->currentAddress = Property::getServerIp(); + $this->getLocalAddresses(); + } + + if ($action === 'ip') { + // New address is to be set + $this->getLocalAddresses(); + $this->updateLocalAddress(); + } + + if ($action === 'ipxe') { + // iPXE stuff changes + $this->updatePxeMenu(); + } + + if($action === 'save-script') { + // Save new iPXE script + $this->updateIpxeScript(); + } + + if($action === 'default-script') { + // Restore iPXE script to default + $this->defaultIpxe(); + } + } + + protected function doRender() + { + Render::setTitle(Dictionary::translate('lang_serverConfiguration')); + + $taskid = Request::any('taskid'); + if ($taskid !== false && Taskmanager::isTask($taskid)) { + Render::addTemplate('ipxe_update', array('taskid' => $taskid)); + } + + Render::addTemplate('ipaddress', array( + 'ips' => $this->taskStatus['data']['addresses'] + )); + $data = $this->currentMenu; + if (!isset($data['defaultentry'])) + $data['defaultentry'] = 'net'; + if ($data['defaultentry'] === 'net') + $data['active-net'] = 'checked'; + if ($data['defaultentry'] === 'hdd') + $data['active-hdd'] = 'checked'; + if ($data['defaultentry'] === 'custom') + $data['active-custom'] = 'checked'; + Render::addTemplate('ipxe', $data); + } + + // ----------------------------------------------------------------------------------------------- + + private function getLocalAddresses() + { + $this->taskStatus = Taskmanager::submit('LocalAddressesList', array()); + + if ($this->taskStatus === false) { + $this->taskStatus['data']['addresses'] = false; + return false; + } + + if ($this->taskStatus['statusCode'] === TASK_WAITING) { // TODO: Async if just displaying + $this->taskStatus = Taskmanager::waitComplete($this->taskStatus['id']); + } + + $sortIp = array(); + foreach (array_keys($this->taskStatus['data']['addresses']) as $key) { + $item = & $this->taskStatus['data']['addresses'][$key]; + if (!isset($item['ip']) || !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $item['ip']) || substr($item['ip'], 0, 4) === '127.') { + unset($this->taskStatus['data']['addresses'][$key]); + continue; + } + if ($this->currentAddress === $item['ip']) { + $item['default'] = true; + } + $sortIp[] = $item['ip']; + } + unset($item); + array_multisort($sortIp, SORT_STRING, $this->taskStatus['data']['addresses']); + return true; + } + + private function updateLocalAddress() + { + $newAddress = Request::post('ip', 'none'); + $valid = false; + foreach ($this->taskStatus['data']['addresses'] as $item) { + if ($item['ip'] !== $newAddress) + continue; + $valid = true; + break; + } + if ($valid) { + Property::setServerIp($newAddress); + global $tidIpxe; + if (isset($tidIpxe) && $tidIpxe !== false) + Util::redirect('?do=ServerSetup&taskid=' . $tidIpxe); + } else { + Message::addError('invalid-ip', $newAddress); + } + Util::redirect(); + } + + private function updatePxeMenu() + { + $timeout = Request::post('timeout', 10); + if ($timeout === '') + $timeout = 0; + if (!is_numeric($timeout) || $timeout < 0) { + Message::addError('value-invalid', 'timeout', $timeout); + } + $this->currentMenu['defaultentry'] = Request::post('defaultentry', 'net'); + $this->currentMenu['timeout'] = $timeout; + $this->currentMenu['custom'] = Request::post('custom', ''); + $this->currentMenu['masterpasswordclear'] = Request::post('masterpassword', ''); + if (empty($this->currentMenu['masterpasswordclear'])) + $this->currentMenu['masterpassword'] = 'invalid'; + else + $this->currentMenu['masterpassword'] = Crypto::hash6($this->currentMenu['masterpasswordclear']); + Property::setBootMenu($this->currentMenu); + $id = Trigger::ipxe(); + Util::redirect('?do=ServerSetup&taskid=' . $id); + } + + private function downloadIpxe($ipxe){ + $file = '/opt/taskmanager/data/ipxe/src/bin/ipxe.' . $ipxe; + if (file_exists($file)) { + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename='.basename($file)); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Length: ' . filesize($file)); + ob_clean(); + flush(); + readfile($file); + exit(); + } + } + + private function updateIpxeScript(){ + $newScript = Request::post('custom-script'); + file_put_contents("/opt/taskmanager/data/pxe.embed",$newScript); + Util::redirect('?do=ServerSetup'); + } + + private function defaultIpxe(){ + $default = file_get_contents("/opt/taskmanager/data/pxe_default.embed"); + $default = str_replace("{{ip}}", "http://" . Property::getServerIp(), $default); + file_put_contents("/opt/taskmanager/data/pxe.embed",$default); + Util::redirect('?do=ServerSetup'); + } +} diff --git a/modules/serversetup/templates/ipaddress.html b/modules/serversetup/templates/ipaddress.html new file mode 100644 index 00000000..e4967703 --- /dev/null +++ b/modules/serversetup/templates/ipaddress.html @@ -0,0 +1,34 @@ +<div class="panel panel-default"> + <div class="panel-heading"> + {{lang_bootAddress}} + </div> + <div class="panel-body"> + <p> + {{lang_chooseIP}} + </p> + <form method="post" action="?do=ServerSetup"> + <input type="hidden" name="action" value="ip"> + <input type="hidden" name="token" value="{{token}}"> + <table class="slx-table"> + {{#ips}} + <tr> + <td>{{ip}}</td> + {{#default}} + <td> + <span class="btn btn-success btn-xs"><span class="glyphicon glyphicon-ok"></span> {{lang_active}}</span> + </td> + {{/default}} + {{^default}} + <td> + <button class="btn btn-primary btn-xs" name="ip" value="{{ip}}"><span class="glyphicon glyphicon-flag"></span> {{lang_set}}</button> + </td> + {{/default}} + </tr> + {{/ips}} + </table> + <p> + {{lang_bootHint}} + </p> + </form> + </div> +</div>
\ No newline at end of file diff --git a/modules/serversetup/templates/ipxe.html b/modules/serversetup/templates/ipxe.html new file mode 100644 index 00000000..54d7db16 --- /dev/null +++ b/modules/serversetup/templates/ipxe.html @@ -0,0 +1,149 @@ +<div class="panel panel-default"> + <div class="panel-heading"> + {{lang_mountIpxe}} + </div> + <div class="panel-body"> + <p>{{lang_ipxeInfo}}</p> + <label for="ext">{{lang_extension}}:</label> + <select name="ext" class="form-control"> + <option value="kkpxe">.kkpxe</option> + <option value="usb">.usb</option> + <option value="iso">.iso</option> + </select> + <br> + <form method="post" action="?do=ServerSetup" style="display:inline;"> + <input type="hidden" name="action" value="save-script"> + <input type="hidden" name="token" value="{{token}}"> + + <label for="custom-script">{{lang_customScript}}</label> + <textarea class="form-control" name="custom-script" rows="9" style="resize:none">{{script}}</textarea> + <br> + <input class="btn btn-default btn-sm" type="submit" value="{{lang_saveScript}}" /> + </form> + <form method="post" action="?do=ServerSetup" style="display:inline;"> + <input type="hidden" name="action" value="default-script"> + <input type="hidden" name="token" value="{{token}}"> + <input class="btn btn-default btn-sm" type="submit" value="{{lang_restoreDefault}}" /> + </form> + </div> + <div class="panel-footer"> + <button id="mount-button" onclick="mountIpxe();" class="btn btn-primary" type="button" data-toggle="modal" data-target="#ipxe-modal" data-backdrop="static"> {{lang_compile}} + </button> + </div> +</div> + +<form method="post" action="?do=ServerSetup"> + <input type="text" name="prevent_autofill" id="prevent_autofill" value="" style="display:none;"> + <input type="password" name="password_fake" id="password_fake" value="" style="display:none;"> + <input type="hidden" name="action" value="ipxe"> + <input type="hidden" name="token" value="{{token}}"> + <div class="panel panel-default"> + <div class="panel-heading"> + {{lang_bootMenu}} + </div> + <div class="panel-body"> + <p> + {{lang_bootInfo}} + </p> + <br> + + <div class="form-group"> + <strong>{{lang_bootBehavior}}</strong> + <div><label class="radio-inline"><input type="radio" name="defaultentry" value="net" {{active-net}}> bwLehrpool</label></div> + <div><label class="radio-inline"><input type="radio" name="defaultentry" value="hdd" {{active-hdd}}> {{lang_localHDD}}</label></div> + <div><label class="radio-inline"><input type="radio" name="defaultentry" value="custom" {{active-custom}}> {{lang_customEntry}} ("custom")</label></div> + </div> + + <div class="form-group"> + <strong>{{lang_menuDisplayTime}}</strong> + <div class="input-group form-narrow"> + <input type="text" class="form-control" name="timeout" value="{{timeout}}" pattern="\d+"> + <span class="input-group-addon">{{lang_seconds}}</span> + </div> + </div> + + <div class="form-group"> + <strong>{{lang_masterPassword}}</strong> + <div class="form-narrow"> + <input type="{{password_type}}" class="form-control" name="masterpassword" value="{{masterpasswordclear}}"> + </div> + <i>{{lang_masterPasswordHelp}}</i> + </div> + + <div class="form-group"> + <strong>{{lang_menuCustom}}</strong> <a class="btn btn-default btn-xs" data-toggle="modal" data-target="#help-custom"><span class="glyphicon glyphicon-question-sign"></span></a> + <textarea class="form-control" name="custom" rows="8">{{custom}}</textarea> + </div> + </div> + + <div class="panel-footer"> + <button class="btn btn-primary" name="action" value="ipxe">{{lang_bootMenuCreate}}</button> + </div> + </div> +</form> + +<div class="modal fade" id="help-custom" tabindex="-1" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header">{{lang_menuCustom}}</div> + <div class="modal-body"> + {{lang_menuCustomHint1}} + <br>{{lang_example}}: + <pre>LABEL custom + MENU LABEL ^My Boot Entry + KERNEL http://1.2.3.4/kernel + INITRD http://1.2.3.4/initramfs-stage31 + APPEND custom=option + IPAPPEND 3</pre> + {{lang_menuCustomHint2}} LABEL <strong>custom</strong> + {{lang_menuCustomHint3}} + </div> + <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">{{lang_close}}</a></div> + </div> + </div> +</div> + +<div class="modal fade" id="ipxe-modal" tabindex="-1" role="dialog" aria-labelledby="ipxe-modal-label" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> + <h4 class="modal-title" id="ipxe-modal-label">{{lang_compilingIpxe}}</h4> + </div> + <div class="modal-body" id="ipxe-modal-body"> + + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button> + <button id="download-btn" type="button" class="btn btn-primary" disabled="disabled" onclick="downloadIpxe()">{{lang_download}}</button> + </div> + </div> + </div> +</div> + +<script> + function mountIpxe() { + document.getElementById('ipxe-modal-body').innerHTML = "<div class='alert alert-info' role='alert'>{{lang_ipxeWarning}}</div>" + + "{{lang_loading}} <img src='fonts/loader.gif'/>"; + $("#download-btn").prop("disabled",true); + var xmlhttp = new XMLHttpRequest(); + var extension = $("select[name=ext]").val(); + xmlhttp.open("GET","?do=ServerSetup&async=true&submitTask=true&extension=" + extension,true); + xmlhttp.onreadystatechange= function() { + if (xmlhttp.readyState==4 && xmlhttp.status==200) { + var initResponse = xmlhttp.responseText; + if(initResponse != "success") + document.getElementById('ipxe-modal-body').innerHTML = initResponse; + else { + document.getElementById('ipxe-modal-body').innerHTML = "{{lang_success}}: ipxe." + extension; + $("#download-btn").prop("disabled",false); + } + } + } + xmlhttp.send(); + } + + function downloadIpxe() { + window.location = "?do=ServerSetup&download=" + $("select[name=ext]").val(); + } +</script> diff --git a/modules/serversetup/templates/ipxe_update.html b/modules/serversetup/templates/ipxe_update.html new file mode 100644 index 00000000..9c598667 --- /dev/null +++ b/modules/serversetup/templates/ipxe_update.html @@ -0,0 +1,20 @@ +<div class="panel panel-default"> + <div class="panel-heading">{{lang_menuGeneration}}</div> + <div class="panel-body"> + <div data-tm-id="{{taskid}}" data-tm-log="error" data-tm-callback="restartCb">{{lang_menuGeneration}}</div> + <div id="genfailed" class="alert alert-danger" style="display:none"> + {{lang_generationFailed}} + </div> + </div> +</div> + +<script type="text/javascript"> + function restartCb(task) + { + if (!task || !task.statusCode) + return; + if (task.statusCode === 'TASK_ERROR') { + $('#genfailed').show('slow'); + } + } +</script> |