summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Hofmaier2020-10-13 18:24:41 +0200
committerChristian Hofmaier2020-10-13 18:24:41 +0200
commit6ef48b756c51e33f64f74fc9fdd96c9e6f4ff1c0 (patch)
tree1b25330880065efa168cb5c69773d545df7cfc01
parent[locationinfo] add forward link to location module (diff)
parent[dozmod] Implement deleting orphaned files from vm store (diff)
downloadslx-admin-6ef48b756c51e33f64f74fc9fdd96c9e6f4ff1c0.tar.gz
slx-admin-6ef48b756c51e33f64f74fc9fdd96c9e6f4ff1c0.tar.xz
slx-admin-6ef48b756c51e33f64f74fc9fdd96c9e6f4ff1c0.zip
Merge branch 'master' of git.openslx.org:openslx-ng/slx-admin
merge branch 'master'
-rw-r--r--inc/event.inc.php4
-rw-r--r--inc/module.inc.php7
-rw-r--r--inc/taskmanager.inc.php6
-rw-r--r--inc/trigger.inc.php10
-rw-r--r--modules-available/dozmod/lang/de/messages.json3
-rw-r--r--modules-available/dozmod/lang/de/permissions.json2
-rw-r--r--modules-available/dozmod/lang/de/template-tags.json9
-rw-r--r--modules-available/dozmod/lang/en/messages.json1
-rw-r--r--modules-available/dozmod/lang/en/permissions.json2
-rw-r--r--modules-available/dozmod/lang/en/template-tags.json9
-rw-r--r--modules-available/dozmod/pages/expiredimages.inc.php105
-rw-r--r--modules-available/dozmod/permissions/permissions.json6
-rw-r--r--modules-available/dozmod/templates/images-delete.html172
-rw-r--r--modules-available/dozmod/templates/images-orphaned.html25
-rw-r--r--modules-available/exams/baseconfig/getconfig.inc.php3
-rw-r--r--modules-available/locations/templates/locations.html2
-rw-r--r--modules-available/remoteaccess/baseconfig/getconfig.inc.php40
-rw-r--r--modules-available/runmode/baseconfig/getconfig.inc.php6
-rw-r--r--modules-available/serversetup-bwlp-ipxe/api.inc.php9
-rw-r--r--modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php2
-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/pages/list.inc.php13
-rw-r--r--modules-available/statistics/templates/clientlist.html10
-rw-r--r--script/slx-fixes.js3
25 files changed, 317 insertions, 138 deletions
diff --git a/inc/event.inc.php b/inc/event.inc.php
index e5ade41b..2d916b48 100644
--- a/inc/event.inc.php
+++ b/inc/event.inc.php
@@ -64,7 +64,7 @@ class Event
} else {
$res = Taskmanager::waitComplete($ipxeId, 5000);
if (Taskmanager::isFailed($res)) {
- EventLog::failure('Update PXE Menu failed', $res['data']['error']);
+ EventLog::failure('Update PXE Menu failed', $res['data']['error'] ?? $res['data']['error'] ?? '');
$everythingFine = false;
}
}
@@ -79,7 +79,7 @@ class Event
$mountStatus = Taskmanager::waitComplete($mountId, 10000);
}
if ($mountId !== false && Taskmanager::isFailed($mountStatus)) {
- EventLog::failure('Mounting VM store failed', $mountStatus['data']['messages']);
+ EventLog::failure('Mounting VM store failed', $mountStatus['data']['messages'] ?? '');
$everythingFine = false;
} elseif ($mountId !== false && !Taskmanager::isFinished($mountStatus)) {
// TODO: Still running - create callback
diff --git a/inc/module.inc.php b/inc/module.inc.php
index 5525c0a4..55713cd0 100644
--- a/inc/module.inc.php
+++ b/inc/module.inc.php
@@ -10,7 +10,12 @@ class Module
* @var \Module[]
*/
private static $modules = false;
-
+
+ /**
+ * @param string $name ID/Internal name of module
+ * @param false $ignoreDepFail whether to return the module even if some of its dependencies failed
+ * @return false|Module
+ */
public static function get($name, $ignoreDepFail = false)
{
if (!isset(self::$modules[$name]))
diff --git a/inc/taskmanager.inc.php b/inc/taskmanager.inc.php
index 85d5ee39..f7c72e04 100644
--- a/inc/taskmanager.inc.php
+++ b/inc/taskmanager.inc.php
@@ -140,7 +140,8 @@ class Taskmanager
return false;
$done = false;
$deadline = microtime(true) + $timeout / 1000;
- do {
+ while (($remaining = $deadline - microtime(true)) > 0) {
+ usleep(min(100000, $remaining * 100000));
$status = self::status($task);
if (!isset($status['statusCode']))
break;
@@ -148,8 +149,7 @@ class Taskmanager
$done = true;
break;
}
- usleep(100000);
- } while (microtime(true) < $deadline);
+ }
if ($done) { // For now we do this unconditionally, but maybe we want to keep them longer some time?
self::release($task);
}
diff --git a/inc/trigger.inc.php b/inc/trigger.inc.php
index 134bab53..5024b907 100644
--- a/inc/trigger.inc.php
+++ b/inc/trigger.inc.php
@@ -98,7 +98,7 @@ class Trigger
*
* @param array $vmstore VM Store configuration to use. If false, read from properties
* @param bool $ifLocalOnly Only execute task if the storage type is local (used for DNBD3)
- * @return array|false task status of mount procedure, or false on error
+ * @return string|false task id of mount procedure, or false on error
*/
public static function mount($vmstore = false, $ifLocalOnly = false)
{
@@ -130,7 +130,7 @@ class Trigger
}else {
$opts = null;
}
- return Taskmanager::submit('MountVmStore', array(
+ $status = Taskmanager::submit('MountVmStore', array(
'address' => $addr,
'type' => 'images',
'opts' => $opts,
@@ -138,6 +138,12 @@ class Trigger
'username' => $vmstore['cifsuser'],
'password' => $vmstore['cifspasswd']
));
+ if (!Taskmanager::isFailed($status)) {
+ // In case we have a concurrent active task, this should be enough
+ // for the taskmanager to give us the existing id
+ $status = Taskmanager::waitComplete($status, 100);
+ }
+ return $status['data']['existingTask'] ?? $status['id'] ?? false;
}
/**
diff --git a/modules-available/dozmod/lang/de/messages.json b/modules-available/dozmod/lang/de/messages.json
index a2d6a0ae..7eba0b2d 100644
--- a/modules-available/dozmod/lang/de/messages.json
+++ b/modules-available/dozmod/lang/de/messages.json
@@ -1,6 +1,5 @@
{
"all-templates-reset": "Alle Templates wurden zur\u00fcckgesetzt",
- "delete-images": "L\u00f6schung: {{0}}",
"dozmod-error": "Fehler bei der Kommunikation mit dem bwLehrpool-Suite server: {{0}}",
"images-pending-delete-exist": "Zur L\u00f6schung markierte VM-Versionen: {{0}}",
"ldap-filter-created": "LDAP Filter wurde erfolgreich erstellt",
@@ -33,4 +32,4 @@
"timeout": "Zeit\u00fcberschreitung",
"unknown-targetid": "Target {{0}} nicht bekannt",
"unknown-userid": "Unbekannter Nutzer, {{0}}"
-}
+} \ No newline at end of file
diff --git a/modules-available/dozmod/lang/de/permissions.json b/modules-available/dozmod/lang/de/permissions.json
index 8e743e5c..6159362d 100644
--- a/modules-available/dozmod/lang/de/permissions.json
+++ b/modules-available/dozmod/lang/de/permissions.json
@@ -8,6 +8,8 @@
"networkrules.view": "Netzwerk-Regeln einsehen.",
"networkshares.save": "\u00c4nderungen an den Netzlaufwerken speichern.",
"networkshares.view": "Netzlaufwerke einsehen.",
+ "orphaned.delete": "Verwaiste Dateien vom VM-Store l\u00f6schen.",
+ "orphaned.scan": "Nach Verwaisten Dateien auf VM-Store suchen.",
"runscripts.save": "Startkripte erstellen\/bearbeiten.",
"runscripts.view": "Startscripte auflisten.",
"runtimeconfig.save": "\u00c4nderungen an der Laufzeit-Konfiguration speichern.",
diff --git a/modules-available/dozmod/lang/de/template-tags.json b/modules-available/dozmod/lang/de/template-tags.json
index f03fd7b6..9b2b5653 100644
--- a/modules-available/dozmod/lang/de/template-tags.json
+++ b/modules-available/dozmod/lang/de/template-tags.json
@@ -11,6 +11,7 @@
"lang_bwlehrpoolsuite": "bwLehrpool-Suite",
"lang_canLoginOrganization": "Nutzer dieser Einrichtung k\u00f6nnen sich am Satelliten anmelden",
"lang_canLoginUser": "Nutzer kann sich am Satelliten anmelden",
+ "lang_confirmDeleteOrphanedFiles": "Sind Sie sicher, dass Sie alle aufgelisteten Dateien unwiderruflich vom VM-Store l\u00f6schen wollen?",
"lang_createTime": "Erstellt",
"lang_currentFilter": "Aktueller Filter",
"lang_defaultImagePermissionAdmin": "Administrieren",
@@ -21,6 +22,7 @@
"lang_defaultLecturePermissions": "F\u00fcr Veranstaltungen",
"lang_defaultPermissions": "Standardberechtigungen",
"lang_delButton": "Gew\u00e4hlte VMs endg\u00fcltig l\u00f6schen",
+ "lang_deleteExpiredHeading": "Zu l\u00f6schende VM-Versionen",
"lang_descriptionPermissionConfig": "Dies sind die Berechtigungen, die ein Benutzer standardm\u00e4\u00dfig f\u00fcr fremde VMs\/Veranstaltungen hat. Sie werden angewandt, wenn der Besitzer keine anderweitigen Berechtigungen w\u00e4hlt.",
"lang_descriptionRuntimeLimits": "Hier k\u00f6nnen Sie verschiedene Limits festlegen, z.B. wie lange eine VM nach dem Hochladen g\u00fcltig ist. Nach Ablauf dieses Zeitraums ist der Verantwortliche gezwungen, eine neue Version der VM hochzuladen. Damit k\u00f6nnen Sie das Ansammeln nicht mehr ben\u00f6tigter VMs eind\u00e4mmen. Weiterhin k\u00f6nnen Sie die maximale Anzahl gleichzeitiger Transfers pro Benutzer einschr\u00e4nken.\r\n\r\nVer\u00e4nderte Einstellungen wirken sich nicht auf bereits bestehende VMs aus.",
"lang_description_delete_images": "Diese Liste zeigt VMs, die entweder abgelaufen sind, oder deren Datei besch\u00e4digt, verschoben oder gel\u00f6scht wurde. Diese Images sind zur Zeit im Lehrpool nicht verf\u00fcgbar, ihre endg\u00fcltige L\u00f6schung muss aber manuell best\u00e4tigt werden, um gr\u00f6\u00dfere Katastrophen durch Softwarefehler, verstellte Systemuhren etc. zu vermeiden.",
@@ -33,11 +35,11 @@
"lang_emailNotifications": "EMail-Benachrichtigungen aktiviert",
"lang_error": "Fehler",
"lang_event": "Ereignis",
+ "lang_fileName": "Dateiname",
"lang_fileSize": "Dateigr\u00f6\u00dfe",
"lang_followingPlaceholdersUnused": "Folgende Platzhalter m\u00fcssen im Template verwendet werden",
"lang_hasNewer": "Neuere Version existiert",
"lang_hash": "Hash",
- "lang_heading": "Zu l\u00f6schende VM-Versionen",
"lang_hidden": "Versteckt",
"lang_host": "Host",
"lang_image": "VM",
@@ -77,6 +79,9 @@
"lang_normal": "Normal",
"lang_organization": "Einrichtung",
"lang_organizationListHeader": "Nutzungsrechte f\u00fcr den Satelliten festlegen",
+ "lang_orphanDeleteButton": "Dateien l\u00f6schen",
+ "lang_orphanedFilesDesc": "Hier aufgelistete Dateien geh\u00f6ren zu keiner aus der Datenbank bekannten VM. Sollten Sie sich sicher sein, dass diese Dateien nicht anderweitig verwendet werden, oder h\u00e4ndisch auf dem VM-Store abgelegt wurden, k\u00f6nnen Sie diese Dateien l\u00f6schen, um Speicherplatz zu gewinnen. Wenn Sie sich nicht sicher sind, untersuchen Sie die Situation auf dem VM-Store h\u00e4ndisch.",
+ "lang_orphanedFilesHeading": "Verwaiste Images und Dateien auf dem VM-Store",
"lang_os": "Betriebssystem",
"lang_owner": "Besitzer",
"lang_passwordCleartextHint": "Bitte beachten Sie, dass ein hier explizit angegebenes Passwort im Klartext an das Poolsystem \u00fcbergeben wird. Sie sollten also nur dedizierte Funktionsaccounts nutzen, die keinen Zugriff auf weitere Systeme erm\u00f6glichen.",
@@ -93,6 +98,7 @@
"lang_runScriptDeleteConfirmation": "Skript wirklich l\u00f6schen?",
"lang_runtimeConfig": "Laufzeit-Konfiguration",
"lang_runtimeConfigLimits": "Beschr\u00e4nkungen",
+ "lang_scanButton": "VM-Store durchsuchen",
"lang_scriptContent": "Skriptinhalt",
"lang_scriptExtension": "Dateinamenerweiterung",
"lang_scriptExtensionHead": "Erweiterung",
@@ -121,6 +127,7 @@
"lang_sslExplicit": "Explizites SSL (\"STARTTLS\")",
"lang_sslImplicit": "Implizites SSL",
"lang_sslNone": "Kein SSL",
+ "lang_status": "Status",
"lang_superUser": "Ist SuperUser (darf alle Veranstaltungen und VMs bearbeiten\/l\u00f6schen)",
"lang_system": "System",
"lang_target": "Ziel",
diff --git a/modules-available/dozmod/lang/en/messages.json b/modules-available/dozmod/lang/en/messages.json
index 84677402..1b46339b 100644
--- a/modules-available/dozmod/lang/en/messages.json
+++ b/modules-available/dozmod/lang/en/messages.json
@@ -1,6 +1,5 @@
{
"all-templates-reset": "All templates have been reset",
- "delete-images": "Delete: {{0}}",
"dozmod-error": "Error communicating with the bwLehrpool-Suite server: {{0}}",
"images-pending-delete-exist": "VMs marked for deletion: {{0}}",
"ldap-filter-created": "LDAP filter was successfully created",
diff --git a/modules-available/dozmod/lang/en/permissions.json b/modules-available/dozmod/lang/en/permissions.json
index 3d82bdda..479b150b 100644
--- a/modules-available/dozmod/lang/en/permissions.json
+++ b/modules-available/dozmod/lang/en/permissions.json
@@ -8,6 +8,8 @@
"networkrules.view": "View network rules.",
"networkshares.save": "Save network drives.",
"networkshares.view": "View network drives.",
+ "orphaned.delete": "Delete orphaned files from VM store.",
+ "orphaned.scan": "Scan for orphaned files on VM store.",
"runscripts.save": "Save startup scripts.",
"runscripts.view": "View startup scripts.",
"runtimeconfig.save": "Save limits and defaults of a runtime configuration.",
diff --git a/modules-available/dozmod/lang/en/template-tags.json b/modules-available/dozmod/lang/en/template-tags.json
index 1ab25a06..425ceefa 100644
--- a/modules-available/dozmod/lang/en/template-tags.json
+++ b/modules-available/dozmod/lang/en/template-tags.json
@@ -11,6 +11,7 @@
"lang_bwlehrpoolsuite": "bwLehrpool-Suite",
"lang_canLoginOrganization": "Users from this organization can login",
"lang_canLoginUser": "This user can login",
+ "lang_confirmDeleteOrphanedFiles": "Are you sure you want to permanently delete the files listed below?",
"lang_createTime": "Created",
"lang_currentFilter": "Current filter",
"lang_defaultImagePermissionAdmin": "Administrate",
@@ -21,6 +22,7 @@
"lang_defaultLecturePermissions": "For lectures",
"lang_defaultPermissions": "Default permissions",
"lang_delButton": "Permanently delete selected images",
+ "lang_deleteExpiredHeading": "Images marked for deletion",
"lang_descriptionPermissionConfig": "These are the default permissions being used for VMs and lectures if the owner does not specify any.",
"lang_descriptionRuntimeLimits": "Here you can define some limits, e.g. how long a newly uploaded VM will be valid. This should make sure that you don't end up with a lot of old, unused VMs over time.\r\n\r\nModified settings won't apply for already existing VMs.",
"lang_description_delete_images": "This is a list of VMs that either expired, or where the disk image is damaged or missing. These VMs are not available in bwLehrpool currently, but you have to manually confirm the deletion of the disk images for safety reasons (clock skew etc.)",
@@ -33,11 +35,11 @@
"lang_emailNotifications": "E-Mail notifications enabled",
"lang_error": "Error",
"lang_event": "Event",
+ "lang_fileName": "File name",
"lang_fileSize": "File size",
"lang_followingPlaceholdersUnused": "The following placeholders are not being used",
"lang_hasNewer": "newer version exists",
"lang_hash": "Hash",
- "lang_heading": "Images Marked for Deletion",
"lang_hidden": "Hidden",
"lang_host": "Host",
"lang_image": "VM",
@@ -77,6 +79,9 @@
"lang_normal": "Normal",
"lang_organization": "Organization",
"lang_organizationListHeader": "Set access permissions for organizations",
+ "lang_orphanDeleteButton": "Delete files",
+ "lang_orphanedFilesDesc": "Files listed here could not be matched to any VM from the database. If you're sure that these files aren't used for anything else (e.g. shared VM store), you can delete these files to free up space. If you're not sure, leave them alone or manually examine the situation on the file system.",
+ "lang_orphanedFilesHeading": "Orphaned files on VM store",
"lang_os": "Operating System",
"lang_owner": "Owner",
"lang_passwordCleartextHint": "Please not that explicitly provided credentials will be passed in clear text. You should only use dedicated accounts that don't give access to anything else than the desired network share(s).",
@@ -93,6 +98,7 @@
"lang_runScriptDeleteConfirmation": "Do you want to delete this run-script?",
"lang_runtimeConfig": "Limits and Defaults",
"lang_runtimeConfigLimits": "Limitations",
+ "lang_scanButton": "Scan VM store",
"lang_scriptContent": "Script content",
"lang_scriptExtension": "Script extension",
"lang_scriptExtensionHead": "Extension",
@@ -121,6 +127,7 @@
"lang_sslExplicit": "Explicit SSL (\"STARTTLS\")",
"lang_sslImplicit": "Implicit SSL",
"lang_sslNone": "No SSL",
+ "lang_status": "Status",
"lang_superUser": "Is super user (can edit\/delete all lectures and VMs)",
"lang_system": "System",
"lang_target": "Target",
diff --git a/modules-available/dozmod/pages/expiredimages.inc.php b/modules-available/dozmod/pages/expiredimages.inc.php
index 2b5a2274..ffd7b026 100644
--- a/modules-available/dozmod/pages/expiredimages.inc.php
+++ b/modules-available/dozmod/pages/expiredimages.inc.php
@@ -5,40 +5,7 @@ class SubPage
public static function doPreprocess()
{
- $action = Request::post('action', false, 'string');
- if ($action === 'delimages') {
- if (User::hasPermission("expiredimages.delete")) {
- $result = self::handleDeleteImages();
- if (!empty($result)) {
- Message::addInfo('delete-images', $result);
- }
- Util::redirect('?do=DozMod');
- }
- }
- }
-
- private static function handleDeleteImages()
- {
- $images = Request::post('images', false);
- if (is_array($images)) {
- foreach ($images as $image => $val) {
- if (strtolower($val) !== 'on')
- continue;
- Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'"
- . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array(
- 'imageversionid' => $image
- ));
- }
- if (!empty($images)) {
- $ret = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 10, $code);
- if ($code == 999) {
- $ret .= "\nConnection to DMSD failed.";
- }
- return $ret;
- }
- }
- return false;
}
private static function loadExpiredImages()
@@ -80,7 +47,9 @@ class SubPage
if (empty($expiredImages)) {
Message::addSuccess('no-expired-images');
} else {
- Render::addTemplate('images-delete', array('images' => $expiredImages, 'allowedDelete' => User::hasPermission("expiredimages.delete")));
+ $data = ['images' => $expiredImages];
+ Permission::addGlobalTags($data['perm'], null, ['expiredimages.delete', 'orphaned.scan']);
+ Render::addTemplate('images-delete', $data);
}
}
@@ -88,10 +57,72 @@ class SubPage
{
$action = Request::post('action');
if ($action === 'delimages') {
- User::assertPermission("expiredimages.delete");
- die(self::handleDeleteImages());
+ self::handleDeleteImages();
+ } elseif ($action === 'orphaned') {
+ self::handleOrphaned();
+ } else {
+ echo 'Huh?';
+ }
+ }
+
+ private static function handleDeleteImages()
+ {
+ User::assertPermission("expiredimages.delete");
+ $images = Request::post('images', false);
+ $result = false;
+ if (is_array($images)) {
+ foreach ($images as $image => $val) {
+ if (strtolower($val) !== 'on')
+ continue;
+ Database::exec("UPDATE sat.imageversion SET deletestate = 'WANT_DELETE'"
+ . " WHERE deletestate = 'SHOULD_DELETE' AND imageversionid = :imageversionid", array(
+ 'imageversionid' => $image
+ ));
+ }
+ if (!empty($images)) {
+ $result = Download::asStringPost('http://127.0.0.1:9080/do/delete-images', false, 10, $code);
+ if ($code == 999) {
+ $result .= "\nConnection to DMSD failed.";
+ }
+ }
+ }
+ if (!empty($result)) {
+ echo $result;
+ }
+ }
+
+ private static function handleOrphaned()
+ {
+ if (Request::post('delete', 0, 'int') !== 0) {
+ User::assertPermission("orphaned.delete");
+ $action = 'delete';
+ } else {
+ User::assertPermission("orphaned.scan");
+ $action = 'scan';
+ }
+ // Talk to dmsd
+ $result = Download::asStringPost('http://127.0.0.1:9080/do/scan-orphaned-files', ['action' => $action],
+ 10, $code);
+ if ($code == 999) {
+ $result = '<div class="alert alert-warning">'
+ . $result . ' - Connection to DMSD failed.</div>';
+ } else {
+ $json = json_decode($result, true);
+ if (is_array($json)) {
+ $result = [];
+ $showDelete = false;
+ foreach ($json as $k => $v) {
+ $result[] = ['file' => $k, 'status' => $v];
+ if ($v === 'EXISTS') {
+ $showDelete = true;
+ }
+ }
+ $data = ['files' => $result, 'show_delete' => $showDelete];
+ Permission::addGlobalTags($data['perm'], null, ['orphaned.delete']);
+ $result = Render::parse('images-orphaned', $data);
+ }
}
- die('Huh?');
+ echo $result;
}
}
diff --git a/modules-available/dozmod/permissions/permissions.json b/modules-available/dozmod/permissions/permissions.json
index c8958089..12d0acd3 100644
--- a/modules-available/dozmod/permissions/permissions.json
+++ b/modules-available/dozmod/permissions/permissions.json
@@ -2,6 +2,12 @@
"expiredimages.delete": {
"location-aware": false
},
+ "orphaned.scan": {
+ "location-aware": false
+ },
+ "orphaned.delete": {
+ "location-aware": false
+ },
"actionlog.view": {
"location-aware": false
},
diff --git a/modules-available/dozmod/templates/images-delete.html b/modules-available/dozmod/templates/images-delete.html
index 78690426..9d61559f 100644
--- a/modules-available/dozmod/templates/images-delete.html
+++ b/modules-available/dozmod/templates/images-delete.html
@@ -2,96 +2,142 @@
<div class="panel panel-default">
<div class="panel-heading">
- {{lang_heading}}
+ {{lang_deleteExpiredHeading}}
</div>
<div class="panel-body">
<p>{{lang_description_delete_images}}</p>
<div class="table-responsive">
- <form id="delform" method="post" action="?do=DozMod" onsubmit="return slxPostdel()">
+ <form id="delform" method="post" action="?do=dozmod">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="section" value="expiredimages">
<input type="hidden" name="action" value="delimages">
<table class="table table-stripped table-condensed stupidtable">
<thead>
- <tr>
- <th data-sort="string">{{lang_image}}</th>
- <th data-sort="int">{{lang_version}}</th>
- <th data-sort="string">{{lang_owner}}</th>
- <th><span class="glyphicon glyphicon-upload" title="{{lang_hasNewer}}"></span></th>
- <th data-sort="int">{{lang_size}}</th>
- <th>
- <div class="checkbox">
- <input id="del-all" type="checkbox" onclick="slxChangeAll()">
- <label for="del-all"></label>
- <span class="glyphicon glyphicon-trash" title="{{lang_delete}}"></span>
- </div>
- </th>
- </tr>
+ <tr>
+ <th data-sort="string">{{lang_image}}</th>
+ <th data-sort="int">{{lang_version}}</th>
+ <th data-sort="string">{{lang_owner}}</th>
+ <th><span class="glyphicon glyphicon-upload" title="{{lang_hasNewer}}"></span></th>
+ <th data-sort="int">{{lang_size}}</th>
+ <th>
+ <div class="checkbox">
+ <input id="del-all" type="checkbox">
+ <label for="del-all"></label>
+ <span class="glyphicon glyphicon-trash" title="{{lang_delete}}"></span>
+ </div>
+ </th>
+ </tr>
</thead>
<tbody>
- {{#images}}
- <tr>
- <td class="text-left text-nowrap {{name_extra_class}}">{{displayname}}<br><span class="small">{{imageversionid}}</span></td>
- <td class="text-left text-nowrap" data-sort-value="{{createtime}}" >{{version}}</td>
- <td class="text-left text-nowrap"><a href="mailto:{{email}}">{{lastname}}, {{firstname}}</a></td>
- <td class="text-left text-nowrap"><span class="glyphicon {{hasNewerClass}}"></span></td>
- <td class="text-left text-nowrap" data-sort-value="{{rawfilesize}}">{{filesize}}</td>
- <td>
- <div class="checkbox">
- <input type="checkbox" id="images[{{imageversionid}}]" class="del-check" name="images[{{imageversionid}}]" {{checked}}>
- <label for="images[{{imageversionid}}]"></label>
- </div>
- </td>
- </tr>
- {{/images}}
+ {{#images}}
+ <tr>
+ <td class="text-left text-nowrap {{name_extra_class}}">{{displayname}}<br><span class="small">{{imageversionid}}</span>
+ </td>
+ <td class="text-left text-nowrap" data-sort-value="{{createtime}}">{{version}}</td>
+ <td class="text-left text-nowrap"><a href="mailto:{{email}}">{{lastname}}, {{firstname}}</a></td>
+ <td class="text-left text-nowrap"><span class="glyphicon {{hasNewerClass}}"></span></td>
+ <td class="text-left text-nowrap" data-sort-value="{{rawfilesize}}">{{filesize}}</td>
+ <td>
+ <div class="checkbox">
+ <input type="checkbox" id="images[{{imageversionid}}]" class="del-check"
+ name="images[{{imageversionid}}]" {{checked}}>
+ <label for="images[{{imageversionid}}]"></label>
+ </div>
+ </td>
+ </tr>
+ {{/images}}
</tbody>
</table>
- <button {{^allowedDelete}}disabled{{/allowedDelete}} style="margin-left: 20px" id="delbtn" class="btn btn-danger pull-right" type="submit" name="button" value="save"><span class="glyphicon glyphicon-trash"></span> {{lang_delButton}}</button>
+ <button {{perm.expiredimages.delete.disabled}} id="expired-delete"
+ class="btn btn-danger pull-right" type="submit">
+ <span class="glyphicon glyphicon-trash"></span>
+ {{lang_delButton}}
+ </button>
</form>
<pre style="display:none" id="deloutput"></pre>
</div>
</div>
</div>
-<script type="text/javascript"><!--
+<div class="panel panel-default">
+ <div class="panel-heading">
+ {{lang_orphanedFilesHeading}}
+ </div>
+ <div class="panel-body">
+ <p>{{lang_orphanedFilesDesc}}</p>
+ <div id="orphan-ajax">
+ <button {{perm.orphaned.scan.disabled}} id="orphan-scan"
+ class="btn btn-default pull-right" type="button">
+ <span class="glyphicon glyphicon-search"></span>
+ {{lang_scanButton}}
+ </button>
+ </div>
+ </div>
+</div>
-function slxPostdel() {
- var f = $('#delform');
- $('#delbtn').prop('disabled', true);
- $.post('?do=DozMod', f.serialize()).done(function (data) {
- $('#deloutput').text(data).css('display', '');
- }).fail(function () {
- $('#deloutput').text('ERROR').css('display', '');
- });
- return false;
-}
+<div class="hidden" id="confirm-orphan-delete">
+ {{lang_confirmDeleteOrphanedFiles}}
+</div>
-function slxChangeAll()
-{
- if ($('#del-all').is(':checked')) {
- $('.del-check').prop('checked', true);
- } else {
- $('.del-check').prop('checked', false);
- }
-}
+<script type="text/javascript"><!--
-function slxChangeSingle()
-{
- var ons = 0;
- var offs = 0;
- $('.del-check').each(function(idx, elem) {
- if (elem.checked) {
- ons++;
+document.addEventListener("DOMContentLoaded", function () {
+ $('#del-all').click(function () {
+ if ($(this).is(':checked')) {
+ $('.del-check').prop('checked', true);
} else {
- offs++;
+ $('.del-check').prop('checked', false);
}
});
- $('#del-all').prop('checked', offs === 0).prop('indeterminate', ons > 0 && offs > 0);
-}
-
-document.addEventListener("DOMContentLoaded", function() {
+ var slxChangeSingle = function () {
+ var ons = 0;
+ var offs = 0;
+ $('.del-check').each(function (idx, elem) {
+ if (elem.checked) {
+ ons++;
+ } else {
+ offs++;
+ }
+ });
+ // TODO indeterminate doesn't work with styled checkbox
+ $('#del-all').prop('checked', offs === 0).prop('indeterminate', ons > 0 && offs > 0);
+ };
$('.del-check').click(slxChangeSingle);
slxChangeSingle();
+ // Handler for delete expired images button
+ var delform = $('#delform');
+ delform.submit(function (e) {
+ e.preventDefault();
+ $('#expired-delete').prop('disabled', true);
+ $.post('?do=dozmod', delform.serialize()).done(function (data) {
+ $('#deloutput').text(data).css('display', '');
+ }).fail(function () {
+ $('#deloutput').text('ERROR').css('display', '');
+ });
+ });
+ // Handler for scanning/deleting orphaned files
+ var slxOrphans = function (del) {
+ $.post('?do=dozmod', {
+ token: TOKEN, section: 'expiredimages', action: 'orphaned', delete: del
+ }).done(function (data) {
+ $('#orphan-ajax').html(data).find('#orphan-delete')
+ .click(slxModalConfirmHandler)
+ .click(function () {
+ slxOrphans(1);
+ });
+ }).fail(function () {
+ $('#orphan-ajax').text('ERROR');
+ });
+ };
+ // Handler for scanning for orphaned files
+ $('#orphan-scan').click(function () {
+ $('#orphan-scan').prop('disabled', true);
+ slxOrphans(0);
+ });
+ window.addEventListener('unload', function () {
+ // Do something here that forces browsers to throw away any JS state on nav.back
+ $('input[type=checkbox]').prop('checked', false);
+ });
}, false);
//--> </script>
diff --git a/modules-available/dozmod/templates/images-orphaned.html b/modules-available/dozmod/templates/images-orphaned.html
new file mode 100644
index 00000000..d1b74a42
--- /dev/null
+++ b/modules-available/dozmod/templates/images-orphaned.html
@@ -0,0 +1,25 @@
+<table class="table">
+ <thead>
+ <tr>
+ <th>{{lang_fileName}}</th>
+ <th>{{lang_status}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#files}}
+ <tr>
+ <td>{{file}}</td>
+ <td>{{status}}</td>
+ </tr>
+ {{/files}}
+ </tbody>
+</table>
+
+{{#show_delete}}
+<button {{perm.orphaned.delete.disabled}} id="orphan-delete"
+ class="btn btn-danger pull-right" type="button"
+ data-confirm="#confirm-orphan-delete">
+ <span class="glyphicon glyphicon-search"></span>
+ {{lang_orphanDeleteButton}}
+</button>
+{{/show_delete}} \ No newline at end of file
diff --git a/modules-available/exams/baseconfig/getconfig.inc.php b/modules-available/exams/baseconfig/getconfig.inc.php
index 10aa1d84..120bdbff 100644
--- a/modules-available/exams/baseconfig/getconfig.inc.php
+++ b/modules-available/exams/baseconfig/getconfig.inc.php
@@ -22,6 +22,9 @@ $foofoo = function($machineUuid) {
ConfigHolder::add('SLX_AUTOLOGIN', $autoLogin, 10000);
}
ConfigHolder::add('SLX_SYSTEMD_TARGET', 'exam-mode', 10000);
+ ConfigHolder::add('SLX_RUNMODE_MODULE', 'exams', 10000);
+ // No saver
+ ConfigHolder::add('SLX_SCREEN_SAVER_TIMEOUT', '0', 1000);
}
};
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
index ed049d73..2f4af8af 100644
--- a/modules-available/locations/templates/locations.html
+++ b/modules-available/locations/templates/locations.html
@@ -28,7 +28,7 @@
</div>
{{/mismatchMachines}}
- <table class="table table-condensed locations" style="margin-bottom:0">
+ <table class="table table-condensed table-hover locations" style="margin-bottom:0">
<tr>
<th width="100%">{{lang_locationName}}</th>
<th>
diff --git a/modules-available/remoteaccess/baseconfig/getconfig.inc.php b/modules-available/remoteaccess/baseconfig/getconfig.inc.php
index 8aa6430e..3c849b45 100644
--- a/modules-available/remoteaccess/baseconfig/getconfig.inc.php
+++ b/modules-available/remoteaccess/baseconfig/getconfig.inc.php
@@ -1,21 +1,31 @@
<?php
-// Locations from closest to furthest (order)
-$locationId = ConfigHolder::get('SLX_LOCATIONS');
-if ($locationId !== false) {
- $locationId = (int)$locationId;
- $ret = Database::queryFirst("SELECT l.locationid FROM remoteaccess_x_location l
+(function ($machineUuid) {
+ // Leave clients in any runmode alone
+ $res = Database::queryFirst('SELECT machineuuid FROM runmode WHERE machineuuid = :uuid',
+ array('uuid' => $machineUuid), true);
+ if (is_array($res))
+ return;
+
+ // Locations from closest to furthest (order)
+ $locationId = ConfigHolder::get('SLX_LOCATIONS');
+ if ($locationId !== false) {
+ $locationId = (int)$locationId;
+ $ret = Database::queryFirst("SELECT l.locationid FROM remoteaccess_x_location l
INNER JOIN remoteaccess_group g USING (groupid)
WHERE locationid = :lid AND g.active = 1",
- ['lid' => $locationId], true); // TODO Remove true after next point release (2020-05-12)
- if ($ret !== false) {
- // TODO Properly merge
- if (Property::get(RemoteAccess::PROP_TRY_VIRT_HANDOVER)) {
- ConfigHolder::add("SLX_REMOTE_VNC", 'vmware virtualbox');
- } else {
- ConfigHolder::add("SLX_REMOTE_VNC", 'x11vnc');
+ ['lid' => $locationId], true); // TODO Remove true after next point release (2020-05-12)
+ if ($ret !== false) {
+ // TODO Properly merge
+ if (Property::get(RemoteAccess::PROP_TRY_VIRT_HANDOVER)) {
+ ConfigHolder::add("SLX_REMOTE_VNC", 'vmware virtualbox');
+ } else {
+ ConfigHolder::add("SLX_REMOTE_VNC", 'x11vnc');
+ }
+ ConfigHolder::add("SLX_REMOTE_HOST_ACCESS", Property::get(RemoteAccess::PROP_ALLOWED_VNC_NET));
+ ConfigHolder::add('SLX_RUNMODE_MODULE', 'remoteaccess');
+ // No saver
+ ConfigHolder::add('SLX_SCREEN_SAVER_TIMEOUT', '0', 1000);
}
- ConfigHolder::add("SLX_REMOTE_HOST_ACCESS", Property::get(RemoteAccess::PROP_ALLOWED_VNC_NET));
- ConfigHolder::add('SLX_RUNMODE_MODULE', 'remoteaccess');
}
-}
+})($uuid); \ No newline at end of file
diff --git a/modules-available/runmode/baseconfig/getconfig.inc.php b/modules-available/runmode/baseconfig/getconfig.inc.php
index 8ea2b2a6..9c36cc75 100644
--- a/modules-available/runmode/baseconfig/getconfig.inc.php
+++ b/modules-available/runmode/baseconfig/getconfig.inc.php
@@ -1,6 +1,6 @@
<?php
-$foofoo = function($machineUuid) {
+(function($machineUuid) {
$res = Database::queryFirst('SELECT module, modeid, modedata FROM runmode WHERE machineuuid = :uuid',
array('uuid' => $machineUuid));
if ($res === false)
@@ -17,6 +17,4 @@ $foofoo = function($machineUuid) {
ConfigHolder::add('SLX_SYSTEMD_TARGET', $config->systemdDefaultTarget, 10000);
}
ConfigHolder::add('SLX_RUNMODE_MODULE', $res['module']);
-};
-
-$foofoo($uuid); \ No newline at end of file
+})($uuid);
diff --git a/modules-available/serversetup-bwlp-ipxe/api.inc.php b/modules-available/serversetup-bwlp-ipxe/api.inc.php
index 7575ad55..1ac885df 100644
--- a/modules-available/serversetup-bwlp-ipxe/api.inc.php
+++ b/modules-available/serversetup-bwlp-ipxe/api.inc.php
@@ -19,9 +19,16 @@
$entry = MenuEntry::get($entryId);
$data = $builder->getMenuEntry($entry);
} else {
+ // Get bootstrap code if required...
$data = $builder->bootstrapLive();
if ($data === false) {
- $menu = IPxeMenu::forClient($builder->clientIp(), $builder->uuid());
+ // ...otherwise, generate normal code
+ $menuId = Request::get('menuid', false, 'int');
+ if ($menuId !== false) {
+ $menu = IPxeMenu::get($menuId, true);
+ } else {
+ $menu = IPxeMenu::forClient($builder->clientIp(), $builder->uuid());
+ }
$data = $builder->getMenu($menu, true);
}
}
diff --git a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php
index 3b2b847f..c8f644ab 100644
--- a/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php
+++ b/modules-available/serversetup-bwlp-ipxe/inc/scriptbuilderipxe.inc.php
@@ -36,7 +36,7 @@ class ScriptBuilderIpxe extends ScriptBuilderBase
}
unset($v);
}
- unset($fromQuery['menuid'], $fromQuery['entryid'], $fromQuery['special']);
+ unset($fromQuery['entryid'], $fromQuery['special']);
if ($key !== null) {
$fromQuery[$key] = $value;
}
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index b2d2afa3..d152390f 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -7,8 +7,11 @@
"lang_biosUpdate": "BIOS Update",
"lang_biosUpdateLink": "Zur Herstellerseite",
"lang_biosVersion": "BIOS-Version",
+ "lang_bootedWithoutAnyRunmode": "Ohne besonderen Betriebsmodus gestartet",
+ "lang_clientInDifferentRunmode": "Aktueller Betriebsmodus",
"lang_clientList": "Liste ausgew\u00e4hlter Rechner",
"lang_configVars": "Konfigurationsvariablen",
+ "lang_configuredRunMode": "Konfigurierter Betriebsmodus",
"lang_cores": "Kerne",
"lang_cpuCores": "CPU-Kerne",
"lang_cpuModel": "CPU",
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index 152da3e7..6a9b0623 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -7,8 +7,11 @@
"lang_biosUpdate": "BIOS update",
"lang_biosUpdateLink": "Go to vendor's site",
"lang_biosVersion": "BIOS version",
+ "lang_bootedWithoutAnyRunmode": "Booted without any mode",
+ "lang_clientInDifferentRunmode": "Current mode",
"lang_clientList": "List of selected machines",
"lang_configVars": "Config Variables",
+ "lang_configuredRunMode": "Configured mode of operation",
"lang_cores": "Cores",
"lang_cpuCores": "CPU cores",
"lang_cpuModel": "CPU",
diff --git a/modules-available/statistics/pages/list.inc.php b/modules-available/statistics/pages/list.inc.php
index 2dab152d..9f2df897 100644
--- a/modules-available/statistics/pages/list.inc.php
+++ b/modules-available/statistics/pages/list.inc.php
@@ -40,7 +40,8 @@ class SubPage
}
}
$res = Database::simpleQuery("SELECT m.machineuuid, m.locationid, m.macaddr, m.clientip, m.lastseen,
- m.logintime, m.state, m.currentuser, m.realcores, m.mbram, m.kvmstate, m.cpumodel, m.id44mb, m.hostname, m.notes IS NOT NULL AS hasnotes,
+ m.logintime, m.state, m.currentuser, m.currentrunmode, m.realcores, m.mbram, m.kvmstate, m.cpumodel, m.id44mb,
+ m.hostname, m.notes IS NOT NULL AS hasnotes,
m.badsectors, Count(s.machineuuid) AS confvars $xtra FROM machine m
LEFT JOIN setting_machine s USING (machineuuid)
$join WHERE $where GROUP BY m.machineuuid", $args);
@@ -101,6 +102,16 @@ class SubPage
unset($row['currentuser']);
}
}
+ if ($row['state'] === 'IDLE' || $row['state'] === 'OCCUPIED') {
+ if ((!empty($row['currentrunmode']) || !empty($row['rmmodule']))
+ && $row['currentrunmode'] !== $row['rmmodule']) {
+ $row['wrongRunMode'] = true;
+ if (!empty($row['currentrunmode'])) {
+ $wrongModule = Module::get($row['currentrunmode']);
+ $row['currentrunmode'] = $wrongModule === false ? $row['currentrunmode'] : $wrongModule->getDisplayName();
+ }
+ }
+ }
$row['state_' . $row['state']] = true;
if ($row['locationid'] > 0) {
$row['location'] = $location[$row['locationid']];
diff --git a/modules-available/statistics/templates/clientlist.html b/modules-available/statistics/templates/clientlist.html
index 3d49a770..aeca368d 100644
--- a/modules-available/statistics/templates/clientlist.html
+++ b/modules-available/statistics/templates/clientlist.html
@@ -98,10 +98,18 @@
{{/link_details}}
<div class="small uuid">{{machineuuid}}</div>
{{#rmmodule}}
- <div class="small">{{lang_runMode}}:
+ <div class="small">{{lang_configuredRunMode}}:
<a class="slx-bold" href="?do=runmode&amp;module={{rmmodule}}">{{moduleName}}</a> / {{modeName}}
</div>
{{/rmmodule}}
+ {{#wrongRunMode}}
+ <div class="small text-danger">
+ {{lang_clientInDifferentRunmode}}: {{currentrunmode}}
+ {{^currentrunmode}}
+ {{lang_bootedWithoutAnyRunmode}}
+ {{/currentrunmode}}
+ </div>
+ {{/wrongRunMode}}
{{#currentuser}}
<div class="small">
{{lang_user}}:
diff --git a/script/slx-fixes.js b/script/slx-fixes.js
index 992ca337..a06761e4 100644
--- a/script/slx-fixes.js
+++ b/script/slx-fixes.js
@@ -61,6 +61,7 @@ $(document).ready(function() {
var $title, $body, $button, $modal = null, $cache = {};
slxModalConfirmHandler = function (e) {
e.preventDefault();
+ e.stopImmediatePropagation();
var $this = $(this);
if ($modal === null) {
$modal = $('<div class="modal fade" id="modal-autogen" tabindex="-1" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal">&times;</button>'
@@ -74,7 +75,7 @@ $(document).ready(function() {
$title.text($this.data('title') || $this.text());
$button.html($this.data('close') || $this.html()).attr('class', $this.attr('class')).removeClass('btn-xs btn-sm btn-lg').off('click').click(function() {
// Click and reconnect click handler so pressing "back" on the next page works
- $this.off('click').click().click(slxModalConfirmHandler);
+ $this.off('click', slxModalConfirmHandler).click().click(slxModalConfirmHandler);
});
var $wat, str = $this.data('confirm');
if (str.substr(0, 9) === '#confirm-') {