From ca312a6ace43a6fde58d1c09057c7b0bd34f15a2 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 27 Nov 2019 17:25:46 +0100 Subject: [statistics/rebootcontrol] Implement editing subnet --- .../rebootcontrol/inc/rebootcontrol.inc.php | 6 +- modules-available/rebootcontrol/install.inc.php | 2 +- .../rebootcontrol/lang/de/messages.json | 8 +- .../rebootcontrol/lang/de/permissions.json | 13 +- .../rebootcontrol/lang/de/template-tags.json | 4 +- modules-available/rebootcontrol/page.inc.php | 244 +++------------------ .../rebootcontrol/pages/jumphost.inc.php | 167 ++++++++++++++ .../rebootcontrol/pages/subnet.inc.php | 150 +++++++++++++ modules-available/rebootcontrol/pages/task.inc.php | 101 +++++++++ .../rebootcontrol/templates/jumphost-edit.html | 4 +- .../rebootcontrol/templates/jumphost-list.html | 3 +- .../templates/status-checkconnection.html | 4 - .../rebootcontrol/templates/status-reboot.html | 5 - .../rebootcontrol/templates/subnet-edit.html | 64 ++++++ .../rebootcontrol/templates/subnet-list.html | 58 +++++ .../rebootcontrol/templates/task-list.html | 2 +- 16 files changed, 603 insertions(+), 232 deletions(-) create mode 100644 modules-available/rebootcontrol/pages/jumphost.inc.php create mode 100644 modules-available/rebootcontrol/pages/subnet.inc.php create mode 100644 modules-available/rebootcontrol/pages/task.inc.php create mode 100644 modules-available/rebootcontrol/templates/subnet-edit.html create mode 100644 modules-available/rebootcontrol/templates/subnet-list.html (limited to 'modules-available/rebootcontrol') diff --git a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php index 680f9eff..8a85e3ff 100644 --- a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php +++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php @@ -123,13 +123,17 @@ class RebootControl if ($privkey === false) { $privkey = SSHKey::getPrivateKey(); } - return Taskmanager::submit('RemoteExec', [ + $task = Taskmanager::submit('RemoteExec', [ 'clients' => $valid, 'command' => $command, 'timeoutSeconds' => $timeout, 'sshkey' => $privkey, 'port' => 9922, // Fallback if no port given in client struct ]); + if (!Taskmanager::isFailed($task)) { + Property::addToList(RebootControl::KEY_TASKLIST, '0/' . $task["id"], 60 * 24); + } + return $task; } public static function connectionCheckCallback($task, $hostId) diff --git a/modules-available/rebootcontrol/install.inc.php b/modules-available/rebootcontrol/install.inc.php index 0ce5dd23..eb484d3e 100644 --- a/modules-available/rebootcontrol/install.inc.php +++ b/modules-available/rebootcontrol/install.inc.php @@ -12,7 +12,7 @@ $output[] = tableCreate('reboot_subnet', " `lastseen` INT(10) UNSIGNED NOT NULL DEFAULT '0', `seencount` INT(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`subnetid`), - KEY `range` (`start`, `end`)"); + UNIQUE KEY `range` (`start`, `end`)"); $output[] = tableCreate('reboot_jumphost', " `hostid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, diff --git a/modules-available/rebootcontrol/lang/de/messages.json b/modules-available/rebootcontrol/lang/de/messages.json index 2a7e1299..788e57a2 100644 --- a/modules-available/rebootcontrol/lang/de/messages.json +++ b/modules-available/rebootcontrol/lang/de/messages.json @@ -1,4 +1,10 @@ { + "invalid-port": "Ung\u00fcltiger Port: {{0}}", + "jumphost-saved": "Sprung-Host {{0}} gespeichert", "no-clients-selected": "Keine Clients ausgew\u00e4hlt", - "some-machine-not-found": "Einige Clients aus dem POST request wurden nicht gefunden" + "no-current-tasks": "Keine aktuellen oder k\u00fcrzlich abgeschlossenen Aufgaben", + "no-such-jumphost": "Sprung-Host {{0}} existiert nicht", + "no-such-task": "Task {{0}} existiert nicht", + "some-machine-not-found": "Einige Clients aus dem POST request wurden nicht gefunden", + "unknown-task-type": "Unbekannter Task-Typ" } \ No newline at end of file diff --git a/modules-available/rebootcontrol/lang/de/permissions.json b/modules-available/rebootcontrol/lang/de/permissions.json index 12ec4c83..fdc0b35f 100644 --- a/modules-available/rebootcontrol/lang/de/permissions.json +++ b/modules-available/rebootcontrol/lang/de/permissions.json @@ -1,5 +1,12 @@ { - "action.shutdown": "Client herunterfahren.", - "action.reboot": "Client neustarten.", - "newkeypair": "Neues Schlüsselpaar generieren." + "action.reboot": "Client neustarten.", + "action.shutdown": "Client herunterfahren.", + "action.wol": "Client per WOL starten.", + "jumphost.assign-subnet": "Einem Sprung-Host ein Subnet zuweisen.", + "jumphost.edit": "Einen Sprung-Host bearbeiten.", + "jumphost.view": "Liste der Sprung-Hosts sehen.", + "newkeypair": "Neues Schl\u00fcsselpaar generieren.", + "subnet.edit": "Subnets hinzuf\u00fcgen\/entfernen.", + "subnet.flag": "Eigenschaften eines Subnets bearbeiten.", + "subnet.view": "Liste der Subnets sehen." } \ 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 519c2286..365a739b 100644 --- a/modules-available/rebootcontrol/lang/de/template-tags.json +++ b/modules-available/rebootcontrol/lang/de/template-tags.json @@ -11,13 +11,13 @@ "lang_editJumpHost": "Sprung-Host bearbeiten", "lang_error": "Nicht erreichbar", "lang_genNew": "Neues Schl\u00fcsselpaar generieren", - "lang_host": "Horst", + "lang_host": "Host", "lang_hostDeleteConfirm": "Diesen Sprung-Host l\u00f6schen?", "lang_hostNonZeroExit": "Das hinterlegte Script hat einen Exit-Code ungleich 0 zur\u00fcckgeliefert", "lang_hostNotReachable": "Host nicht erreichbar", "lang_hostReachable": "Host erreichbar", "lang_ip": "IP", - "lang_jumpHosts": "Spring-Hosts", + "lang_jumpHosts": "Sprung-Hosts", "lang_keypairConfirmCheck": "Ich bin sicher", "lang_location": "Standort", "lang_mode": "Modus", diff --git a/modules-available/rebootcontrol/page.inc.php b/modules-available/rebootcontrol/page.inc.php index 67996f02..eaa3c2e6 100644 --- a/modules-available/rebootcontrol/page.inc.php +++ b/modules-available/rebootcontrol/page.inc.php @@ -3,6 +3,11 @@ class Page_RebootControl extends Page { + /** + * @var bool whether we have a SubPage from the pages/ subdir + */ + private $haveSubpage = false; + /** * Called before any page rendering happens - early hook to check parameters etc. */ @@ -15,17 +20,34 @@ class Page_RebootControl extends Page Util::redirect('?do=Main'); // does not return } - $action = Request::post('action', 'show', 'string'); + if (User::hasPermission('jumphost.*')) { + Dashboard::addSubmenu('?do=rebootcontrol&show=jumphost', Dictionary::translate('jumphosts', true)); + } + if (User::hasPermission('subnet.*')) { + Dashboard::addSubmenu('?do=rebootcontrol&show=subnet', Dictionary::translate('subnets', true)); + } + + $section = Request::any('show', false, 'string'); + if ($section !== false) { + $section = preg_replace('/[^a-z]/', '', $section); + if (file_exists('modules/rebootcontrol/pages/' . $section . '.inc.php')) { + require_once 'modules/rebootcontrol/pages/' . $section . '.inc.php'; + $this->haveSubpage = true; + SubPage::doPreprocess(); + } else { + Message::addError('main.invalid-action', $section); + return; + } + } else { + $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 ($action === 'reboot' || $action === 'shutdown') { + $this->execRebootShutdown($action); + } } + if (Request::isPost()) { - Util::redirect('?do=rebootcontrol'); + Util::redirect('?do=rebootcontrol' . ($section ? '&show=' . $section : '')); } } @@ -76,216 +98,26 @@ class Page_RebootControl extends Page } $task = RebootControl::execute($actualClients, $mode, $minutes, $locationId); if (Taskmanager::isTask($task)) { - Util::redirect("?do=rebootcontrol&show=task&taskid=" . $task["id"]); + Util::redirect("?do=rebootcontrol&show=task&what=task&taskid=" . $task["id"]); } return; } - 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; - } - } - } - 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']); - } - /** * Menu etc. has already been generated, now it's time to generate page content. */ protected function doRender() { - $show = Request::get('show', 'jumphosts', 'string'); - // Always show public key (it's public, isn't it?) $data = ['pubkey' => SSHKey::getPublicKey()]; Permission::addGlobalTags($data['perms'], null, ['newkeypair']); Render::addTemplate('header', $data); - if ($show === 'task') { - $this->showTask(); - } elseif ($show === 'jumphosts') { - $this->showJumpHosts(); - } elseif ($show === 'jumphost') { - $this->showJumpHost(); - } - } - - private function showTask() - { - $taskid = Request::get("taskid", Request::REQUIRED, 'string'); - $task = Taskmanager::status($taskid); - - if (!Taskmanager::isTask($task) || !isset($task['data'])) { - Message::addError('no-such-task', $taskid); + if ($this->haveSubpage) { + SubPage::doRender(); return; } - - $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'); - } - } - - 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); - - // 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]); - } - - } - - 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; } protected function doAjax() @@ -301,13 +133,3 @@ 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/pages/jumphost.inc.php b/modules-available/rebootcontrol/pages/jumphost.inc.php new file mode 100644 index 00000000..111560ef --- /dev/null +++ b/modules-available/rebootcontrol/pages/jumphost.inc.php @@ -0,0 +1,167 @@ + $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&what=task&taskid=' . $task['id']); + } + + private static 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; + } + } + } + if ($ret > 0) { + Message::addSuccess('jumphost-saved', $id); + self::execCheckConnection($id); + } else { + Message::addError('no-such-jumphost', $id); + } + } + + /* + * Render + */ + + public static function doRender() + { + $id = Request::get('id', false, 'string'); + if ($id !== false) { + self::showJumpHost($id); + } else { + self::showJumpHosts(); + } + } + + private static 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); + } + + private static function showJumpHost($id) + { + User::assertPermission('jumphost.edit'); + 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 = self::getJumpHost($id); + } + Render::addTemplate('jumphost-edit', $host); + } + + public static function doAjax() + { + + } + + /* + * MISC + */ + + private static 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; + } + +} \ No newline at end of file diff --git a/modules-available/rebootcontrol/pages/subnet.inc.php b/modules-available/rebootcontrol/pages/subnet.inc.php new file mode 100644 index 00000000..946d2d64 --- /dev/null +++ b/modules-available/rebootcontrol/pages/subnet.inc.php @@ -0,0 +1,150 @@ + $range['end_l']) { + Message::addError('invalid-range', $range['start'], $range['end']); + return; + } + $ret = Database::exec('INSERT INTO reboot_subnet (start, end, fixed, isdirect) + VALUES (:start, :end, 1, 0)', [ + 'start' => sprintf('%u', $range['start_l']), + 'end' => sprintf('%u', $range['end_l']), + ], true); + if ($ret === false) { + Message::addError('subnet-already-exists'); + } else { + Message::addSuccess('subnet-created'); + Util::redirect('?do=rebootcontrol&show=subnet&what=subnet&id=' . Database::lastInsertId()); + } + } + + private static function editSubnet() + { + User::assertPermission('subnet.flag'); + $id = Request::post('id', Request::REQUIRED, 'int'); + $subnet = Database::queryFirst('SELECT subnetid + FROM reboot_subnet WHERE subnetid = :id', ['id' => $id]); + if ($subnet === false) { + Message::addError('invalid-subnet', $id); + return; + } + $params = [ + 'id' => $id, + 'fixed' => !empty(Request::post('fixed', false, 'string')), + 'isdirect' => !empty(Request::post('isdirect', false, 'string')), + ]; + Database::exec('UPDATE reboot_subnet SET fixed = :fixed, isdirect = If(:fixed, :isdirect, isdirect) + WHERE subnetid = :id', $params); + if (User::hasPermission('jumphost.assign-subnet')) { + $hosts = Request::post('jumphost', [], 'array'); + if (!empty($hosts)) { + $hosts = array_keys($hosts); + Database::exec('DELETE FROM reboot_jumphost_x_subnet WHERE subnetid = :id AND hostid NOT IN (:hosts)', + ['id' => $id, 'hosts' => $hosts]); + $hosts = array_map(function($item) use ($id) { + return [$item, $id]; + }, $hosts); + Database::exec('INSERT IGNORE INTO reboot_jumphost_x_subnet (hostid, subnetid) VALUES :hosts', ['hosts' => $hosts]); + } + } + Message::addSuccess('subnet-updated'); + } + + /* + * Render + */ + + public static function doRender() + { + $what = Request::get('what', 'list', 'string'); + if ($what === 'list') { + self::showSubnets(); + } elseif ($what === 'subnet') { + self::showSubnet(); + } + } + + private static function showSubnets() + { + User::assertPermission('subnet.*'); + $nets = []; + $res = Database::simpleQuery('SELECT subnetid, start, end, fixed, isdirect, + lastdirectcheck, lastseen, seencount, Count(hxs.hostid) AS jumphostcount + FROM reboot_subnet + LEFT JOIN reboot_jumphost_x_subnet hxs USING (subnetid) + GROUP BY subnetid, start, end + ORDER BY start ASC, end DESC'); + $deadline = strtotime('-60 days'); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['start_s'] = long2ip($row['start']); + $row['end_s'] = long2ip($row['end']); + $row['lastseen_s'] = Util::prettyTime($row['lastseen']); + if ($row['lastseen'] && $row['lastseen'] < $deadline) { + $row['lastseen_class'] = 'text-danger'; + } + $nets[] = $row; + } + $data = ['subnets' => $nets]; + Render::addTemplate('subnet-list', $data); + Module::isAvailable('js_ip'); + } + + private static function showSubnet() + { + User::assertPermission('subnet.*'); + $id = Request::get('id', Request::REQUIRED, 'int'); + $subnet = Database::queryFirst('SELECT subnetid, start, end, fixed, isdirect + FROM reboot_subnet WHERE subnetid = :id', ['id' => $id]); + if ($subnet === false) { + Message::addError('invalid-subnet', $id); + return; + } + $subnet['start_s'] = long2ip($subnet['start']); + $subnet['end_s'] = long2ip($subnet['end']); + $res = Database::simpleQuery('SELECT h.hostid, h.host, h.port, hxs.subnetid FROM reboot_jumphost h + LEFT JOIN reboot_jumphost_x_subnet hxs ON (h.hostid = hxs.hostid AND hxs.subnetid = :id) + ORDER BY h.host ASC', ['id' => $id]); + $jh = []; + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + $row['checked'] = $row['subnetid'] === null ? '' : 'checked'; + $jh[] = $row; + } + $subnet['jumpHosts'] = $jh; + Permission::addGlobalTags($subnet['perms'], null, ['subnet.flag', 'jumphost.view', 'jumphost.assign-subnet']); + Render::addTemplate('subnet-edit', $subnet); + } + + public static function doAjax() + { + + } + +} \ No newline at end of file diff --git a/modules-available/rebootcontrol/pages/task.inc.php b/modules-available/rebootcontrol/pages/task.inc.php new file mode 100644 index 00000000..15449aaf --- /dev/null +++ b/modules-available/rebootcontrol/pages/task.inc.php @@ -0,0 +1,101 @@ + $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'); + } + } + + private static function showTaskList() + { + // Append list of active reboot/shutdown tasks + $allowedLocs = User::getAllowedLocations("*"); + $active = RebootControl::getActiveTasks($allowedLocs); + if (empty($active)) { + Message::addInfo('no-current-tasks'); + } else { + foreach ($active as &$entry) { + $entry['locationName'] = Location::getName($entry['locationId']); + } + unset($entry); + Render::addTemplate('task-list', ['list' => $active]); + } + } + + public static function doAjax() + { + + } + +} + + +// 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/templates/jumphost-edit.html b/modules-available/rebootcontrol/templates/jumphost-edit.html index 82629d2e..7a79dc86 100644 --- a/modules-available/rebootcontrol/templates/jumphost-edit.html +++ b/modules-available/rebootcontrol/templates/jumphost-edit.html @@ -2,7 +2,7 @@
- +
@@ -34,7 +34,7 @@
- diff --git a/modules-available/rebootcontrol/templates/jumphost-list.html b/modules-available/rebootcontrol/templates/jumphost-list.html index 3fbe4c21..8453d2bb 100644 --- a/modules-available/rebootcontrol/templates/jumphost-list.html +++ b/modules-available/rebootcontrol/templates/jumphost-list.html @@ -6,7 +6,8 @@ - + + diff --git a/modules-available/rebootcontrol/templates/status-checkconnection.html b/modules-available/rebootcontrol/templates/status-checkconnection.html index c30b42d9..e31d95ea 100644 --- a/modules-available/rebootcontrol/templates/status-checkconnection.html +++ b/modules-available/rebootcontrol/templates/status-checkconnection.html @@ -1,9 +1,5 @@

{{lang_checkingJumpHost}}: {{host}}

- - - {{lang_back}} -
diff --git a/modules-available/rebootcontrol/templates/status-reboot.html b/modules-available/rebootcontrol/templates/status-reboot.html index 594faa4c..4be95e81 100644 --- a/modules-available/rebootcontrol/templates/status-reboot.html +++ b/modules-available/rebootcontrol/templates/status-reboot.html @@ -1,10 +1,5 @@

{{lang_location}}: {{locationName}}

- - - {{lang_back}} - -
diff --git a/modules-available/rebootcontrol/templates/subnet-edit.html b/modules-available/rebootcontrol/templates/subnet-edit.html new file mode 100644 index 00000000..4c3702ba --- /dev/null +++ b/modules-available/rebootcontrol/templates/subnet-edit.html @@ -0,0 +1,64 @@ + + + + + + +
+
+ {{lang_editSubnet}}: {{start_s}} - {{end_s}} +
+
+
+
+ + +
+
+

{{lang_fixSubnetDesc}}

+
+
+
+ + +
+
+

{{lang_reachableFromServerDesc}}

+
+
+ + {{#jumpHosts}} +
+
+
+ + +
+
+
+ {{/jumpHosts}} +
+
+ +
+ + \ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/subnet-list.html b/modules-available/rebootcontrol/templates/subnet-list.html new file mode 100644 index 00000000..e2747316 --- /dev/null +++ b/modules-available/rebootcontrol/templates/subnet-list.html @@ -0,0 +1,58 @@ + + +

{{lang_subnets}}

+ +

{{lang_subnetsDescription}}

+ +
+ + + + + + + + + + + + {{#subnets}} + + + + + + + + + {{/subnets}} + +
{{lang_start}}{{lang_end}}{{lang_isFixed}}{{lang_isDirect}}{{lang_jumphostCount}}{{lang_lastseen}}
+ {{start_s}} + + {{end_s}} + {{#fixed}}{{/fixed}}{{#isdirect}}{{/isdirect}}{{jumphostcount}}{{lastseen_s}}
+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/modules-available/rebootcontrol/templates/task-list.html b/modules-available/rebootcontrol/templates/task-list.html index 87515085..8ae4975f 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