From 95c8a36dfc7e63f23eae3b411fd1a371da6f774c Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Fri, 22 Nov 2019 17:14:06 +0100 Subject: [rebootcontrol] Start adding WOL functionality --- inc/taskmanagercallback.inc.php | 11 +- .../rebootcontrol/inc/rebootcontrol.inc.php | 64 ++++ modules-available/rebootcontrol/install.inc.php | 49 +++ .../rebootcontrol/lang/de/module.json | 4 +- .../rebootcontrol/lang/de/template-tags.json | 37 +- .../rebootcontrol/lang/en/module.json | 4 +- .../rebootcontrol/lang/en/template-tags.json | 2 + modules-available/rebootcontrol/page.inc.php | 371 ++++++++++++++------- .../rebootcontrol/permissions/permissions.json | 21 ++ .../rebootcontrol/templates/_page.html | 184 ---------- .../rebootcontrol/templates/header.html | 74 ++-- .../rebootcontrol/templates/jumphost-edit.html | 42 +++ .../rebootcontrol/templates/jumphost-list.html | 64 ++++ .../templates/status-checkconnection.html | 51 +++ .../rebootcontrol/templates/status-reboot.html | 68 ++++ .../rebootcontrol/templates/status.html | 70 ---- .../rebootcontrol/templates/task-list.html | 2 +- 17 files changed, 682 insertions(+), 436 deletions(-) create mode 100644 modules-available/rebootcontrol/install.inc.php delete mode 100644 modules-available/rebootcontrol/templates/_page.html create mode 100644 modules-available/rebootcontrol/templates/jumphost-edit.html create mode 100644 modules-available/rebootcontrol/templates/jumphost-list.html create mode 100644 modules-available/rebootcontrol/templates/status-checkconnection.html create mode 100644 modules-available/rebootcontrol/templates/status-reboot.html delete mode 100644 modules-available/rebootcontrol/templates/status.html diff --git a/inc/taskmanagercallback.inc.php b/inc/taskmanagercallback.inc.php index 2be9fe73..21bece38 100644 --- a/inc/taskmanagercallback.inc.php +++ b/inc/taskmanagercallback.inc.php @@ -201,5 +201,14 @@ class TaskmanagerCallback $mod->activate(1, false); MiniLinux::linuxDownloadCallback($task, $args); } - + + public static function rbcConnCheck($task, $args) + { + $mod = Module::get('rebootcontrol'); + if ($mod === false) + return; + $mod->activate(1, false); + RebootControl::connectionCheckCallback($task, $args); + } + } diff --git a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php index ec4b84ed..680f9eff 100644 --- a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php +++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php @@ -82,4 +82,68 @@ class RebootControl return $return; } + /** + * Execute given command or script on a list of hosts. The list of hosts is an array of structs containing + * each a known machine-uuid and/or hostname, and optionally a port to use, which would otherwise default to 9922, + * and optionally a username to use, which would default to root. + * The command should be compatible with the remote user's default shell (most likely bash). + * + * @param array $clients [ { clientip: , machineuuid: , port: , username: }, ... ] + * @param string $command Command or script to execute on client + * @param int $timeout in seconds + * @param string|false $privkey SSH private key to use to connect + * @return array|false + */ + public static function runScript($clients, $command, $timeout = 5, $privkey = false) + { + $valid = []; + $invalid = []; + foreach ($clients as $client) { + if (is_string($client)) { + $invalid[strtoupper($client)] = []; // Assume machineuuid + } elseif (!isset($client['clientip']) && !isset($client['machineuuid'])) { + error_log('RebootControl::runScript called with list entry that has neither IP nor UUID'); + } elseif (!isset($client['clientip'])) { + $invalid[$client['machineuuid']] = $client; + } else { + $valid[] = $client; + } + } + if (!empty($invalid)) { + $res = Database::simpleQuery('SELECT machineuuid, clientip FROM machine WHERE machineuuid IN (:uuids)', + ['uuids' => array_keys($invalid)]); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if (isset($invalid[$row['machineuuid']])) { + $valid[] = $row + $invalid[$row['machineuuid']]; + } else { + $valid[] = $row; + } + } + } + if ($privkey === false) { + $privkey = SSHKey::getPrivateKey(); + } + return Taskmanager::submit('RemoteExec', [ + 'clients' => $valid, + 'command' => $command, + 'timeoutSeconds' => $timeout, + 'sshkey' => $privkey, + 'port' => 9922, // Fallback if no port given in client struct + ]); + } + + public static function connectionCheckCallback($task, $hostId) + { + $reachable = 0; + if (isset($task['data']['result'])) { + foreach ($task['data']['result'] as $res) { + if ($res['exitCode'] == 0) { + $reachable = 1; + } + } + } + Database::exec('UPDATE reboot_jumphost SET reachable = :reachable WHERE hostid = :id', + ['id' => $hostId, 'reachable' => $reachable]); + } + } \ No newline at end of file diff --git a/modules-available/rebootcontrol/install.inc.php b/modules-available/rebootcontrol/install.inc.php new file mode 100644 index 00000000..0ce5dd23 --- /dev/null +++ b/modules-available/rebootcontrol/install.inc.php @@ -0,0 +1,49 @@ +action = Request::any('action', 'show', 'string'); + $action = Request::post('action', 'show', 'string'); + if ($action === 'reboot' || $action === 'shutdown') { + $this->execRebootShutdown($action); + } elseif ($action === 'savejumphost') { + $this->saveJumpHost(); + } elseif ($action === 'jumphost') { + $this->postJumpHostDispatch(); + } + if (Request::isPost()) { + Util::redirect('?do=rebootcontrol'); + } + } - if ($this->action === 'reboot' || $this->action === 'shutdown') { + private function execRebootShutdown($action) + { + $requestedClients = Request::post('clients', false, 'array'); + if (!is_array($requestedClients) || empty($requestedClients)) { + Message::addError('no-clients-selected'); + return; + } - $requestedClients = Request::post('clients', false, 'array'); - if (!is_array($requestedClients) || empty($requestedClients)) { - Message::addError('no-clients-selected'); - Util::redirect(); + $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'); + } + // Filter ones with no permission + foreach (array_keys($actualClients) as $idx) { + if (!User::hasPermission('action.' . $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'); + return; + } + 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 ($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&show=task&taskid=" . $task["id"]); + } + return; + } - $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'); - } - // 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']; + private function saveJumpHost() + { + User::assertPermission('jumphost.edit'); + $id = Request::post('hostid', Request::REQUIRED, 'string'); + $host = Request::post('host', Request::REQUIRED, 'string'); + $port = Request::post('port', Request::REQUIRED, 'int'); + if ($port < 1 || $port > 65535) { + Message::addError('invalid-port', $port); + return; + } + $username = Request::post('username', Request::REQUIRED, 'string'); + $sshkey = Request::post('sshkey', Request::REQUIRED, 'string'); + $script = preg_replace('/\r\n?/', "\n", Request::post('script', Request::REQUIRED, 'string')); + if ($id === 'new') { + $ret = Database::exec('INSERT INTO reboot_jumphost (host, port, username, sshkey, script, reachable) + VALUE (:host, :port, :username, :sshkey, :script, 0)', compact('host', 'port', 'username', 'sshkey', 'script')); + $id = Database::lastInsertId(); + } else { + $ret = Database::exec('UPDATE reboot_jumphost SET + host = :host, port = :port, username = :username, sshkey = :sshkey, script = :script, reachable = 0 + WHERE hostid = :id', compact('host', 'port', 'username', 'sshkey', 'script', 'id')); + if ($ret === 0) { + $ret = Database::queryFirst('SELECT hostid FROM reboot_jumphost WHERE hostid = :id', ['id' => $id]); + if ($ret !== false) { + $ret = 1; } } - // 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"); - } } + if ($ret > 0) { + Message::addSuccess('jumphost-saved', $id); + $this->execCheckConnection($id); + } else { + Message::addError('no-such-jumphost', $id); + } + } + + private function postJumpHostDispatch() + { + $id = Request::post('checkid', false, 'int'); + if ($id !== false) { + // Check connectivity + $this->execCheckConnection($id); + return; + } + } + private function execCheckConnection($hostid) + { + $host = $this->getJumpHost($hostid); + $script = str_replace(['%IP%', '%MACS%'], ['255.255.255.255', '00:11:22:33:44:55'], $host['script']); + $task = RebootControl::runScript([[ + 'clientip' => $host['host'], + 'port' => $host['port'], + 'username' => $host['username'], + ]], $script, 5, $host['sshkey']); + if (!Taskmanager::isTask($task)) + return; + TaskmanagerCallback::addCallback($task, 'rbcConnCheck', $hostid); + Util::redirect('?do=rebootcontrol&show=task&type=checkhost&taskid=' . $task['id']); } /** @@ -81,86 +148,150 @@ class Page_RebootControl extends Page protected function doRender() { - if ($this->action === 'show') { - - $data = []; - $task = Request::get("taskid", false, 'string'); - if ($task !== false) { - $task = Taskmanager::status($task); - } + $show = Request::get('show', 'jumphosts', 'string'); - if (Taskmanager::isTask($task)) { + // Always show public key (it's public, isn't it?) + $data = ['pubkey' => SSHKey::getPublicKey()]; + Permission::addGlobalTags($data['perms'], null, ['newkeypair']); + Render::addTemplate('header', $data); - $data['taskId'] = $task['id']; - $data['locationId'] = $task['data']['locationId']; - $data['locationName'] = Location::getName($task['data']['locationId']); - $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', false, 'int'); - $allowedLocs = User::getAllowedLocations("action.*"); - if (empty($allowedLocs)) { - User::assertPermission('action.*'); - } + if ($show === 'task') { + $this->showTask(); + } elseif ($show === 'jumphosts') { + $this->showJumpHosts(); + } elseif ($show === 'jumphost') { + $this->showJumpHost(); + } + } - if ($requestedLocation === false) { - if (in_array(0, $allowedLocs)) { - $requestedLocation = 0; - } else { - $requestedLocation = reset($allowedLocs); - } - } + private function showTask() + { + $taskid = Request::get("taskid", Request::REQUIRED, 'string'); + $task = Taskmanager::status($taskid); - $data['locations'] = Location::getLocations($requestedLocation, 0, true); + if (!Taskmanager::isTask($task) || !isset($task['data'])) { + Message::addError('no-such-task', $taskid); + return; + } - // 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(); + $td =& $task['data']; + $type = Request::get('type', false, 'string'); + if ($type === false) { + // Try to guess + if (isset($td['locationId']) || isset($td['clients'])) { + $type = 'reboot'; + } elseif (isset($td['result'])) { + $type = 'exec'; + } + } + if ($type === 'reboot') { + $data = [ + 'taskId' => $task['id'], + 'locationId' => $td['locationId'], + 'locationName' => Location::getName($td['locationId']), + ]; + $uuids = array_map(function ($entry) { + return $entry['machineuuid']; + }, $td['clients']); + $data['clients'] = RebootQueries::getMachinesByUuid($uuids); + Render::addTemplate('status-reboot', $data); + } elseif ($type === 'exec') { + $data = [ + 'taskId' => $task['id'], + ]; + Render::addTemplate('status-exec', $data); + } elseif ($type === 'checkhost') { + $ip = array_key_first($td['result']); + $data = [ + 'taskId' => $task['id'], + 'host' => $ip, + ]; + Render::addTemplate('status-checkconnection', $data); + } else { + Message::addError('unknown-task-type'); + } + } - // Only enable shutdown/reboot-button if user has permission for the location - Permission::addGlobalTags($data['perms'], $requestedLocation, ['newkeypair', 'action.shutdown', 'action.reboot']); + private function showJumpHosts() + { + User::assertPermission('jumphost.*'); + $hosts = []; + $res = Database::simpleQuery('SELECT hostid, host, port, Count(jxs.subnetid) AS subnetCount, reachable + FROM reboot_jumphost jh + LEFT JOIN reboot_jumphost_x_subnet jxs USING (hostid) + GROUP BY hostid + ORDER BY hostid'); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $hosts[] = $row; + } + $data = [ + 'jumpHosts' => $hosts + ]; + Permission::addGlobalTags($data['perms'], null, ['jumphost.edit', 'jumphost.assign-subnet']); + Render::addTemplate('jumphost-list', $data); - Render::addTemplate('header', $data); + // Append list of active reboot/shutdown tasks + $allowedLocs = User::getAllowedLocations("action.*"); + $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]); + } - // 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]); - } + private function showJumpHost() + { + User::assertPermission('jumphost.edit'); + $id = Request::get('id', Request::REQUIRED, 'string'); + if ($id === 'new') { + $host = ['hostid' => 'new', 'port' => 22, 'script' => "# Assume bash\n" + . "MACS='%MACS%'\n" + . "IP='%IP'\n" + . "EW=false\n" + . "WOL=false\n" + . "command -v etherwake > /dev/null && ( [ \"\$(id -u)\" = 0 ] || [ -u \"\$(which etherwake)\" ] ) && EW=true\n" + . "command -v wakeonlan > /dev/null && WOL=true\n" + . "if \$EW && ( ! \$WOL || [ \"\$IP\" = '255.255.255.255' ] ); then\n" + . "\tifaces=\"\$(ls -1 /sys/class/net/)\"\n" + . "\t[ -z \"\$ifaces\" ] && ifaces=eth0\n" + . "\tfor ifc in \$ifaces; do\n" + . "\t\t[ \"\$ifc\" = 'lo' ] && continue\n" + . "\t\tfor mac in \$MACS; do\n" + . "\t\t\tetherwake -i \"\$ifc\" \"\$mac\"\n" + . "\t\tdone\n" + . "\tdone\n" + . "elif \$WOL; then\n" + . "\twakeonlan -i \"\$IP\" \$MACS\n" + . "else\n" + . "\techo 'No suitable WOL tool found' >&2\n" + . "\texit 1\n" + . "fi\n"]; + } else { + $host = $this->getJumpHost($id); + } + Render::addTemplate('jumphost-edit', $host); + } - } + private function getJumpHost($hostid) + { + $host = Database::queryFirst('SELECT hostid, host, port, username, sshkey, script + FROM reboot_jumphost + WHERE hostid = :id', ['id' => $hostid]); + if ($host === false) { + Message::addError('no-such-jumphost', $hostid); + Util::redirect('?do=rebootcontrol'); } + return $host; } - function doAjax() + protected function doAjax() { - $this->action = Request::post('action', false, 'string'); - if ($this->action === 'generateNewKeypair') { + $action = Request::post('action', false, 'string'); + if ($action === 'generateNewKeypair') { User::assertPermission("newkeypair"); Property::set("rebootcontrol-private-key", false); echo SSHKey::getPublicKey(); @@ -169,6 +300,14 @@ class Page_RebootControl extends Page } } +} - +// Remove when we require >= 7.3.0 +if (!function_exists('array_key_first')) { + function array_key_first(array $arr) { + foreach($arr as $key => $unused) { + return $key; + } + return NULL; + } } diff --git a/modules-available/rebootcontrol/permissions/permissions.json b/modules-available/rebootcontrol/permissions/permissions.json index a058ffbf..5983447e 100644 --- a/modules-available/rebootcontrol/permissions/permissions.json +++ b/modules-available/rebootcontrol/permissions/permissions.json @@ -2,10 +2,31 @@ "newkeypair": { "location-aware": false }, + "subnet.view": { + "location-aware": false + }, + "subnet.edit": { + "location-aware": false + }, + "subnet.flag": { + "location-aware": false + }, + "jumphost.view": { + "location-aware": false + }, + "jumphost.edit": { + "location-aware": false + }, + "jumphost.assign-subnet": { + "location-aware": false + }, "action.reboot": { "location-aware": true }, "action.shutdown": { "location-aware": true + }, + "action.wol": { + "location-aware": true } } \ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/_page.html b/modules-available/rebootcontrol/templates/_page.html deleted file mode 100644 index a124e165..00000000 --- a/modules-available/rebootcontrol/templates/_page.html +++ /dev/null @@ -1,184 +0,0 @@ -

{{location}}

- -
- -
-
- - - - - - - - - - - - - - {{#data}} - - - - - - - - - {{/data}} - -
{{lang_client}}{{lang_ip}}{{lang_status}}{{lang_session}}{{lang_user}}{{lang_selected}}
- {{hostname}} - {{^hostname}}{{clientip}}{{/hostname}} - {{clientip}} - {{#status}} - {{lang_on}} - {{/status}} - {{^status}} - {{lang_off}} - {{/status}} - {{#status}}{{currentsession}}{{/status}}{{#status}}{{currentuser}}{{/status}} -
- - -
-
-
-
- - - - - -
- - - \ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/header.html b/modules-available/rebootcontrol/templates/header.html index e171ccd6..4f240b81 100644 --- a/modules-available/rebootcontrol/templates/header.html +++ b/modules-available/rebootcontrol/templates/header.html @@ -1,34 +1,9 @@ - -
- -
- - - - -
+ +

{{lang_moduleHeading}}

\ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/jumphost-edit.html b/modules-available/rebootcontrol/templates/jumphost-edit.html new file mode 100644 index 00000000..82629d2e --- /dev/null +++ b/modules-available/rebootcontrol/templates/jumphost-edit.html @@ -0,0 +1,42 @@ +

{{lang_editJumpHost}}

+ +
+ + + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+

{{lang_wakeScriptHelp}}

+
+
+
+ + +
+
\ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/jumphost-list.html b/modules-available/rebootcontrol/templates/jumphost-list.html new file mode 100644 index 00000000..3fbe4c21 --- /dev/null +++ b/modules-available/rebootcontrol/templates/jumphost-list.html @@ -0,0 +1,64 @@ +

{{lang_settings}}

+ +

{{lang_wolReachability}}

+ +

{{lang_jumpHosts}}

+ +
+ + + + + + + + + + + + + {{#jumpHosts}} + + + + + + + {{/jumpHosts}} + +
{{lang_host}}{{lang_assignedSubnets}}{{lang_reachable}}
{{host}}:{{port}} + {{subnetCount}} + + + + + {{#reachable}} + + {{/reachable}} + {{^reachable}} + + {{/reachable}} + + + + + + +
+
+ + + \ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/status-checkconnection.html b/modules-available/rebootcontrol/templates/status-checkconnection.html new file mode 100644 index 00000000..c30b42d9 --- /dev/null +++ b/modules-available/rebootcontrol/templates/status-checkconnection.html @@ -0,0 +1,51 @@ +

{{lang_checkingJumpHost}}: {{host}}

+ + + + {{lang_back}} + +
+
+ + {{lang_hostReachable}} +
+
+ + {{lang_hostNonZeroExit}} +
+
+ + {{lang_hostNotReachable}} +
+ +
+ +

+
+ +
{{lang_checkingJumpHost}}
+ diff --git a/modules-available/rebootcontrol/templates/status-reboot.html b/modules-available/rebootcontrol/templates/status-reboot.html new file mode 100644 index 00000000..594faa4c --- /dev/null +++ b/modules-available/rebootcontrol/templates/status-reboot.html @@ -0,0 +1,68 @@ +

{{lang_location}}: {{locationName}}

+ + + + {{lang_back}} + + +
+ + + + + + + + + + + {{#clients}} + + + + + + {{/clients}} + +
{{lang_client}}{{lang_ip}} + {{lang_status}} +
{{hostname}}{{^hostname}}{{machineuuid}}{{/hostname}}{{clientip}}
+
+ +
+ + diff --git a/modules-available/rebootcontrol/templates/status.html b/modules-available/rebootcontrol/templates/status.html deleted file mode 100644 index c05b2fad..00000000 --- a/modules-available/rebootcontrol/templates/status.html +++ /dev/null @@ -1,70 +0,0 @@ -
-
- {{lang_location}}: {{locationName}} - - - -
-
- -
- - - - - - - - - - - {{#clients}} - - - - - - {{/clients}} - -
{{lang_client}}{{lang_ip}} - {{lang_status}} -
{{hostname}}{{^hostname}}{{machineuuid}}{{/hostname}}{{clientip}}
-
- -
- - diff --git a/modules-available/rebootcontrol/templates/task-list.html b/modules-available/rebootcontrol/templates/task-list.html index 063ba949..87515085 100644 --- a/modules-available/rebootcontrol/templates/task-list.html +++ b/modules-available/rebootcontrol/templates/task-list.html @@ -13,7 +13,7 @@ {{#list}} - {{mode}} + {{mode}} {{locationName}} -- cgit v1.2.3-55-g7522