summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUdo Walter2017-11-21 17:52:04 +0100
committerUdo Walter2017-11-21 17:52:04 +0100
commit653851f35d0eea172c2302e9f1b6f0d03c70096c (patch)
tree42f267c360104b98d2e3199273adba62a1e2023b
parent[internetaccess] reworked permission system from "click and you get error" to... (diff)
parent[statistics] Also adapt MAC-UUID fixing code to mltk changes (diff)
downloadslx-admin-653851f35d0eea172c2302e9f1b6f0d03c70096c.tar.gz
slx-admin-653851f35d0eea172c2302e9f1b6f0d03c70096c.tar.xz
slx-admin-653851f35d0eea172c2302e9f1b6f0d03c70096c.zip
Merge remote-tracking branch 'origin/master' into permission-manager
# Conflicts: # modules-available/backup/templates/_page.html # style/default.css
-rw-r--r--api.php4
-rw-r--r--doc/design_guidelines33
-rw-r--r--inc/download.inc.php34
-rw-r--r--inc/event.inc.php4
-rw-r--r--inc/module.inc.php14
-rw-r--r--inc/taskmanager.inc.php2
-rw-r--r--index.php2
-rw-r--r--install/content.sql4
-rw-r--r--install/schema.sql195
-rw-r--r--modules-available/backup/hooks/main-warning.inc.php8
-rw-r--r--modules-available/backup/lang/de/messages.json2
-rw-r--r--modules-available/backup/lang/de/template-tags.json2
-rw-r--r--modules-available/backup/lang/en/messages.json2
-rw-r--r--modules-available/backup/lang/en/template-tags.json2
-rw-r--r--modules-available/backup/page.inc.php11
-rw-r--r--modules-available/backup/templates/_page.html5
-rw-r--r--modules-available/baseconfig/api.inc.php2
-rw-r--r--modules-available/baseconfig_bwlp/baseconfig/settings.json6
-rw-r--r--modules-available/dnbd3/api.inc.php7
-rw-r--r--modules-available/dnbd3/baseconfig/getconfig.inc.php53
-rw-r--r--modules-available/dnbd3/config.json4
-rw-r--r--modules-available/dnbd3/hooks/cron.inc.php3
-rw-r--r--modules-available/dnbd3/hooks/main-warning.inc.php25
-rw-r--r--modules-available/dnbd3/hooks/runmode/config.json6
-rw-r--r--modules-available/dnbd3/inc/dnbd3.inc.php38
-rw-r--r--modules-available/dnbd3/inc/dnbd3rpc.inc.php58
-rw-r--r--modules-available/dnbd3/inc/dnbd3util.inc.php202
-rw-r--r--modules-available/dnbd3/lang/de/messages.json11
-rw-r--r--modules-available/dnbd3/lang/de/module.json4
-rw-r--r--modules-available/dnbd3/lang/de/template-tags.json57
-rw-r--r--modules-available/dnbd3/lang/en/messages.json3
-rw-r--r--modules-available/dnbd3/lang/en/module.json4
-rw-r--r--modules-available/dnbd3/lang/pt/module.json3
-rw-r--r--modules-available/dnbd3/page.inc.php417
-rw-r--r--modules-available/dnbd3/templates/fragment-server-settings.html14
-rw-r--r--modules-available/dnbd3/templates/page-proxy-altservers.html36
-rw-r--r--modules-available/dnbd3/templates/page-proxy-clients.html20
-rw-r--r--modules-available/dnbd3/templates/page-proxy-config.html4
-rw-r--r--modules-available/dnbd3/templates/page-proxy-header.html1
-rw-r--r--modules-available/dnbd3/templates/page-proxy-loclist.html27
-rw-r--r--modules-available/dnbd3/templates/page-proxy-stats.html9
-rw-r--r--modules-available/dnbd3/templates/page-server-locations.html96
-rw-r--r--modules-available/dnbd3/templates/page-serverlist.html404
-rw-r--r--modules-available/dozmod/api.inc.php94
-rw-r--r--modules-available/dozmod/lang/de/template-tags.json1
-rw-r--r--modules-available/dozmod/lang/en/template-tags.json1
-rw-r--r--modules-available/dozmod/page.inc.php1
-rw-r--r--modules-available/dozmod/style.css1
-rw-r--r--modules-available/dozmod/templates/runtimeconfig.html38
-rw-r--r--modules-available/exams/baseconfig/getconfig.inc.php8
-rwxr-xr-xmodules-available/locationinfo/inc/coursebackend/exchange.todo (renamed from modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php)0
-rw-r--r--modules-available/locationinfo/inc/locationinfo.inc.php1
-rw-r--r--modules-available/locationinfo/page.inc.php4
-rw-r--r--modules-available/locations/inc/location.inc.php56
-rw-r--r--modules-available/rebootcontrol/api.inc.php6
-rw-r--r--modules-available/rebootcontrol/hooks/config-tgz.inc.php5
-rw-r--r--modules-available/rebootcontrol/inc/rebootcontrol.inc.php30
-rw-r--r--modules-available/rebootcontrol/inc/sshkey.inc.php6
-rw-r--r--modules-available/rebootcontrol/page.inc.php9
-rw-r--r--modules-available/roomplanner/baseconfig/getconfig.inc.php3
-rw-r--r--modules-available/runmode/baseconfig/getconfig.inc.php3
-rw-r--r--modules-available/runmode/inc/runmode.inc.php38
-rw-r--r--modules-available/runmode/install.inc.php2
-rw-r--r--modules-available/statistics/api.inc.php71
-rw-r--r--modules-available/statistics/hooks/cron.inc.php17
-rw-r--r--modules-available/statistics/lang/de/template-tags.json3
-rw-r--r--modules-available/statistics/lang/en/template-tags.json3
-rw-r--r--modules-available/statistics/page.inc.php21
-rw-r--r--modules-available/statistics/templates/filterbox.html3
-rw-r--r--modules-available/sysconfig/addconfig.inc.php5
-rw-r--r--modules-available/sysconfig/api.inc.php85
-rw-r--r--modules-available/sysconfig/inc/configtgz.inc.php49
-rw-r--r--modules-available/sysconfig/inc/sysconfig.inc.php2
-rw-r--r--modules-available/sysconfig/page.inc.php2
-rw-r--r--modules-available/syslog/api.inc.php6
-rw-r--r--modules-available/syslog/hooks/cron.inc.php5
-rw-r--r--modules-available/syslog/install.inc.php10
77 files changed, 1971 insertions, 456 deletions
diff --git a/api.php b/api.php
index c7508fbd..880285be 100644
--- a/api.php
+++ b/api.php
@@ -25,9 +25,9 @@ function isLocalExecution()
}
if (!empty($_REQUEST['do'])) {
- $module = preg_replace('/[^a-z]/', '', $_REQUEST['do']);
+ $module = preg_replace('/[^a-z0-9]/', '', $_REQUEST['do']);
} elseif (!empty($argv[1])) {
- $module = preg_replace('/[^a-z]/', '', $argv[1]);
+ $module = preg_replace('/[^a-z0-9]/', '', $argv[1]);
$argc = count($argv) - 1;
for ($i = 2; $i < $argc; ++$i) {
if (substr($argv[$i], 0, 2) === '--') {
diff --git a/doc/design_guidelines b/doc/design_guidelines
new file mode 100644
index 00000000..e74fe942
--- /dev/null
+++ b/doc/design_guidelines
@@ -0,0 +1,33 @@
+Design Guidelines
+=================
+
+Buttons generell rechts wenn möglich.
+
+Reihenfolge von links nach rechts: erst Nebenfunktion (cancel etc) dann Hauptfunktion ganz rechts (save etc)
+
+Einzelne Buttons: normale Größe, glyphicon (außer bei Nebenfunktion), Text
+
+mehrere Buttons mit selber gleicher Funktion (zB. in Tabelle): btn-sm, nur glyphicon ohne Text
+
+Button Farben:
+ Aktionen mit irreversiblen Folgen (zB. Delete) = Rot
+ Hauptfunktion (zB. Save) = Blau
+ Hinzufügen = Grün
+ Sonderfunktionen (zB. send test mail) = Gelb
+ Nebenfunktion (zB. cancel) = Weiß
+
+Tabellen sortierbar machen mit Stupidtable-Plugin (wenn sinnvoll)
+
+Checkboxen und Radio Buttons im Bootstrap Style
+
+Titel des Moduls immer ganz oben
+
+Modals mit Aktion haben cancel Button (“cancel | save”)
+
+Info-Modals ohne Aktion haben keine Buttons unten und stattdessen nur ein x oben rechts
+
+Modals statt confirm()
+
+Unterüberschriften in Panels für schönere Gliederung
+
+Speichern erst mit Klick auf Save Button
diff --git a/inc/download.inc.php b/inc/download.inc.php
index b3496e85..e5764d37 100644
--- a/inc/download.inc.php
+++ b/inc/download.inc.php
@@ -3,29 +3,36 @@
class Download
{
+ private static $curlHandle = false;
+
/**
* Common initialization for download and downloadToFile
* Return file handle to header file
*/
private static function initCurl($url, $timeout, &$head)
{
- $ch = curl_init();
- if ($ch === false)
- Util::traceError('Could not initialize cURL');
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, ceil($timeout / 2));
- curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($ch, CURLOPT_AUTOREFERER, true);
- curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
- curl_setopt($ch, CURLOPT_MAXREDIRS, 6);
+ if (self::$curlHandle === false) {
+ self::$curlHandle = curl_init();
+ if (self::$curlHandle === false) {
+ Util::traceError('Could not initialize cURL');
+ }
+ curl_setopt(self::$curlHandle, CURLOPT_CONNECTTIMEOUT, ceil($timeout / 2));
+ curl_setopt(self::$curlHandle, CURLOPT_TIMEOUT, $timeout);
+ curl_setopt(self::$curlHandle, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt(self::$curlHandle, CURLOPT_AUTOREFERER, true);
+ curl_setopt(self::$curlHandle, CURLOPT_BINARYTRANSFER, true);
+ curl_setopt(self::$curlHandle, CURLOPT_MAXREDIRS, 6);
+ }
+
+ curl_setopt(self::$curlHandle, CURLOPT_URL, $url);
+
$tmpfile = tempnam('/tmp/', 'bwlp-');
$head = fopen($tmpfile, 'w+b');
unlink($tmpfile);
if ($head === false)
Util::traceError("Could not open temporary head file $tmpfile for writing.");
- curl_setopt($ch, CURLOPT_WRITEHEADER, $head);
- return $ch;
+ curl_setopt(self::$curlHandle, CURLOPT_WRITEHEADER, $head);
+ return self::$curlHandle;
}
/**
@@ -55,7 +62,6 @@ class Download
} else {
$code = 999;
}
- curl_close($ch);
return $data;
}
@@ -89,7 +95,6 @@ class Download
} else {
$code = 999;
}
- curl_close($ch);
return $data;
}
@@ -111,7 +116,6 @@ class Download
curl_setopt($ch, CURLOPT_FILE, $fh);
$res = curl_exec($ch);
$head = self::getContents($head);
- curl_close($ch);
fclose($fh);
if ($res === false) {
@unlink($target);
diff --git a/inc/event.inc.php b/inc/event.inc.php
index a16be97b..66601607 100644
--- a/inc/event.inc.php
+++ b/inc/event.inc.php
@@ -31,10 +31,6 @@ class Event
$ldadpId = Trigger::ldadp();
$ipxeId = Trigger::ipxe();
- Taskmanager::submit('DozmodLauncher', array(
- 'operation' => 'start'
- ));
-
// Check status of all tasks
// Mount vm store
if ($mountId === false) {
diff --git a/inc/module.inc.php b/inc/module.inc.php
index 7211c68c..ec3d095b 100644
--- a/inc/module.inc.php
+++ b/inc/module.inc.php
@@ -29,12 +29,14 @@ class Module
* @param string $moduleId module to check
* @return bool true if module is available and activated
*/
- public static function isAvailable($moduleId)
+ public static function isAvailable($moduleId, $activate = true)
{
$module = self::get($moduleId);
if ($module === false)
return false;
- $module->activate();
+ if ($activate) {
+ $module->activate();
+ }
return !$module->hasMissingDependencies();
}
@@ -56,7 +58,7 @@ class Module
$mod->depsChecked = true;
foreach ($mod->dependencies as $dep) {
if (!self::resolveDepsByName($dep)) {
- error_log("Disabling module {$mod->name}: Dependency $dep failed.");
+ trigger_error("Disabling module {$mod->name}: Dependency $dep failed.", E_USER_WARNING);
$mod->depsMissing = true;
return false;
}
@@ -75,9 +77,9 @@ class Module
foreach (self::$modules as $module) {
if (self::resolveDeps($module)) {
$ret[] = $module;
- }
- if ($sortById) {
- $sort[] = $module->name;
+ if ($sortById) {
+ $sort[] = $module->name;
+ }
}
}
if ($sortById) {
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
index 1920190f..dcf54448 100644
--- a/inc/taskmanager.inc.php
+++ b/inc/taskmanager.inc.php
@@ -96,7 +96,7 @@ class Taskmanager
/**
* Wait for the given task's completion.
*
- * @param type $task task to wait for
+ * @param array $task task to wait for
* @param int $timeout maximum time in ms to wait for completion of task
* @return array result/status of task, or false if it couldn't be queried
*/
diff --git a/index.php b/index.php
index a3f45ff3..4b1302a6 100644
--- a/index.php
+++ b/index.php
@@ -70,7 +70,7 @@ abstract class Page
public static function init()
{
$name = empty($_REQUEST['do']) ? 'Main' : $_REQUEST['do'];
- $name = preg_replace('/[^A-Za-z_]/', '', $name);
+ $name = preg_replace('/[^A-Za-z0-9_]/', '', $name);
$name = strtolower($name);
Module::init();
self::$module = Module::get($name);
diff --git a/install/content.sql b/install/content.sql
deleted file mode 100644
index ca730611..00000000
--- a/install/content.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-USE openslx;
-
-INSERT INTO property (name, dateline, value) VALUES ('webif-version', 0, 11);
-
diff --git a/install/schema.sql b/install/schema.sql
deleted file mode 100644
index b8ea768b..00000000
--- a/install/schema.sql
+++ /dev/null
@@ -1,195 +0,0 @@
-SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
-SET time_zone = "+00:00";
-
-CREATE DATABASE openslx;
-USE openslx;
-
-CREATE TABLE `callback` (
- `taskid` varchar(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
- `dateline` int(10) unsigned NOT NULL,
- `cbfunction` varchar(16) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
- `args` text NOT NULL,
- PRIMARY KEY (`taskid`,`cbfunction`),
- KEY `dateline` (`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `cat_setting` (
- `catid` int(10) unsigned NOT NULL,
- `sortval` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`catid`),
- KEY `sortval` (`sortval`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `clientlog` (
- `logid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `dateline` int(10) unsigned NOT NULL,
- `logtypeid` varchar(30) NOT NULL,
- `clientip` varchar(40) NOT NULL,
- `description` varchar(255) NOT NULL,
- `extra` text NOT NULL,
- PRIMARY KEY (`logid`),
- KEY `dateline` (`dateline`),
- KEY `logtypeid` (`logtypeid`,`dateline`),
- KEY `clientip` (`clientip`,`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `configtgz` (
- `configid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `title` varchar(200) NOT NULL,
- `filepath` varchar(255) NOT NULL,
- `status` enum('OK','OUTDATED','MISSING') NOT NULL DEFAULT 'MISSING',
- PRIMARY KEY (`configid`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `configtgz_module` (
- `moduleid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `title` varchar(200) NOT NULL,
- `moduletype` varchar(16) NOT NULL,
- `filepath` varchar(250) NOT NULL,
- `contents` text NOT NULL,
- `version` int(10) unsigned NOT NULL DEFAULT '0',
- `status` enum('OK','MISSING','OUTDATED') NOT NULL DEFAULT 'MISSING',
- PRIMARY KEY (`moduleid`),
- KEY `title` (`title`),
- KEY `moduletype` (`moduletype`,`title`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `configtgz_x_module` (
- `configid` int(10) unsigned NOT NULL,
- `moduleid` int(10) unsigned NOT NULL,
- PRIMARY KEY (`configid`,`moduleid`),
- KEY `moduleid` (`moduleid`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `eventlog` (
- `logid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `dateline` int(10) unsigned NOT NULL,
- `logtypeid` varchar(30) NOT NULL,
- `description` varchar(255) NOT NULL,
- `extra` text NOT NULL,
- PRIMARY KEY (`logid`),
- KEY `dateline` (`dateline`),
- KEY `logtypeid` (`logtypeid`,`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `machine` (
- `machineuuid` char(36) CHARACTER SET ascii NOT NULL,
- `roomid` int(10) unsigned DEFAULT NULL,
- `macaddr` char(17) CHARACTER SET ascii NOT NULL,
- `clientip` varchar(45) CHARACTER SET ascii NOT NULL,
- `firstseen` int(10) unsigned NOT NULL,
- `lastseen` int(10) unsigned NOT NULL,
- `logintime` int(10) unsigned NOT NULL,
- `position` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
- `lastboot` int(10) unsigned NOT NULL,
- `realcores` smallint(5) unsigned NOT NULL,
- `mbram` int(10) unsigned NOT NULL,
- `kvmstate` enum('UNKNOWN','UNSUPPORTED','DISABLED','ENABLED') NOT NULL,
- `cpumodel` varchar(120) NOT NULL,
- `systemmodel` varchar(120) NOT NULL DEFAULT '',
- `id44mb` int(10) unsigned NOT NULL,
- `badsectors` int(10) unsigned NOT NULL,
- `data` mediumtext NOT NULL,
- `hostname` varchar(200) NOT NULL DEFAULT '',
- `notes` text,
- PRIMARY KEY (`machineuuid`),
- KEY `macaddr` (`macaddr`),
- KEY `clientip` (`clientip`),
- KEY `realcores` (`realcores`),
- KEY `mbram` (`mbram`),
- KEY `kvmstate` (`kvmstate`),
- KEY `id44mb` (`id44mb`),
- KEY `roomid` (`roomid`),
- KEY `lastseen` (`lastseen`),
- KEY `cpumodel` (`cpumodel`),
- KEY `systemmodel` (`systemmodel`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `news` (
- `newsid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `dateline` int(10) unsigned NOT NULL,
- `title` varchar(200) DEFAULT NULL,
- `content` text,
- PRIMARY KEY (`newsid`),
- KEY `dateline` (`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `permission` (
- `mask` int(10) unsigned NOT NULL,
- `identifier` varchar(32) NOT NULL,
- PRIMARY KEY (`mask`),
- UNIQUE KEY `identifier` (`identifier`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `property` (
- `name` varchar(50) NOT NULL,
- `dateline` int(10) unsigned NOT NULL DEFAULT '0',
- `value` text NOT NULL,
- PRIMARY KEY (`name`),
- KEY `dateline` (`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `setting` (
- `setting` varchar(28) NOT NULL,
- `catid` int(10) unsigned NOT NULL,
- `defaultvalue` text NOT NULL,
- `permissions` int(10) unsigned NOT NULL,
- `validator` varchar(250) NOT NULL DEFAULT '',
- PRIMARY KEY (`setting`),
- KEY `catid` (`catid`,`setting`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `setting_distro` (
- `distroid` int(10) unsigned NOT NULL,
- `setting` varchar(28) NOT NULL,
- `value` text NOT NULL,
- `displayvalue` text NOT NULL,
- PRIMARY KEY (`distroid`,`setting`),
- KEY `setting` (`setting`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `setting_global` (
- `setting` varchar(28) NOT NULL,
- `value` text NOT NULL,
- `displayvalue` text NOT NULL,
- PRIMARY KEY (`setting`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `statistic` (
- `logid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `dateline` int(10) unsigned NOT NULL,
- `typeid` varchar(30) NOT NULL,
- `machineuuid` varchar(36) CHARACTER SET ascii DEFAULT NULL,
- `clientip` varchar(40) NOT NULL,
- `username` varchar(255) NOT NULL,
- `data` varchar(255) NOT NULL,
- PRIMARY KEY (`logid`),
- KEY `dateline` (`dateline`),
- KEY `logtypeid` (`typeid`,`dateline`),
- KEY `clientip` (`clientip`,`dateline`),
- KEY `machineuuid` (`machineuuid`,`dateline`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-CREATE TABLE `user` (
- `userid` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `login` varchar(100) NOT NULL,
- `passwd` varchar(150) NOT NULL,
- `fullname` varchar(100) DEFAULT NULL,
- `phone` varchar(100) DEFAULT NULL,
- `email` varchar(100) DEFAULT NULL,
- `permissions` int(10) unsigned NOT NULL,
- `lasteventid` int(10) unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`userid`),
- UNIQUE KEY `login` (`login`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-
-ALTER TABLE `configtgz_x_module`
- ADD CONSTRAINT `configtgz_x_module_ibfk_1` FOREIGN KEY (`configid`) REFERENCES `configtgz` (`configid`) ON DELETE CASCADE,
- ADD CONSTRAINT `configtgz_x_module_ibfk_2` FOREIGN KEY (`moduleid`) REFERENCES `configtgz_module` (`moduleid`);
-
-ALTER TABLE `setting`
- ADD CONSTRAINT `setting_ibfk_1` FOREIGN KEY (`catid`) REFERENCES `cat_setting` (`catid`) ON UPDATE CASCADE;
-
-ALTER TABLE `setting_distro`
- ADD CONSTRAINT `setting_distro_ibfk_1` FOREIGN KEY (`setting`) REFERENCES `setting` (`setting`) ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/modules-available/backup/hooks/main-warning.inc.php b/modules-available/backup/hooks/main-warning.inc.php
new file mode 100644
index 00000000..0a0be7cc
--- /dev/null
+++ b/modules-available/backup/hooks/main-warning.inc.php
@@ -0,0 +1,8 @@
+<?php
+
+$last = Property::get('backup.last-time', 0);
+if ($last === 0) {
+ Message::addWarning('backup.last-time-unknown', true);
+} elseif ($last + (30 * 86400) < time()) {
+ Message::addWarning('backup.last-time', true, date('d.m.Y', $last));
+}
diff --git a/modules-available/backup/lang/de/messages.json b/modules-available/backup/lang/de/messages.json
index 6d16f366..36581b2a 100644
--- a/modules-available/backup/lang/de/messages.json
+++ b/modules-available/backup/lang/de/messages.json
@@ -1,5 +1,7 @@
{
"backup-failed": "Erstellen des Backups fehlgeschlagen",
+ "last-time": "Das letzte Backup wurde am {{0}} gemacht. Es wird empfohlen, regelm\u00e4\u00dfig ein Backup des Servers herunterzuladen.",
+ "last-time-unknown": "Es wird empfohlen, regelm\u00e4\u00dfig ein Backup des Servers herunterzuladen.",
"missing-file": "Es wurde keine Datei ausgew\u00e4hlt!",
"restore-done": "Wiederherstellung abgeschlossen",
"upload-failed": "Upload schlug fehl: {{0}}"
diff --git a/modules-available/backup/lang/de/template-tags.json b/modules-available/backup/lang/de/template-tags.json
index 770e34d2..1e41abbc 100644
--- a/modules-available/backup/lang/de/template-tags.json
+++ b/modules-available/backup/lang/de/template-tags.json
@@ -5,6 +5,7 @@
"lang_browseForFile": "Durchsuchen",
"lang_download": "Herunterladen",
"lang_dozmodExplanation": "Die Datenbank des Dozentenmoduls wiederherstellen. Dazu geh\u00f6ren die Metadaten der Virtuellen Maschinen, die Veranstaltungen, etc. Bitte beachten Sie, dass hierzu auf dem konfigurierten VM-Store die passenden VM-Abbilder vorliegen m\u00fcssen, da diese extern gespeichert werden. Wenn sich der Servername oder die -adresse ge\u00e4ndert haben stellen Sie bitte sicher, dass die relativen Pfade innerhalb des Netzlaufwerks gleich geblieben sind. Ansonsten werden die wiederhergestellten VMs nicht verwendbar sein.",
+ "lang_lastBackup": "Letzte Sicherung",
"lang_reboot": "Systemneustart",
"lang_restore": "Hochladen",
"lang_restoreConfig": "Konfiguration wiederherstellen",
@@ -15,5 +16,6 @@
"lang_selectFile": "Bitte w\u00e4hlen Sie ein Backup-Archiv",
"lang_stopping": "Stoppe",
"lang_systemExplanation": "Die Grundkonfiguration des Satelliten wiederherstellen: Authentifizierungmethode, Passw\u00f6rter, Proxies, VM-Storage, etc.\r\nACHTUNG: Wenn Sie ein Backup von vor WS15\/16 einspielen (Backup-Format vor Version 10), wird die Systemkonfiguration in jedem Fall wiederhergestellt, auch wenn Sie diesen Haken nicht setzen.",
+ "lang_unknown": "Unbekannt",
"lang_waitReboot": "Warte auf Reboot."
} \ No newline at end of file
diff --git a/modules-available/backup/lang/en/messages.json b/modules-available/backup/lang/en/messages.json
index 944cc80f..4c9685d5 100644
--- a/modules-available/backup/lang/en/messages.json
+++ b/modules-available/backup/lang/en/messages.json
@@ -1,5 +1,7 @@
{
"backup-failed": "Backup failed!",
+ "last-time": "Last backup was downloaded on {{0}}. It's recommended to download a server backup regularly.",
+ "last-time-unknown": "It's recommended to download a server backup regularly.",
"missing-file": "There was no file selected!",
"restore-done": "Restore done",
"upload-failed": "Upload failed: {{0}}"
diff --git a/modules-available/backup/lang/en/template-tags.json b/modules-available/backup/lang/en/template-tags.json
index cfaad9c8..8cb131f7 100644
--- a/modules-available/backup/lang/en/template-tags.json
+++ b/modules-available/backup/lang/en/template-tags.json
@@ -5,6 +5,7 @@
"lang_browseForFile": "Browse",
"lang_download": "Download",
"lang_dozmodExplanation": "This restores all the virtual machine and lecture meta data created using the \"Dozentenmodul\". Please make sure the VM-storage configured still contains all the VM-Images associated with the virtual machines. If the location of the storage changed, make sure the relative pathes on the share are still the same, otherwise the virtual machines won't be usable.",
+ "lang_lastBackup": "Last backup",
"lang_reboot": "System reboot",
"lang_restore": "Upload",
"lang_restoreConfig": "Restore config",
@@ -15,5 +16,6 @@
"lang_selectFile": "Please select a backup archive",
"lang_stopping": "Stopping",
"lang_systemExplanation": "Restore basic configuration like authentication method, passwords, vm storage location, proxy config, etc. WARNING: If you restore a configuration backup that was made before WS15\/16 (backup format version <10), the system configuration will be restored regardless of this check mark.",
+ "lang_unknown": "Unknown",
"lang_waitReboot": "Waiting for reboot."
} \ No newline at end of file
diff --git a/modules-available/backup/page.inc.php b/modules-available/backup/page.inc.php
index 34777db8..77d677c7 100644
--- a/modules-available/backup/page.inc.php
+++ b/modules-available/backup/page.inc.php
@@ -3,6 +3,8 @@
class Page_Backup extends Page
{
+ const LAST_BACKUP_PROP = 'backup.last-time';
+
private $action = false;
private $templateData = array();
@@ -26,7 +28,13 @@ class Page_Backup extends Page
if ($this->action === 'restore') {
Render::addTemplate('restore', $this->templateData);
} else {
- Render::addTemplate('_page');
+ $lastBackup = (int)Property::get(self::LAST_BACKUP_PROP, 0);
+ if ($lastBackup === 0) {
+ $lastBackup = false;
+ } else {
+ $lastBackup = date('d.m.Y', $lastBackup);
+ }
+ Render::addTemplate('_page', ['last_backup' => $lastBackup]);
}
}
@@ -64,6 +72,7 @@ class Page_Backup extends Page
}
@fclose($fh);
@unlink($task['data']['backupFile']);
+ Property::set(self::LAST_BACKUP_PROP, time());
die();
}
diff --git a/modules-available/backup/templates/_page.html b/modules-available/backup/templates/_page.html
index 1111e9ad..7276bf5c 100644
--- a/modules-available/backup/templates/_page.html
+++ b/modules-available/backup/templates/_page.html
@@ -8,6 +8,11 @@
<div class="panel-body">
<p>{{lang_backupDescription}}</p>
<button class="btn btn-primary pull-right" type="submit"><span class="glyphicon glyphicon-save"></span> {{lang_download}}</button>
+ <div class="text-right">
+ {{lang_lastBackup}}:
+ {{^last_backup}}{{lang_unknown}}{{/last_backup}}
+ {{last_backup}}
+ </div>
</div>
</div>
</form>
diff --git a/modules-available/baseconfig/api.inc.php b/modules-available/baseconfig/api.inc.php
index 6e985050..64204774 100644
--- a/modules-available/baseconfig/api.inc.php
+++ b/modules-available/baseconfig/api.inc.php
@@ -75,7 +75,7 @@ class ConfigHolder
if ($pos == 0) {
echo " <disabled>\n";
} else {
- echo ': ', str_replace(array("\r", "\n"), array('\r', '\n'), $item['value']), "\n";
+ echo " <overridden>\n";
}
continue;
}
diff --git a/modules-available/baseconfig_bwlp/baseconfig/settings.json b/modules-available/baseconfig_bwlp/baseconfig/settings.json
index 79127ccc..d0da2253 100644
--- a/modules-available/baseconfig_bwlp/baseconfig/settings.json
+++ b/modules-available/baseconfig_bwlp/baseconfig/settings.json
@@ -23,6 +23,12 @@
"permissions": "2",
"validator": ""
},
+ "SLX_NET_SEARCH": {
+ "catid": "networking",
+ "defaultvalue": "",
+ "permissions": "2",
+ "validator": ""
+ },
"SLX_NTP_SERVER": {
"catid": "timesync",
"defaultvalue": "0.de.pool.ntp.org 1.de.pool.ntp.org",
diff --git a/modules-available/dnbd3/api.inc.php b/modules-available/dnbd3/api.inc.php
new file mode 100644
index 00000000..68f8007c
--- /dev/null
+++ b/modules-available/dnbd3/api.inc.php
@@ -0,0 +1,7 @@
+<?php
+
+if (Dnbd3::isEnabled()) {
+ die('YES');
+} else {
+ die('NO');
+}
diff --git a/modules-available/dnbd3/baseconfig/getconfig.inc.php b/modules-available/dnbd3/baseconfig/getconfig.inc.php
new file mode 100644
index 00000000..e0389c71
--- /dev/null
+++ b/modules-available/dnbd3/baseconfig/getconfig.inc.php
@@ -0,0 +1,53 @@
+<?php
+
+if (!Dnbd3::isEnabled()) return;
+
+if (!Dnbd3::hasNfsFallback()) {
+ ConfigHolder::add("SLX_VM_NFS", false, 1000);
+ ConfigHolder::add("SLX_VM_NFS_USER", false, 1000);
+ ConfigHolder::add("SLX_VM_NFS_PASSWD", false, 1000);
+}
+
+// Locations from closest to furthest (order)
+$locations = ConfigHolder::get('SLX_LOCATIONS');
+if ($locations === false) {
+ $locationIds = [0];
+} else {
+ $locationIds = explode(' ', $locations);
+ if (empty($locationIds)) {
+ $locationIds[] = 0;
+ }
+}
+
+$res = Database::simpleQuery('SELECT s.fixedip, m.clientip, sxl.locationid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
+ WHERE sxl.locationid IS NULL OR sxl.locationid IN (:lids)', array('lids' => $locationIds));
+// Lookup of priority - first index (0) will be closest location in chain
+$locationsAssoc = array_flip($locationIds);
+$servers = array();
+while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['fixedip'] === '<self>') {
+ $row['fixedip'] = Property::getServerIp();
+ $defPrio = 2000;
+ } else {
+ $defPrio = 1000;
+ }
+ $ip = $row['clientip'] ? $row['clientip'] : $row['fixedip'];
+ if ($defPrio === 1000 && is_null($row['locationid'])) {
+ $serverLoc = Location::getFromIp($ip);
+ if ($serverLoc !== false) {
+ $row['locationid'] = $serverLoc;
+ }
+ }
+ $old = isset($servers[$ip]) ? $servers[$ip] : $defPrio;
+ if (is_null($row['locationid']) || !isset($locationsAssoc[$row['locationid']])) {
+ $servers[$ip] = min($defPrio, $old) . '.' . mt_rand();
+ } else {
+ $servers[$ip] = min($locationsAssoc[$row['locationid']], $old) . '.' . mt_rand();
+ }
+}
+
+asort($servers, SORT_NUMERIC | SORT_ASC);
+ConfigHolder::add('SLX_DNBD3_SERVERS', implode(' ', array_keys($servers)));
+ConfigHolder::add('SLX_VM_DNBD3', 'yes');
diff --git a/modules-available/dnbd3/config.json b/modules-available/dnbd3/config.json
new file mode 100644
index 00000000..f84a4170
--- /dev/null
+++ b/modules-available/dnbd3/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"main.settings-server",
+ "dependencies":["locations","runmode"]
+}
diff --git a/modules-available/dnbd3/hooks/cron.inc.php b/modules-available/dnbd3/hooks/cron.inc.php
new file mode 100644
index 00000000..3da4cae4
--- /dev/null
+++ b/modules-available/dnbd3/hooks/cron.inc.php
@@ -0,0 +1,3 @@
+<?php
+
+Dnbd3Util::updateServerStatus();
diff --git a/modules-available/dnbd3/hooks/main-warning.inc.php b/modules-available/dnbd3/hooks/main-warning.inc.php
new file mode 100644
index 00000000..258d03d0
--- /dev/null
+++ b/modules-available/dnbd3/hooks/main-warning.inc.php
@@ -0,0 +1,25 @@
+<?php
+
+if (Dnbd3::isEnabled()) {
+ $res = Database::simpleQuery('SELECT s.fixedip, s.lastseen AS dnbd3lastseen, s.errormsg, m.clientip, m.hostname
+ FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ WHERE errormsg IS NOT NULL');
+
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $error = $row['errormsg'] ? $row['errormsg'] : '<unknown error>';
+ $lastSeen = date('d.m.Y H:i', $row['dnbd3lastseen']);
+ if ($row['fixedip'] === '<self>') {
+ Message::addError('dnbd3.main-dnbd3-unreachable', true, $error, $lastSeen);
+ continue;
+ }
+ if (!is_null($row['fixedip'])) {
+ $ip = $row['fixedip'];
+ } else {
+ $ip = $row['clientip'] . '/' . $row['hostname'];
+ }
+ Message::addWarning('dnbd3.dnbd3-proxy-unreachable', true, $ip, $error, $lastSeen);
+ }
+
+ unset($res);
+}
diff --git a/modules-available/dnbd3/hooks/runmode/config.json b/modules-available/dnbd3/hooks/runmode/config.json
new file mode 100644
index 00000000..095cb42f
--- /dev/null
+++ b/modules-available/dnbd3/hooks/runmode/config.json
@@ -0,0 +1,6 @@
+{
+ "isClient": false,
+ "configHook": "Dnbd3Util::runmodeConfigHook",
+ "noSysconfig": true,
+ "systemdDefaultTarget": "dnbd3-proxy"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/inc/dnbd3.inc.php b/modules-available/dnbd3/inc/dnbd3.inc.php
new file mode 100644
index 00000000..9607c544
--- /dev/null
+++ b/modules-available/dnbd3/inc/dnbd3.inc.php
@@ -0,0 +1,38 @@
+<?php
+
+class Dnbd3 {
+
+ const PROP_ENABLED = 'dnbd3.enabled';
+ const PROP_NFS_FALLBACK = 'dnbd3.nfs-fallback';
+
+ public static function isEnabled()
+ {
+ return Property::get(self::PROP_ENABLED, 0) ? true : false;
+ }
+
+ public static function setEnabled($bool)
+ {
+ Property::set(self::PROP_ENABLED, $bool ? 1 : 0);
+ $task = Taskmanager::submit('Systemctl', array(
+ 'operation' => ($bool ? 'start' : 'stop'),
+ 'service' => 'dnbd3-server'
+ ));
+ return $task;
+ }
+
+ public static function hasNfsFallback()
+ {
+ return Property::get(self::PROP_NFS_FALLBACK, 0) ? true : false;
+ }
+
+ public static function setNfsFallback($bool)
+ {
+ Property::set(self::PROP_NFS_FALLBACK, $bool ? 1 : 0);
+ }
+
+ public static function getLocalStatus()
+ {
+
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/inc/dnbd3rpc.inc.php b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
new file mode 100644
index 00000000..cdcda508
--- /dev/null
+++ b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
@@ -0,0 +1,58 @@
+<?php
+
+class Dnbd3Rpc {
+
+ const QUERY_UNREACHABLE = 1;
+ const QUERY_NOT_200 = 2;
+ const QUERY_NOT_JSON = 3;
+
+ /**
+ * Query given DNBD3 server for status information.
+ *
+ * @param string $server server address
+ * @param int $port server port
+ * @param bool $stats include general stats
+ * @param bool $clients include client list
+ * @param bool $images include image list
+ * @param bool $diskSpace include disk space stats
+ * @param bool $config get config
+ * @param bool $altservers list of alt servers with status
+ * @return int|array the queried data as an array, or false on error
+ */
+ public static function query($server, $port, $stats, $clients, $images, $diskSpace = false, $config = false, $altservers = false)
+ {
+ // Special case - local server
+ if ($server === '<self>') {
+ $server = '127.0.0.1';
+ }
+ $url = 'http://' . $server . ':' . $port . '/query?';
+ if ($stats) {
+ $url .= 'q=stats&';
+ }
+ if ($clients) {
+ $url .= 'q=clients&';
+ }
+ if ($images) {
+ $url .= 'q=images&';
+ }
+ if ($diskSpace) {
+ $url .= 'q=space&';
+ }
+ if ($config) {
+ $url .= 'q=config&';
+ }
+ if ($altservers) {
+ $url .= 'q=altservers&';
+ }
+ $str = Download::asString($url, 3, $code);
+ if ($str === false)
+ return self::QUERY_UNREACHABLE;
+ if ($code !== 200)
+ return self::QUERY_NOT_200;
+ $ret = json_decode($str, true);
+ if (!is_array($ret))
+ return self::QUERY_NOT_JSON;
+ return $ret;
+ }
+
+}
diff --git a/modules-available/dnbd3/inc/dnbd3util.inc.php b/modules-available/dnbd3/inc/dnbd3util.inc.php
new file mode 100644
index 00000000..a9b9241e
--- /dev/null
+++ b/modules-available/dnbd3/inc/dnbd3util.inc.php
@@ -0,0 +1,202 @@
+<?php
+
+class Dnbd3Util {
+
+ public static function updateServerStatus()
+ {
+ $dynClients = RunMode::getForMode('dnbd3', 'proxy', false, true);
+ $satServerIp = Property::getServerIp();
+ $servers = array();
+ $res = Database::simpleQuery('SELECT s.serverid, s.machineuuid, s.fixedip, s.lastup, s.lastdown, m.clientip
+ FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)');
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (!is_null($row['clientip'])) {
+ $ip = $row['clientip'];
+ } elseif (!is_null($row['fixedip'])) {
+ $ip = $row['fixedip'];
+ } else {
+ continue; // Huh?
+ }
+ if (!is_null($row['machineuuid'])) {
+ unset($dynClients[$row['machineuuid']]);
+ if ($row['clientip'] === $satServerIp) {
+ // Lolwut, sat server is openslx client configured for proxy mode!? baleeted.
+ RunMode::setRunMode($row['machineuuid'], 'dnbd3', null, null, null);
+ continue;
+ }
+ }
+ $server = array(
+ 'serverid' => $row['serverid'],
+ 'addr' => $ip,
+ );
+ $servers[] = $server;
+ }
+ // See if any clients are in dnbd3 proxy mode but don't have a matching row in the dnbd3_server table
+ foreach ($dynClients as $client) {
+ Database::exec('INSERT IGNORE INTO dnbd3_server (machineuuid) VALUES (:machineuuid)',
+ array('machineuuid' => $client['machineuuid']));
+ // Missing from $servers now but we'll handle them in the next run, so don't bother
+ }
+ // Same for this server - we use the special fixedip '<self>' for it and need to make surecx we don't have the
+ // IP address of the server itself in the list.
+ Database::exec('DELETE FROM dnbd3_server WHERE fixedip = :serverip', array('serverip' => $satServerIp));
+ Database::exec("INSERT IGNORE INTO dnbd3_server (fixedip) VALUES ('<self>')");
+ // Delete orphaned entires with machineuuid from dnbd3_server where we don't have a runmode entry
+ Database::exec('DELETE s FROM dnbd3_server s
+ LEFT JOIN runmode r USING (machineuuid)
+ WHERE s.machineuuid IS NOT NULL AND r.module IS NULL');
+ // Now query them all
+ $NOW = time();
+ foreach ($servers as $server) {
+ $port = 5003;
+ $data = Dnbd3Rpc::query($server['addr'], $port, true, false, false, true);
+ if ($data === Dnbd3Rpc::QUERY_UNREACHABLE) {
+ $error = 'No (HTTP) reply on port ' . $port;
+ } elseif ($data === Dnbd3Rpc::QUERY_NOT_200) {
+ $error = 'No HTTP 200 OK on port ' . $port;
+ } elseif ($data === Dnbd3Rpc::QUERY_NOT_JSON) {
+ $error = 'Reply to status query is not JSON';
+ } elseif (!is_array($data) || !isset($data['runId'])) {
+ if (is_array($data) && isset($data['errorMsg'])) {
+ $error = 'DNBD3: ' . $data['errorMsg'];
+ } else {
+ $error = 'Reply to status query has unexpected format';
+ }
+ } else {
+ $error = false;
+ }
+ if ($error !== false) {
+ Database::exec('UPDATE dnbd3_server SET uptime = 0, clientcount = 0, errormsg = :errormsg WHERE serverid = :serverid',
+ array('serverid' => $server['serverid'], 'errormsg' => $error));
+ continue;
+ }
+ // Seems up - since we only get absolute rx/tx values from the server, we have to prevent update race conditions
+ // and make sure the server was not restarted in the meantime (use runid and uptime for this)
+ Database::exec('UPDATE dnbd3_server SET runid = :runid, lastseen = :now, uptime = :uptime,
+ totalup = totalup + If(runid = :runid AND uptime <= :uptime, If(lastup < :up, :up - lastup, 0), If(:uptime < 1800, :up, 0)),
+ totaldown = totaldown + If(runid = :runid AND uptime <= :uptime, If(lastdown < :down, :down - lastdown, 0), If(:uptime < 1800, :up, 0)),
+ lastup = :up, lastdown = :down, clientcount = :clientcount, disktotal = :disktotal, diskfree = :diskfree, errormsg = NULL
+ WHERE serverid = :serverid', array(
+ 'runid' => $data['runId'],
+ 'now' => $NOW,
+ 'uptime' => $data['uptime'],
+ 'up' => $data['bytesSent'],
+ 'down' => $data['bytesReceived'],
+ 'clientcount' => $data['clientCount'],
+ 'serverid' => $server['serverid'],
+ 'disktotal' => $data['spaceTotal'],
+ 'diskfree' => $data['spaceFree'],
+ ));
+ }
+ }
+
+ /**
+ * A client is booting that has runmode dnbd3 proxy - set config vars accordingly.
+ *
+ * @param string $machineUuid
+ * @param string $mode always 'proxy'
+ * @param string $modeData
+ */
+ public static function runmodeConfigHook($machineUuid, $mode, $modeData)
+ {
+ // Get all directly assigned locations
+ $res = Database::simpleQuery('SELECT locationid FROM dnbd3_server
+ INNER JOIN dnbd3_server_x_location USING (serverid)
+ WHERE machineuuid = :uuid',
+ array('uuid' => $machineUuid));
+ $assignedLocs = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $assignedLocs[] = $row['locationid'];
+ }
+ $modeData = (array)json_decode($modeData, true) + self::defaultRunmodeConfig();
+ if (!empty($assignedLocs) && isset($modeData['firewall']) && $modeData['firewall']) {
+ // Get all sub-locations too
+ $recursiveLocs = $assignedLocs;
+ $locations = Location::getLocationsAssoc();
+ foreach ($assignedLocs as $l) {
+ if (isset($locations[$l])) {
+ $recursiveLocs = array_merge($recursiveLocs, $locations[$l]['children']);
+ }
+ }
+ $res = Database::simpleQuery('SELECT startaddr, endaddr FROM subnet WHERE locationid IN (:locs)',
+ array('locs' => array_values($recursiveLocs)));
+ // Got subnets, build whitelist
+ // TODO: Coalesce overlapping ranges
+ $opt = '';
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $opt .= ' ' . self::range2Cidr($row['startaddr'], $row['endaddr']);
+ }
+ if (!empty($opt)) {
+ ConfigHolder::add('SLX_DNBD3_WHITELIST', $opt, 1000);
+ }
+ }
+ // Send list of other proxy servers
+ $res = Database::simpleQuery('SELECT s.fixedip, m.clientip, sxl.locationid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
+ WHERE s.machineuuid <> :uuid OR s.machineuuid IS NULL', array('uuid' => $machineUuid));
+ $public = array();
+ $private = array();
+ $self = Property::getServerIp();
+ $public[$self] = $self;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $ip = $row['clientip'] ? $row['clientip'] : $row['fixedip'];
+ if ($ip === '<self>') {
+ continue;
+ }
+ if (is_null($row['locationid'])) {
+ if (!array_key_exists($ip, $private)) {
+ $public[$ip] = $ip;
+ }
+ } else {
+ unset($public[$ip]);
+ $private[$ip] = $ip;
+ }
+ }
+ if (!empty($public)) {
+ shuffle($public);
+ ConfigHolder::add('SLX_DNBD3_PUBLIC', implode(' ', $public));
+ }
+ if (!empty($private)) {
+ shuffle($private);
+ ConfigHolder::add('SLX_DNBD3_PRIVATE', implode(' ', $private));
+ }
+ if (isset($modeData['bgr']) && $modeData['bgr']) {
+ // Background replication
+ ConfigHolder::add('SLX_DNBD3_BGR', '1');
+ }
+ ConfigHolder::add('SLX_ADDONS', '', 1000);
+ ConfigHolder::add('SLX_SHUTDOWN_TIMEOUT', '', 1000);
+ ConfigHolder::add('SLX_SHUTDOWN_SCHEDULE', '', 1000);
+ ConfigHolder::add('SLX_REBOOT_TIMEOUT', '', 1000);
+ ConfigHolder::add('SLX_REBOOT_SCHEDULE', '', 1000);
+ }
+
+ /**
+ * Get smallest subnet in CIDR notation that covers the given range.
+ * The subnet denoted by the CIDR notation might actually be larger
+ * than the range described by $start and $end.
+ *
+ * @param int $start start address
+ * @param int $end end address
+ * @return string CIDR notation
+ */
+ private static function range2Cidr($start, $end)
+ {
+ $bin = decbin((int)$start ^ (int)$end);
+ if ($bin === '0')
+ return long2ip($start);
+ $mask = 32 - strlen($bin);
+ return long2ip($start) . '/' . $mask;
+ }
+
+ public static function defaultRunmodeConfig()
+ {
+ return array(
+ 'bgr' => true,
+ 'firewall' => false
+ );
+ }
+
+}
diff --git a/modules-available/dnbd3/lang/de/messages.json b/modules-available/dnbd3/lang/de/messages.json
new file mode 100644
index 00000000..05b8f968
--- /dev/null
+++ b/modules-available/dnbd3/lang/de/messages.json
@@ -0,0 +1,11 @@
+{
+ "dnbd3-proxy-unreachable": "DNBD3-Proxy {{0}} ist offline seit {{2}}. ({{1}})",
+ "invalid-ipv4": "{{0}} ist keine g\u00fcltige IPv4-Adresse",
+ "main-dnbd3-unreachable": "Zentraler DNBD3-Server des Satelliten ist offline seit {{1}}. ({{0}})",
+ "not-automatic-server": "{{0}} wird nicht \u00fcber den Satelliten verwaltet",
+ "server-added": "Server {{0}} hinzugef\u00fcgt",
+ "server-already-exists": "Server {{0}} existiert bereits",
+ "server-deleted": "Server {{0}} gel\u00f6scht",
+ "server-non-existent": "Server {{0}} existiert nicht",
+ "server-unreachable": "Server nicht erreichbar"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/de/module.json b/modules-available/dnbd3/lang/de/module.json
new file mode 100644
index 00000000..cf748a49
--- /dev/null
+++ b/modules-available/dnbd3/lang/de/module.json
@@ -0,0 +1,4 @@
+{
+ "module_name": "DNBD3",
+ "page_title": "DNBD3-Verwaltung"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/de/template-tags.json b/modules-available/dnbd3/lang/de/template-tags.json
new file mode 100644
index 00000000..0573a68e
--- /dev/null
+++ b/modules-available/dnbd3/lang/de/template-tags.json
@@ -0,0 +1,57 @@
+{
+ "lang_addServer": "Server hinzuf\u00fcgen",
+ "lang_allowedSubnets": "Zum Zugriff freigegebene Subnets",
+ "lang_altservers": "Uplinks",
+ "lang_backgroundReplication": "Replikation im Hintergrund",
+ "lang_backgroundReplicationInfo": "Sobald eine VM \u00fcber den Proxy angefragt wird, spiegelt der Proxy im Hintergrund den vollst\u00e4ndigen Inhalt des VM-Abbildes, nicht nur die angefragten Bl\u00f6cke.",
+ "lang_bytesSent": "Gesendet",
+ "lang_changeDnbd3Status": "DNBD3 ein-\/ausschalten",
+ "lang_client": "Client",
+ "lang_clientCount": "Clients",
+ "lang_clientList": "Liste der Clients",
+ "lang_clientsByLocation": "Clients nach Raum\/Ort",
+ "lang_comment": "Kommentar",
+ "lang_count": "Anzahl",
+ "lang_disabled": "Deaktiviert",
+ "lang_diskFree": "Freier Speicher",
+ "lang_dnbd3IntroText": "DNBD3 ist ganz toll. Einfach einschalten und los gehts, zu beachten gibt es rein gar nichts!",
+ "lang_dnbd3Management": "DNBD3 Verwaltung",
+ "lang_dnbd3Status": "DNBD3 Status",
+ "lang_editProxyHeading": "Proxy-Einstellungen bearbeiten",
+ "lang_enableDnbd3": "DNBD3 aktivieren",
+ "lang_enabled": "Aktiviert",
+ "lang_enterIpOfServer": "Bitte geben Sie die IP-Adresse des hinzuzuf\u00fcgenden Servers ein",
+ "lang_externalServer": "Externer DNBD3-Server",
+ "lang_externalServerAdd": "Externen Server hinzuf\u00fcgen",
+ "lang_externalServerHelp": "Ein externer Server wird nicht \u00fcber den Satellitenserver konfiguriert und verwaltet. Das Installieren, Einrichten und ggf. Aktualisieren der DNBD3-Serversoftware muss manuell durchgef\u00fchrt werden.\r\nDies bietet mehr Flexibilit\u00e4t bei der Konfiguration und Anpassung, z.B. bei der Verwendung von RAID- oder bcache-Setups, oder wenn der DNBD3-Server auf einer Maschine laufen soll, die noch andere Services bereitstellt.\r\nWeitere Informationen dazu finden Sie im Wiki.",
+ "lang_firewallInfo": "Wird ein Proxy auf einen oder mehrere R\u00e4ume beschr\u00e4nkt, werden Clients aus anderen R\u00e4umen diesen Proxy nicht verwenden. Technisch ist der Zugriff aus anderen R\u00e4umen jedoch trotzdem noch m\u00f6glich. Mit aktivieren dieser Option wird der Zugriff aus anderen R\u00e4umen per Firewall verhindert.",
+ "lang_firewalled": "Zugriff auf zugewiesene R\u00e4ume beschr\u00e4nken",
+ "lang_flags": "Flags",
+ "lang_global": "Global",
+ "lang_lastSeen": "Letzte Aktivit\u00e4t",
+ "lang_latency": "Latenz",
+ "lang_location": "Ort",
+ "lang_locations": "Orte",
+ "lang_manageAccessTo": "Zugriff auf Server festlegen:",
+ "lang_managedServer": "Automatisch konfigurierter DNBD3-Proxy",
+ "lang_managedServerAdd": "Automatisch konfigurierten Proxy hinzuf\u00fcgen",
+ "lang_managedServerHelp": "Automatisch konfigurierte DNBD3-Proxies booten wie gew\u00f6hnliche bwLehrpool-Clients via PXE \u00fcber den Satelliten-Server. Sobald ein bwLehrpool-Client als DNBD3-Proxy konfiguriert wird, erh\u00e4lt er beim Booten eine gesonderte Konfiguration, sodass er fortan exklusiv als DNBD3-Proxy arbeitet, und nicht mehr als Arbeitsstation zur Verf\u00fcgung steht.\r\nDer Vorteil ist, dass die Konfiguration automatisiert erfolgt, und durch w\u00f6chentliche Reboots sichergestellt wird, dass eventuelle Updates des MiniLinux angewendet werden.\r\nIn diesem Fall legen Sie bitte eine Partition mit der ID 45 auf der Festplatte des Proxy-Servers an; diese wird persistent Behandelt und im Gegensatz zur ID44-Partition nicht beim Booten formatiert. Generell sollte diese Partition so gro\u00df wie m\u00f6glich sein, abh\u00e4ngig von der Anzahl der genutzten VMs. Bei Platzmangel l\u00f6scht der Proxy automatisch die VM, die am l\u00e4ngsten nicht verwendet wurde, um neuen VMs Platz zu machen.\r\nWeitere Informationen dazu finden Sie im Wiki.",
+ "lang_numFails": "Fehler",
+ "lang_proxyConfig": "Konfiguration",
+ "lang_proxyLocationText": "Bla bla bla BLA!!!!",
+ "lang_proxyServerTHead": "Server\/Proxy",
+ "lang_reboot": "Reboot",
+ "lang_rebootProxyHeading": "Proxy neustarten",
+ "lang_rebootProxyText": "Bei \u00c4nderungen an der Konfiguration muss der Server neugestartet werden, damit die \u00c4nderungen wirksam werden.",
+ "lang_recursiveCount": "Rekursiv",
+ "lang_rxTotal": "Gesamt empfangen",
+ "lang_serverList": "Serverliste",
+ "lang_sessionRx": "Seit Neustart empfangen",
+ "lang_sessionTx": "Seit Neustart gesendet",
+ "lang_settings": "Einstellungen",
+ "lang_storageSize": "Speichergr\u00f6\u00dfe",
+ "lang_test": "Testen",
+ "lang_txTotal": "Gesamt gesendet",
+ "lang_uptime": "Aktuelle Laufzeit",
+ "lang_wantToDelete": "Wollen Sie diesen Server wirklich entfernen? (Rebooten\/Ausschalten muss in diesem Fall manuell vorgenommen werden)"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/en/messages.json b/modules-available/dnbd3/lang/en/messages.json
new file mode 100644
index 00000000..4c658e11
--- /dev/null
+++ b/modules-available/dnbd3/lang/en/messages.json
@@ -0,0 +1,3 @@
+{
+ "server-unreachable": "Server not reachable"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/en/module.json b/modules-available/dnbd3/lang/en/module.json
new file mode 100644
index 00000000..d2bb6fd3
--- /dev/null
+++ b/modules-available/dnbd3/lang/en/module.json
@@ -0,0 +1,4 @@
+{
+ "module_name": "DNBD3",
+ "page_title": "DNBD3 management"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/lang/pt/module.json b/modules-available/dnbd3/lang/pt/module.json
new file mode 100644
index 00000000..8cc7f8e4
--- /dev/null
+++ b/modules-available/dnbd3/lang/pt/module.json
@@ -0,0 +1,3 @@
+{
+ "module_name": "Log do Servidor"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/page.inc.php b/modules-available/dnbd3/page.inc.php
new file mode 100644
index 00000000..2836cbf4
--- /dev/null
+++ b/modules-available/dnbd3/page.inc.php
@@ -0,0 +1,417 @@
+<?php
+
+class Page_Dnbd3 extends Page
+{
+
+ protected function doPreprocess()
+ {
+ User::load();
+
+ if (!User::isLoggedIn()) {
+ Message::addError('main.no-permission');
+ Util::redirect('?do=Main');
+ }
+ $action = Request::post('action', false, 'string');
+ if ($action === 'refresh') {
+ Dnbd3Util::updateServerStatus();
+ } elseif ($action === 'delserver') {
+ $this->deleteServer();
+ } elseif ($action === 'addserver') {
+ $this->addServer();
+ } elseif ($action === 'editserver') {
+ $this->editServer();
+ } elseif ($action === 'savelocations') {
+ $this->saveServerLocations();
+ } elseif ($action === 'toggle-usage') {
+ $this->toggleUsage();
+ }
+ if (Request::isPost()) {
+ Util::redirect('?do=dnbd3');
+ }
+ }
+
+ private function editServer()
+ {
+ $server = $this->getServerById();
+ if (!isset($server['machineuuid'])) {
+ Message::addError('not-automatic-server', $server['ip']);
+ return;
+ }
+ $bgr = Request::post('bgr', false, 'bool');
+ $firewall = Request::post('firewall', false, 'bool');
+ RunMode::setRunMode($server['machineuuid'], 'dnbd3', 'proxy',
+ json_encode(compact('bgr', 'firewall')), false);
+ }
+
+ private function toggleUsage()
+ {
+ $enabled = Request::post('enabled', false, 'bool');
+ $nfs = Request::post('with-nfs', false, 'bool');
+ $task = Dnbd3::setEnabled($enabled);
+ Dnbd3::setNfsFallback($nfs);
+ Taskmanager::waitComplete($task, 5000);
+ }
+
+ private function saveServerLocations()
+ {
+ $server = $this->getServerById();
+ $locids = Request::post('location', [], 'array');
+ if (empty($locids)) {
+ Database::exec('DELETE FROM dnbd3_server_x_location WHERE serverid = :serverid',
+ array('serverid' => $server['serverid']));
+ } else {
+ Database::exec('DELETE FROM dnbd3_server_x_location WHERE serverid = :serverid AND locationid NOT IN (:lids)',
+ array('serverid' => $server['serverid'], 'lids' => $locids));
+ foreach ($locids as $lid) {
+ Database::exec('INSERT IGNORE INTO dnbd3_server_x_location (serverid, locationid) VALUES (:serverid, :lid)',
+ array('serverid' => $server['serverid'], 'lid' => $lid));
+ }
+ }
+ }
+
+ private function addServer()
+ {
+ $ip = Request::post('newip', false, 'string');
+ if ($ip === false) {
+ Message::addError('main.parameter-missing', 'ip');
+ return;
+ }
+ $ip = ip2long(trim($ip));
+ if ($ip !== false) {
+ $ip = long2ip($ip);
+ }
+ if ($ip === false) {
+ Message::addError('invalid-ipv4', $ip);
+ return;
+ }
+ $res = Database::queryFirst('SELECT serverid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ WHERE s.fixedip = :ip OR m.clientip = :ip', compact('ip'));
+ if ($res !== false) {
+ Message::addError('server-already-exists', $ip);
+ return;
+ }
+ Database::exec('INSERT INTO dnbd3_server (fixedip) VALUES (:ip)', compact('ip'));
+ Message::addSuccess('server-added', $ip);
+ }
+
+ private function deleteServer()
+ {
+ $server = $this->getServerById();
+ if ($server['fixedip'] === '<self>')
+ return;
+ if (!is_null($server['machineuuid'])) {
+ RunMode::setRunMode($server['machineuuid'], 'dnbd3', null, null, null);
+ }
+ Database::exec('DELETE FROM dnbd3_server WHERE serverid = :serverid',
+ array('serverid' => $server['serverid']));
+ Message::addSuccess('server-deleted', $server['ip']);
+ }
+
+ /*
+ * RENDER
+ */
+
+ protected function doRender()
+ {
+ $show = Request::get('show', false, 'string');
+ if ($show === 'proxy') {
+ $this->showProxyDetails();
+ } elseif ($show === 'locations') {
+ $this->showServerLocationEdit();
+ } elseif ($show === false) {
+ $this->showServerList();
+ } else {
+ Util::redirect('?do=dnbd3');
+ }
+ }
+
+ private function showServerList()
+ {
+ $dynClients = RunMode::getForMode(Page::getModule(), 'proxy', true, true);
+ $res = Database::simpleQuery('SELECT s.serverid, s.machineuuid, s.fixedip, s.lastseen AS dnbd3lastseen,
+ s.uptime, s.totalup, s.totaldown, s.clientcount, s.disktotal, s.diskfree, Count(sxl.locationid) AS locations,
+ s.errormsg
+ FROM dnbd3_server s
+ LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
+ GROUP BY s.serverid');
+ $servers = array();
+ $sort = array();
+ $NOW = time();
+ while ($server = $res->fetch(PDO::FETCH_ASSOC)) {
+ if (isset($dynClients[$server['machineuuid']])) {
+ $server += $dynClients[$server['machineuuid']];
+ unset($dynClients[$server['machineuuid']]);
+ }
+ if ($server['uptime'] != 0) {
+ $server['uptime'] += ($NOW - $server['dnbd3lastseen']);
+ }
+ $server['dnbd3lastseen_s'] = $server['dnbd3lastseen'] ? date('d.m.Y H:i', $server['dnbd3lastseen']) : '-';
+ $server['uptime_s'] = $server['uptime'] ? floor($server['uptime'] / 86400) . 'd ' . gmdate('H:i', $server['uptime']) : '-';
+ $server['totalup_s'] = Util::readableFileSize($server['totalup']);
+ $server['totaldown_s'] = Util::readableFileSize($server['totaldown']);
+ if ($server['disktotal'] > 0) {
+ $server['disktotal_s'] = Util::readableFileSize($server['disktotal']);
+ $server['diskfree_s'] = Util::readableFileSize($server['diskfree']);
+ $server['diskUsePercent'] = floor(100 - 100 * $server['diskfree'] / $server['disktotal']);
+ } else {
+ $server['disktotal_s'] = '?';
+ $server['diskfree_s'] = '?';
+ $server['diskUsePercent'] = 0;
+ }
+ $server['self'] = ($server['fixedip'] === '<self>');
+ if (isset($server['clientip']) && !is_null($server['clientip'])) {
+ if ($NOW - $server['lastseen'] > 360) {
+ $server['slxDown'] = true;
+ } else {
+ $server['slxOk'] = true;
+ }
+ }
+ if ($server['self']) {
+ $sort[] = '---';
+ } else {
+ $sort[] = $server['fixedip'] . '.' . $server['machineuuid'];
+ }
+ $servers[] = $server;
+ }
+ foreach ($dynClients as $server) {
+ $servers[] = $server;
+ $sort[] = '-' . $server['machineuuid'];
+ Database::exec('INSERT INTO dnbd3_server (machineuuid) VALUES (:uuid)', array('uuid' => $server['machineuuid']));
+ }
+ array_multisort($sort, SORT_ASC, $servers);
+ Render::addTemplate('page-serverlist', array(
+ 'list' => $servers,
+ 'enabled' => Dnbd3::isEnabled(),
+ 'enabled_checked_s' => Dnbd3::isEnabled() ? 'checked' : '',
+ 'nfs_checked_s' => Dnbd3::hasNfsFallback() ? 'checked' : '',
+ 'rebootcontrol' => Module::isAvailable('rebootcontrol', false)
+ ));
+ }
+
+ private function showProxyDetails()
+ {
+ $server = $this->getServerById();
+ Render::addTemplate('page-proxy-header', $server);
+ $stats = Dnbd3Rpc::query($server['ip'], 5003,true, true, false, true);
+ if (!is_array($stats) || !isset($stats['runId'])) {
+ Message::addError('server-unreachable');
+ return;
+ }
+ $stats['bytesSent_s'] = Util::readableFileSize($stats['bytesSent']);
+ $stats['bytesReceived_s'] = Util::readableFileSize($stats['bytesReceived']);
+ $stats['uptime_s'] = floor($stats['uptime'] / 86400) . 'd ' . gmdate('H:i:s', $stats['uptime']);
+ Render::addTemplate('page-proxy-stats', $stats);
+ $images = Dnbd3Rpc::query($server['ip'], 5003,false, false, true);
+ $confAlts = Dnbd3Rpc::query($server['ip'], 5003,false, false, false, false, true, true);
+ $ips = array();
+ $sort = array();
+ foreach ($stats['clients'] as &$c) {
+ $c['bytesSent_s'] = Util::readableFileSize($c['bytesSent']);
+ $sort[] = $c['bytesSent'];
+ $ips[] = preg_replace('/:\d+$/', '', $c['address']);
+ }
+ array_multisort($sort, SORT_DESC, $stats['clients']);
+ Render::openTag('div', ['class' => 'row']);
+ // Config
+ if (is_string($confAlts['config'])) {
+ Render::addTemplate('page-proxy-config', $confAlts);
+ }
+ if (is_array($confAlts['altservers'])) {
+ foreach ($confAlts['altservers'] as &$as) {
+ $as['rtt'] = round(array_sum($as['rtt']) / count($as['rtt']) / 1000, 2);
+ }
+ unset($as);
+ Render::addTemplate('page-proxy-altservers', $confAlts);
+ }
+ Render::closeTag('div');
+ Render::openTag('div', ['class' => 'row']);
+ // Count locations
+ $res = Database::simpleQuery('SELECT locationid, Count(*) AS cnt FROM machine WHERE clientip IN (:ips) GROUP BY locationid', compact('ips'));
+ $locCount = Location::getLocationsAssoc();
+ $locCount[0] = array(
+ 'locationname' => '/',
+ 'depth' => 0,
+ 'recCount' => 0,
+ );
+ foreach ($locCount as &$loc) {
+ $loc['recCount'] = 0;
+ }
+ $showLocs = false;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ settype($row['locationid'], 'int');
+ $loc =& $locCount[$row['locationid']];
+ $loc['clientCount'] = $row['cnt'];
+ $loc['recCount'] += $row['cnt'];
+ if ($row['locationid'] !== 0) {
+ $showLocs = true;
+ }
+ $loc['keep'] = true;
+ if (isset($loc['parents'])) {
+ foreach ($loc['parents'] as $p) {
+ $locCount[$p]['keep'] = true;
+ $locCount[$p]['recCount'] += $row['cnt'];
+ }
+ }
+ }
+ if ($showLocs) {
+ $locCount = array_filter($locCount, function ($v) { return isset($v['keep']); });
+ Render::addTemplate('page-proxy-loclist', array('list' => array_values($locCount)));
+ }
+ Render::addTemplate('page-proxy-clients', $stats);
+ Render::closeTag('div');
+ }
+
+ private function showServerLocationEdit()
+ {
+ $server = $this->getServerById();
+ // Get selected ones
+ $res = Database::simpleQuery('SELECT locationid FROM dnbd3_server_x_location WHERE serverid = :serverid',
+ array('serverid' => $server['serverid']));
+ $selectedLocations = array();
+ while ($loc = $res->fetchColumn(0)) {
+ $selectedLocations[$loc] = true;
+ }
+ // Build location list
+ $server['locations'] = array_values(Location::getSubnetsByLocation());
+ $filtered = array();
+ foreach ($server['locations'] as &$loc) {
+ $filtered['l'.$loc['locationid']] = array(
+ 'children' => $loc['children'],
+ 'subnets' => $loc['subnets']
+ );
+ if (isset($selectedLocations[$loc['locationid']])) {
+ $loc['checked_s'] = 'checked';
+ }
+ }
+ unset($loc);
+ $server['jsonLocations'] = json_encode($filtered);
+ Render::addTemplate('page-server-locations', $server);
+ }
+
+ private function getServerById($serverId = false)
+ {
+ if ($serverId === false) {
+ $serverId = Request::any('server', false, 'int');
+ }
+ if ($serverId === false) {
+ if (AJAX)
+ die('Missing parameter');
+ Message::addError('main.parameter-missing', 'server');
+ Util::redirect('?do=dnbd3');
+ }
+ $server = Database::queryFirst('SELECT s.serverid, s.machineuuid, s.fixedip, m.clientip, m.hostname
+ FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ WHERE s.serverid = :serverId', compact('serverId'));
+ if ($server === false) {
+ if (AJAX)
+ die('Invalid server id');
+ Message::addError('server-non-existent', 'server');
+ Util::redirect('?do=dnbd3');
+ }
+ if (!is_null($server['clientip'])) {
+ $server['ip'] = $server['clientip'];
+ } elseif (!is_null($server['fixedip'])) {
+ $server['ip'] = $server['fixedip'];
+ } else {
+ $server['ip'] = '127.0.0.1';
+ }
+ return $server;
+ }
+
+ /*
+ * AJAX
+ */
+
+ protected function doAjax()
+ {
+ User::load();
+ if (!User::isLoggedIn())
+ die('No');
+ $action = Request::any('action', false, 'string');
+ if ($action === 'servertest') {
+ $this->ajaxServerTest();
+ } elseif ($action === 'editserver') {
+ $this->ajaxEditServer();
+ } elseif ($action === 'reboot') {
+ $this->ajaxReboot();
+ } else {
+ die($action . '???');
+ }
+ }
+
+ private function ajaxServerTest()
+ {
+ Header('Content-Type: application/json; charset=utf-8');
+ $ip = Request::post('ip', false, 'string');
+ if ($ip === false)
+ die('{"error": "Missing parameter", "fatal": true}');
+ $ip = ip2long(trim($ip));
+ if ($ip !== false) {
+ $ip = long2ip($ip);
+ }
+ if ($ip === false)
+ die('{"error": "Supports IPv4 only", "fatal": true}');
+ // Dup?
+ $res = Database::queryFirst('SELECT serverid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ WHERE s.fixedip = :ip OR m.clientip = :ip', compact('ip'));
+ if ($res !== false)
+ die('{"error": "Server with this IP already exists", "fatal": true}');
+ // Query
+ $reply = Dnbd3Rpc::query($ip, 5003,true, false, false, true);
+ if ($reply === Dnbd3Rpc::QUERY_UNREACHABLE)
+ die('{"error": "Could not reach server"}');
+ if ($reply === Dnbd3Rpc::QUERY_NOT_200)
+ die('{"error": "Server did not reply with 200 OK"}');
+ if ($reply === Dnbd3Rpc::QUERY_NOT_JSON)
+ die('{"error": "No JSON received from server"}');
+ if (!is_array($reply) || !isset($reply['uptime']) || !isset($reply['clientCount']))
+ die('{"error": "Reply does not suggest this is a dnbd3 server"}');
+ echo json_encode($reply);
+ }
+
+ private function ajaxEditServer()
+ {
+ $server = $this->getServerById();
+ if (!isset($server['machineuuid'])) {
+ echo 'Not automatic server.';
+ return;
+ }
+ $rm = RunMode::getForMode(Page::getModule(), 'proxy', false, true);
+ if (!isset($rm[$server['machineuuid']])) {
+ echo 'Error: RunMode entry missing.';
+ return;
+ }
+ $modeData = (array)json_decode($rm[$server['machineuuid']]['modedata'], true);
+ $server += $modeData + Dnbd3Util::defaultRunmodeConfig();
+ echo Render::parse('fragment-server-settings', $server);
+ }
+
+ private function ajaxReboot()
+ {
+ $server = $this->getServerById();
+ if (!isset($server['machineuuid'])) {
+ die('Not automatic server.');
+ }
+ if (!Module::isAvailable('rebootcontrol')) {
+ die('No rebootcontrol');
+ }
+ $uuid = $server['machineuuid'];
+ $task = RebootControl::reboot([ $uuid ]);
+ if ($task === false) {
+ die('Taskmanager unreachable');
+ }
+ $task = Taskmanager::waitComplete($task, 2000);
+ if (is_array($task) && isset($task['data']) && isset($task['data']['clientStatus']) && isset($task['data']['clientStatus'][$uuid])) {
+ $status = $task['data']['clientStatus'][$uuid];
+ if (!empty($task['data']['error'])) {
+ $status .= "\n --- \n" . $task['data']['error'];
+ }
+ die($status);
+ }
+ die('Unknown :-(');
+ }
+
+}
diff --git a/modules-available/dnbd3/templates/fragment-server-settings.html b/modules-available/dnbd3/templates/fragment-server-settings.html
new file mode 100644
index 00000000..61b0626d
--- /dev/null
+++ b/modules-available/dnbd3/templates/fragment-server-settings.html
@@ -0,0 +1,14 @@
+<input type="hidden" name="server" value="{{serverid}}">
+
+<div class="checkbox">
+ <input type="checkbox" name="bgr" id="bgr" {{#bgr}}checked{{/bgr}}>
+ <label for="bgr">{{lang_backgroundReplication}}</label>
+</div>
+<i>{{lang_backgroundReplicationInfo}}</i>
+<br>
+<div class="checkbox">
+ <input type="checkbox" name="firewall" id="firewall" {{#firewall}}checked{{/firewall}}>
+ <label for="firewall">{{lang_firewalled}}</label>
+</div>
+<i>{{lang_firewallInfo}}</i>
+<br>
diff --git a/modules-available/dnbd3/templates/page-proxy-altservers.html b/modules-available/dnbd3/templates/page-proxy-altservers.html
new file mode 100644
index 00000000..00a884cc
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-altservers.html
@@ -0,0 +1,36 @@
+<div class="col-md-6">
+ <h2>{{lang_altservers}}</h2>
+ <table class="table table-condensed">
+ <tr>
+ <th>{{lang_proxyServerTHead}}</th>
+ <th>{{lang_numFails}}</th>
+ <th class="text-right">{{lang_latency}}</th>
+ <th>{{lang_flags}}</th>
+ <th>{{lang_comment}}</th>
+ </tr>
+ {{#altservers}}
+ <tr>
+ <td class="text-nowrap">{{host}}</td>
+ <td>
+ {{^isClientOnly}}
+ {{numFails}}
+ {{/isClientOnly}}
+ </td>
+ <td class="text-right text-nowrap">
+ {{^isClientOnly}}
+ {{rtt}}&thinsp;ms
+ {{/isClientOnly}}
+ </td>
+ <td>
+ {{#isClientOnly}}
+ [CO]
+ {{/isClientOnly}}
+ {{#isPrivate}}
+ [PO]
+ {{/isPrivate}}
+ </td>
+ <td><table class="slx-ellipsis"><tr><td>{{comment}}</td></tr></table></td>
+ </tr>
+ {{/altservers}}
+ </table>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-proxy-clients.html b/modules-available/dnbd3/templates/page-proxy-clients.html
new file mode 100644
index 00000000..9e7cec4c
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-clients.html
@@ -0,0 +1,20 @@
+<div class="col-md-6">
+ <h2>{{lang_clientList}}</h2>
+
+ <table class="table table-condensed">
+ <tr>
+ <th>{{lang_client}}</th>
+ <th class="text-right">{{lang_bytesSent}}</th>
+ </tr>
+ {{#clients}}
+ <tr>
+ <td>
+ {{address}}
+ </td>
+ <td data-sort="int" data-sort-value="{{bytesSent}}" class="text-right">
+ {{bytesSent_s}}
+ </td>
+ </tr>
+ {{/clients}}
+ </table>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-proxy-config.html b/modules-available/dnbd3/templates/page-proxy-config.html
new file mode 100644
index 00000000..adc73a57
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-config.html
@@ -0,0 +1,4 @@
+<div class="col-md-6">
+ <h2>{{lang_proxyConfig}}</h2>
+ <pre>{{config}}</pre>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-proxy-header.html b/modules-available/dnbd3/templates/page-proxy-header.html
new file mode 100644
index 00000000..6f3f1b7f
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-header.html
@@ -0,0 +1 @@
+<h1>{{ip}}</h1> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-proxy-loclist.html b/modules-available/dnbd3/templates/page-proxy-loclist.html
new file mode 100644
index 00000000..67c90683
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-loclist.html
@@ -0,0 +1,27 @@
+<div class="col-md-6">
+ <h2>{{lang_clientsByLocation}}</h2>
+
+ <table class="table table-condensed">
+ <tr>
+ <th>{{lang_location}}</th>
+ <th class="text-right">{{lang_count}}</th>
+ <th class="text-right">{{lang_recursiveCount}}</th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td>
+ {{#depth}}
+ <div style="display:inline-block;width:{{depth}}em"></div>
+ {{/depth}}
+ {{locationname}}
+ </td>
+ <td class="text-right">
+ {{clientCount}}
+ </td>
+ <td class="text-right">
+ {{recCount}}
+ </td>
+ </tr>
+ {{/list}}
+ </table>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-proxy-stats.html b/modules-available/dnbd3/templates/page-proxy-stats.html
new file mode 100644
index 00000000..e7811028
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-proxy-stats.html
@@ -0,0 +1,9 @@
+<div class="panel panel-default">
+ <div class="panel-body">
+ {{lang_sessionTx}}: <b>{{bytesSent_s}}</b>
+ ––
+ {{lang_sessionRx}}: <b>{{bytesReceived_s}}</b>
+ ––
+ {{lang_uptime}}: <b>{{uptime_s}}</b>
+ </div>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-server-locations.html b/modules-available/dnbd3/templates/page-server-locations.html
new file mode 100644
index 00000000..20dddaac
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-server-locations.html
@@ -0,0 +1,96 @@
+<h1>{{lang_manageAccessTo}} {{ip}}</h1>
+
+<p><i>{{lang_proxyLocationText}}</i></p>
+
+<form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="server" value="{{serverid}}">
+
+ <div class="buttonbar text-right">
+ <button type="submit" class="btn btn-primary" name="action" value="savelocations">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+
+ <div class="row">
+ <div class="col-md-6">
+ <h3>{{lang_allowedSubnets}}</h3>
+ <div id="subnet-list">
+
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>{{lang_locations}}</h3>
+ {{#locations}}
+ <div class="checkbox">
+ {{#depth}}
+ <div style="display:inline-block;width:{{depth}}em"></div>
+ {{/depth}}
+ <input id="cb-{{locationid}}" class="loc-check" type="checkbox" name="location[]" value="{{locationid}}" {{checked_s}}>
+ <label for="cb-{{locationid}}">{{locationname}}</label>
+ </div>
+ {{/locations}}
+ </div>
+ </div>
+
+ <div class="buttonbar text-right">
+ <button type="submit" class="btn btn-success" name="action" value="savelocations">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+</form>
+
+<script type="application/javascript"><!--
+document.addEventListener('DOMContentLoaded', function() {
+
+ const locData = {{{jsonLocations}}};
+ const $snList = $('#subnet-list');
+
+ function updateLocations() {
+ var allLocs = [];
+ $('.loc-check:checked').each(function () {
+ var lid = parseInt(this.value);
+ var locs = getAllLocs(lid);
+ for (var i = 0; i < locs.length; ++i) {
+ if (allLocs.indexOf(locs[i]) === -1) allLocs.push(locs[i]);
+ }
+ });
+ var subnets = [];
+ $snList.empty();
+ for (var i = 0; i < allLocs.length; ++i) {
+ var loc = locData['l'+allLocs[i]];
+ if (!loc || !loc.subnets) continue;
+ for (var j = 0; j < loc.subnets.length; ++j) {
+ var line = long2ip(loc.subnets[j].startaddr) + ' - ' + long2ip(loc.subnets[j].endaddr);
+ if (subnets.indexOf(line) === -1) {
+ subnets.push(line);
+ $snList.append($('<div>').text(line));
+ }
+ }
+ }
+ if (subnets.length === 0) {
+ $snList.text('{{lang_global}}');
+ }
+ }
+
+ function getAllLocs(lid) {
+ var e = locData['l'+lid];
+ if (!e || !e.children) return [];
+ var ret = e.children;
+ ret.push(lid);
+ return ret;
+ }
+
+ function long2ip(ip) {
+ // discuss at: http://locutus.io/php/long2ip/
+ // original by: Waldo Malqui Silva (http://waldo.malqui.info)
+ if (!isFinite(ip)) return false;
+ return [ip >>> 24, ip >>> 16 & 0xFF, ip >>> 8 & 0xFF, ip & 0xFF].join('.');
+ }
+
+ $('.loc-check').change(updateLocations);
+ updateLocations();
+});
+//--></script> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-serverlist.html b/modules-available/dnbd3/templates/page-serverlist.html
new file mode 100644
index 00000000..c5905dcd
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-serverlist.html
@@ -0,0 +1,404 @@
+<h1>{{lang_dnbd3Management}}</h1>
+<p><i>{{lang_dnbd3IntroText}}</i></p>
+
+<div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_dnbd3Status}}:
+ <b>
+ {{#enabled}}{{lang_enabled}}{{/enabled}}
+ {{^enabled}}{{lang_disabled}} (NFS/CIFS){{/enabled}}
+ </b>
+ – <a href="#" data-toggle="collapse" data-target="#toggle-div">{{lang_changeDnbd3Status}}</a>
+ </div>
+ <div class="panel-collapse collapse" id="toggle-div">
+ <div class="panel-body">
+ <form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <div class="checkbox">
+ <input id="enable-dnbd3" type="checkbox" name="enabled" {{enabled_checked_s}}>
+ <label for="enable-dnbd3">{{lang_enableDnbd3}}</label>
+ </div>
+ <div class="checkbox">
+ <input id="allow-nfs" type="checkbox" name="with-nfs" {{nfs_checked_s}}>
+ <label for="allow-nfs">{{lang_allowNfsFallback}}</label>
+ </div>
+ <button type="submit" name="action" value="toggle-usage" class="btn btn-success">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </form>
+ </div>
+ </div>
+</div>
+
+<form method="post" onsubmit="$('#refbtn').prop('disabled', true).find('span').addClass('slx-rotation')" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="refresh">
+ <h2>
+ {{lang_serverList}}
+ <button id="refbtn" type="submit" class="btn btn-default"><span class="glyphicon glyphicon-refresh"></span></button>
+ </h2>
+</form>
+
+<form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="delserver">
+ <table class="table">
+ <thead>
+ <tr>
+ <th></th>
+ <th>{{lang_proxyServerTHead}}</th>
+ <th class="text-right">{{lang_storageSize}}</th>
+ <th class="text-right">{{lang_clientCount}}</th>
+ <th class="text-right">{{lang_lastSeen}}</th>
+ <th class="text-right">{{lang_uptime}}</th>
+ <th class="text-right">{{lang_txTotal}}</th>
+ <th class="text-right">{{lang_rxTotal}}</th>
+ <th class="text-right">{{lang_locations}}</th>
+ <th></th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {{#list}}
+ <tr>
+ <td class="text-right text-nowrap">
+ {{#slxOk}}
+ <span class="glyphicon glyphicon-ok text-success"></span>
+ {{/slxOk}}
+ {{#slxDown}}
+ <span class="glyphicon glyphicon-off"></span>
+ {{/slxDown}}
+ {{#uptime}}
+ <span class="glyphicon glyphicon-ok text-success"></span>
+ {{/uptime}}
+ {{^uptime}}
+ <span class="glyphicon glyphicon-remove text-danger"></span>
+ {{/uptime}}
+ </td>
+ <td class="{{#self}}slx-bold{{/self}}">
+ {{#machineuuid}}
+ <a class="pull-right btn btn-default btn-xs" href="?do=Statistics&uuid={{machineuuid}}">
+ <span class="glyphicon glyphicon-eye-open"></span>
+ </a>
+ {{/machineuuid}}
+ <a href="?do=dnbd3&amp;show=proxy&amp;server={{serverid}}">
+ {{fixedip}}
+ <span class="small">{{clientip}}</span>
+ </a>
+ <div class="small">{{hostname}}</div>
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{disktotal}}">
+ <div style="border:1px solid #ddd;background:linear-gradient(to right, #f85 {{diskUsePercent}}%, transparent {{diskUsePercent}}%)"
+ class="text-center text-nowrap"
+ title="{{lang_diskFree}}: {{diskfree_s}}">
+ {{disktotal_s}}
+ </div>
+ {{#errormsg}}
+ <div class="small text-nowrap text-danger" style="margin-right:-500px">
+ {{errormsg}}
+ </div>
+ {{/errormsg}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" class="text-right">
+ {{clientcount}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{dnbd3lastseen}}" class="text-right text-nowrap">
+ {{dnbd3lastseen_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{uptime}}" class="text-right text-nowrap">
+ {{uptime_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{totalup}}" class="text-right text-nowrap">
+ {{totalup_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{totaldown}}" class="text-right text-nowrap">
+ {{totaldown_s}}
+ </td>
+ <td class="text-right text-nowrap">
+ {{^self}}
+ {{^locations}}
+ <i>{{lang_global}}</i>
+ {{/locations}}
+ {{#locations}}
+ {{locations}}
+ {{/locations}}
+ <a href="?do=dnbd3&amp;show=locations&amp;server={{serverid}}" class="btn btn-default btn-xs">
+ <span class="glyphicon glyphicon-map-marker"></span>
+ </a>
+ {{/self}}
+ </td>
+ <td class="text-right text-nowrap">
+ {{#machineuuid}}
+ {{#rebootcontrol}}
+ <button class="btn btn-warning btn-xs reboot-btn" type="button" data-id="{{serverid}}"
+ data-toggle="modal" data-target="#server-reboot-modal" title="{{lang_reboot}}">
+ <span class="glyphicon glyphicon-repeat"></span>
+ </button>
+ {{/rebootcontrol}}
+ <button class="btn btn-default btn-xs edit-btn" type="button" data-id="{{serverid}}"
+ data-toggle="modal" data-target="#server-edit-modal" title="{{lang_settings}}">
+ <span class="glyphicon glyphicon-cog"></span>
+ </button>
+ {{/machineuuid}}
+ {{^self}}
+ <button class="btn btn-danger btn-xs" name="server" value="{{serverid}}"
+ onclick="return confirm('{{lang_wantToDelete}}')" title="{{lang_delete}}">
+ <span class="glyphicon glyphicon-trash"></span>
+ </button>
+ {{/self}}
+ </td>
+ </tr>
+ {{/list}}
+ </tbody>
+ </table>
+</form>
+
+<div class="btn-toolbar pull-right">
+ <div class="btn-group">
+ <button type="button" class="btn btn-success" data-toggle="modal" data-target="#add-modal">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_externalServerAdd}}
+ </button>
+ <button type="button" class="btn btn-default" data-toggle="modal" data-target="#help-external">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </button>
+ </div>
+ <div class="btn-group">
+ <a class="btn btn-success" href="?do=runmode&amp;module=dnbd3&amp;modeid=proxy&amp;redirect=?do=dnbd3">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_managedServerAdd}}
+ </a>
+ <button type="button" class="btn btn-default" data-toggle="modal" data-target="#help-automatic">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ </button>
+ </div>
+</div>
+
+<div id="help-external" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_externalServer}}</b></h4>
+ </div>
+ <div class="modal-body">
+ {{lang_externalServerHelp}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary pull-right" data-dismiss="modal">
+ {{lang_close}}
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div id="help-automatic" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_managedServer}}</b></h4>
+ </div>
+ <div class="modal-body">
+ {{lang_managedServerHelp}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary pull-right" data-dismiss="modal">
+ {{lang_close}}
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div id="server-edit-modal" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="editserver">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_editProxyHeading}}</b></h4>
+ </div>
+ <div class="modal-body" id="server-edit-body">
+ .
+ </div>
+ <div class="modal-footer">
+ <button type="submit" class="btn btn-primary pull-right">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
+
+<div id="server-reboot-modal" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_rebootProxyHeading}}</b></h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_rebootProxyText}}</p>
+ <p id="reboot-status"></p>
+ </div>
+ <div class="modal-footer text-right">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_close}}</button>
+ <button id="btn-exec-reboot" type="button" class="btn btn-primary">
+ <span class="glyphicon glyphicon-repeat"></span>
+ {{lang_reboot}}
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div id="add-modal" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_addServer}}</b></h4>
+ </div>
+ <form id="addform" method="post" action="?do=dnbd3">
+ <div class="modal-body">
+ <p>{{lang_enterIpOfServer}}</p>
+
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="text" class="form-control" name="newip" id="newip">
+ <div id="addtest" class="text-danger"></div>
+ </div>
+ <div class="modal-footer">
+ <div class="btn-toolbar pull-right">
+ <button id="testbtn" type="submit" class="btn btn-warning" name="action" value="addserver">
+ <span class="glyphicon glyphicon-question-sign"></span>
+ {{lang_test}}
+ </button>
+ <button id="savebtn" type="submit" class="btn btn-primary" name="action" value="addserver" disabled>
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
+
+<div class="clearfix"></div>
+
+<script type="application/javascript"><!--
+document.addEventListener('DOMContentLoaded', function () {
+ var slxWorking = false;
+ var testedIp;
+ const $form = $('#addform');
+ const $inputs = $form.find(':input');
+ const $result =$('#addtest');
+ const $ip = $('#newip');
+ const $save = $('#savebtn');
+
+ const changeFunc = function() {
+ $save.prop('disabled', $ip.val() !== testedIp);
+ };
+ $ip.change(changeFunc).keypress(function () { setTimeout(changeFunc, 1); });
+
+ $form.submit(function (event) {
+ console.log('pre-sub');
+ console.log($save.prop('disabled'));
+ if (!$save.prop('disabled')) return;
+ console.log('post-sub');
+ event.preventDefault();
+ runTest();
+ });
+
+ $('#testbtn').click(function (event) {
+ console.log('pre-focus');
+ if ($ip.is(':focus')) return;
+ console.log('post-focus');
+ event.preventDefault();
+ runTest();
+ });
+
+ function runTest() {
+ if (slxWorking === false) {
+ var ip = $ip.val();
+ var form = $('#addform');
+ slxWorking = Math.random();
+ var workCopy = slxWorking;
+ $inputs.prop('disabled', true);
+ $result.empty().removeClass('text-danger').text('...working...');
+ var resetFunc = function(ok) {
+ if (slxWorking === workCopy) {
+ slxWorking = false;
+ $inputs.prop('disabled', false);
+ if (!ok) $result.empty().addClass('text-danger').text('Timeout.');
+ }
+ };
+ setTimeout(resetFunc, 3000);
+ testedIp = ip;
+ $.post('?do=dnbd3', {action:'servertest', ip:ip, token:TOKEN}, function (data) {
+ if (workCopy !== slxWorking) return;
+ resetFunc(true);
+ if (!data || data.fatal) {
+ $save.prop('disabled', true);
+ }
+ if (data && data.error) {
+ $result.empty().addClass('text-danger').text(data.error);
+ } else {
+ $result.empty().removeClass('text-danger').text('OK, Uptime: ' + data.uptime + ', Clients: ' + data.clientCount);
+ }
+ }, 'json').fail(function(oh, what) {
+ resetFunc(true);
+ $result.empty().addClass('text-danger').text('Fail ' + what);
+ });
+ }
+ }
+
+ $('.edit-btn').click(function() {
+ var id = $(this).data('id');
+ $('#server-edit-body').text('loading').load('?do=dnbd3&action=editserver&server=' + id);
+ });
+
+ var rebootServerId = 0;
+ $('.reboot-btn').click(function() {
+ rebootServerId = $(this).data('id');
+ $('#btn-exec-reboot').prop('disabled', false);
+ $('#reboot-status').empty();
+ });
+ $('#btn-exec-reboot').click(function() {
+ $(this).prop('disabled', true);
+ var $t = $('#reboot-status');
+ if (rebootServerId === 0) {
+ $t.text('No ID!?');
+ return;
+ }
+ $t.html('<span class="glyphicon glyphicon-refresh slx-rotation"></span>');
+ var sid = rebootServerId;
+ var query = function() {
+ $.ajax({
+ "data": {"token": TOKEN, "action": "reboot", "server": sid},
+ "method": "POST",
+ "dataType": "text",
+ "url": "?do=dnbd3"
+ }).done(function (data) {
+ $t.text(data);
+ if (data.indexOf('REBOOTING') !== -1 || data.indexOf('CONNECTING') !== -1) {
+ setTimeout(query, 5000);
+ $t.append($('<span class="glyphicon glyphicon-refresh slx-rotation"></span>'));
+ }
+ }).fail(function () {
+ $.text('Failed');
+ });
+ };
+ query();
+ rebootServerId = 0;
+ });
+});
+
+//--></script> \ No newline at end of file
diff --git a/modules-available/dozmod/api.inc.php b/modules-available/dozmod/api.inc.php
index accfe813..74aaa003 100644
--- a/modules-available/dozmod/api.inc.php
+++ b/modules-available/dozmod/api.inc.php
@@ -17,7 +17,7 @@ if (!Module::isAvailable('locations')) {
define('LIST_URL', CONFIG_DOZMOD_URL . '/vmchooser/list');
define('VMX_URL', CONFIG_DOZMOD_URL . '/vmchooser/lecture');
-$availableRessources = ['list', 'vmx', 'test', 'netrules', 'runscript'];
+$availableRessources = ['list', 'vmx', 'test', 'netrules', 'runscript', 'netshares'];
/* BEGIN: A simple caching mechanism ---------------------------- */
@@ -45,7 +45,8 @@ function cache_has($key)
if ($mtime === false) {
return false; // cache miss
}
- if (time() - $mtime > CONFIG_DOZMOD_EXPIRE) {
+ $now = time();
+ if ($now < $mtime || $now - $mtime > CONFIG_DOZMOD_EXPIRE) {
return false;
} else {
return true;
@@ -94,7 +95,9 @@ function xmlToLectureIds($responseXML)
$uuids = [];
foreach ($xml->eintrag as $e) {
- $uuids[] = strval($e->uuid['param'][0]);
+ if (isset($e->uuid) && isset($e->uuid['param']) && isset($e->uuid['param'][0])) {
+ $uuids[] = strval($e->uuid['param'][0]);
+ }
}
return $uuids;
}
@@ -149,11 +152,13 @@ function getListForLocations($locationIds, $raw)
if (Module::isAvailable('exams')) {
// If we have the exam mode module, we can enforce a server side check and make sure it agrees with the client
$serverExamMode = Exams::isInExamMode($locationIds);
- $clientServerMismatch = ($serverExamMode !== $examMode);
+ if ($raw) {
+ $clientServerMismatch = ($serverExamMode !== $examMode);
+ }
$examMode = $serverExamMode;
}
// Only enforce exam mode validity check if the client requests the raw xml data
- if ($raw && $clientServerMismatch) {
+ if ($clientServerMismatch) {
sendExamModeMismatch(); // does not return
}
// Proceed normally from here on
@@ -175,7 +180,7 @@ function getListForLocations($locationIds, $raw)
$url .= '&exams';
}
$value = Download::asString($url, 60, $code);
- if ($value === false)
+ if ($value === false || $code < 200 || $code > 299)
return false;
cache_put($rawKey, $value);
$list = xmlToLectureIds($value);
@@ -203,67 +208,45 @@ function _getVmData($lecture_uuid, $subResource = false)
$url .= '/' . $subResource;
}
$response = Download::asString($url, 60, $code);
+ if ($code < 200 || $code > 299)
+ return (int)$code;
return $response;
}
-/** Caching wrapper around _getVMX() **/
-function outputVMX($lecture_uuid)
-{
- $key = 'vmx_' . $lecture_uuid;
- if (cache_has($key)) {
- cache_get_passthru($key);
- } else {
- $value = _getVmData($lecture_uuid);
- if ($value === false)
- return false;
- cache_put($key, $value);
- die($value);
- }
-}
-
-function outputNetrules($lecture_uuid)
+/** Caching wrapper around _getVmData() **/
+function outputResource($lecture_uuid, $resource)
{
- $key = 'netrules_' . $lecture_uuid;
+ $key = $resource . '_' . $lecture_uuid;
if (cache_has($key)) {
cache_get_passthru($key);
} else {
- $value = _getVmData($lecture_uuid, 'netrules');
- if ($value === false)
- return false;
- cache_put($key, $value);
- die($value);
- }
-}
-
-function outputRunscript($lecture_uuid)
-{
- $key = 'runscript_' . $lecture_uuid;
- if (cache_has($key)) {
- cache_get_passthru($key);
- } else {
- $value = _getVmData($lecture_uuid, 'runscript');
+ $value = _getVmData($lecture_uuid, $resource);
if ($value === false)
return false;
+ if (is_int($value)) {
+ http_response_code($value);
+ exit;
+ }
cache_put($key, $value);
die($value);
}
+ return false;
}
function fatalDozmodUnreachable()
{
Header('HTTP/1.1 504 Gateway Timeout');
- die('Resource not available');
+ die('DMSD currently not available');
}
-function readLectureParam()
+function readLectureParam($locationIds)
{
- global $location_ids;
$lecture = Request::get('lecture', false, 'string');
if ($lecture === false) {
Header('HTTP/1.1 400 Bad Request');
die('Missing lecture UUID');
}
- $lectures = getLectureUuidsForLocations($location_ids);
+ $lectures = getLectureUuidsForLocations($locationIds);
if ($lectures === false) {
fatalDozmodUnreachable();
}
@@ -295,34 +278,17 @@ if (substr($ip, 0, 7) === '::ffff:') {
/* lookup location id(s) */
-$location_ids = Location::getFromIp($ip);
+$location_ids = Location::getFromIp($ip, true);
$location_ids = Location::getLocationRootChain($location_ids);
-if ($resource === 'vmx') {
- $lecture = readLectureParam();
- outputVMX($lecture);
- // outputVMX does not return on success
- fatalDozmodUnreachable();
-}
-
-if ($resource === 'netrules') {
- $lecture = readLectureParam();
- outputNetrules($lecture);
- // no return on success
- fatalDozmodUnreachable();
-}
-
-if ($resource === 'runscript') {
- $lecture = readLectureParam();
- outputRunscript($lecture);
- // no return on success
- fatalDozmodUnreachable();
-}
-
if ($resource === 'list') {
outputLectureXmlForLocation($location_ids);
// Won't return on success...
fatalDozmodUnreachable();
+} else {
+ $lecture = readLectureParam($location_ids);
+ outputResource($lecture, $resource);
+ fatalDozmodUnreachable();
}
Header('HTTP/1.1 400 Bad Request');
diff --git a/modules-available/dozmod/lang/de/template-tags.json b/modules-available/dozmod/lang/de/template-tags.json
index 0c7d8348..4b49579a 100644
--- a/modules-available/dozmod/lang/de/template-tags.json
+++ b/modules-available/dozmod/lang/de/template-tags.json
@@ -45,6 +45,7 @@
"lang_mailTemplates": "E-Mail Templates",
"lang_maxImageValidity": "G\u00fcltigkeitsdauer neuer VM-Versionen (Tage)",
"lang_maxLectureVisibility": "Sp\u00e4testes Enddatum einer Veranstaltung (Tage in der Zukunft)",
+ "lang_maxLocationsPerLecture": "Max. explizite Orte pro Veranstaltung",
"lang_maxTransfers": "Maximale Zahl gleichzeitiger Up-\/Downloads pro Benutzer",
"lang_miscOptions": "Verschiedene Einstellungen",
"lang_modified": "Modifiziert",
diff --git a/modules-available/dozmod/lang/en/template-tags.json b/modules-available/dozmod/lang/en/template-tags.json
index 95edfe49..ed8f3465 100644
--- a/modules-available/dozmod/lang/en/template-tags.json
+++ b/modules-available/dozmod/lang/en/template-tags.json
@@ -45,6 +45,7 @@
"lang_mailTemplates": "E-Mail Templates",
"lang_maxImageValidity": "New VM validity (days)",
"lang_maxLectureVisibility": "Max time lecture end date may lie in the future (days)",
+ "lang_maxLocationsPerLecture": "Max. explicit locations per lecture",
"lang_maxTransfers": "Max concurrent transfers per user",
"lang_miscOptions": "Misc options",
"lang_modified": "modified",
diff --git a/modules-available/dozmod/page.inc.php b/modules-available/dozmod/page.inc.php
index 19c05555..ffb38663 100644
--- a/modules-available/dozmod/page.inc.php
+++ b/modules-available/dozmod/page.inc.php
@@ -353,6 +353,7 @@ class Page_DozMod extends Page
'int' => [
'maxImageValidityDays' => array('min' => 7, 'max' => 999),
'maxLectureValidityDays' => array('min' => 7, 'max' => 999),
+ 'maxLocationsPerLecture' => array('min' => 0, 'max' => 999),
'maxTransfers' => array('min' => 1, 'max' => 10),
],
'bool' => [
diff --git a/modules-available/dozmod/style.css b/modules-available/dozmod/style.css
index 07853324..23dd121e 100644
--- a/modules-available/dozmod/style.css
+++ b/modules-available/dozmod/style.css
@@ -30,6 +30,7 @@
.table-input-group tr.input-group input.form-control {
width: auto;
+ min-width: 95px;
}
.table-input-group tr.input-group td:last-child input {
diff --git a/modules-available/dozmod/templates/runtimeconfig.html b/modules-available/dozmod/templates/runtimeconfig.html
index 8d27a14e..cff61441 100644
--- a/modules-available/dozmod/templates/runtimeconfig.html
+++ b/modules-available/dozmod/templates/runtimeconfig.html
@@ -41,26 +41,26 @@
<input type="hidden" name="defaultImagePermissions[link]" value="0"/>
<div class="checkbox">
- <input type="checkbox" name="defaultImagePermissions[admin]" value="1" {{defaultImagePermissions.admin}} id ="image_admin" class="form-control">
- <label class="" for="image_admin">
+ <input type="checkbox" name="defaultImagePermissions[admin]" value="1" {{defaultImagePermissions.admin}} id="image_admin" class="form-control">
+ <label for="image_admin">
{{lang_defaultImagePermissionAdmin}}
</label>
</div>
<div class="checkbox">
- <input type="checkbox" name="defaultImagePermissions[edit]" value="1" {{defaultImagePermissions.edit}} id ="image_edit" class="form-control">
- <label>
+ <input type="checkbox" name="defaultImagePermissions[edit]" value="1" {{defaultImagePermissions.edit}} id="image_edit" class="form-control">
+ <label for="image_edit">
{{lang_defaultImagePermissionEdit}}
</label>
</div>
<div class="checkbox">
- <input type="checkbox" name="defaultImagePermissions[download]" value="1" {{defaultImagePermissions.download}} id ="image_download" class="form-control">
- <label>
+ <input type="checkbox" name="defaultImagePermissions[download]" value="1" {{defaultImagePermissions.download}} id="image_download" class="form-control">
+ <label for="image_download">
{{lang_defaultImagePermissionDownload}}
</label>
</div>
<div class="checkbox">
- <input type="checkbox" name="defaultImagePermissions[link]" value="1" {{defaultImagePermissions.link}} id ="image_link" class="form-control">
- <label>
+ <input type="checkbox" name="defaultImagePermissions[link]" value="1" {{defaultImagePermissions.link}} id="image_link" class="form-control">
+ <label for="image_link">
{{lang_defaultImagePermissionLink}}
</label>
</div>
@@ -71,21 +71,27 @@
<p><i>{{lang_descriptionRuntimeLimits}}</i></p>
<table class="table-input-group">
<tr class="input-group">
- <td class="input-group-addon" for="max_image_validity">{{lang_maxImageValidity}}</td>
+ <td class="input-group-addon">{{lang_maxImageValidity}}</td>
+ <td>
+ <input name="maxImageValidityDays" class="form-control" type="number" value="{{maxImageValidityDays}}" min="7" max="999" pattern="^\d+$">
+ </td>
+ </tr>
+ <tr class="input-group">
+ <td class="input-group-addon">{{lang_maxLectureVisibility}}</td>
<td>
- <input name="maxImageValidityDays" id="max_image_validity" class="form-control" type="number" value="{{maxImageValidityDays}}" min="7" max="999" pattern="^\d+$">
+ <input name="maxLectureValidityDays" class="form-control" type="number" value="{{maxLectureValidityDays}}" min="1" max="999" pattern="^\d+$">
</td>
</tr>
<tr class="input-group">
- <td class="input-group-addon" for="max_lecture_validity">{{lang_maxLectureVisibility}}</td>
+ <td class="input-group-addon">{{lang_maxLocationsPerLecture}}</td>
<td>
- <input name="maxLectureValidityDays" id="max_lecture_validity" class="form-control" type="number" value="{{maxLectureValidityDays}}" min="1" max="999" pattern="^\d+$">
+ <input name="maxLocationsPerLecture" class="form-control" type="number" value="{{maxLocationsPerLecture}}" min="0" max="999" pattern="^\d+$">
</td>
</tr>
<tr class="input-group">
- <td class="input-group-addon" for="max_transfers">{{lang_maxTransfers}}</td>
+ <td class="input-group-addon">{{lang_maxTransfers}}</td>
<td>
- <input name="maxTransfers" id="max_transfers" class="form-control" type="number" value="{{maxTransfers}}" min="1" max="10" pattern="^\d+$">
+ <input name="maxTransfers" class="form-control" type="number" value="{{maxTransfers}}" min="1" max="10" pattern="^\d+$">
</td>
</tr>
</table>
@@ -95,8 +101,8 @@
<legend>{{lang_miscOptions}}</legend>
<div class="checkbox">
<input type="hidden" name="allowLoginByDefault" value="0">
- <input type="checkbox" name="allowLoginByDefault" value="1" {{allowLoginByDefault}} id ="allowLoginByDefault" class="form-control">
- <label>
+ <input type="checkbox" name="allowLoginByDefault" value="1" {{allowLoginByDefault}} id="allowLoginByDefault" class="form-control">
+ <label for="allowLoginByDefault">
{{lang_allowLoginByDefault}}
</label>
<p><i>{{lang_allowLoginDescription}}</i></p>
diff --git a/modules-available/exams/baseconfig/getconfig.inc.php b/modules-available/exams/baseconfig/getconfig.inc.php
index 325badb0..37a2caf4 100644
--- a/modules-available/exams/baseconfig/getconfig.inc.php
+++ b/modules-available/exams/baseconfig/getconfig.inc.php
@@ -7,11 +7,13 @@ if ($locations === false) {
$locationIds = explode(' ', $locations);
}
if (Exams::isInExamMode($locationIds, $lectureId, $autoLogin)) {
- ConfigHolder::add('SLX_EXAM', 'yes', 100000);
+ ConfigHolder::add('SLX_EXAM', 'yes', 10000);
if (strlen($lectureId) > 0) {
- ConfigHolder::add('SLX_EXAM_START', $lectureId, 100000);
+ ConfigHolder::add('SLX_EXAM_START', $lectureId, 10000);
}
if (strlen($autoLogin) > 0) {
- ConfigHolder::add('SLX_AUTOLOGIN', $autoLogin, 100000);
+ ConfigHolder::add('SLX_AUTOLOGIN', $autoLogin, 10000);
}
+ ConfigHolder::add('SLX_SYSTEMD_TARGET', 'exam-mode', 10000);
+ ConfigHolder::add("SLX_PVS_HYBRID", false, 10000);
}
diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php b/modules-available/locationinfo/inc/coursebackend/exchange.todo
index 538f7382..538f7382 100755
--- a/modules-available/locationinfo/inc/coursebackend/coursebackend_exchange.inc.php
+++ b/modules-available/locationinfo/inc/coursebackend/exchange.todo
diff --git a/modules-available/locationinfo/inc/locationinfo.inc.php b/modules-available/locationinfo/inc/locationinfo.inc.php
index a5feb9ed..830fb050 100644
--- a/modules-available/locationinfo/inc/locationinfo.inc.php
+++ b/modules-available/locationinfo/inc/locationinfo.inc.php
@@ -104,6 +104,7 @@ class LocationInfo
ConfigHolder::add('SLX_ADDONS', '', 1000);
ConfigHolder::add('SLX_LOGOUT_TIMEOUT', '', 1000);
ConfigHolder::add('SLX_SCREEN_STANDBY_TIMEOUT', '', 1000);
+ ConfigHolder::add('SLX_AUTOLOGIN', '1', 1000);
}
}
diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php
index f8aa1c5b..30c38362 100644
--- a/modules-available/locationinfo/page.inc.php
+++ b/modules-available/locationinfo/page.inc.php
@@ -331,7 +331,7 @@ class Page_LocationInfo extends Page
$backend = CourseBackend::getInstance($servertype);
if ($backend === false) {
- Messages::addError('invalid-backend-type', $servertype);
+ Message::addError('invalid-backend-type', $servertype);
Util::redirect('?do=locationinfo');
}
@@ -582,7 +582,7 @@ class Page_LocationInfo extends Page
/**
* Ajax the time table
*
- * @param $id id of the location
+ * @param int $id id of the location
*/
private function ajaxConfigLocation($id)
{
diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php
index c2bf14ef..1581f744 100644
--- a/modules-available/locations/inc/location.inc.php
+++ b/modules-available/locations/inc/location.inc.php
@@ -66,7 +66,7 @@ class Location
'locationid' => (int)$node['locationid'],
'parentlocationid' => (int)$node['parentlocationid'],
'parents' => $parents,
- 'children' => empty($node['children']) ? array() : array_map(function ($item) { return $item['locationid']; }, $node['children']),
+ 'children' => empty($node['children']) ? array() : array_map(function ($item) { return (int)$item['locationid']; }, $node['children']),
'locationname' => $node['locationname'],
'depth' => $depth,
'isleaf' => true,
@@ -255,16 +255,25 @@ class Location
* Ignores any manually assigned locationid (fixedlocationid).
*
* @param string $ip IP address of client
+ * @param bool $honorRoomPlanner consider a fixed location assigned manually by roomplanner
* @return bool|int locationid, or false if no match
*/
- public static function getFromIp($ip)
+ public static function getFromIp($ip, $honorRoomPlanner = false)
{
if (Module::get('statistics') !== false) {
// Shortcut - try to use subnetlocationid in machine table
- $ret = Database::queryFirst("SELECT subnetlocationid FROM machine WHERE clientip = :ip", compact('ip'));
+ if ($honorRoomPlanner) {
+ $ret = Database::queryFirst("SELECT locationid AS loc FROM machine
+ WHERE clientip = :ip
+ ORDER BY lastseen DESC LIMIT 1", compact('ip'));
+ } else {
+ $ret = Database::queryFirst("SELECT subnetlocationid AS loc FROM machine
+ WHERE clientip = :ip
+ ORDER BY lastseen DESC LIMIT 1", compact('ip'));
+ }
if ($ret !== false) {
- if ($ret['subnetlocationid'] > 0) {
- return (int)$ret['subnetlocationid'];
+ if ($ret['loc'] > 0) {
+ return (int)$ret['loc'];
}
return false;
}
@@ -285,29 +294,28 @@ class Location
{
$locationId = false;
$ipLoc = self::getFromIp($ip);
- if ($ipLoc !== false && $uuid !== false) {
- // Machine ip maps to a location, and we have a client supplied uuid
- $uuidLoc = self::getFromMachineUuid($uuid);
- if ($uuidLoc === $ipLoc) {
- $locationId = $uuidLoc;
- } else if ($uuidLoc !== false) {
- // Validate that the location the IP maps to is in the chain we get using the
- // location determined by the uuid
- $uuidLocations = self::getLocationRootChain($uuidLoc);
- $ipLocations = self::getLocationRootChain($ipLoc);
- if (in_array($uuidLoc, $ipLocations) // UUID loc is further up, OK
- || (in_array($ipLoc, $uuidLocations) && count($ipLocations) + 1 >= count($uuidLocations)) // UUID is max one level deeper than IP loc, accept as well
- ) {
- // Close enough, allow
+ if ($ipLoc !== false) {
+ // Set locationId to ipLoc for now, it will be overwritten later if another case applies.
+ $locationId = $ipLoc;
+ if ($uuid !== false) {
+ // Machine ip maps to a location, and we have a client supplied uuid (which might not be known if the client boots for the first time)
+ $uuidLoc = self::getFromMachineUuid($uuid);
+ if ($uuidLoc === $ipLoc) {
$locationId = $uuidLoc;
- } else {
+ } else if ($uuidLoc !== false) {
+ // Validate that the location the IP maps to is in the chain we get using the
+ // location determined by the uuid
+ $uuidLocations = self::getLocationRootChain($uuidLoc);
+ $ipLocations = self::getLocationRootChain($ipLoc);
+ if (in_array($uuidLoc, $ipLocations) // UUID loc is further up, OK
+ || (in_array($ipLoc, $uuidLocations) && count($ipLocations) + 1 >= count($uuidLocations)) // UUID is max one level deeper than IP loc, accept as well
+ ) {
+ // Close enough, allow
+ $locationId = $uuidLoc;
+ }
// UUID and IP disagree too much, play safe and ignore the UUID
- $locationId = $ipLoc;
}
}
- } else if ($ipLoc !== false) {
- // No uuid, but ip maps to location; use that
- $locationId = $ipLoc;
}
return $locationId;
}
diff --git a/modules-available/rebootcontrol/api.inc.php b/modules-available/rebootcontrol/api.inc.php
index dad25375..6ebc8399 100644
--- a/modules-available/rebootcontrol/api.inc.php
+++ b/modules-available/rebootcontrol/api.inc.php
@@ -2,8 +2,10 @@
if (Request::any('action') === 'rebuild' && isLocalExecution()) {
if (Module::isAvailable('sysconfig')) {
- SSHKey::getPublicKey();
- ConfigTgz::rebuildAllConfigs();
+ SSHKey::getPrivateKey($regen);
+ if (!$regen) {
+ ConfigTgz::rebuildAllConfigs();
+ }
echo "OK";
}
exit(0);
diff --git a/modules-available/rebootcontrol/hooks/config-tgz.inc.php b/modules-available/rebootcontrol/hooks/config-tgz.inc.php
index 0b706960..90e32e8a 100644
--- a/modules-available/rebootcontrol/hooks/config-tgz.inc.php
+++ b/modules-available/rebootcontrol/hooks/config-tgz.inc.php
@@ -1,14 +1,15 @@
<?php
$pubkey = SSHKey::getPublicKey();
-$tmpfile = '/tmp/bwlp-' . md5($pubkey) . '.tar';
+$tmpfile = '/tmp/bwlp-' . md5($pubkey) . '-2.tar';
if (!is_file($tmpfile) || !is_readable($tmpfile) || filemtime($tmpfile) + 86400 < time()) {
if (file_exists($tmpfile)) {
unlink($tmpfile);
}
try {
$a = new PharData($tmpfile);
- $a->addFromString("/root/.ssh/authorized_keys.d/rebootcontrol", $pubkey);
+ $a["/etc/ssh/mgmt/authorized_keys"] = $pubkey;
+ $a["/etc/ssh/mgmt/authorized_keys"]->chmod(0600);
$file = $tmpfile;
} catch (Exception $e) {
EventLog::failure('Could not include ssh key for reboot-control in config.tgz', (string)$e);
diff --git a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
new file mode 100644
index 00000000..789552cd
--- /dev/null
+++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
@@ -0,0 +1,30 @@
+<?php
+
+class RebootControl
+{
+
+ /**
+ * @param string[] $uuids List of machineuuids to reboot
+ * @return false|array task struct for the reboot job
+ */
+ public static function reboot($uuids)
+ {
+ $list = RebootQueries::getMachinesByUuid($uuids);
+ if (empty($list))
+ return false;
+ return self::execute($list, false, 0, 0);
+ }
+
+ public static function execute($list, $shutdown, $minutes, $locationId)
+ {
+ return Taskmanager::submit("RemoteReboot", array(
+ "clients" => $list,
+ "shutdown" => $shutdown,
+ "minutes" => $minutes,
+ "locationId" => $locationId,
+ "sshkey" => SSHKey::getPrivateKey(),
+ "port" => 9922, // Hard-coded, must match mgmt-sshd module
+ ));
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/inc/sshkey.inc.php b/modules-available/rebootcontrol/inc/sshkey.inc.php
index b4e36d25..cce9b3dc 100644
--- a/modules-available/rebootcontrol/inc/sshkey.inc.php
+++ b/modules-available/rebootcontrol/inc/sshkey.inc.php
@@ -3,7 +3,7 @@
class SSHKey
{
- public static function getPrivateKey() {
+ public static function getPrivateKey(&$regen = false) {
$privKey = Property::get("rebootcontrol-private-key");
if (!$privKey) {
$rsaKey = openssl_pkey_new(array(
@@ -11,6 +11,10 @@ class SSHKey
'private_key_type' => OPENSSL_KEYTYPE_RSA));
openssl_pkey_export( openssl_pkey_get_private($rsaKey), $privKey);
Property::set("rebootcontrol-private-key", $privKey);
+ if (Module::isAvailable('sysconfig')) {
+ ConfigTgz::rebuildAllConfigs();
+ }
+ $regen = true;
}
return $privKey;
}
diff --git a/modules-available/rebootcontrol/page.inc.php b/modules-available/rebootcontrol/page.inc.php
index d7083528..db7882d9 100644
--- a/modules-available/rebootcontrol/page.inc.php
+++ b/modules-available/rebootcontrol/page.inc.php
@@ -45,14 +45,7 @@ class Page_RebootControl extends Page
// TODO: we could also check if the locationid is equal or a sublocation of the $locationId from above
// (this would be more of a sanity check though, or does the UI allow selecting machines from different locations)
- $task = Taskmanager::submit("RemoteReboot", array(
- "clients" => $list,
- "shutdown" => $shutdown,
- "minutes" => $minutes,
- "locationId" => $locationId,
- "sshkey" => $privKey,
- "port" => 22, // TODO: Get from ssh config
- ));
+ $task = RebootControl::execute($list, $shutdown, $minutes, $locationId);
Util::redirect("?do=rebootcontrol&taskid=".$task["id"]);
}
diff --git a/modules-available/roomplanner/baseconfig/getconfig.inc.php b/modules-available/roomplanner/baseconfig/getconfig.inc.php
index 92e7a8d3..aa8721ef 100644
--- a/modules-available/roomplanner/baseconfig/getconfig.inc.php
+++ b/modules-available/roomplanner/baseconfig/getconfig.inc.php
@@ -5,7 +5,10 @@ ConfigHolder::add("SLX_PVS_CONFIG_URL", 'http://' . $_SERVER['SERVER_ADDR'] . $_
$res = Database::queryFirst('SELECT dedicatedmgr FROM location_roomplan WHERE managerip = :ip LIMIT 1', ['ip' => $ip]);
if ($res !== false) {
if ((int)$res['dedicatedmgr'] !== 0) {
+ // TODO: Runmode
ConfigHolder::add("SLX_PVS_DEDICATED", 'yes');
+ ConfigHolder::add("SLX_EXAM", false, 100000);
+ ConfigHolder::add("SLX_SYSTEMD_TARGET", false, 100000);
} else {
ConfigHolder::add("SLX_PVS_HYBRID", 'yes');
}
diff --git a/modules-available/runmode/baseconfig/getconfig.inc.php b/modules-available/runmode/baseconfig/getconfig.inc.php
index fe04b5ef..2d622fc7 100644
--- a/modules-available/runmode/baseconfig/getconfig.inc.php
+++ b/modules-available/runmode/baseconfig/getconfig.inc.php
@@ -14,9 +14,6 @@ $foofoo = function($machineUuid) {
if ($config->systemdDefaultTarget !== false) {
ConfigHolder::add('SLX_SYSTEMD_TARGET', $config->systemdDefaultTarget, 10000);
}
- if ($config->noSysconfig) {
- ConfigHolder::add('SLX_NO_CONFIG_TGZ', '1', 10000);
- }
// Disable exam mode - not sure if this is generally a good idea; for now, all modes we can think of would
// not make sense that way so do this for now
if (ConfigHolder::get('SLX_EXAM') !== false) {
diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php
index 0f4994f4..271542b8 100644
--- a/modules-available/runmode/inc/runmode.inc.php
+++ b/modules-available/runmode/inc/runmode.inc.php
@@ -63,8 +63,30 @@ class RunMode
}
/**
+ * @param string $machineuuid
+ * @param bool $detailed whether to return meta data about machine, not just machineuuid
+ * @param bool $assoc use machineuuid as array key
+ * @return false|array {'machineuuid', 'isclient', 'module', 'modeid', 'modedata',
+ * <'hostname', 'clientip', 'macaddr', 'locationid', 'lastseen'>}
+ */
+ public static function getRunMode($machineuuid, $detailed = false)
+ {
+ if ($detailed) {
+ $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid, m.lastseen';
+ } else {
+ $sel = '';
+ }
+ return Database::queryFirst(
+ "SELECT m.machineuuid, r.isclient, r.module, r.modeid, r.modedata $sel
+ FROM machine m INNER JOIN runmode r USING (machineuuid)
+ WHERE m.machineuuid = :machineuuid LIMIT 1",
+ compact('machineuuid'));
+ }
+
+ /**
* @param string|\Module $module
- * @return array
+ * @param bool true = wrap in array where key is modeid
+ * @return array key=machineuuid, value={'machineuuid', 'modeid', 'modedata'}
*/
public static function getForModule($module, $groupByModeId = false)
{
@@ -91,15 +113,17 @@ class RunMode
* @param string|\Module $module
* @param string $modeId
* @param bool $detailed whether to return meta data about machine, not just machineuuid
- * @return array
+ * @param bool $assoc use machineuuid as array key
+ * @return array <key=machineuuid>, value={'machineuuid', 'modedata',
+ * <'hostname', 'clientip', 'macaddr', 'locationid', 'lastseen'>}
*/
- public static function getForMode($module, $modeId, $detailed = false)
+ public static function getForMode($module, $modeId, $detailed = false, $assoc = false)
{
if (is_object($module)) {
$module = $module->getIdentifier();
}
if ($detailed) {
- $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid';
+ $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid, m.lastseen';
$join = 'INNER JOIN machine m USING (machineuuid)';
} else {
$join = $sel = '';
@@ -114,7 +138,11 @@ class RunMode
if ($detailed && empty($row['hostname'])) {
$row['hostname'] = $row['clientip'];
}
- $ret[] = $row;
+ if ($assoc) {
+ $ret[$row['machineuuid']] = $row;
+ } else {
+ $ret[] = $row;
+ }
}
return $ret;
}
diff --git a/modules-available/runmode/install.inc.php b/modules-available/runmode/install.inc.php
index e2b1ed0f..ec710bfa 100644
--- a/modules-available/runmode/install.inc.php
+++ b/modules-available/runmode/install.inc.php
@@ -33,7 +33,7 @@ if (!tableHasColumn('runmode', 'isclient')) {
if ($ret === false) {
finalResponse(UPDATE_FAILED, 'Could not add lastchange field');
} elseif ($ret > 0) {
- $ret[] = UPDATE_DONE;
+ $res[] = UPDATE_DONE;
}
}
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index 126c6e91..c395220a 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -16,7 +16,7 @@ if ($type{0} === '~') {
$uuid = Request::post('uuid', '', 'string');
if (strlen($uuid) !== 36) die("Invalid UUID.\n");
$macaddr = Request::post('macaddr', '', 'string');
- if (!empty($macaddr) && substr($uuid, 0, 16) === '000000000000000-') {
+ if (!empty($macaddr) && substr($uuid, 0, 16) === '000000000000001-') {
// Override uuid if the mac is known and unique
$res = Database::simpleQuery('SELECT machineuuid FROM machine WHERE macaddr = :macaddr AND machineuuid <> :uuid', compact('macaddr', 'uuid'));
$override = false;
@@ -31,14 +31,17 @@ if ($type{0} === '~') {
$uuid = $override;
}
}
+ // External mode of operation?
+ $mode = Request::post('mode', false, 'string');
$NOW = time();
- $old = Database::queryFirst('SELECT clientip, logintime, lastseen FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ $old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
if ($old !== false) {
settype($old['logintime'], 'integer');
settype($old['lastseen'], 'integer');
+ settype($old['lastboot'], 'integer');
}
// Handle event type
- if ($type === '~poweron') {
+ if ($mode === false && $type === '~poweron') {
// Poweron & hw stats
$uptime = Request::post('uptime', '', 'integer');
if (strlen($macaddr) > 17) die("Invalid MAC.\n");
@@ -138,42 +141,54 @@ if ($type{0} === '~') {
} else if ($type === '~runstate') {
// Usage (occupied/free)
$sessionLength = 0;
- $used = Request::post('used', 0, 'integer');
if ($old === false) die("Unknown machine.\n");
if ($old['clientip'] !== $ip) {
EventLog::warning("[runstate] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
die("Address changed.\n");
}
- // Figure out what's happening
- if ($used === 0) {
- // Is not in use
+ $used = Request::post('used', 0, 'integer');
+ if ($old['lastboot'] === 0 && $NOW - $old['lastseen'] > 300) {
+ $strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), ';
+ } else {
+ $strUpdateBoottime = '';
+ }
+ // 1) Log last session length if we didn't see the machine for a while
+ if ($NOW - $old['lastseen'] > 610 && $old['lastseen'] !== 0) {
+ // Old session timed out - might be caused by hard reboot
if ($old['logintime'] !== 0) {
- // Was in use, is free now
- // 1) Log last session length
- if ($NOW - $old['lastseen'] > 610) {
- // Old session timed out - might be caused by hard reboot
+ if ($old['lastseen'] > $old['logintime']) {
$sessionLength = $old['lastseen'] - $old['logintime'];
- } else {
- $sessionLength = $NOW - $old['logintime'];
}
+ $old['logintime'] = 0;
}
- Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(), logintime = 0 WHERE machineuuid = :uuid', array('uuid' => $uuid));
- } else {
- // Machine is in use
- if ($old['logintime'] !== 0 && $NOW - $old['lastseen'] > 610) {
- // Old session timed out - might be caused by hard reboot
- $sessionLength = $old['lastseen'] - $old['logintime'];
- }
+ $old['lastboot'] = 0;
+ }
+ // Figure out what's happening - state changes
+ if ($used === 0 && $old['logintime'] !== 0) {
+ // Is not in use, was in use before
+ $sessionLength = $NOW - $old['logintime'];
+ Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
+ . $strUpdateBoottime
+ . ' logintime = 0, currentuser = NULL WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ } elseif ($used === 1 && $old['logintime'] === 0) {
+ // Machine is in use, was free before
if ($sessionLength !== 0 || $old['logintime'] === 0) {
// This event is a start of a new session, rather than an update
- Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(), logintime = UNIX_TIMESTAMP() WHERE machineuuid = :uuid', array('uuid' => $uuid));
- } else {
- // Nothing changed, simple lastseen update
- Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'
+ . $strUpdateBoottime
+ . ' logintime = UNIX_TIMESTAMP(), currentuser = :user WHERE machineuuid = :uuid', array(
+ 'uuid' => $uuid,
+ 'user' => Request::post('user', null, 'string'),
+ ));
}
+ } else {
+ // Nothing changed, simple lastseen update
+ Database::exec('UPDATE machine SET '
+ . $strUpdateBoottime
+ . ' lastseen = UNIX_TIMESTAMP() WHERE machineuuid = :uuid', array('uuid' => $uuid));
}
// 9) Log last session length if applicable
- if ($sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) {
+ if ($mode === false && $sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) {
Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
. " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array(
'start' => $old['logintime'],
@@ -188,7 +203,7 @@ if ($type{0} === '~') {
EventLog::warning("[poweroff] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
die("Address changed.\n");
}
- if ($old['logintime'] !== 0) {
+ if ($mode === false && $old['logintime'] !== 0) {
$sessionLength = $old['lastseen'] - $old['logintime'];
if ($sessionLength > 0 && $sessionLength < 86400*2) {
Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
@@ -201,7 +216,7 @@ if ($type{0} === '~') {
}
}
Database::exec('UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), lastboot = 0 WHERE machineuuid = :uuid', array('uuid' => $uuid));
- } elseif ($type === '~screens') {
+ } elseif ($mode === false && $type === '~screens') {
$screens = Request::post('screen', false, 'array');
if (is_array($screens)) {
// `devicetype`, `devicename`, `subid`, `machineuuid`
@@ -273,7 +288,7 @@ if ($type{0} === '~') {
}
}
} else {
- die("INVALID ACTION '$type'");
+ die("INVALID ACTION '$type'\n");
}
die("OK. (RESULT=0)\n");
}
diff --git a/modules-available/statistics/hooks/cron.inc.php b/modules-available/statistics/hooks/cron.inc.php
index 94c65248..575ab6ba 100644
--- a/modules-available/statistics/hooks/cron.inc.php
+++ b/modules-available/statistics/hooks/cron.inc.php
@@ -1,8 +1,5 @@
<?php
-Database::exec("DELETE FROM statistic WHERE (UNIX_TIMESTAMP() - dateline) > 86400 * 190");
-Database::exec("DELETE FROM machine WHERE (UNIX_TIMESTAMP() - lastseen) > 86400 * 365");
-
function logstats() {
$NOW = time();
$cutoff = $NOW - 86400 * 30;
@@ -15,4 +12,18 @@ function logstats() {
'vals' => $known['val'] . '#' . $on['val'] . '#' . $used['val'],
));
}
+
logstats();
+
+if (mt_rand(1, 10) === 1) {
+ Database::exec("DELETE FROM statistic WHERE (UNIX_TIMESTAMP() - 86400 * 190) > dateline");
+ if (mt_rand(1, 100) === 1) {
+ Database::exec("OPTIMIZE TABLE statistic");
+ }
+}
+if (mt_rand(1, 10) === 1) {
+ Database::exec("DELETE FROM machine WHERE (UNIX_TIMESTAMP() - 86400 * 365) > lastseen");
+ if (mt_rand(1, 100) === 1) {
+ Database::exec("OPTIMIZE TABLE machine");
+ }
+}
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index ab22ee86..474d952a 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -70,6 +70,7 @@
"lang_showList": "Liste",
"lang_showVisualization": "Visualisierung",
"lang_sockets": "Sockel",
+ "lang_subnet": "Subnetz",
"lang_tempPart": "Temp. Partition",
"lang_tempPartStats": "Tempor\u00e4re Partition",
"lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.",
@@ -82,4 +83,4 @@
"lang_virtualCores": "Virtuelle Kerne",
"lang_when": "Wann",
"lang_withBadSectors": "Clients mit potentiell defekten Festplatten (mehr als 10 defekte Sektoren)"
-} \ No newline at end of file
+}
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index 6597d953..f514b894 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -70,6 +70,7 @@
"lang_showList": "List",
"lang_showVisualization": "Visualization",
"lang_sockets": "Sockets",
+ "lang_subnet": "Subnet",
"lang_tempPart": "Temp. partition",
"lang_tempPartStats": "Temporary partition",
"lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.",
@@ -82,4 +83,4 @@
"lang_virtualCores": "Virtual cores",
"lang_when": "When",
"lang_withBadSectors": "Clients with potentially bad HDDs (more than 10 reallocated sectors)"
-} \ No newline at end of file
+}
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index dd3e5ee6..a003a303 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -308,6 +308,14 @@ class Page_Statistics extends Page
}
}
+ private function redirectFirst($where, $join, $args)
+ {
+ $res = Database::queryFirst("SELECT machineuuid FROM machine $join WHERE ($where) LIMIT 1", $args);
+ if ($res !== false) {
+ Util::redirect('?do=statistics&uuid=' . $res['machineuuid']);
+ }
+ }
+
/**
* @param \FilterSet $filterSet
*/
@@ -316,6 +324,10 @@ class Page_Statistics extends Page
$filterSet->makeFragments($where, $join, $sort, $args);
$known = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE ($where)", $args);
+ // If we only have one machine, redirect to machine details
+ if ($known['val'] == 1) {
+ $this->redirectFirst($where, $join, $args);
+ }
$on = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastboot <> 0 AND ($where)", $args);
$used = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastboot <> 0 AND logintime <> 0 AND ($where)", $args);
$hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE badsectors >= 10 AND ($where)", $args);
@@ -566,7 +578,13 @@ class Page_Statistics extends Page
. " $join WHERE $where $sort", $args);
$rows = array();
$NOW = time();
+ $singleMachine = 'none';
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($singleMachine === 'none') {
+ $singleMachine = $row['machineuuid'];
+ } else {
+ $singleMachine = false;
+ }
if ($row['lastboot'] == 0) {
$row['state_off'] = true;
} elseif ($row['logintime'] == 0) {
@@ -598,6 +616,9 @@ class Page_Statistics extends Page
}
$rows[] = $row;
}
+ if ($singleMachine !== false && $singleMachine !== 'none') {
+ Util::redirect('?do=statistics&uuid=' . $singleMachine);
+ }
Render::addTemplate('clientlist', array(
'rowCount' => count($rows),
'rows' => $rows,
diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html
index 4bbc1d82..c2630ed9 100644
--- a/modules-available/statistics/templates/filterbox.html
+++ b/modules-available/statistics/templates/filterbox.html
@@ -98,7 +98,8 @@ var slxFilterNames = {
clientip: '{{lang_ip}}',
state: '{{lang_usageState}}',
location: '{{lang_location}}',
- currentuser: '{{lang_currentUser}}'
+ currentuser: '{{lang_currentUser}}',
+ subnet: '{{lang_subnet}}'
};
slxLocations = {{{locations}}};
diff --git a/modules-available/sysconfig/addconfig.inc.php b/modules-available/sysconfig/addconfig.inc.php
index 34a46d52..f66bc508 100644
--- a/modules-available/sysconfig/addconfig.inc.php
+++ b/modules-available/sysconfig/addconfig.inc.php
@@ -21,7 +21,7 @@ abstract class AddConfig_Base
/**
*
- * @param type $step
+ * @param string $step
* @return \AddConfig_Base
*/
public static function setStep($step)
@@ -161,10 +161,11 @@ class AddConfig_Start extends AddConfig_Base
} else {
$title = '';
}
+ $dummy = 0;
foreach ($modGroups as &$mod) {
if (!empty($mod['modules']) && $mod['unique']) {
array_unshift($mod['modules'], array(
- 'moduleid' => 0,
+ 'moduleid' => 'x' . (++$dummy),
'title' => Dictionary::translate('lang_noModuleFromThisGroup'),
));
}
diff --git a/modules-available/sysconfig/api.inc.php b/modules-available/sysconfig/api.inc.php
index d6cdc0e6..897b44a7 100644
--- a/modules-available/sysconfig/api.inc.php
+++ b/modules-available/sysconfig/api.inc.php
@@ -9,18 +9,6 @@ $uuid = Request::any('uuid', false, 'string');
if ($uuid !== false && strlen($uuid) !== 36) {
$uuid = false;
}
-$locationId = false;
-if (Module::isAvailable('locations')) {
- $locationId = Location::getFromIpAndUuid($ip, $uuid);
- if ($locationId !== false) {
- $locationChain = Location::getLocationRootChain($locationId);
- $locationChain[] = 0;
- }
-}
-if ($locationId === false) {
- $locationId = 0;
- $locationChain = array(0);
-}
// What we do if we can't supply the requested config
function deliverEmpty($message)
@@ -30,34 +18,59 @@ function deliverEmpty($message)
die('Config file could not be found or read!');
}
-// Get config module path
+$runmode = false;
+if (Module::isAvailable('runmode')) {
+ $runmode = RunMode::getRunMode($uuid);
+ if ($runmode !== false) {
+ $runmode = RunMode::getModuleConfig($runmode['module']);
+ }
+}
+if ($runmode !== false && $runmode->noSysconfig && file_exists(SysConfig::GLOBAL_MINIMAL_CONFIG)) {
+ $row = array('filepath' => SysConfig::GLOBAL_MINIMAL_CONFIG);
+} else {
+ $locationId = false;
+ if (Module::isAvailable('locations')) {
+ $locationId = Location::getFromIpAndUuid($ip, $uuid);
+ if ($locationId !== false) {
+ $locationChain = Location::getLocationRootChain($locationId);
+ $locationChain[] = 0;
+ }
+ }
+ if ($locationId === false) {
+ $locationId = 0;
+ $locationChain = array(0);
+ }
+
+ // Get config module path
-// We get all the configs for the whole location chain up to root
-$res = Database::simpleQuery("SELECT c.title, c.filepath, c.status, cl.locationid FROM configtgz c"
- . " INNER JOIN configtgz_location cl USING (configid)"
- . " WHERE cl.locationid IN (" . implode(',', $locationChain) . ")");
-$best = 1000;
-$row = false;
-while ($r = $res->fetch(PDO::FETCH_ASSOC)) {
- settype($r['locationid'], 'int');
- $index = array_search($r['locationid'], $locationChain);
- if ($index === false || $index > $best)
- continue;
- if (!file_exists($r['filepath'])) {
- if ($r['locationid'] === 0) {
- EventLog::failure("The global config.tgz '{$r['title']}' was not found at '{$r['filepath']}'. Please regenerate the system configuration");
- } else {
- EventLog::warning("config.tgz '{$r['title']}' for location $locationId not found at '{$r['filepath']}', trying fallback....");
+ // We get all the configs for the whole location chain up to root
+ $res = Database::simpleQuery("SELECT c.title, c.filepath, c.status, cl.locationid FROM configtgz c"
+ . " INNER JOIN configtgz_location cl USING (configid)"
+ . " WHERE cl.locationid IN (" . implode(',', $locationChain) . ")");
+
+ $best = 1000;
+ $row = false;
+ while ($r = $res->fetch(PDO::FETCH_ASSOC)) {
+ settype($r['locationid'], 'int');
+ $index = array_search($r['locationid'], $locationChain);
+ if ($index === false || $index > $best)
+ continue;
+ if (!file_exists($r['filepath'])) {
+ if ($r['locationid'] === 0) {
+ EventLog::failure("The global config.tgz '{$r['title']}' was not found at '{$r['filepath']}'. Please regenerate the system configuration");
+ } else {
+ EventLog::warning("config.tgz '{$r['title']}' for location $locationId not found at '{$r['filepath']}', trying fallback....");
+ }
+ continue;
}
- continue;
+ $best = $index;
+ $row = $r;
}
- $best = $index;
- $row = $r;
-}
-if ($row === false) {
- // TODO Not found in DB
- deliverEmpty("No config.tgz for location $locationId found (src $ip)");
+ if ($row === false) {
+ // TODO Not found in DB
+ deliverEmpty("No config.tgz for location $locationId found (src $ip)");
+ }
}
Header('Content-Type: application/gzip');
diff --git a/modules-available/sysconfig/inc/configtgz.inc.php b/modules-available/sysconfig/inc/configtgz.inc.php
index c03d1c5e..dbf1bc99 100644
--- a/modules-available/sysconfig/inc/configtgz.inc.php
+++ b/modules-available/sysconfig/inc/configtgz.inc.php
@@ -12,7 +12,7 @@ class ConfigTgz
{
;
}
-
+
public function id()
{
return $this->configId;
@@ -101,22 +101,9 @@ class ConfigTgz
if (!empty($module['filepath']) && file_exists($module['filepath']))
$files[] = $module['filepath'];
}
- // Get stuff other modules want to inject
- $handler = function($hook) {
- include $hook->file;
- return isset($file) ? $file : false;
- };
- foreach (Hook::load('config-tgz') as $hook) {
- $file = $handler($hook);
- if ($file !== false) {
- $files[] = $file;
- }
- }
- // Hand over to tm
- $task = Taskmanager::submit('RecompressArchive', array(
- 'inputFiles' => $files,
- 'outputFile' => $this->file
- ));
+
+ $task = self::recompress($files, $this->file);
+
// Wait for completion
if ($timeoutMs > 0 && !Taskmanager::isFailed($task) && !Taskmanager::isFinished($task))
$task = Taskmanager::waitComplete($task, $timeoutMs);
@@ -199,6 +186,32 @@ class ConfigTgz
*/
/**
+ * @param string[] $files source files to include
+ * @param string $destFile where to store final result
+ * @return false|array taskmanager task
+ */
+ private static function recompress($files, $destFile)
+ {
+ // Get stuff other modules want to inject
+ $handler = function($hook) {
+ include $hook->file;
+ return isset($file) ? $file : false;
+ };
+ foreach (Hook::load('config-tgz') as $hook) {
+ $file = $handler($hook);
+ if ($file !== false) {
+ $files[] = $file;
+ }
+ }
+
+ // Hand over to tm
+ return Taskmanager::submit('RecompressArchive', array(
+ 'inputFiles' => $files,
+ 'outputFile' =>$destFile
+ ));
+ }
+
+ /**
* Marks all modules as outdated and triggers generate()
* on each one. This mostly makes sense to call if a global module
* that is injected via a hook has changed.
@@ -215,6 +228,8 @@ class ConfigTgz
$module->generate();
}
}
+ // Build the global "empty" config that just includes global hooks
+ self::recompress([], SysConfig::GLOBAL_MINIMAL_CONFIG);
}
public static function insert($title, $moduleIds)
diff --git a/modules-available/sysconfig/inc/sysconfig.inc.php b/modules-available/sysconfig/inc/sysconfig.inc.php
index 15bd4104..13549948 100644
--- a/modules-available/sysconfig/inc/sysconfig.inc.php
+++ b/modules-available/sysconfig/inc/sysconfig.inc.php
@@ -3,6 +3,8 @@
class SysConfig
{
+ const GLOBAL_MINIMAL_CONFIG = '/opt/openslx/configs/config-global.tgz';
+
public static function getAll()
{
$res = Database::simpleQuery("SELECT c.configid, c.title, c.filepath, c.status, Group_Concat(cl.locationid) AS locs FROM configtgz c"
diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php
index c169f490..043645df 100644
--- a/modules-available/sysconfig/page.inc.php
+++ b/modules-available/sysconfig/page.inc.php
@@ -197,7 +197,7 @@ class Page_SysConfig extends Page
$res = Database::simpleQuery("SELECT c.configid, c.title, c.filepath, c.status,"
. " GROUP_CONCAT(DISTINCT cl.locationid) AS loclist, GROUP_CONCAT(cxm.moduleid) AS modlist"
. " FROM configtgz c"
- . " INNER JOIN configtgz_x_module cxm USING (configid)"
+ . " LEFT JOIN configtgz_x_module cxm USING (configid)"
. " LEFT JOIN configtgz_location cl ON (c.configid = cl.configid)"
. " GROUP BY configid"
. " ORDER BY title ASC");
diff --git a/modules-available/syslog/api.inc.php b/modules-available/syslog/api.inc.php
index 156bc35c..18c42c31 100644
--- a/modules-available/syslog/api.inc.php
+++ b/modules-available/syslog/api.inc.php
@@ -26,8 +26,10 @@ $longdesc = Request::post('longdesc', '', 'string');
if ($type{0} !== '.' && $type{0} !== '~') {
// Spam from IP
- $row = Database::queryFirst('SELECT Count(*) AS cnt FROM clientlog WHERE clientip = :client AND dateline + 3600 > UNIX_TIMESTAMP()', array(':client' => $ip));
- if ($row !== false && $row['cnt'] > 150) exit(0);
+ $row = Database::queryFirst('SELECT Count(*) AS cnt FROM clientlog WHERE clientip = :client AND dateline + 1800 > UNIX_TIMESTAMP()', array(':client' => $ip));
+ if ($row !== false && $row['cnt'] > 250) {
+ exit(0);
+ }
Database::exec('INSERT INTO clientlog (dateline, logtypeid, clientip, machineuuid, description, extra) VALUES (UNIX_TIMESTAMP(), :type, :client, :uuid, :description, :longdesc)', array(
'type' => $type,
diff --git a/modules-available/syslog/hooks/cron.inc.php b/modules-available/syslog/hooks/cron.inc.php
index c796675f..bae882a9 100644
--- a/modules-available/syslog/hooks/cron.inc.php
+++ b/modules-available/syslog/hooks/cron.inc.php
@@ -1,5 +1,8 @@
<?php
if (mt_rand(1, 10) === 1) {
- Database::exec("DELETE FROM clientlog WHERE (UNIX_TIMESTAMP() - dateline) > 86400 * 190");
+ Database::exec("DELETE FROM clientlog WHERE (UNIX_TIMESTAMP() - 86400 * 190) > dateline");
+ if (mt_rand(1, 100) === 1) {
+ Database::exec("OPTIMIZE TABLE clientlog");
+ }
} \ No newline at end of file
diff --git a/modules-available/syslog/install.inc.php b/modules-available/syslog/install.inc.php
index 539f2449..406a6cc6 100644
--- a/modules-available/syslog/install.inc.php
+++ b/modules-available/syslog/install.inc.php
@@ -2,7 +2,7 @@
$res = array();
-$res[] = tableCreate('clientlog', "
+$res[] = $tc = tableCreate('clientlog', "
`logid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`dateline` int(10) unsigned NOT NULL,
`logtypeid` varchar(30) NOT NULL,
@@ -14,7 +14,7 @@ $res[] = tableCreate('clientlog', "
KEY `dateline` (`dateline`),
KEY `logtypeid` (`logtypeid`,`dateline`),
KEY `clientip` (`clientip`,`dateline`),
- KEY `machineuuid` (`machineuuid`,`dateline`)
+ KEY `machineuuid` (`machineuuid`,`logid`)
");
// Update path
@@ -29,6 +29,12 @@ if (!tableHasColumn('clientlog', 'machineuuid')) {
$res[] = UPDATE_DONE;
}
+// 2017-11-03: Create proper index for query in statistics module
+if ($tc !== UPDATE_DONE) {
+ Database::exec("ALTER TABLE `openslx`.`clientlog` DROP INDEX `machineuuid` ,
+ ADD INDEX `machineuuid` ( `machineuuid` , `logid` )");
+}
+
// Create response for browser
if (in_array(UPDATE_DONE, $res)) {