summaryrefslogtreecommitdiffstats
path: root/modules-available/rebootcontrol
diff options
context:
space:
mode:
authorSimon Rettberg2017-04-12 12:30:51 +0200
committerSimon Rettberg2017-04-12 12:30:51 +0200
commit5a922e7d360f7aa2ea3c4a84a5610940f06cd037 (patch)
treed4a78c213380c58219a1d545bb2606a7e9bab1ca /modules-available/rebootcontrol
parent[statistics_reporting] Send backlogged reports in cronjob (diff)
downloadslx-admin-5a922e7d360f7aa2ea3c4a84a5610940f06cd037.tar.gz
slx-admin-5a922e7d360f7aa2ea3c4a84a5610940f06cd037.tar.xz
slx-admin-5a922e7d360f7aa2ea3c4a84a5610940f06cd037.zip
[rebootcontrol] New module for shutting down and rebooting clients
Squashed commit of the following: commit 00a9af598b949dec748f2b6ddb42f124f2352451 Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Wed Apr 12 12:28:18 2017 +0200 [rebootcontrol] Enforce POST for keypair regen triggering commit fb5da4d50c364ec53086b7da7d9f9f094ac0b87e Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Wed Apr 12 12:00:59 2017 +0200 [rebootcontrol] Resolve lecture uuids in client list commit 0fc97f2c7b4a085efcd86262afe351784d4a6743 Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Wed Apr 12 11:43:17 2017 +0200 [rebootcontrol] Support REBOOT_AT state commit 8383db423d5ef86da49d0ed1ae719254bcd65d79 Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Tue Apr 11 15:15:20 2017 +0200 [rebootcontrol] Add REBOOT_AT translation commit d19b59015f383f04369fb89ac67abae4872c3d19 Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Mon Apr 10 15:58:55 2017 +0200 [rebootcontrol] Remove redundant location queries; improve pages a bit Rows are now marked using the designated active class, a client can be selected clicking anywhere in the last column. commit 674053e18b9e800f113471c19050ab468f73f233 Merge: 2210fbb 3dedcae Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Mon Apr 10 11:32:55 2017 +0200 Merge branch 'master' into reboot-control commit 2210fbb4947643988adef716594d79cf58482b87 Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Sat Apr 8 11:22:44 2017 +0200 [rebootcontrol] Adapt to changed task structure; handle clients by UUID commit 0ca01982028e08aa99e896d47c669ec77ec7dada Author: Simon Rettberg <rettberg@informatik.uni-freiburg.de> Date: Fri Apr 7 15:45:36 2017 +0200 [rebootcontrol] Fix config.tgz hook commit 0bb4ae9291452112895ec3f3703bc868ee8d80d2 Author: Simon Rettberg <spam@aol.com> Date: Tue Mar 14 19:54:53 2017 +0100 [rebootcontrol] Fix race condition when multiple config.tgz are generated Since the same file got created and deleted in a rapid succession, this could lead to the RecompressArchive task failing with a "file not found" error. commit 74e735de601961620fd65500c3f2375af16d61fa Author: Udo Walter <you@example.com> Date: Mon Mar 13 12:32:36 2017 +0100 [reboot-control] added status column commit 06d52b3c106d9db37b877297531da0f36452b81b Author: Udo Walter <you@example.com> Date: Thu Mar 9 13:15:05 2017 +0100 [reboot-control] added config-tgz hook to include the ssh key commit 83abeda07ab995b7439011829fb98caf94b5c63e Author: Christian Hofmaier <you@example.com> Date: Mon Mar 6 16:58:14 2017 +0100 [reboot-control] made sortable by checkbox column commit 207c78e4431689eb3e2944f6a3657522806abf89 Merge: 712a9a5 deaa6b2 Author: Udo Walter <you@example.com> Date: Mon Mar 6 15:41:37 2017 +0100 Merge remote-tracking branch 'origin/master' into reboot-control commit 712a9a5f2d51e3a866e31df94722919279ed7fc2 Author: Udo Walter <you@example.com> Date: Mon Mar 6 15:36:59 2017 +0100 [reboot-control] added possibility to generate a new ssh public private keypair commit d40de6044d4d63b4c25a1a950c4a126c6becc1ca Author: Christian Hofmaier <you@example.com> Date: Thu Mar 2 15:02:12 2017 +0100 [reboot-control] disabled possibility to reboot/shutdown if no clients selected commit d008eb6adb486857b898609e1e89adf4473be50c Merge: 5259dd5 7b198f9 Author: Udo Walter <you@example.com> Date: Thu Mar 2 14:42:28 2017 +0100 Merge remote-tracking branch 'origin/master' into reboot-control commit 5259dd5957a328fe4b12c9cf0e47f7c90c9d8bed Merge: 3c81c7e 1ed086a Author: Udo Walter <you@example.com> Date: Thu Mar 2 14:30:43 2017 +0100 Merge branch 'reboot-control' of git.openslx.org:openslx-ng/slx-admin into reboot-control commit 3c81c7eb35b878c01e1d4b70000dab6c76559012 Author: Christian Hofmaier <you@example.com> Date: Thu Mar 2 13:49:09 2017 +0100 [reboot-control] bugfix @ locationid=none commit 1925d0469626ee7cf4dd7baa9c05274c1d243c7c Author: Christian Hofmaier <you@example.com> Date: Thu Mar 2 13:40:11 2017 +0100 [reboot-control] if no clients are selected, redirect back, not to status page commit 0cd8ec5787baac16f4169b11cf9ce46733baa124 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 27 18:11:09 2017 +0100 [reboot-control] second (last) part javascript revision commit 1133a5c8b488c8381762e727dbeab31a1860e1eb Author: Christian Hofmaier <you@example.com> Date: Thu Feb 16 16:10:29 2017 +0100 [reboot-control] first small part javascript revision commit ace5ab7e45b232a54299d9a3c41c60fa839dfbc1 Author: Udo Walter <you@example.com> Date: Tue Feb 14 15:44:20 2017 +0100 [reboot-control] pass ssh private key to the reboot task commit 5df5cb3b8ae35d56eefe94e8557aa13cb1178391 Author: Christian Hofmaier <you@example.com> Date: Tue Feb 14 14:36:26 2017 +0100 [reboot-control] added comments commit ff3cbfbb8100071bc748ce15e4c37b1d028cbb0d Author: Christian Hofmaier <you@example.com> Date: Tue Feb 14 14:14:01 2017 +0100 [reboot-control] added api commit afce01b6f629b20e195a6e6ee4cd581d7ef8c079 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:54:45 2017 +0100 [reboot-control] fixed warning commit ff7c072d1462ed23de8441ebdb06d6435606a2b1 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:27:56 2017 +0100 [reboot-control] small css change commit 55195c89b7846c25d246badf96863bd24a9b8d2d Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:23:51 2017 +0100 [reboot-control] deleted slider, using textbox now for shutdown-timer commit 23864e7e658cd0e061875c5c353497c92ab77bc9 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 17:17:39 2017 +0100 [reboot-control] fixed typo. (extremely) small design change. commit 821501e290abd89c8ce78e764a997655779a71b1 Author: Udo Walter <you@example.com> Date: Thu Feb 9 16:54:55 2017 +0100 [reboot-control] added glyphicons to the buttons commit be5f54a29cde1b2de7577b022ec97032104d8437 Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:52:09 2017 +0100 [reboot-control] fixed bugs caused by renaming the module commit 89bb0068d2a232c9a2dc4ebf23e84f626db6e1a9 Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:21:52 2017 +0100 [reboot-control] fixed class name commit 60d080b30e02cf42395e1fe1b4ca7df11d4e3ff1 Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:11:15 2017 +0100 [reboot-control] renamed reboot_control to rebootcontrol commit dfc9a48705501c73c611674b0c7f2bfa3178a38c Author: Udo Walter <you@example.com> Date: Wed Feb 8 17:28:14 2017 +0100 [reboot-control] added status page and reorganized status update commit f7850d060dd32682ddc6795b2836ba45e9e6fe44 Author: Christian Hofmaier <you@example.com> Date: Tue Feb 7 15:58:48 2017 +0100 [reboot-control] variable language consistency commit 24757e1e2395ae70b8e06f9c72a6b3b1050895b3 Author: Udo Walter <you@example.com> Date: Fri Feb 3 17:52:53 2017 +0100 [reboot-control] improved (un)select all button commit f611bd2b594f0e0ffe8f81dfe0260758f86419ec Author: Christian Hofmaier <you@example.com> Date: Fri Feb 3 17:27:02 2017 +0100 [reboot-control] removed warning commit 1e58de43049dd6e2fa0ce0d32df0b1ed2e90cd63 Author: Christian Hofmaier <you@example.com> Date: Fri Feb 3 16:56:53 2017 +0100 [reboot-control] added stylesheet, Show hand-cursor for sortable columns, rows are now marked when chosen commit 6661568437cb36328b346730a3e0d5dc57e85cfc Author: Udo Walter <you@example.com> Date: Tue Jan 31 16:06:53 2017 +0100 [reboot-control] added option to shutdown clients in future (server-side) commit 4a0aa0bc53508206ff473aa9589c5160db3f85d5 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 31 15:13:58 2017 +0100 [reboot-control] added option to shutdown clients in future commit 0411d3f2a19d639229dba6a58bf5dbaccac607b3 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 31 13:06:28 2017 +0100 [reboot-control] added shutdown option/button commit 9155820fd6ebceae70d98e23479358229457868d Author: Udo Walter <you@example.com> Date: Fri Jan 27 13:06:06 2017 +0100 [reboot-control] added status translation commit 2715df67d51f07a7f80cb02759b1726fd8b0ddd3 Author: Christian Hofmaier <you@example.com> Date: Fri Jan 27 13:01:18 2017 +0100 [reboot-control] added table sorting (based on stupidtable plugin) commit 959fb45b039db04af759efe96d6c9b8eafda6227 Author: Udo Walter <you@example.com> Date: Fri Jan 27 11:58:09 2017 +0100 [reboot-control] added reboot functionality commit b54f970b8e229a976ac9938626bce1b065f741a8 Author: Christian Hofmaier <you@example.com> Date: Fri Jan 27 11:28:07 2017 +0100 [reboot-control] reboot buttons, style changes commit 4a5a933a91fad7a936790d57c5eea42d0f53f614 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 24 16:20:29 2017 +0100 [reboot-control] warningfix commit f9a6212eca2bbb9902827c8824d853f042ffa4f1 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 24 16:19:30 2017 +0100 [reboot-control] user interface commit 59e425ac93dfa181891cac92a435ef34972e90a7 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 17 16:05:12 2017 +0100 [reboot-control] created branch and empty files to work with commit 1ed086a17b248016882383a99c85356ee33342b2 Author: Christian Hofmaier <you@example.com> Date: Thu Mar 2 13:49:09 2017 +0100 [reboot-control] bugfix @ locationid=none commit ee36e147133d8b1a8ae3fe406677f3483bd1db02 Author: Christian Hofmaier <you@example.com> Date: Thu Mar 2 13:40:11 2017 +0100 [reboot-control] if no clients are selected, redirect back, not to status page commit 3c6d7946fd4e9e10649d8d41ab89bc90f83112ff Author: Christian Hofmaier <you@example.com> Date: Mon Feb 27 18:11:09 2017 +0100 [reboot-control] second (last) part javascript revision commit 550aa8a776cb1a328693f1fee5722f10c1ff07b8 Author: Christian Hofmaier <you@example.com> Date: Thu Feb 16 16:10:29 2017 +0100 [reboot-control] first small part javascript revision commit 67f851c63fd937ffca2f8a1e6174982591cded2a Merge: e1701e0 a7b1a42 Author: Udo Walter <you@example.com> Date: Tue Feb 14 15:46:05 2017 +0100 Merge remote-tracking branch 'origin/reboot-control' into reboot-control commit e1701e01065131777f8643b015b32832f21e864c Author: Udo Walter <you@example.com> Date: Tue Feb 14 15:44:20 2017 +0100 [reboot-control] pass ssh private key to the reboot task commit a7b1a42a00742b995a7ef6a8d28fa661966ec5be Author: Christian Hofmaier <you@example.com> Date: Tue Feb 14 14:36:26 2017 +0100 [reboot-control] added comments commit 7af992b2fcee946b04a1fdea4e7cc28b70436bf8 Author: Christian Hofmaier <you@example.com> Date: Tue Feb 14 14:14:01 2017 +0100 [reboot-control] added api commit 57beae14c82cd1a39a6b266963c78d81aa50d494 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:54:45 2017 +0100 [reboot-control] fixed warning commit 1a4842c22d7f8e85033f793bd801e2d82ec351c4 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:27:56 2017 +0100 [reboot-control] small css change commit da57d7491ad17a6d64be0c4624472ffa80efb43a Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 18:23:51 2017 +0100 [reboot-control] deleted slider, using textbox now for shutdown-timer commit a1b16068d4f56223587084897b7c6ca5a4c54805 Author: Christian Hofmaier <you@example.com> Date: Mon Feb 13 17:17:39 2017 +0100 [reboot-control] fixed typo. (extremely) small design change. commit a84f2f50eb4e0d6853020212f162780e23f0a696 Author: Udo Walter <you@example.com> Date: Thu Feb 9 16:54:55 2017 +0100 [reboot-control] added glyphicons to the buttons commit 06f52dfc9518ffda317cea2dab5cf16dbe66524c Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:52:09 2017 +0100 [reboot-control] fixed bugs caused by renaming the module commit e5edd1b1d4291ac403587e938200c24b3bcebb22 Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:21:52 2017 +0100 [reboot-control] fixed class name commit 1c2b3afc26dec0d9fefb7692b4670e80cd16362a Author: Udo Walter <you@example.com> Date: Thu Feb 9 15:11:15 2017 +0100 [reboot-control] renamed reboot_control to rebootcontrol commit f2e8624ecf9858276f63be10c279633fd45bf9d9 Author: Udo Walter <you@example.com> Date: Wed Feb 8 17:28:14 2017 +0100 [reboot-control] added status page and reorganized status update commit 0d918fead580748f5a3cb1db6ab754e34c844192 Author: Christian Hofmaier <you@example.com> Date: Tue Feb 7 15:58:48 2017 +0100 [reboot-control] variable language consistency commit 54fe25485bd1f09d332f50b3d0645b166e8012d7 Author: Udo Walter <you@example.com> Date: Fri Feb 3 17:52:53 2017 +0100 [reboot-control] improved (un)select all button commit 852261f194eb235e68f7256d3d526f5ae3936e13 Author: Christian Hofmaier <you@example.com> Date: Fri Feb 3 17:27:02 2017 +0100 [reboot-control] removed warning commit 63bbeb4f684eeb8e36a462cda76b3534f96a99e6 Author: Christian Hofmaier <you@example.com> Date: Fri Feb 3 16:56:53 2017 +0100 [reboot-control] added stylesheet, Show hand-cursor for sortable columns, rows are now marked when chosen commit d42b680196e727a0a67e5aaa954ba3a723c300f7 Author: Udo Walter <you@example.com> Date: Tue Jan 31 16:06:53 2017 +0100 [reboot-control] added option to shutdown clients in future (server-side) commit 49f8d204439d091970911d1528f9d3b839442d27 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 31 15:13:58 2017 +0100 [reboot-control] added option to shutdown clients in future commit 666b61b9d5786ffedc01dc4a20285dd607b443ba Author: Christian Hofmaier <you@example.com> Date: Tue Jan 31 13:06:28 2017 +0100 [reboot-control] added shutdown option/button commit d866d5e36fe7f5ef6f6fd7a9bd661b471a8b040d Author: Udo Walter <you@example.com> Date: Fri Jan 27 13:06:06 2017 +0100 [reboot-control] added status translation commit d56d8361c4113fc9ccaf575b372ea4c7ec0d4123 Author: Christian Hofmaier <you@example.com> Date: Fri Jan 27 13:01:18 2017 +0100 [reboot-control] added table sorting (based on stupidtable plugin) commit bac9bcbd84f3fc6d6b593e5ae4113b23f538ff6c Author: Udo Walter <you@example.com> Date: Fri Jan 27 11:58:09 2017 +0100 [reboot-control] added reboot functionality commit adfc00ebb64c8001927f5d60459c71b5da1b2dca Author: Christian Hofmaier <you@example.com> Date: Fri Jan 27 11:28:07 2017 +0100 [reboot-control] reboot buttons, style changes commit 3eac6c54c9999f810f17c05c460d086ce30fd6d8 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 24 16:20:29 2017 +0100 [reboot-control] warningfix commit 00f90f2d27593a18108e89daad48a99d5c150dea Author: Christian Hofmaier <you@example.com> Date: Tue Jan 24 16:19:30 2017 +0100 [reboot-control] user interface commit b576fc14ca48f47793c2cea2a701ae22cd3110a6 Author: Christian Hofmaier <you@example.com> Date: Tue Jan 17 16:05:12 2017 +0100 [reboot-control] created branch and empty files to work with
Diffstat (limited to 'modules-available/rebootcontrol')
-rw-r--r--modules-available/rebootcontrol/api.inc.php36
-rw-r--r--modules-available/rebootcontrol/clientscript.js22
-rw-r--r--modules-available/rebootcontrol/config.json4
-rw-r--r--modules-available/rebootcontrol/hooks/config-tgz.inc.php18
-rw-r--r--modules-available/rebootcontrol/inc/rebootqueries.inc.php44
-rw-r--r--modules-available/rebootcontrol/inc/sshkey.inc.php40
-rw-r--r--modules-available/rebootcontrol/lang/de/messages.json4
-rw-r--r--modules-available/rebootcontrol/lang/de/module.json5
-rw-r--r--modules-available/rebootcontrol/lang/de/template-tags.json31
-rw-r--r--modules-available/rebootcontrol/lang/en/messages.json4
-rw-r--r--modules-available/rebootcontrol/lang/en/module.json5
-rw-r--r--modules-available/rebootcontrol/lang/en/template-tags.json31
-rw-r--r--modules-available/rebootcontrol/lang/pt/template-tags.json30
-rw-r--r--modules-available/rebootcontrol/page.inc.php106
-rw-r--r--modules-available/rebootcontrol/style.css47
-rw-r--r--modules-available/rebootcontrol/templates/_page.html245
-rw-r--r--modules-available/rebootcontrol/templates/status.html62
17 files changed, 734 insertions, 0 deletions
diff --git a/modules-available/rebootcontrol/api.inc.php b/modules-available/rebootcontrol/api.inc.php
new file mode 100644
index 00000000..77687f8e
--- /dev/null
+++ b/modules-available/rebootcontrol/api.inc.php
@@ -0,0 +1,36 @@
+<?php
+/*
+ Needed POST-Parameters:
+ 'token' -- for authentification
+ 'action' -- which action should be performed (shutdown or reboot)
+ 'clients' -- which are to reboot/shutdown (json encoded array!)
+ 'timer' -- (optional) when to perform action in minutes (default value is 0)
+*/
+
+$ips = json_decode(Request::post('clients'));
+$minutes = Request::post('timer', 0, 'int');
+
+$clients = array();
+foreach ($ips as $client) {
+ $clients[] = array("ip" => $client);
+}
+
+if (Request::post('token') == Property::get("rebootcontrol_APIPOSTKEY")) {
+ if (Request::isPost()) {
+ if (Request::post('action') == 'shutdown') {
+ $shutdown = true;
+ $task = Taskmanager::submit("RemoteReboot", array("clients" => $clients, "shutdown" => $shutdown, "minutes" => $minutes));
+ echo $task["id"];
+ } else if (Request::post('action') == 'reboot') {
+ $shutdown = false;
+ $task = Taskmanager::submit("RemoteReboot", array("clients" => $clients, "shutdown" => $shutdown, "minutes" => $minutes));
+ echo $task["id"];
+ } else {
+ echo "Only action=shutdown and action=reboot available.";
+ }
+ } else {
+ echo "Only POST Method available.";
+ }
+} else {
+ echo "Not authorized";
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/clientscript.js b/modules-available/rebootcontrol/clientscript.js
new file mode 100644
index 00000000..d3ecbe48
--- /dev/null
+++ b/modules-available/rebootcontrol/clientscript.js
@@ -0,0 +1,22 @@
+document.addEventListener("DOMContentLoaded", function() {
+ var table = $("table");
+ table.stupidtable({
+ "ipsort":function(a,b){
+ var aa = a.split(".");
+ var bb = b.split(".");
+
+ var resulta = aa[0]*0x1000000 + aa[1]*0x10000 + aa[2]*0x100 + aa[3]*1;
+ var resultb = bb[0]*0x1000000 + bb[1]*0x10000 + bb[2]*0x100 + bb[3]*1;
+
+ return resulta-resultb;
+ }
+ });
+
+ table.on("aftertablesort", function (event, data) {
+ var th = $(this).find("th");
+ th.find(".arrow").remove();
+ var dir = $.fn.stupidtable.dir;
+ var arrow = data.direction === dir.ASC ? "down" : "up";
+ th.eq(data.column).append(' <span class="arrow glyphicon glyphicon-chevron-'+arrow+'"></span>');
+ });
+}); \ No newline at end of file
diff --git a/modules-available/rebootcontrol/config.json b/modules-available/rebootcontrol/config.json
new file mode 100644
index 00000000..2cc05822
--- /dev/null
+++ b/modules-available/rebootcontrol/config.json
@@ -0,0 +1,4 @@
+{
+ "category":"main.content",
+ "dependencies": [ "locations", "js_stupidtable" ]
+}
diff --git a/modules-available/rebootcontrol/hooks/config-tgz.inc.php b/modules-available/rebootcontrol/hooks/config-tgz.inc.php
new file mode 100644
index 00000000..0b706960
--- /dev/null
+++ b/modules-available/rebootcontrol/hooks/config-tgz.inc.php
@@ -0,0 +1,18 @@
+<?php
+
+$pubkey = SSHKey::getPublicKey();
+$tmpfile = '/tmp/bwlp-' . md5($pubkey) . '.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);
+ $file = $tmpfile;
+ } catch (Exception $e) {
+ EventLog::failure('Could not include ssh key for reboot-control in config.tgz', (string)$e);
+ }
+} elseif (is_file($tmpfile) && is_readable($tmpfile)) {
+ $file = $tmpfile;
+}
diff --git a/modules-available/rebootcontrol/inc/rebootqueries.inc.php b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
new file mode 100644
index 00000000..df3c13d8
--- /dev/null
+++ b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
@@ -0,0 +1,44 @@
+<?php
+
+class RebootQueries
+{
+
+ // Get Client+IP+CurrentVM+CurrentUser+Location to fill the table
+ public static function getMachineTable($locationId) {
+ if ($locationId === 0) {
+ $where = 'machine.locationid IS NULL';
+ } else {
+ $where = 'machine.locationid = :locationid';
+ }
+ $leftJoin = '';
+ $sessionField = 'machine.currentsession';
+ if (Module::get('dozmod') !== false) {
+ // SELECT lectureid, displayname FROM sat.lecture WHERE lectureid = :lectureid
+ $leftJoin = 'LEFT JOIN sat.lecture ON (lecture.lectureid = machine.currentsession)';
+ $sessionField = 'IFNULL(lecture.displayname, machine.currentsession) AS currentsession';
+ }
+ $res = Database::simpleQuery("
+ SELECT machine.machineuuid, machine.hostname, machine.clientip,
+ IF(machine.lastboot = 0 OR UNIX_TIMESTAMP() - machine.lastseen >= 600, 0, 1) AS status,
+ $sessionField, machine.currentuser, machine.locationid
+ FROM machine
+ $leftJoin
+ WHERE " . $where, array('locationid' => $locationId));
+ return $res->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ /**
+ * Get machines by list of UUIDs
+ * @param string[] $list list of system UUIDs
+ * @return array list of machines with machineuuid, clientip and locationid
+ */
+ public static function getMachinesByUuid($list)
+ {
+ if (empty($list))
+ return array();
+ $qs = '?' . str_repeat(',?', count($list) - 1);
+ $res = Database::simpleQuery("SELECT machineuuid, clientip, locationid FROM machine WHERE machineuuid IN ($qs)", $list);
+ return $res->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/inc/sshkey.inc.php b/modules-available/rebootcontrol/inc/sshkey.inc.php
new file mode 100644
index 00000000..b4e36d25
--- /dev/null
+++ b/modules-available/rebootcontrol/inc/sshkey.inc.php
@@ -0,0 +1,40 @@
+<?php
+
+class SSHKey
+{
+
+ public static function getPrivateKey() {
+ $privKey = Property::get("rebootcontrol-private-key");
+ if (!$privKey) {
+ $rsaKey = openssl_pkey_new(array(
+ 'private_key_bits' => 2048,
+ 'private_key_type' => OPENSSL_KEYTYPE_RSA));
+ openssl_pkey_export( openssl_pkey_get_private($rsaKey), $privKey);
+ Property::set("rebootcontrol-private-key", $privKey);
+ }
+ return $privKey;
+ }
+
+ public static function getPublicKey() {
+ $pkImport = openssl_pkey_get_private(self::getPrivateKey());
+ return self::sshEncodePublicKey($pkImport);
+ }
+
+ private static function sshEncodePublicKey($privKey) {
+ $keyInfo = openssl_pkey_get_details($privKey);
+ $buffer = pack("N", 7) . "ssh-rsa" .
+ self::sshEncodeBuffer($keyInfo['rsa']['e']) .
+ self::sshEncodeBuffer($keyInfo['rsa']['n']);
+ return "ssh-rsa " . base64_encode($buffer);
+ }
+
+ private static function sshEncodeBuffer($buffer) {
+ $len = strlen($buffer);
+ if (ord($buffer[0]) & 0x80) {
+ $len++;
+ $buffer = "\x00" . $buffer;
+ }
+ return pack("Na*", $len, $buffer);
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/de/messages.json b/modules-available/rebootcontrol/lang/de/messages.json
new file mode 100644
index 00000000..2a7e1299
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/de/messages.json
@@ -0,0 +1,4 @@
+{
+ "no-clients-selected": "Keine Clients ausgew\u00e4hlt",
+ "some-machine-not-found": "Einige Clients aus dem POST request wurden nicht gefunden"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/de/module.json b/modules-available/rebootcontrol/lang/de/module.json
new file mode 100644
index 00000000..03196610
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/de/module.json
@@ -0,0 +1,5 @@
+{
+ "module_name": "Reboot Control",
+ "notAssigned": "Nicht zugewiesen",
+ "page_title": "Reboot Control"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/de/template-tags.json b/modules-available/rebootcontrol/lang/de/template-tags.json
new file mode 100644
index 00000000..2a04e746
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/de/template-tags.json
@@ -0,0 +1,31 @@
+{
+ "lang_authFail": "Athentifizierung fehlgeschlagen",
+ "lang_client": "Client",
+ "lang_connecting": "Verbinde...",
+ "lang_error": "Nicht erreichbar",
+ "lang_genNew": "Neues Schl\u00fcsselpaar generieren",
+ "lang_ip": "IP",
+ "lang_location": "Standort",
+ "lang_minutes": " Minuten",
+ "lang_off": "Aus",
+ "lang_on": "An",
+ "lang_online": "Online",
+ "lang_pubKey": "SSH Public Key:",
+ "lang_reboot": "Neustarten",
+ "lang_rebootAt": "Neustart um:",
+ "lang_rebootButton": "Neustarten",
+ "lang_rebootCheck": "Wollen Sie wirklich die ausgew\u00e4hlten Rechner neustarten?",
+ "lang_rebooting": "Neustart...",
+ "lang_selectall": "Alle ausw\u00e4hlen",
+ "lang_selected": "Ausgew\u00e4hlt",
+ "lang_session": "Sitzung",
+ "lang_settings": "Einstellungen",
+ "lang_shutdown": "Herunterfahren",
+ "lang_shutdownAt": "Herunterfahren um: ",
+ "lang_shutdownButton": "Herunterfahren",
+ "lang_shutdownCheck": "Wollen Sie wirklich die ausgew\u00e4hlten Rechner herunterfahren?",
+ "lang_shutdownIn": "Herunterfahren in: ",
+ "lang_status": "Status",
+ "lang_unselectall": "Alle abw\u00e4hlen",
+ "lang_user": "Nutzer"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/en/messages.json b/modules-available/rebootcontrol/lang/en/messages.json
new file mode 100644
index 00000000..50bdd7fe
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/en/messages.json
@@ -0,0 +1,4 @@
+{
+ "no-clients-selected": "No clients selected",
+ "some-machine-not-found": "Some machines from your POST request don't exist"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/en/module.json b/modules-available/rebootcontrol/lang/en/module.json
new file mode 100644
index 00000000..129140dd
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/en/module.json
@@ -0,0 +1,5 @@
+{
+ "module_name": "Reboot Control",
+ "notAssigned": "Not assigned",
+ "page_title": "Reboot Control"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/en/template-tags.json b/modules-available/rebootcontrol/lang/en/template-tags.json
new file mode 100644
index 00000000..ca44171a
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/en/template-tags.json
@@ -0,0 +1,31 @@
+{
+ "lang_authFail": "Authentication failed",
+ "lang_client": "Client",
+ "lang_connecting": "Connecting...",
+ "lang_error": "Not available",
+ "lang_genNew": "Generate new keypair",
+ "lang_ip": "IP",
+ "lang_location": "Location",
+ "lang_minutes": " Minutes",
+ "lang_off": "Off",
+ "lang_on": "On",
+ "lang_online": "Online",
+ "lang_pubKey": "SSH Public Key:",
+ "lang_reboot": "Reboot",
+ "lang_rebootAt": "Reboot at:",
+ "lang_rebootButton": "Reboot",
+ "lang_rebootCheck": "Do you really want to reboot the selected clients?",
+ "lang_rebooting": "Rebooting...",
+ "lang_selectall": "Select all",
+ "lang_selected": "Selected",
+ "lang_session": "Session",
+ "lang_settings": "Settings",
+ "lang_shutdown": "Shut Down",
+ "lang_shutdownAt": "Shutdown at: ",
+ "lang_shutdownButton": "Shutdown",
+ "lang_shutdownCheck": "Do you really want to shut down the selected clients?",
+ "lang_shutdownIn": "Shutdown in: ",
+ "lang_status": "Status",
+ "lang_unselectall": "Unselect all",
+ "lang_user": "User"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/pt/template-tags.json b/modules-available/rebootcontrol/lang/pt/template-tags.json
new file mode 100644
index 00000000..89fa4d96
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/pt/template-tags.json
@@ -0,0 +1,30 @@
+{
+ "lang_client": "Client",
+ "lang_ip": "IP",
+ "lang_session": "Session",
+ "lang_user": "User",
+ "lang_location": "Location",
+ "lang_locations": "Locations",
+ "lang_selectall": "Select all",
+ "lang_unselectall": "Unselect all",
+ "lang_status": "Status",
+ "lang_rebootButton": "Reboot",
+ "lang_rebootCheck": "Do you really want to reboot the selected clients?",
+ "lang_shutdownButton": "Shut Down",
+ "lang_shutdownCheck": "Do you really want to shut down the selected clients?",
+ "lang_cancel": "Cancel",
+ "lang_reboot": "Reboot",
+ "lang_connecting": "Connecting...",
+ "lang_rebooting": "Rebooting...",
+ "lang_online": "Online",
+ "lang_error": "Not available",
+ "lang_shutdown": "Shut Down",
+ "lang_shutdownIn": "Shutdown in: ",
+ "lang_shutdownAt": "Shutdown at: ",
+ "lang_minutes": " Minutes",
+ "lang_back": "Back",
+ "lang_pubKey": "SSH Public Key:",
+ "lang_settings": "Settings",
+ "lang_genNew": "Generate new keypair",
+ "lang_selected": "Selected"
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/page.inc.php b/modules-available/rebootcontrol/page.inc.php
new file mode 100644
index 00000000..d7083528
--- /dev/null
+++ b/modules-available/rebootcontrol/page.inc.php
@@ -0,0 +1,106 @@
+<?php
+
+class Page_RebootControl extends Page
+{
+
+ private $action = false;
+
+ /**
+ * Called before any page rendering happens - early hook to check parameters etc.
+ */
+ protected function doPreprocess()
+ {
+ User::load();
+
+ if (!User::isLoggedIn()) {
+ Message::addError('main.no-permission');
+ Util::redirect('?do=Main'); // does not return
+ }
+
+ $this->action = Request::any('action', 'show', 'string');
+
+
+ if ($this->action === 'startReboot' || $this->action === 'startShutdown') {
+ $clients = Request::post('clients');
+ if (!is_array($clients) || empty($clients)) {
+ Message::addError('no-clients-selected');
+ Util::redirect();
+ }
+ $locationId = Request::post('locationId', false, 'int');
+ if ($locationId === false) {
+ Message::addError('locations.invalid-location-id', $locationId);
+ Util::redirect();
+ }
+ $shutdown = $this->action === "startShutdown";
+ $minutes = Request::post('minutes', 0, 'int');
+ $privKey = SSHKey::getPrivateKey();
+
+ $list = RebootQueries::getMachinesByUuid($clients);
+ if (count($list) !== count($clients)) {
+ // We could go ahead an see which ones were not found in DB but this should not happen anyways unless the
+ // user manipulated the request
+ Message::addWarning('some-machine-not-found');
+ }
+ // TODO: Iterate over list and check if a locationid is not in permissions
+ // 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
+ ));
+
+ Util::redirect("?do=rebootcontrol&taskid=".$task["id"]);
+ }
+
+ }
+
+ /**
+ * Menu etc. has already been generated, now it's time to generate page content.
+ */
+
+ protected function doRender()
+ {
+ if ($this->action === 'show') {
+
+ $taskId = Request::get("taskid");
+
+ if ($taskId && Taskmanager::isTask($taskId)) {
+ $task = Taskmanager::status($taskId);
+ $data['taskId'] = $taskId;
+ $data['locationId'] = $task['data']['locationId'];
+ $data['locationName'] = Location::getName($task['data']['locationId']);
+ $data['clients'] = $task['data']['clients'];
+ Render::addTemplate('status', $data);
+ } else {
+ //location you want to see, default are "not assigned" clients
+ $requestedLocation = Request::get('location', 0, 'int');
+
+ $data['data'] = RebootQueries::getMachineTable($requestedLocation);
+ $data['locations'] = Location::getLocations($requestedLocation, 0, true);
+
+ $data['pubKey'] = SSHKey::getPublicKey();
+
+ Render::addTemplate('_page', $data);
+ }
+ }
+ }
+
+ function doAjax()
+ {
+ $this->action = Request::post('action', false, 'string');
+ if ($this->action === 'generateNewKeypair') {
+ Property::set("rebootcontrol-private-key", false);
+ echo SSHKey::getPublicKey();
+ } else {
+ echo 'Invalid action.';
+ }
+ }
+
+
+
+}
diff --git a/modules-available/rebootcontrol/style.css b/modules-available/rebootcontrol/style.css
new file mode 100644
index 00000000..442cd5de
--- /dev/null
+++ b/modules-available/rebootcontrol/style.css
@@ -0,0 +1,47 @@
+.rebootTimerForm {
+ margin-top: 20px;
+}
+
+.statusColumn {
+ text-align: center;
+}
+
+.table > tbody > tr > td {
+ vertical-align: middle;
+ height: 50px;
+}
+
+.checkbox {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+#rebootButton, #settingsButton, #selectAllButton, #unselectAllButton {
+ margin-left: 10px;
+}
+
+#rebootButton, #shutdownButton, #selectAllButton, #unselectAllButton {
+ width: 140px;
+}
+
+#dataTable {
+ margin-top: 20px;
+}
+
+
+#shutdownTimer {
+ text-align: center;
+}
+#pubKeyTitle {
+ display: inline-block;
+ margin-top: 7px;
+ margin-bottom: 20px;
+}
+
+pre {
+ white-space: pre-wrap;
+}
+
+th[data-sort] {
+ cursor: pointer;
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/templates/_page.html b/modules-available/rebootcontrol/templates/_page.html
new file mode 100644
index 00000000..690316df
--- /dev/null
+++ b/modules-available/rebootcontrol/templates/_page.html
@@ -0,0 +1,245 @@
+<form id="tableDataForm" method="post" action="?do=rebootcontrol" class="form-inline">
+ <input type="hidden" name="token" value="{{token}}">
+ <div class="row">
+ <div class="col-md-12">
+ <label>{{lang_location}}:
+ <select id="locationDropdown" name="locationId" class="form-control" onchange="selectLocation()">
+ {{#locations}}
+ <option value="{{locationid}}" {{#selected}}selected{{/selected}}>{{locationpad}} {{locationname}}</option>
+ {{/locations}}
+ </select>
+ </label>
+ <button type="button" id="settingsButton" class="btn btn-default pull-right" data-toggle="modal" data-target="#settingsModal"><span class="glyphicon glyphicon-cog"></span></button>
+ <button type="button" id="selectAllButton" class="btn btn-primary pull-right" onclick="selectAllRows()"><span class="glyphicon glyphicon-check"></span> {{lang_selectall}}</button>
+ <button type="button" id="unselectAllButton" class="btn btn-default pull-right" onclick="unselectAllRows()" style="display: none;"><span class="glyphicon glyphicon-unchecked"></span> {{lang_unselectall}}</button>
+ <button type="button" id="rebootButton" class="btn btn-warning pull-right" data-toggle="modal" data-target="#rebootModal" disabled><span class="glyphicon glyphicon-repeat"></span> {{lang_rebootButton}}</button>
+ <button type="button" id="shutdownButton" class="btn btn-danger pull-right" data-toggle="modal" data-target="#shutdownModal" disabled><span class="glyphicon glyphicon-off"></span> {{lang_shutdownButton}}</button>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-12">
+ <table class="table table-condensed table-hover" id="dataTable">
+ <thead>
+ <tr>
+ <th data-sort="string">{{lang_client}}</th>
+ <th data-sort="ipsort">{{lang_ip}}</th>
+ <th data-sort="string">{{lang_status}}</th>
+ <th data-sort="string">{{lang_session}}</th>
+ <th data-sort="string">{{lang_user}}</th>
+ <th data-sort="int" data-sort-default="desc">{{lang_selected}}</th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {{#data}}
+ <tr>
+ <td>
+ {{hostname}}
+ {{^hostname}}{{clientip}}{{/hostname}}
+ </td>
+ <td>{{clientip}}</td>
+ <td class="statusColumn">
+ {{#status}}
+ {{lang_on}}
+ {{/status}}
+ {{^status}}
+ {{lang_off}}
+ {{/status}}
+ </td>
+ <td>{{#status}}{{currentsession}}{{/status}}</td>
+ <td>{{#status}}{{currentuser}}{{/status}}</td>
+ <td data-sort-value="0" class="checkboxColumn">
+ <div class="checkbox">
+ <input id="m-{{machineuuid}}" type="checkbox" name="clients[]" value='{{machineuuid}}'>
+ <label for="m-{{machineuuid}}"></label>
+ </div>
+ </td>
+ </tr>
+ {{/data}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+
+ <!-- Modals -->
+
+ <div id="settingsModal" 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_settings}}</b></h4>
+ </div>
+ <div class="modal-body">
+ <span id="pubKeyTitle">{{lang_pubKey}}</span>
+ <button class="btn btn-s btn-warning pull-right" onclick="generateNewKeypair()" type="button">{{lang_genNew}}</button>
+ <pre id="pubKey">{{pubKey}}</pre>
+ </div>
+ <div class="modal-footer">
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class ="modal fade" id="rebootModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel">{{lang_rebootButton}}</h4>
+ </div>
+ <div class="modal-body">
+ {{lang_rebootCheck}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="startReboot" class="btn btn-warning"><span class="glyphicon glyphicon-repeat"></span> {{lang_reboot}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class ="modal fade" id="shutdownModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <h4 class="modal-title" id="myModalLabel2">{{lang_shutdownButton}}</h4>
+ </div>
+ <div class="modal-body">
+ {{lang_shutdownCheck}}
+ {{lang_shutdownIn}} <input id="shutdownTimer" name="minutes" title="{{lang_shutdownIn}}" type="number" value="0" min="0" onkeypress="return isNumberKey(event)"> {{lang_minutes}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">{{lang_cancel}}</button>
+ <button type="submit" name="action" value="startShutdown" class="btn btn-danger"><span class="glyphicon glyphicon-off"></span> {{lang_shutdownButton}}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</form>
+
+
+<script type="application/javascript">
+ document.addEventListener("DOMContentLoaded", function() {
+ markCheckedRows();
+
+ $('input:checkbox').change(
+ function(){
+ //give each checkbox the function to mark the row (in green)
+ if ($(this).is(':checked')) {
+ markRows($(this).closest("tr"), true);
+ $(this).closest("td").data("sort-value", 1);
+ } else {
+ markRows($(this).closest("tr"), false);
+ $(this).closest("td").data("sort-value", 0);
+ }
+
+ //if all are checked, change the selectAll-Button to unselectAll. if one is not checked, change unselectAll to selectAll
+ var dataTable = $("#dataTable");
+ var unchecked = dataTable.find("input[type=checkbox]:not(:checked)").length;
+ if (unchecked == 0) {
+ $('#selectAllButton').hide();
+ $('#unselectAllButton').show();
+ } else if (unchecked == 1) {
+ $('#selectAllButton').show();
+ $('#unselectAllButton').hide();
+ }
+
+ //if no client is selected, disable the shutdown/reboot button, and enable them if a client is selected
+ var checked = dataTable.find("input[type=checkbox]:checked").length;
+ if (checked == 0) {
+ $('#rebootButton').prop('disabled', true);
+ $('#shutdownButton').prop('disabled', true);
+ } else {
+ $('#rebootButton').prop('disabled', false);
+ $('#shutdownButton').prop('disabled', false);
+ }
+ });
+ $('.checkboxColumn').click(function(e) {
+ if (e.target === this) {
+ $(this).find('input[type="checkbox"]').click();
+ }
+ });
+ });
+
+ // Change Location when selected in Dropdown Menu
+ function selectLocation() {
+ var dropdown = $("#locationDropdown");
+ var location = dropdown.val();
+ window.location.replace("?do=rebootcontrol&location="+location);
+ }
+
+ // Check all checkboxes, change selectAll button, make shutdown/reboot button enabled as clients will certainly be selected
+ function selectAllRows() {
+ var checked = $("tr input:checkbox:checked");
+
+ //change button
+ $('#selectAllButton').hide();
+ $('#unselectAllButton').show();
+
+ //check rows and mark them
+ $('input[type="checkbox"]', '#dataTable').prop('checked', true);
+ markRows($("tr:not(:first)"), true);
+ $(".checkboxColumn").data("sort-value", 1);
+
+ //enable shutdown/reboot button
+ $('#rebootButton').prop('disabled', false);
+ $('#shutdownButton').prop('disabled', false);
+ }
+
+ // Uncheck all checkboxes, change unselectAll Button, make shutdown/reboot button disabled as clients will certainly be not selected
+ function unselectAllRows() {
+ //change button
+ $('#selectAllButton').show();
+ $('#unselectAllButton').hide();
+
+ //uncheck rows and unmark them
+ $('input[type="checkbox"]', '#dataTable').prop('checked', false);
+ markRows($("tr"), false);
+ $(".checkboxColumn").data("sort-value", 0);
+
+ //disable shutdown/reboot button
+ $('#rebootButton').prop('disabled', true);
+ $('#shutdownButton').prop('disabled', true);
+ }
+
+ // mark all previous checked rows (used when loading site)
+ function markCheckedRows() {
+ var checked = $("tr input:checkbox:checked");
+ markRows(checked.closest("tr"), true);
+ var unchecked = $("#dataTable").find("input[type=checkbox]:not(:checked)").length;
+ if(unchecked == 0) {
+ $('#selectAllButton').hide();
+ $('#unselectAllButton').show();
+ }
+ }
+
+ // only allow numbers to get typed into the "shutdown in X Minutes" box.
+ function isNumberKey(evt){
+ var charCode = (evt.which) ? evt.which : event.keyCode;
+ return !(charCode > 31 && (charCode < 48 || charCode > 57));
+ }
+
+ function markRows($rows, marked) {
+ if (marked) {
+ $rows.addClass('active');
+ } else {
+ $rows.removeClass('active');
+ }
+ }
+
+ function generateNewKeypair() {
+ $.ajax({
+ url: '?do=rebootcontrol',
+ type: 'POST',
+ data: { action: "generateNewKeypair", token: TOKEN },
+ success: function(value) {
+ $('#pubKey').text(value);
+ }
+ });
+ }
+
+</script> \ No newline at end of file
diff --git a/modules-available/rebootcontrol/templates/status.html b/modules-available/rebootcontrol/templates/status.html
new file mode 100644
index 00000000..35bbe42f
--- /dev/null
+++ b/modules-available/rebootcontrol/templates/status.html
@@ -0,0 +1,62 @@
+<div>
+ <form class="form-inline">
+ <b>{{lang_location}}: {{locationName}}</b>
+ <input type="hidden" name="do" value="rebootcontrol">
+ <input type="hidden" name="location" value="{{locationId}}">
+ <button type="submit" class="btn btn-primary pull-right"><span class="glyphicon glyphicon-arrow-left"></span> {{lang_back}}</button>
+ </form>
+</div>
+
+<div data-tm-id="{{taskId}}" data-tm-log="error" data-tm-callback="updateStatus"></div>
+
+<div>
+ <table class="table table-hover" id="dataTable">
+ <thead>
+ <tr>
+ <th data-sort="string">{{lang_client}}</th>
+ <th data-sort="ipsort">{{lang_ip}}</th>
+ <th data-sort="string">
+ {{lang_status}}
+ </th>
+ </tr>
+ </thead>
+
+ <tbody>
+ {{#clients}}
+ <tr>
+ <td>{{machineuuid}}</td>
+ <td>{{clientip}}</td>
+ <td id="status-{{machineuuid}}"></td>
+ </tr>
+ {{/clients}}
+ </tbody>
+ </table>
+</div>
+
+<script type="application/javascript">
+ statusStrings = {
+ "CONNECTING" : "{{lang_connecting}}",
+ "REBOOTING" : "{{lang_rebooting}}",
+ "REBOOT_AT" : "{{lang_rebootAt}}",
+ "ONLINE" : "{{lang_online}}",
+ "ERROR" : "{{lang_error}}",
+ "SHUTDOWN" : "{{lang_shutdown}}",
+ "SHUTDOWN_AT" : "{{lang_shutdownAt}}",
+ "AUTH_FAIL" : "{{lang_authFail}}"
+ };
+
+ function updateStatus(task) {
+ if (!task || !task.data || !task.data.clientStatus)
+ return;
+ var clientStatus = task.data.clientStatus;
+ for (var uuid in clientStatus) {
+ if (clientStatus.hasOwnProperty(uuid)) {
+ var shutdownTime = ' ';
+ if (clientStatus[uuid] === 'SHUTDOWN_AT' || clientStatus[uuid] === 'REBOOT_AT') {
+ shutdownTime += task.data.time;
+ }
+ $("#status-" + uuid).text(statusStrings[clientStatus[uuid]] + shutdownTime);
+ }
+ }
+ }
+</script>