summaryrefslogtreecommitdiffstats
path: root/modules-available/rebootcontrol
diff options
context:
space:
mode:
authorroot2019-02-19 18:53:50 +0100
committerroot2019-02-19 18:53:50 +0100
commit0ad4c0f8196b61699754762aacbaab0223478ab9 (patch)
treede434c4aea8d07ecd01cd3badd48d057d62c2d1b /modules-available/rebootcontrol
parent[usb-lock-off] Edit rule cleanup and fix of the dropdown boxes. (diff)
parent[statistics] Fix RAM change warning to handle increase too (diff)
downloadslx-admin-usb-lock-off.tar.gz
slx-admin-usb-lock-off.tar.xz
slx-admin-usb-lock-off.zip
Merge branch 'master' into usb-lock-offusb-lock-off
Diffstat (limited to 'modules-available/rebootcontrol')
-rw-r--r--modules-available/rebootcontrol/config.json9
-rw-r--r--modules-available/rebootcontrol/inc/rebootcontrol.inc.php65
-rw-r--r--modules-available/rebootcontrol/inc/rebootqueries.inc.php4
-rw-r--r--modules-available/rebootcontrol/lang/de/module.json1
-rw-r--r--modules-available/rebootcontrol/lang/de/permissions.json5
-rw-r--r--modules-available/rebootcontrol/lang/de/template-tags.json11
-rw-r--r--modules-available/rebootcontrol/lang/en/module.json1
-rw-r--r--modules-available/rebootcontrol/lang/en/permissions.json5
-rw-r--r--modules-available/rebootcontrol/lang/en/template-tags.json11
-rw-r--r--modules-available/rebootcontrol/page.inc.php130
-rw-r--r--modules-available/rebootcontrol/permissions/permissions.json11
-rw-r--r--modules-available/rebootcontrol/style.css13
-rw-r--r--modules-available/rebootcontrol/templates/_page.html256
-rw-r--r--modules-available/rebootcontrol/templates/header.html79
-rw-r--r--modules-available/rebootcontrol/templates/status.html26
-rw-r--r--modules-available/rebootcontrol/templates/task-list.html33
16 files changed, 436 insertions, 224 deletions
diff --git a/modules-available/rebootcontrol/config.json b/modules-available/rebootcontrol/config.json
index d8ab5868..43d2c28f 100644
--- a/modules-available/rebootcontrol/config.json
+++ b/modules-available/rebootcontrol/config.json
@@ -1,4 +1,7 @@
{
- "category":"main.beta",
- "dependencies": [ "locations", "js_stupidtable" ]
-}
+ "category": "main.beta",
+ "dependencies": [
+ "locations",
+ "js_stupidtable"
+ ]
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
index 789552cd..ec4b84ed 100644
--- a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
+++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php
@@ -3,28 +3,83 @@
class RebootControl
{
+ const KEY_TASKLIST = 'rebootcontrol.tasklist';
+
+ const REBOOT = 'REBOOT';
+ const KEXEC_REBOOT = 'KEXEC_REBOOT';
+ const SHUTDOWN = 'SHUTDOWN';
+
/**
* @param string[] $uuids List of machineuuids to reboot
+ * @param bool $kexec whether to trigger kexec-reboot instead of full BIOS cycle
* @return false|array task struct for the reboot job
*/
- public static function reboot($uuids)
+ public static function reboot($uuids, $kexec = false)
{
$list = RebootQueries::getMachinesByUuid($uuids);
if (empty($list))
return false;
- return self::execute($list, false, 0, 0);
+ return self::execute($list, $kexec ? RebootControl::KEXEC_REBOOT : RebootControl::REBOOT, 0, 0);
}
- public static function execute($list, $shutdown, $minutes, $locationId)
+ /**
+ * @param array $list list of clients containing each keys 'machineuuid' and 'clientip'
+ * @param string $mode reboot mode: RebootControl::REBOOT ::KEXEC_REBOOT or ::SHUTDOWN
+ * @param int $minutes delay in minutes for action
+ * @param int $locationId meta data only: locationId of clients
+ * @return array|false the task, or false if it could not be started
+ */
+ public static function execute($list, $mode, $minutes, $locationId)
{
- return Taskmanager::submit("RemoteReboot", array(
+ $task = Taskmanager::submit("RemoteReboot", array(
"clients" => $list,
- "shutdown" => $shutdown,
+ "mode" => $mode,
"minutes" => $minutes,
"locationId" => $locationId,
"sshkey" => SSHKey::getPrivateKey(),
"port" => 9922, // Hard-coded, must match mgmt-sshd module
));
+ if (!Taskmanager::isFailed($task)) {
+ Property::addToList(RebootControl::KEY_TASKLIST, $locationId . '/' . $task["id"], 60 * 24);
+ }
+ return $task;
+ }
+
+ /**
+ * @param int[]|null $locations filter by these locations
+ * @return array list of active tasks for reboots/shutdowns.
+ */
+ public static function getActiveTasks($locations = null)
+ {
+ if (is_array($locations) && in_array(0,$locations)) {
+ $locations = null;
+ }
+ $list = Property::getList(RebootControl::KEY_TASKLIST);
+ $return = [];
+ foreach ($list as $entry) {
+ $p = explode('/', $entry, 2);
+ if (count($p) !== 2) {
+ Property::removeFromList(RebootControl::KEY_TASKLIST, $entry);
+ continue;
+ }
+ if (is_array($locations) && !in_array($p[0], $locations)) // Ignore
+ continue;
+ $id = $p[1];
+ $task = Taskmanager::status($id);
+ if (!Taskmanager::isTask($task)) {
+ Property::removeFromList(RebootControl::KEY_TASKLIST, $entry);
+ continue;
+ }
+ $return[] = [
+ 'taskId' => $task['id'],
+ 'locationId' => $task['data']['locationId'],
+ 'time' => $task['data']['time'],
+ 'mode' => $task['data']['mode'],
+ 'clientCount' => count($task['data']['clients']),
+ 'status' => $task['statusCode'],
+ ];
+ }
+ return $return;
}
} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/inc/rebootqueries.inc.php b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
index 3dc3183f..063b36e4 100644
--- a/modules-available/rebootcontrol/inc/rebootqueries.inc.php
+++ b/modules-available/rebootcontrol/inc/rebootqueries.inc.php
@@ -44,13 +44,13 @@ class RebootQueries
/**
* Get machines by list of UUIDs
* @param string[] $list list of system UUIDs
- * @return array list of machines with machineuuid, clientip and locationid
+ * @return array list of machines with machineuuid, hostname, clientip, state and locationid
*/
public static function getMachinesByUuid($list)
{
if (empty($list))
return array();
- $res = Database::simpleQuery("SELECT machineuuid, clientip, locationid FROM machine
+ $res = Database::simpleQuery("SELECT machineuuid, hostname, clientip, state, locationid FROM machine
WHERE machineuuid IN (:list)", compact('list'));
return $res->fetchAll(PDO::FETCH_ASSOC);
}
diff --git a/modules-available/rebootcontrol/lang/de/module.json b/modules-available/rebootcontrol/lang/de/module.json
index 03196610..1f325354 100644
--- a/modules-available/rebootcontrol/lang/de/module.json
+++ b/modules-available/rebootcontrol/lang/de/module.json
@@ -1,5 +1,4 @@
{
"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/permissions.json b/modules-available/rebootcontrol/lang/de/permissions.json
new file mode 100644
index 00000000..12ec4c83
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/de/permissions.json
@@ -0,0 +1,5 @@
+{
+ "action.shutdown": "Client herunterfahren.",
+ "action.reboot": "Client neustarten.",
+ "newkeypair": "Neues Schlüsselpaar generieren."
+} \ 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
index c5bd1670..c678ef88 100644
--- a/modules-available/rebootcontrol/lang/de/template-tags.json
+++ b/modules-available/rebootcontrol/lang/de/template-tags.json
@@ -1,12 +1,18 @@
{
+ "lang_activeTasks": "Laufende Jobs",
"lang_authFail": "Authentifizierung fehlgeschlagen",
"lang_client": "Client",
+ "lang_clientCount": "# Clients",
+ "lang_confirmNewKeypair": "Wirklich neues Schl\u00fcsselpaar erzeugen?",
"lang_connecting": "Verbinde...",
"lang_error": "Nicht erreichbar",
"lang_genNew": "Neues Schl\u00fcsselpaar generieren",
"lang_ip": "IP",
+ "lang_kexecRebootCheck": "Schneller Reboot direkt in bwLehrpool",
"lang_location": "Standort",
"lang_minutes": " Minuten",
+ "lang_mode": "Modus",
+ "lang_newKeypairExplanation": "Sie k\u00f6nnen ein neues Schl\u00fcsselpaar erzeugen lassen. In diesem Fall wird das alte Schl\u00fcsselpaar verworfen, sodass alle zum jetzigen Zeitpunkt bereits gestarteten Rechner nicht mehr aus der Ferne bedient werden k\u00f6nnen, bis diese manuell neugestartet wurden.",
"lang_off": "Aus",
"lang_on": "An",
"lang_online": "Online",
@@ -16,6 +22,7 @@
"lang_rebootButton": "Neustarten",
"lang_rebootCheck": "Wollen Sie die ausgew\u00e4hlten Rechner wirklich neustarten?",
"lang_rebootControl": "Reboot Control",
+ "lang_rebootIn": "Neustart in:",
"lang_rebooting": "Neustart...",
"lang_selectall": "Alle ausw\u00e4hlen",
"lang_selected": "Ausgew\u00e4hlt",
@@ -27,6 +34,6 @@
"lang_shutdownCheck": "Wollen Sie die ausgew\u00e4hlten Rechner wirklich herunterfahren?",
"lang_shutdownIn": "Herunterfahren in: ",
"lang_status": "Status",
- "lang_unselectall": "Alle abw\u00e4hlen",
- "lang_user": "Nutzer"
+ "lang_time": "Zeit",
+ "lang_unselectall": "Alle abw\u00e4hlen"
} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/lang/en/module.json b/modules-available/rebootcontrol/lang/en/module.json
index 129140dd..1f325354 100644
--- a/modules-available/rebootcontrol/lang/en/module.json
+++ b/modules-available/rebootcontrol/lang/en/module.json
@@ -1,5 +1,4 @@
{
"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/permissions.json b/modules-available/rebootcontrol/lang/en/permissions.json
new file mode 100644
index 00000000..34badbaf
--- /dev/null
+++ b/modules-available/rebootcontrol/lang/en/permissions.json
@@ -0,0 +1,5 @@
+{
+ "action.shutdown": "Shutdown Client.",
+ "action.reboot": "Reboot Client.",
+ "newkeypair": "Generate new Keypair."
+} \ 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
index 63a5b4a8..c64014ff 100644
--- a/modules-available/rebootcontrol/lang/en/template-tags.json
+++ b/modules-available/rebootcontrol/lang/en/template-tags.json
@@ -1,12 +1,18 @@
{
+ "lang_activeTasks": "Active tasks",
"lang_authFail": "Authentication failed",
"lang_client": "Client",
+ "lang_clientCount": "# clients",
+ "lang_confirmNewKeypair": "Really create new key pair?",
"lang_connecting": "Connecting...",
"lang_error": "Not available",
"lang_genNew": "Generate new keypair",
"lang_ip": "IP",
+ "lang_kexecRebootCheck": "Quick reboot straight to bwLehrpool (kexec)",
"lang_location": "Location",
"lang_minutes": " Minutes",
+ "lang_mode": "Mode",
+ "lang_newKeypairExplanation": "You can create a new keypair, which will replace the old one. Please note that after doing so, you cannot poweroff or reboot clients that are already running, since they still use the old key. They have to be rebooted manually first.",
"lang_off": "Off",
"lang_on": "On",
"lang_online": "Online",
@@ -16,6 +22,7 @@
"lang_rebootButton": "Reboot",
"lang_rebootCheck": "Do you really want to reboot the selected clients?",
"lang_rebootControl": "Reboot Control",
+ "lang_rebootIn": "Reboot in:",
"lang_rebooting": "Rebooting...",
"lang_selectall": "Select all",
"lang_selected": "Selected",
@@ -27,6 +34,6 @@
"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"
+ "lang_time": "Time",
+ "lang_unselectall": "Unselect all"
} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/page.inc.php b/modules-available/rebootcontrol/page.inc.php
index fc3ded8f..3a438504 100644
--- a/modules-available/rebootcontrol/page.inc.php
+++ b/modules-available/rebootcontrol/page.inc.php
@@ -20,33 +20,57 @@ class Page_RebootControl extends Page
$this->action = Request::any('action', 'show', 'string');
- if ($this->action === 'startReboot' || $this->action === 'startShutdown') {
- $clients = Request::post('clients');
- if (!is_array($clients) || empty($clients)) {
+ if ($this->action === 'reboot' || $this->action === 'shutdown') {
+
+ $requestedClients = Request::post('clients', false, 'array');
+ if (!is_array($requestedClients) || empty($requestedClients)) {
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');
- $list = RebootQueries::getMachinesByUuid($clients);
- if (count($list) !== count($clients)) {
+ $actualClients = RebootQueries::getMachinesByUuid($requestedClients);
+ if (count($actualClients) !== count($requestedClients)) {
// 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 = RebootControl::execute($list, $shutdown, $minutes, $locationId);
-
- Util::redirect("?do=rebootcontrol&taskid=".$task["id"]);
+ // Filter ones with no permission
+ foreach (array_keys($actualClients) as $idx) {
+ if (!User::hasPermission('action.' . $this->action, $actualClients[$idx]['locationid'])) {
+ Message::addWarning('locations.no-permission-location', $actualClients[$idx]['locationid']);
+ unset($actualClients[$idx]);
+ } else {
+ $locationId = $actualClients[$idx]['locationid'];
+ }
+ }
+ // See if anything is left
+ if (!is_array($actualClients) || empty($actualClients)) {
+ Message::addError('no-clients-selected');
+ Util::redirect();
+ }
+ usort($actualClients, function($a, $b) {
+ $a = ($a['state'] === 'IDLE' || $a['state'] === 'OCCUPIED');
+ $b = ($b['state'] === 'IDLE' || $b['state'] === 'OCCUPIED');
+ if ($a === $b)
+ return 0;
+ return $a ? -1 : 1;
+ });
+ if ($this->action === 'shutdown') {
+ $mode = 'SHUTDOWN';
+ $minutes = Request::post('s-minutes', 0, 'int');
+ } elseif (Request::any('quick', false, 'string') === 'on') {
+ $mode = 'KEXEC_REBOOT';
+ $minutes = Request::post('r-minutes', 0, 'int');
+ } else {
+ $mode = 'REBOOT';
+ $minutes = Request::post('r-minutes', 0, 'int');
+ }
+ $task = RebootControl::execute($actualClients, $mode, $minutes, $locationId);
+ if (Taskmanager::isTask($task)) {
+ Util::redirect("?do=rebootcontrol&taskid=" . $task["id"]);
+ } else {
+ Util::redirect("?do=rebootcontrol");
+ }
}
}
@@ -59,25 +83,76 @@ class Page_RebootControl extends Page
{
if ($this->action === 'show') {
- $taskId = Request::get("taskid");
+ $data = [];
+ $task = Request::get("taskid", false, 'string');
+ if ($task !== false) {
+ $task = Taskmanager::status($task);
+ }
- if ($taskId && Taskmanager::isTask($taskId)) {
- $task = Taskmanager::status($taskId);
- $data['taskId'] = $taskId;
+ if (Taskmanager::isTask($task)) {
+
+ $data['taskId'] = $task['id'];
$data['locationId'] = $task['data']['locationId'];
$data['locationName'] = Location::getName($task['data']['locationId']);
- $data['clients'] = $task['data']['clients'];
+ $uuids = array_map(function($entry) {
+ return $entry['machineuuid'];
+ }, $task['data']['clients']);
+ $data['clients'] = RebootQueries::getMachinesByUuid($uuids);
Render::addTemplate('status', $data);
+
} else {
+
//location you want to see, default are "not assigned" clients
- $requestedLocation = Request::get('location', 0, 'int');
+ $requestedLocation = Request::get('location', false, 'int');
+ $allowedLocs = User::getAllowedLocations("action.*");
+ if (empty($allowedLocs)) {
+ User::assertPermission('action.*');
+ }
+
+ if ($requestedLocation === false) {
+ if (in_array(0, $allowedLocs)) {
+ $requestedLocation = 0;
+ } else {
+ $requestedLocation = reset($allowedLocs);
+ }
+ }
- $data['data'] = RebootQueries::getMachineTable($requestedLocation);
$data['locations'] = Location::getLocations($requestedLocation, 0, true);
+ // disable each location user has no permission for
+ foreach ($data['locations'] as &$loc) {
+ if (!in_array($loc["locationid"], $allowedLocs)) {
+ $loc["disabled"] = "disabled";
+ } elseif ($loc["locationid"] == $requestedLocation) {
+ $data['location'] = $loc['locationname'];
+ }
+ }
+ // Always show public key (it's public, isn't it?)
$data['pubKey'] = SSHKey::getPublicKey();
- Render::addTemplate('_page', $data);
+ // Only enable shutdown/reboot-button if user has permission for the location
+ Permission::addGlobalTags($data['perms'], $requestedLocation, ['newkeypair', 'action.shutdown', 'action.reboot']);
+
+ Render::addTemplate('header', $data);
+
+ // only fill table if user has at least one permission for the location
+ if (!in_array($requestedLocation, $allowedLocs)) {
+ Message::addError('locations.no-permission-location', $requestedLocation);
+ } else {
+ $data['data'] = RebootQueries::getMachineTable($requestedLocation);
+ Render::addTemplate('_page', $data);
+ }
+
+ // Append list of active reboot/shutdown tasks
+ $active = RebootControl::getActiveTasks($allowedLocs);
+ if (!empty($active)) {
+ foreach ($active as &$entry) {
+ $entry['locationName'] = Location::getName($entry['locationId']);
+ }
+ unset($entry);
+ Render::addTemplate('task-list', ['list' => $active]);
+ }
+
}
}
}
@@ -86,6 +161,7 @@ class Page_RebootControl extends Page
{
$this->action = Request::post('action', false, 'string');
if ($this->action === 'generateNewKeypair') {
+ User::assertPermission("newkeypair");
Property::set("rebootcontrol-private-key", false);
echo SSHKey::getPublicKey();
} else {
diff --git a/modules-available/rebootcontrol/permissions/permissions.json b/modules-available/rebootcontrol/permissions/permissions.json
new file mode 100644
index 00000000..a058ffbf
--- /dev/null
+++ b/modules-available/rebootcontrol/permissions/permissions.json
@@ -0,0 +1,11 @@
+{
+ "newkeypair": {
+ "location-aware": false
+ },
+ "action.reboot": {
+ "location-aware": true
+ },
+ "action.shutdown": {
+ "location-aware": true
+ }
+} \ No newline at end of file
diff --git a/modules-available/rebootcontrol/style.css b/modules-available/rebootcontrol/style.css
index 442cd5de..e35bce29 100644
--- a/modules-available/rebootcontrol/style.css
+++ b/modules-available/rebootcontrol/style.css
@@ -16,12 +16,8 @@
margin-bottom: 0;
}
-#rebootButton, #settingsButton, #selectAllButton, #unselectAllButton {
- margin-left: 10px;
-}
-
-#rebootButton, #shutdownButton, #selectAllButton, #unselectAllButton {
- width: 140px;
+.select-button {
+ min-width: 150px;
}
#dataTable {
@@ -32,11 +28,6 @@
#shutdownTimer {
text-align: center;
}
-#pubKeyTitle {
- display: inline-block;
- margin-top: 7px;
- margin-bottom: 20px;
-}
pre {
white-space: pre-wrap;
diff --git a/modules-available/rebootcontrol/templates/_page.html b/modules-available/rebootcontrol/templates/_page.html
index 1bef8dd4..a124e165 100644
--- a/modules-available/rebootcontrol/templates/_page.html
+++ b/modules-available/rebootcontrol/templates/_page.html
@@ -1,93 +1,53 @@
-<div class="page-header">
- <button type="button" id="settingsButton" class="btn btn-default pull-right" data-toggle="modal" data-target="#settingsModal"><span class="glyphicon glyphicon-cog"></span> {{lang_settings}}</button>
- <h1>{{lang_rebootControl}}</h1>
-</div>
+<h3>{{location}}</h3>
-
-<form id="tableDataForm" method="post" action="?do=rebootcontrol" class="form-inline">
+<form 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="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 stupidtable" id="dataTable">
<thead>
- <tr>
- <th data-sort="string">{{lang_client}}</th>
- <th data-sort="ipv4">{{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>
+ <tr>
+ <th data-sort="string">{{lang_client}}</th>
+ <th data-sort="ipv4">{{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}}
- <span class="text-success">{{lang_on}}</span>
- {{/status}}
- {{^status}}
- <span class="text-danger">{{lang_off}}</span>
- {{/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>
+ <tr>
+ <td>
+ {{hostname}}
+ {{^hostname}}{{clientip}}{{/hostname}}
+ </td>
+ <td>{{clientip}}</td>
+ <td class="statusColumn">
+ {{#status}}
+ <span class="text-success">{{lang_on}}</span>
+ {{/status}}
+ {{^status}}
+ <span class="text-danger">{{lang_off}}</span>
+ {{/status}}
+ </td>
+ <td>{{#status}}{{currentsession}}{{/status}}</td>
+ <td>{{#status}}{{currentuser}}{{/status}}</td>
+ <td data-sort-value="0" class="checkboxColumn slx-smallcol">
+ <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"><span class="glyphicon glyphicon-refresh"></span> {{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">
@@ -96,17 +56,27 @@
<h4 class="modal-title" id="myModalLabel">{{lang_rebootButton}}</h4>
</div>
<div class="modal-body">
- {{lang_rebootCheck}}
+ <div>{{lang_rebootCheck}}</div>
+ <div>{{lang_rebootIn}} <input name="r-minutes" title="{{lang_shutdownIn}}" type="number" value="0" min="0" pattern="\d+"> {{lang_minutes}}</div>
+ <div>
+ <div class="checkbox checkbox-inline">
+ <input name="quick" type="checkbox" value="on" id="rb-quick">
+ <label for="rb-quick">{{lang_kexecRebootCheck}}</label>
+ </div>
+ </div>
</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>
+ <button type="submit" {{perms.action.reboot.disabled}} name="action" value="reboot" 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 fade" id="shutdownModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel2">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
@@ -114,12 +84,15 @@
<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>{{lang_shutdownCheck}}</div>
+ {{lang_shutdownIn}} <input name="s-minutes" title="{{lang_shutdownIn}}" type="number" value="0" min="0" pattern="\d+"> {{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>
+ <button type="submit" {{perms.action.shutdown.disabled}} name="action" value="shutdown" class="btn btn-danger">
+ <span class="glyphicon glyphicon-off"></span>
+ {{lang_shutdownButton}}
+ </button>
</div>
</div>
</div>
@@ -128,104 +101,76 @@
<script type="application/javascript">
+ var $dataTable;
+
document.addEventListener("DOMContentLoaded", function() {
- markCheckedRows();
- $('input:checkbox').change(
- function(){
+ $dataTable = $("#dataTable");
+ markCheckedRows();
+ // Handle change of checkboxes in table
+ $('input:checkbox').change(function() {
+ var $this = $(this);
//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);
+ 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);
+ 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) {
+ var unchecked = $dataTable.find("input:checkbox:not(:checked)").length;
+ var checked = $dataTable.find("input:checkbox:checked").length;
+ if (unchecked === 0) {
$('#selectAllButton').hide();
$('#unselectAllButton').show();
- } else if (unchecked == 1) {
+ } else if (checked === 0) {
$('#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);
- }
- });
+ $('#rebootButton').prop('disabled', checked === 0 || '{{perms.action.reboot.disabled}}' === 'disabled');
+ $('#shutdownButton').prop('disabled', checked === 0 || '{{perms.action.shutdown.disabled}}' === 'disabled');
+ });
+ // Propagate click on column with checkbox to checkbox
$('.checkboxColumn').click(function(e) {
if (e.target === this) {
- $(this).find('input[type="checkbox"]').click();
+ $(this).find('input:checkbox').click();
}
});
+ // Arm the (de)select all buttons
+ $('#selectAllButton').click(function() { selectAllRows(true); });
+ $('#unselectAllButton').click(function() { selectAllRows(false); });
});
- // 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);
+ function selectAllRows(selected) {
+ var $box = $dataTable.find('input:checkbox');
+ if ($box.length === 0) return;
+ if (selected) {
+ $box = $box.filter(':not(:checked)');
+ } else {
+ $box = $box.filter(':checked');
+ }
+ if ($box.length === 0) return;
+ $box.prop('checked', !!selected).trigger('change');
}
- // mark all previous checked rows (used when loading site)
+ // mark all previous checked rows (used when loading site), enable (de)select all if list is not empty
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) {
+ var $checked = $dataTable.find("input:checkbox:checked");
+ markRows($checked.closest("tr"), true);
+ var $unchecked = $dataTable.find("input:checkbox:not(:checked)");
+ markRows($unchecked.closest("tr"), false);
+ if($unchecked.length === 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));
+ if ($unchecked.length > 0 || $checked.length > 0) {
+ $('.select-button').prop('disabled', false);
+ }
}
function markRows($rows, marked) {
@@ -236,15 +181,4 @@
}
}
- 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/header.html b/modules-available/rebootcontrol/templates/header.html
new file mode 100644
index 00000000..e171ccd6
--- /dev/null
+++ b/modules-available/rebootcontrol/templates/header.html
@@ -0,0 +1,79 @@
+<div class="page-header">
+ <button type="button" id="settingsButton" class="btn btn-default pull-right" data-toggle="modal" data-target="#settingsModal"><span class="glyphicon glyphicon-cog"></span> {{lang_settings}}</button>
+ <h1>{{lang_rebootControl}}</h1>
+</div>
+
+<div>
+ <label>{{lang_location}}:
+ <select id="locationDropdown" class="form-control" onchange="selectLocation()">
+ {{#locations}}
+ <option value="{{locationid}}" {{disabled}} {{#selected}}selected{{/selected}}>{{locationpad}} {{locationname}}</option>
+ {{/locations}}
+ </select>
+ </label>
+ <div class="pull-right">
+ <button type="button" id="shutdownButton" class="btn btn-danger action-button" data-toggle="modal" data-target="#shutdownModal" disabled>
+ <span class="glyphicon glyphicon-off"></span>
+ {{lang_shutdownButton}}
+ </button>
+ <button type="button" id="rebootButton" class="btn btn-warning action-button" data-toggle="modal" data-target="#rebootModal" disabled>
+ <span class="glyphicon glyphicon-repeat"></span>
+ {{lang_rebootButton}}
+ </button>
+ <button type="button" id="selectAllButton" class="btn btn-primary select-button" disabled>
+ <span class="glyphicon glyphicon-check"></span>
+ {{lang_selectall}}
+ </button>
+ <button type="button" id="unselectAllButton" class="btn btn-default select-button collapse" disabled>
+ <span class="glyphicon glyphicon-unchecked"></span>
+ {{lang_unselectall}}
+ </button>
+ </div>
+</div>
+
+<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">
+ <p>{{lang_pubKey}}</p>
+ <pre id="pubkey">{{pubKey}}</pre>
+ <p>{{lang_newKeypairExplanation}}</p>
+ </div>
+ <div class="modal-footer">
+ <button {{perms.newkeypair.disabled}} class="btn btn-danger pull-right" onclick="generateNewKeypair()" type="button">
+ <span class="glyphicon glyphicon-refresh"></span>
+ {{lang_genNew}}
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script type="application/javascript">
+
+ // Change Location when selected in Dropdown Menu
+ function selectLocation() {
+ var dropdown = $("#locationDropdown");
+ var location = dropdown.val();
+ window.location.replace("?do=rebootcontrol&location="+location);
+ }
+
+ function generateNewKeypair() {
+ if (!confirm('{{lang_confirmNewKeypair}}'))
+ return;
+ $.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
index c2fdab46..c05b2fad 100644
--- a/modules-available/rebootcontrol/templates/status.html
+++ b/modules-available/rebootcontrol/templates/status.html
@@ -7,8 +7,6 @@
</form>
</div>
-<div data-tm-id="{{taskId}}" data-tm-log="error" data-tm-callback="updateStatus"></div>
-
<div>
<table class="table table-hover stupidtable" id="dataTable">
<thead>
@@ -24,7 +22,7 @@
<tbody>
{{#clients}}
<tr>
- <td>{{machineuuid}}</td>
+ <td>{{hostname}}{{^hostname}}{{machineuuid}}{{/hostname}}</td>
<td>{{clientip}}</td>
<td id="status-{{machineuuid}}"></td>
</tr>
@@ -33,6 +31,8 @@
</table>
</div>
+<div data-tm-id="{{taskId}}" data-tm-log="error" data-tm-callback="updateStatus"></div>
+
<script type="application/javascript">
statusStrings = {
"CONNECTING" : "{{lang_connecting}}",
@@ -50,12 +50,20 @@
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);
+ if (!clientStatus.hasOwnProperty(uuid))
+ continue;
+ var $s = $("#status-" + uuid);
+ var status = clientStatus[uuid];
+ if ($s.data('state') === status)
+ continue;
+ $s.data('state', status);
+ var text = statusStrings[status];
+ if (status === 'SHUTDOWN_AT' || status === 'REBOOT_AT') {
+ text += ' ' + task.data.time;
+ }
+ $s.text(text);
+ if (status === 'CONNECTING' || status === 'REBOOTING') {
+ $s.append('<span class="glyphicon glyphicon-hourglass"></span>');
}
}
}
diff --git a/modules-available/rebootcontrol/templates/task-list.html b/modules-available/rebootcontrol/templates/task-list.html
new file mode 100644
index 00000000..063ba949
--- /dev/null
+++ b/modules-available/rebootcontrol/templates/task-list.html
@@ -0,0 +1,33 @@
+<h3>{{lang_activeTasks}}</h3>
+<table class="table">
+ <thead>
+ <tr>
+ <th>{{lang_mode}}</th>
+ <th>{{lang_location}}</th>
+ <th>{{lang_time}}</th>
+ <th>{{lang_clientCount}}</th>
+ <th>{{lang_status}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#list}}
+ <tr>
+ <td>
+ <a href="?do=rebootcontrol&amp;taskid={{taskId}}">{{mode}}</a>
+ </td>
+ <td>
+ {{locationName}}
+ </td>
+ <td>
+ {{time}}
+ </td>
+ <td>
+ {{clientCount}}
+ </td>
+ <td>
+ {{status}}
+ </td>
+ </tr>
+ {{/list}}
+ </tbody>
+</table> \ No newline at end of file