summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2014-05-26 22:49:54 +0200
committerSimon Rettberg2014-05-26 22:49:54 +0200
commita4f4147b6fe5f2a003a61cf8a8f7508c94130b31 (patch)
treede34472a2b9bd83909469affb884619b4858efc1
parentWIP (diff)
downloadslx-admin-a4f4147b6fe5f2a003a61cf8a8f7508c94130b31.tar.gz
slx-admin-a4f4147b6fe5f2a003a61cf8a8f7508c94130b31.tar.xz
slx-admin-a4f4147b6fe5f2a003a61cf8a8f7508c94130b31.zip
WIP
-rw-r--r--api.php16
-rw-r--r--apis/getconfig.inc.php17
-rw-r--r--apis/init.inc.php7
-rw-r--r--apis/taskmanager.inc.php10
-rw-r--r--config.php3
-rw-r--r--inc/property.inc.php47
-rw-r--r--inc/render.inc.php1
-rw-r--r--inc/taskmanager.inc.php41
-rw-r--r--inc/trigger.inc.php40
-rw-r--r--index.php2
-rw-r--r--modules/main.inc.php9
-rw-r--r--modules/minilinux.inc.php73
-rw-r--r--modules/sysconfig.inc.php1
-rw-r--r--modules/vmstore.inc.php72
-rw-r--r--script/taskmanager.js33
-rw-r--r--style/default.css7
-rw-r--r--templates/main-menu.html1
-rw-r--r--templates/minilinux/download.html1
-rw-r--r--templates/minilinux/filelist.html39
-rw-r--r--templates/page-main.html6
-rw-r--r--templates/page-minilinux.html39
-rw-r--r--templates/page-vmstore.html89
-rw-r--r--templates/vmstore/mount.html28
23 files changed, 511 insertions, 71 deletions
diff --git a/api.php b/api.php
index 16408440..f8e9c4d5 100644
--- a/api.php
+++ b/api.php
@@ -2,12 +2,16 @@
error_reporting(E_ALL);
-require_once('inc/user.inc.php');
-require_once('inc/util.inc.php');
-require_once('inc/database.inc.php');
-require_once('inc/permission.inc.php');
-require_once('inc/crypto.inc.php');
-require_once('inc/validator.inc.php');
+require_once 'config.php';
+
+// Autoload classes from ./inc which adhere to naming scheme <lowercasename>.inc.php
+function slxAutoloader($class) {
+ $file = 'inc/' . preg_replace('/[^a-z0-9]/', '', mb_strtolower($class)) . '.inc.php';
+ if (!file_exists($file)) return;
+ require_once $file;
+}
+spl_autoload_register('slxAutoloader');
+
if (empty($_REQUEST['do'])) {
// No specific module - set default
diff --git a/apis/getconfig.inc.php b/apis/getconfig.inc.php
index 1e7a4f53..f29118c8 100644
--- a/apis/getconfig.inc.php
+++ b/apis/getconfig.inc.php
@@ -1,5 +1,7 @@
<?php
+require_once 'inc/property.inc.php';
+
// Dump config from DB
$res = Database::simpleQuery('SELECT setting.setting, setting.defaultvalue, setting.permissions, setting.description, tbl.value
FROM setting
@@ -12,3 +14,18 @@ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
// Additional "intelligent" config
echo "SLX_REMOTE_LOG='http://${_SERVER['SERVER_ADDR']}/slxadmin/api.php?do=clientlog'\n";
+$vmstore = Property::getVmStoreConfig();
+
+if (is_array($vmstore)) {
+ switch ($vmstore['storetype']) {
+ case 'internal';
+ echo "SLX_VM_NFS='{$_SERVER['SERVER_ADDR']}:/srv/openslx/nfs'\n";
+ break;
+ case 'nfs';
+ echo "SLX_VM_NFS='{$vmstore['nfsaddr']}'\n";
+ break;
+ case 'cifs';
+ echo "SLX_VM_NFS='{$vmstore['cifsaddr']}'\n";
+ break;
+ }
+}
diff --git a/apis/init.inc.php b/apis/init.inc.php
new file mode 100644
index 00000000..cdd6ac05
--- /dev/null
+++ b/apis/init.inc.php
@@ -0,0 +1,7 @@
+<?php
+
+if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1')
+ exit(0);
+
+Trigger::ldadp();
+Trigger::mount();
diff --git a/apis/taskmanager.inc.php b/apis/taskmanager.inc.php
index 78cd101c..7bb38a98 100644
--- a/apis/taskmanager.inc.php
+++ b/apis/taskmanager.inc.php
@@ -16,11 +16,15 @@ foreach ($_POST['ids'] as $id) {
}
$return[] = $status;
// HACK HACK - should be pluggable
- if (isset($status['statusCode']) && $status['statusCode'] === TASK_FINISHED
- && $status['id'] === Property::getIPxeTaskId() && Property::getServerIp() !== Property::getIPxeIp()) {
+ if (isset($status['statusCode']) && $status['statusCode'] === TASK_FINISHED // iPXE Update
+ && $id === Property::getIPxeTaskId() && Property::getServerIp() !== Property::getIPxeIp()) {
Property::setIPxeIp(Property::getServerIp());
}
- //
+ if (isset($status['statusCode']) && $status['statusCode'] === TASK_FINISHED // MiniLinux Version check
+ && $id === Property::getVersionCheckTaskId()) {
+ Property::setVersionCheckInformation(Property::getServerIp());
+ }
+ // -- END HACKS --
if (!isset($status['statusCode']) || ($status['statusCode'] !== TASK_WAITING && $status['statusCode'] !== TASK_PROCESSING)) {
Taskmanager::release($id);
}
diff --git a/config.php b/config.php
index ac6a9de4..90422a05 100644
--- a/config.php
+++ b/config.php
@@ -15,8 +15,7 @@ define('CONFIG_SQL_PASS', 'geheim');
define('CONFIG_TGZ_LIST_DIR', '/opt/openslx/configs');
-define('CONFIG_REMOTE_TGZ', 'http://mltk.boot.openslx.org/tgz');
-define('CONFIG_REMOTE_ML', 'http://mltk.boot.openslx.org/update');
+define('CONFIG_REMOTE_ML', 'http://mltk.boot.openslx.org/update/new');
define('CONFIG_TFTP_DIR', '/srv/openslx/tftp');
define('CONFIG_HTTP_DIR', '/srv/openslx/www/boot');
diff --git a/inc/property.inc.php b/inc/property.inc.php
index d186193a..5c316517 100644
--- a/inc/property.inc.php
+++ b/inc/property.inc.php
@@ -84,5 +84,52 @@ class Property
{
self::set('ipxe-menu', json_encode($value));
}
+
+ public static function getVersionCheckTaskId()
+ {
+ return self::get('versioncheck-task');
+ }
+
+ public static function setVersionCheckTaskId($value)
+ {
+ self::set('versioncheck-task', $value);
+ }
+
+ public static function getVersionCheckInformation()
+ {
+ $data = json_decode(self::get('versioncheck-data'), true);
+ if (isset($data['time']) && $data['time'] + 120 > time())
+ return $data;
+ $task = Taskmanager::submit('DownloadText', array(
+ 'url' => CONFIG_REMOTE_ML . '/list.php'
+ ));
+ if (!isset($task['id']))
+ return false;
+ if ($task['statusCode'] !== TASK_FINISHED) {
+ $task = Taskmanager::waitComplete($task['id']);
+ }
+ if ($task['statusCode'] !== TASK_FINISHED || !isset($task['data']['content'])) {
+ return $task['data']['error'];
+ }
+ $data = json_decode($task['data']['content'], true);
+ $data['time'] = time();
+ self::setVersionCheckInformation($data);
+ return $data;
+ }
+
+ public static function setVersionCheckInformation($value)
+ {
+ self::set('versioncheck-data', json_encode($value));
+ }
+
+ public static function getVmStoreConfig()
+ {
+ return json_decode(self::get('vmstore-config'), true);
+ }
+
+ public static function setVmStoreConfig($value)
+ {
+ self::set('vmstore-config', json_encode($value));
+ }
}
diff --git a/inc/render.inc.php b/inc/render.inc.php
index cf0958c2..6853acde 100644
--- a/inc/render.inc.php
+++ b/inc/render.inc.php
@@ -48,7 +48,6 @@ class Render
<script type="text/javascript">
var TOKEN = "' . Session::get('token') . '";
</script>
- </body>
',
self::$header
,
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
index e93cc696..99e35e94 100644
--- a/inc/taskmanager.inc.php
+++ b/inc/taskmanager.inc.php
@@ -5,12 +5,13 @@
*/
class Taskmanager
{
-
+
private static $sock = false;
-
+
private static function init()
{
- if (self::$sock !== false) return;
+ if (self::$sock !== false)
+ return;
self::$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option(self::$sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 0, 'usec' => 300000));
socket_set_option(self::$sock, SOL_SOCKET, SO_SNDTIMEO, array('sec' => 0, 'usec' => 200000));
@@ -20,7 +21,7 @@ class Taskmanager
public static function submit($task, $data, $async = false)
{
self::init();
- $seq = (string)mt_rand();
+ $seq = (string) mt_rand();
if (empty($data)) {
$data = '{}';
} else {
@@ -32,7 +33,8 @@ class Taskmanager
Message::addError('taskmanager-error');
return false;
}
- if ($async) return true;
+ if ($async)
+ return true;
$reply = self::readReply($seq);
if ($reply === false) {
Message::addError('taskmanager-error');
@@ -56,38 +58,50 @@ class Taskmanager
public static function status($taskId)
{
self::init();
- $seq = (string)mt_rand();
+ $seq = (string) mt_rand();
$message = "$seq, status, $taskId";
$sent = socket_send(self::$sock, $message, strlen($message), 0);
$reply = self::readReply($seq);
- if (!is_array($reply)) return false;
+ if (!is_array($reply))
+ return false;
return $reply;
}
-
+
public static function waitComplete($taskId)
{
$done = false;
for ($i = 0; $i < 10; ++$i) {
$status = self::status($taskId);
- if (!isset($status['statusCode'])) break;
+ if (!isset($status['statusCode']))
+ break;
if ($status['statusCode'] != TASK_PROCESSING && $status['statusCode'] != TASK_WAITING) {
$done = true;
break;
}
usleep(150000);
}
- if ($done) self::release ($taskId);
+ if ($done)
+ self::release($taskId);
return $status;
}
+ public static function isFailed($task)
+ {
+ if (!isset($task['statusCode']) || !isset($task['id']))
+ return true;
+ if ($task['statusCode'] !== TASK_WAITING && $task['statusCode'] !== TASK_PROCESSING && $task['statusCode'] !== TASK_FINISHED)
+ return true;
+ return false;
+ }
+
public static function release($taskId)
{
self::init();
- $seq = (string)mt_rand();
+ $seq = (string) mt_rand();
$message = "$seq, release, $taskId";
socket_send(self::$sock, $message, strlen($message), 0);
}
-
+
/**
*
* @param type $seq
@@ -101,7 +115,8 @@ class Taskmanager
if (count($parts) == 2 && $parts[0] == $seq) {
return json_decode($parts[1], true);
}
- if (++$tries > 10) return false;
+ if (++$tries > 10)
+ return false;
}
//error_log(socket_strerror(socket_last_error(self::$sock)));
return false;
diff --git a/inc/trigger.inc.php b/inc/trigger.inc.php
index af12955f..102f7987 100644
--- a/inc/trigger.inc.php
+++ b/inc/trigger.inc.php
@@ -36,5 +36,43 @@ class Trigger
Property::setIPxeTaskId($task['id']);
return $task['id'];
}
+
+ public static function ldadp()
+ {
+ $res = Database::simpleQuery("SELECT moduleid, filepath FROM configtgz_module"
+ . " INNER JOIN configtgz_x_module USING (moduleid)"
+ . " INNER JOIN configtgz USING (configid)"
+ . " WHERE moduletype = 'AD_AUTH'");
+ // TODO: Multiconfig support
+ $id = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (readlink('/srv/openslx/www/boot/default/config.tgz') === $row['filepath']) {
+ $id[] = (int)$row['moduleid'];
+ break;
+ }
+ }
+ $task = Taskmanager::submit('LdadpLauncher', array(
+ 'ids' => $id
+ ));
+ if (!isset($task['id']))
+ return false;
+ return $task['id'];
+ }
+
+ public static function mount()
+ {
+ $vmstore = Property::getVmStoreConfig();
+ if (!is_array($vmstore)) return;
+ $storetype = $vmstore['storetype'];
+ if ($storetype === 'nfs') $addr = $vmstore['nfsaddr'];
+ if ($storetype === 'cifs') $addr = $vmstore['nfsaddr'];
+ if ($storetype === 'internal') $addr = 'none';
+ $this->mountTask = Taskmanager::submit('MountVmStore', array(
+ 'address' => $addr,
+ 'type' => 'images',
+ 'username' => $vmstore['cifsuser'],
+ 'password' => $vmstore['cifspasswd']
+ ));
+ }
-} \ No newline at end of file
+}
diff --git a/index.php b/index.php
index ae03e86c..31a7bcc7 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,7 @@
<?php
+require_once 'config.php';
+
/**
* Page class which all "modules" must be extending from
*/
diff --git a/modules/main.inc.php b/modules/main.inc.php
index ddbc37a4..232d6a0a 100644
--- a/modules/main.inc.php
+++ b/modules/main.inc.php
@@ -20,7 +20,14 @@ class Page_Main extends Page
$ipxe = (Property::getServerIp() !== Property::getIPxeIp());
$sysconfig = !file_exists(CONFIG_HTTP_DIR . '/default/config.tgz');
$minilinux = !file_exists(CONFIG_HTTP_DIR . '/default/kernel') || !file_exists(CONFIG_HTTP_DIR . '/default/initramfs-stage31') || !file_exists(CONFIG_HTTP_DIR . '/default/stage32.sqfs');
- Render::addTemplate('page-main', array('user' => User::getName(), 'ipxe' => $ipxe, 'sysconfig' => $sysconfig, 'minilinux' => $minilinux));
+ $vmstore = !is_array(Property::getVmStoreConfig());
+ Render::addTemplate('page-main', array(
+ 'user' => User::getName(),
+ 'ipxe' => $ipxe,
+ 'sysconfig' => $sysconfig,
+ 'minilinux' => $minilinux,
+ 'vmstore' => $vmstore
+ ));
}
}
diff --git a/modules/minilinux.inc.php b/modules/minilinux.inc.php
index 6e68a34e..e2d85247 100644
--- a/modules/minilinux.inc.php
+++ b/modules/minilinux.inc.php
@@ -15,18 +15,73 @@ class Page_MiniLinux extends Page
protected function doRender()
{
- $files = array();
- mkdir(CONFIG_HTTP_DIR . "/default", 0755, true);
- $this->checkFile($files, 'kernel');
- $this->checkFile($files, 'initramfs-stage31');
- $this->checkFile($files, 'stage32.sqfs');
- $this->checkFile($files, 'vmware.sqfs');
- $this->checkFile($files, 'nvidia_libs.sqfs');
Render::addTemplate('page-minilinux', array(
- 'files' => $files,
- 'token' => Session::get('token')
+ 'listurl' => '?do=MiniLinux&async=true&action=list'
));
}
+
+ protected function doAjax()
+ {
+ $data = Property::getVersionCheckInformation();
+ if (!is_array($data) || !isset($data['systems'])) {
+ echo Render::parse('messagebox-error', array(
+ 'message' => 'Fehler beim Abrufen der Liste: ' . $data
+ ));
+ return;
+ }
+ $action = Request::any('action');
+ switch ($action) {
+ case 'list':
+ $count = 0;
+ foreach ($data['systems'] as &$system) {
+ foreach ($system['files'] as &$file) {
+ $file['uid'] = 'dlid' . $count++;
+ $local = CONFIG_HTTP_DIR . '/' . $system['id'] . '/' . $file['name'];
+ if (!file_exists($local) || md5_file($local) !== substr($file['md5'], 0, 32)) {
+ $file['changed'] = true;
+ }
+ }
+ }
+ echo Render::parse('minilinux/filelist', array(
+ 'systems' => $data['systems'],
+ 'token' => Session::get('token')
+ ));
+ return;
+ case 'download':
+ $id = Request::post('id');
+ $name = Request::post('name');
+ if (!$id || !$name || strpos("$id$name", '/') !== false) {
+ echo "Invalid download request";
+ return;
+ }
+ $found = false;
+ foreach ($data['systems'] as &$system) {
+ if ($system['id'] !== $id) continue;
+ foreach ($system['files'] as &$file) {
+ if ($file['name'] !== $name) continue;
+ $found = true;
+ break;
+ }
+ }
+ if (!$found) {
+ echo "Nonexistent system/file: $id / $name";
+ return;
+ }
+ $task = Taskmanager::submit('DownloadFile', array(
+ 'url' => CONFIG_REMOTE_ML . '/' . $id . '/' . $name,
+ 'destination' => CONFIG_HTTP_DIR . '/' . $id . '/' . $name
+ ));
+ if (!isset($task['id'])) {
+ echo 'Error launching download task: ' . $task['statusCode'];
+ return;
+ }
+ echo Render::parse('minilinux/download', array(
+ 'name' => $name,
+ 'task' => $task['id']
+ ));
+ return;
+ }
+ }
private function checkFile(&$files, $name)
{
diff --git a/modules/sysconfig.inc.php b/modules/sysconfig.inc.php
index ebccf036..06ceb618 100644
--- a/modules/sysconfig.inc.php
+++ b/modules/sysconfig.inc.php
@@ -119,6 +119,7 @@ class Page_SysConfig extends Page
Message::addError('task-error', $task['data']['error']);
} elseif ($task['statusCode'] === TASK_FINISHED) {
Message::addSuccess('config-activated', $row['title']);
+ Trigger::ldadp(); // TODO: Feedback
}
Util::redirect('?do=SysConfig');
}
diff --git a/modules/vmstore.inc.php b/modules/vmstore.inc.php
new file mode 100644
index 00000000..ab06b1af
--- /dev/null
+++ b/modules/vmstore.inc.php
@@ -0,0 +1,72 @@
+<?php
+
+class Page_VmStore extends Page
+{
+ private $mountTask = false;
+
+ protected function doPreprocess()
+ {
+ User::load();
+
+ if (!User::hasPermission('superadmin')) {
+ Message::addError('no-permission');
+ Util::redirect('?do=Main');
+ }
+
+ $action = Request::post('action');
+
+ if ($action === 'setstore') {
+ $this->setStore();
+ }
+ }
+
+ protected function doRender()
+ {
+ $action = Request::post('action');
+ if ($action === 'setstore' && !Taskmanager::isFailed($this->mountTask)) {
+ Render::addTemplate('vmstore/mount', array(
+ 'task' => $this->mountTask['id']
+ ));
+ return;
+ }
+ $vmstore = Property::getVmStoreConfig();
+ if (isset($vmstore['storetype'])) {
+ $vmstore['pre-' . $vmstore['storetype']] = 'checked';
+ }
+ $vmstore['token'] = Session::get('token');
+ Render::addTemplate('page-vmstore', $vmstore);
+ }
+
+ private function setStore()
+ {
+ foreach (array('storetype', 'nfsaddr', 'cifsaddr', 'cifsuser', 'cifspasswd') as $key) {
+ $vmstore[$key] = trim(Request::post($key, ''));
+ }
+ $storetype = $vmstore['storetype'];
+ if (!in_array($storetype, array('internal', 'nfs', 'cifs'))) {
+ Message::addError('value-invalid', 'type', $storetype);
+ Util::redirect('?do=VmStore');
+ }
+ // Validate syntax of nfs/cifs
+ if ($storetype === 'nfs' && !preg_match('#^\S+:\S+$#is', $vmstore['nfsaddr'])) {
+ Message::addError('value-invalid', 'nfsaddr', $vmstore['nfsaddr']);
+ Util::redirect('?do=VmStore');
+ }
+ $vmstore['cifsaddr'] = str_replace('\\', '/', $vmstore['cifsaddr']);
+ if ($storetype === 'cifs' && !preg_match('#^//\S+/.+$#is', $vmstore['cifsaddr'])) {
+ Message::addError('value-invalid', 'nfsaddr', $vmstore['nfsaddr']);
+ Util::redirect('?do=VmStore');
+ }
+ if ($storetype === 'nfs') $addr = $vmstore['nfsaddr'];
+ if ($storetype === 'cifs') $addr = $vmstore['nfsaddr'];
+ if ($storetype === 'internal') $addr = 'none';
+ $this->mountTask = Taskmanager::submit('MountVmStore', array(
+ 'address' => $addr,
+ 'type' => 'images',
+ 'username' => $vmstore['cifsuser'],
+ 'password' => $vmstore['cifspasswd']
+ ));
+ Property::setVmStoreConfig($vmstore);
+ }
+
+} \ No newline at end of file
diff --git a/script/taskmanager.js b/script/taskmanager.js
index fdbe4107..a34b3aa4 100644
--- a/script/taskmanager.js
+++ b/script/taskmanager.js
@@ -1,13 +1,16 @@
var tmItems = false;
var tmErrors = 0;
var TM_MAX_ERRORS = 5;
+var tmIsRunning = false;
function tmInit()
{
tmItems = $("div[data-tm-id]");
- if (tmItems.length === 0) return;
+ if (tmItems.length === 0)
+ return;
tmItems.each(function(i, item) {
item = $(item);
+ if (item.find('.data-tm-icon').length !== 0) return;
if (item.is('[data-tm-progress]')) {
item.append('<div class="data-tm-progress"><div class="progress"><div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div></div></div>');
}
@@ -16,7 +19,9 @@ function tmInit()
}
item.prepend('<span class="data-tm-icon" />');
});
- setTimeout(tmUpdate, 100);
+ if (!tmIsRunning)
+ setTimeout(tmUpdate, 50);
+ tmIsRunning = true;
}
function tmUpdate()
@@ -25,19 +30,25 @@ function tmUpdate()
tmItems.each(function(i, item) {
item = $(item);
var id = item.attr('data-tm-id');
- if (typeof id === 'undefined' || id === false || id === '') return;
+ if (typeof id === 'undefined' || id === false || id === '')
+ return;
active.push(id);
});
- if (active.length === 0) return;
- $.post('api.php?do=taskmanager', { 'ids[]' : active, token : TOKEN }, function (data, status) {
+ if (active.length === 0)
+ return;
+ $.post('api.php?do=taskmanager', {'ids[]': active, token: TOKEN}, function(data, status) {
// POST success
- if (tmResult(data, status)) {
+ tmIsRunning = tmResult(data, status)
+ if (tmIsRunning) {
setTimeout(tmUpdate, 1000);
}
- }, 'json').fail(function (jqXHR, textStatus, errorThrown) {
+ }, 'json').fail(function(jqXHR, textStatus, errorThrown) {
// POST failure
console.log("TaskManager Error: " + textStatus + " - " + errorThrown);
- if (++tmErrors < TM_MAX_ERRORS) setTimeout(tmUpdate, 2000);
+ if (++tmErrors < TM_MAX_ERRORS)
+ setTimeout(tmUpdate, 2000);
+ else
+ tmIsRunning = false;
});
}
@@ -60,7 +71,8 @@ function tmResult(data, status)
var counter = 0;
for (var idx in data.tasks) {
var task = data.tasks[idx];
- if (!task.id) continue;
+ if (!task.id)
+ continue;
counter++;
if (lastRunOnError) {
task.statusCode = 'TASK_ERROR';
@@ -71,7 +83,8 @@ function tmResult(data, status)
--tmErrors;
}
var obj = $('div[data-tm-id="' + task.id + '"]');
- if (!obj) continue;
+ if (!obj)
+ continue;
if (task.statusCode !== 'TASK_WAITING' && task.statusCode !== 'TASK_PROCESSING') {
obj.attr('data-tm-id', '');
}
diff --git a/style/default.css b/style/default.css
index 5e7dfeca..3e5f1a56 100644
--- a/style/default.css
+++ b/style/default.css
@@ -54,7 +54,8 @@ body {
}
.slx-ga {
- min-width: 7em;
+ min-width: 8em;
+ text-align: left;
}
.slx-dialog {
@@ -93,4 +94,8 @@ body {
.slx-table td {
padding-right: 5px;
padding-bottom: 2px;
+}
+
+.input-group {
+ margin-bottom: 2px;
} \ No newline at end of file
diff --git a/templates/main-menu.html b/templates/main-menu.html
index 8510bfec..24ee2238 100644
--- a/templates/main-menu.html
+++ b/templates/main-menu.html
@@ -23,6 +23,7 @@
<li class="divider"></li>
<li class="dropdown-header">Server</li>
<li><a href="?do=ServerSetup">Grundkonfiguration</a></li>
+ <li><a href="?do=VmStore">VM Speicherort</a></li>
</ul>
</li>
</ul>
diff --git a/templates/minilinux/download.html b/templates/minilinux/download.html
new file mode 100644
index 00000000..2e32df5a
--- /dev/null
+++ b/templates/minilinux/download.html
@@ -0,0 +1 @@
+<div data-tm-id="{{task}}" data-tm-log="error" data-tm-progress="progress">{{name}}</div> \ No newline at end of file
diff --git a/templates/minilinux/filelist.html b/templates/minilinux/filelist.html
new file mode 100644
index 00000000..38b709f6
--- /dev/null
+++ b/templates/minilinux/filelist.html
@@ -0,0 +1,39 @@
+<div class="container">
+
+ {{#systems}}
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4>{{title}}</h4>
+ </div>
+ <div class="panel-body" id="download-{{id}}">
+ <ul class="list-group">
+ {{#files}}
+ <li class="list-group-item" id="{{uid}}">
+ {{name}}
+ {{#changed}}<span class="btn btn-primary btn-sm" onclick="slxUpdate('{{uid}}', '{{id}}', '{{name}}')">Aktualisieren</span>{{/changed}}
+ </li>
+ {{/files}}
+ </ul>
+ </div>
+ </div>
+ {{/systems}}
+ {{^systems}}
+ <div class="row well well-sm">Keine Konfigurationspakete gefunden!</div>
+ {{/systems}}
+</div>
+
+<script type="text/javascript">
+function slxUpdate(uid, id, name)
+{
+ $('#' + uid).load('?do=MiniLinux',
+ { action : "download", token : TOKEN, id : id, name : name },
+ function(response, status, xhr) {
+ if (status === "error") {
+ var msg = "Fehler beim Abruf: ";
+ $('#' + uid).html(msg + xhr.status + " " + xhr.statusText);
+ } else {
+ setTimeout(tmInit, 50);
+ }
+ });
+}
+</script> \ No newline at end of file
diff --git a/templates/page-main.html b/templates/page-main.html
index af335a72..519d3abd 100644
--- a/templates/page-main.html
+++ b/templates/page-main.html
@@ -3,6 +3,12 @@
<p>Dies ist die bwLehrpool Konfigurationsoberfläche.</p>
</div>
<ul class="list-group">
+{{#vmstore}}
+ <li class="list-group-item list-group-item-info">
+ Es ist noch kein Speicherort für die Virtuellen Maschinen festgelegt.
+ <a class="btn btn-sm btn-primary" href="?do=VmStore">Konfigurieren &raquo;</a>
+ </li>
+{{/vmstore}}
{{#ipxe}}
<li class="list-group-item list-group-item-info">
Das Bootmenü ist veraltet oder wurde noch nicht generiert.
diff --git a/templates/page-minilinux.html b/templates/page-minilinux.html
index e742f82a..c1a4ebd6 100644
--- a/templates/page-minilinux.html
+++ b/templates/page-minilinux.html
@@ -1,24 +1,15 @@
-<div class="container">
- {{#files}}
- <div class="panel panel-default">
- <div class="panel-heading"><h4>
- &raquo; {{file}}
- {{^progress}}
- {{#update}}
- Neue Version! <a class="btn btn-success" href="#" onclick="this.style.display='none';loadContent('#{{id}}', 'api.php?do=download&amp;type=ml&amp;file={{file}}&amp;id={{id}}')">Download</a>
- {{/update}}
- {{^update}}
- (Aktuell)
- {{/update}}
- {{/progress}}
- </h4></div>
- {{#progress}}
- <script> setTimeout(function() { loadContent('#{{id}}', 'api.php?do=download&progress={{progress}}&id={{id}}&pid={{pid}}&file={{file}}'); }, 1000); // </script>
- {{/progress}}
- <div class="panel-body" id="{{id}}">{{description}}</div>
- </div>
- {{/files}}
- {{^files}}
- <div class="row well well-sm">Keine Konfigurationspakete gefunden!</div>
- {{/files}}
-</div>
+<div class="container" id="systemlist">
+ <div class="panel panel-default">Liste wird abgerufen...</div>
+ <script type="text/javascript">
+ var slx_check = setInterval(function() {
+ if (typeof $ === 'undefined') return;
+ clearInterval(slx_check);
+ $('#systemlist').load('{{{listurl}}}', function( response, status, xhr ) {
+ if ( status === "error" ) {
+ var msg = "Fehler beim Abruf: ";
+ $( "#systemlist" ).html( msg + xhr.status + " " + xhr.statusText );
+ }
+ })
+ }, 100);
+ </script>
+</div> \ No newline at end of file
diff --git a/templates/page-vmstore.html b/templates/page-vmstore.html
new file mode 100644
index 00000000..8732276f
--- /dev/null
+++ b/templates/page-vmstore.html
@@ -0,0 +1,89 @@
+<div class="container">
+ <form role="form" method="post" action="?do=VmStore">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="setstore">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ VM Speicherort <a class="btn btn-default" data-toggle="modal" data-target="#help-store"><span class="glyphicon glyphicon-question-sign"></span></a>
+ </div>
+ <div class="panel-body">
+ <p>Bitte wählen Sie, wo die Images der Virtuellen Maschinen gespeichert werden sollen.</p>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <input type="radio" name="storetype" value="internal" {{pre-internal}}> Intern
+ </div>
+ <div class="panel-body">
+ Keine Weitere Konfiguration notwendig
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <input type="radio" name="storetype" value="nfs" {{pre-nfs}}> NFS
+ </div>
+ <div class="panel-body">
+ <div class="input-group">
+ <span class="input-group-addon slx-ga">
+ NFS-Export
+ </span>
+ <input type="text" class="form-control" name="nfsaddr" value="{{nfsaddr}}" placeholder="1.2.3.4:/export/bwlp">
+ </div>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <input type="radio" name="storetype" value="cifs" {{pre-cifs}}> CIFS
+ </div>
+ <div class="panel-body">
+ <div class="input-group">
+ <span class="input-group-addon slx-ga">
+ UNC-Pfad
+ </span>
+ <input type="text" class="form-control" name="cifsaddr" value="{{cifsaddr}}" placeholder="\\samba.server.example.com\bwlp">
+ </div>
+ <div class="input-group">
+ <span class="input-group-addon slx-ga">
+ Benutzername
+ </span>
+ <input type="text" class="form-control" name="cifsuser" value="{{cifsuser}}" placeholder="Benutzername">
+ </div>
+ <div class="input-group">
+ <span class="input-group-addon slx-ga">
+ Passwort
+ </span>
+ <input type="text" class="form-control" name="cifspasswd" value="{{cifspasswd}}" placeholder="Passwort">
+ </div>
+ </div>
+ </div>
+ <button class="btn btn-primary" type="submit">Speichern</button>
+ </div>
+ </div>
+ </form>
+</div>
+
+<div class="modal fade" id="help-store" tabindex="-1" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">VM Speicherort</div>
+ <div class="modal-body">
+ <p>
+ Für Testzwecke können die VMs direkt auf dem Satellitenserver gespeichert werden. Sofern Sie
+ jedoch die ausgelieferte Satelliten-vmdk betreiben bedenken Sie bitte, dass Sie dann nur ca. 100GB
+ Speicher zur Verfügung haben.
+ </p>
+ <p>
+ Im Produktivbetrieb bietet es sich an, hierfür einen performanten Netzwerkspeicher zu benutzen.
+ Dieser Netzwerkspeicher kann per NFS oder CIFS/SMB eingebunden werden. In jedem Fall muss sichergestellt
+ werden, dass der Satellitenserver zum Hinzufügen neuer Virtueller Maschinen Schreibzugriff auf
+ diesen Netzwerkspeicher hat. Bei der Nutzung von NFSv3 kann dies IP-Basiert eingerichtet werden,
+ für die Nutzung von CIFS/SMB können Sie Zugangsdaten angaben, die zum Schreiben berechtigen.
+ </p>
+ <p>
+ Die bwLehrpool-Clients brauchen lediglich Lesezugriff auf den Netzwerkspeicher (und sollten aus
+ Sicherheitsgründen auch wirklich nur lesen können). Bei CIFS/SMB erreichen Sie dies am einfachsten,
+ indem Sie passwortlosen Gastzugriff mit Leserechten auf die Freigabe erlauben.
+ </p>
+ </div>
+ <div class="modal-footer"><a class="btn btn-primary" data-dismiss="modal">Schließen</a></div>
+ </div>
+ </div>
+</div>
diff --git a/templates/vmstore/mount.html b/templates/vmstore/mount.html
new file mode 100644
index 00000000..d781d061
--- /dev/null
+++ b/templates/vmstore/mount.html
@@ -0,0 +1,28 @@
+<div class="container">
+
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ VM Speicherort wird konfiguriert
+ </div>
+
+ <div class="panel-body">
+ <div data-tm-id="{{task}}" data-tm-log="messages" data-tm-callback="mountCb">Konfigurieren</div>
+
+ <br>
+ <div id="finish" class="pull-right" style="display:none">
+ <a href="?do=VmStore" class="btn btn-primary">Zurück</a>
+ </div>
+ <script type="text/javascript">
+ function mountCb(task)
+ {
+ if (!task || !task.statusCode)
+ return;
+ if (task.statusCode !== 'TASK_WAITING' && task.statusCode !== 'TASK_PROCESSING') {
+ $('#finish').attr('style', '');
+ }
+ }
+ </script>
+
+ </div>
+ </div>
+</div> \ No newline at end of file