From d635a52fd965519eecb717ea5b953a5f12ef9b88 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 7 Jul 2020 16:36:46 +0200 Subject: [systemstatus/sysconfig] Manage LDADP via systemd Finally got rid of our own little service manager. Status/restart handling capabilities were added to the system status page. TODO: Permissions for dnbd3, service (re)starting in general --- inc/event.inc.php | 22 --- inc/trigger.inc.php | 45 +---- modules-available/sysconfig/hooks/cron.inc.php | 2 - .../sysconfig/inc/configmodulebaseldap.inc.php | 41 ++++- modules-available/sysconfig/page.inc.php | 5 +- modules-available/systemstatus/lang/de/module.json | 2 + .../systemstatus/lang/de/template-tags.json | 3 +- modules-available/systemstatus/lang/en/module.json | 2 + .../systemstatus/lang/en/template-tags.json | 3 +- modules-available/systemstatus/page.inc.php | 183 ++++++++++++--------- .../systemstatus/templates/_page.html | 36 ++-- .../systemstatus/templates/ajax-journal.html | 19 +++ .../systemstatus/templates/services.html | 32 ++-- .../systemstatus/templates/systeminfo.html | 5 + 14 files changed, 223 insertions(+), 177 deletions(-) create mode 100644 modules-available/systemstatus/templates/ajax-journal.html diff --git a/inc/event.inc.php b/inc/event.inc.php index 16e5323a..e5ade41b 100644 --- a/inc/event.inc.php +++ b/inc/event.inc.php @@ -41,7 +41,6 @@ class Event // Tasks: fire away $mountStatus = false; $mountId = Trigger::mount(); - $ldadpId = Trigger::ldadp(); $ipxeId = Trigger::ipxe(); // Check status of all tasks @@ -53,17 +52,6 @@ class Event $mountStatus = Taskmanager::waitComplete($mountId, 5000); } - // LDAP AD Proxy - if ($ldadpId === false) { - EventLog::failure('Cannot start LDAP-AD-Proxy: Taskmanager unreachable!'); - $everythingFine = false; - } else { - $res = Taskmanager::waitComplete($ldadpId, 5000); - if (Taskmanager::isFailed($res)) { - EventLog::failure('Starting LDAP-AD-Proxy failed', $res['data']['messages']); - $everythingFine = false; - } - } // Primary IP address if (!$autoIp) { EventLog::failure("The server's IP address could not be determined automatically, and there is no valid address configured."); @@ -116,15 +104,5 @@ class Event } } - /** - * The activated configuration changed. - */ - public static function activeConfigChanged() - { - $task = Trigger::ldadp(); - if ($task === false) - return; - TaskmanagerCallback::addCallback($task, 'ldadpStartup'); - } } diff --git a/inc/trigger.inc.php b/inc/trigger.inc.php index 8ed098fc..134bab53 100644 --- a/inc/trigger.inc.php +++ b/inc/trigger.inc.php @@ -93,37 +93,6 @@ class Trigger return false; } - /** - * Launch all ldadp instances that need to be running. - * - * @param int $exclude if not NULL, id of config module NOT to start - * @param string $parent if not NULL, this will be the parent task of the launch-task - * @return boolean|string false on error, id of task otherwise - */ - public static function ldadp($exclude = NULL, $parent = NULL) - { - // TODO: Fetch list from ConfigModule_AdAuth (call loadDb first) - $res = Database::simpleQuery("SELECT DISTINCT moduleid FROM configtgz_module" - . " INNER JOIN configtgz_x_module USING (moduleid)" - . " INNER JOIN configtgz USING (configid)" - . " INNER JOIN configtgz_location USING (configid)" - . " WHERE moduletype IN ('AdAuth', 'LdapAuth')"); - $id = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { - if (!is_null($exclude) && (int)$row['moduleid'] === (int)$exclude) - continue; - $id[] = (int)$row['moduleid']; - } - $task = Taskmanager::submit('LdadpLauncher', array( - 'ids' => $id, - 'parentTask' => $parent, - 'failOnParentFail' => false - )); - if (!isset($task['id'])) - return false; - return $task['id']; - } - /** * Mount the VM store into the server. * @@ -220,6 +189,9 @@ class Trigger return $parent; } + /** + * Stop any daemons that might be sitting on the VMstore, or database. + */ public static function stopDaemons($parent, &$taskids) { $parent = self::triggerDaemons('stop', $parent, $taskids); @@ -235,15 +207,4 @@ class Trigger return $parent; } - public static function startDaemons($parent, &$taskids) - { - $parent = self::triggerDaemons('start', $parent, $taskids); - $taskid = self::ldadp($parent); - if ($taskid !== false) { - $taskids['ldadpid'] = $taskid; - $parent = $taskid; - } - return $parent; - } - } diff --git a/modules-available/sysconfig/hooks/cron.inc.php b/modules-available/sysconfig/hooks/cron.inc.php index b518ca06..b959060d 100644 --- a/modules-available/sysconfig/hooks/cron.inc.php +++ b/modules-available/sysconfig/hooks/cron.inc.php @@ -1,7 +1,5 @@ location where the location has been deleted Database::exec("DELETE c FROM configtgz_location c LEFT JOIN location l USING (locationid) diff --git a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php index ad3d32c5..39f4f68e 100644 --- a/modules-available/sysconfig/inc/configmodulebaseldap.inc.php +++ b/modules-available/sysconfig/inc/configmodulebaseldap.inc.php @@ -32,12 +32,43 @@ abstract class ConfigModuleBaseLdap extends ConfigModule return $list; } - protected function generateInternal($tgz, $parent) + public static function getActiveModuleIds() + { + return Database::queryColumnArray("SELECT DISTINCT moduleid FROM configtgz_module" + . " INNER JOIN configtgz_x_module USING (moduleid)" + . " INNER JOIN configtgz USING (configid)" + . " INNER JOIN configtgz_location USING (configid)" + . " WHERE moduletype IN ('AdAuth', 'LdapAuth')"); + } + + /** + * Launch all ldadp instances that need to be running. + * + * @param string $command start, restart, check + * @param bool|int|int[] $ids list of IDs to run command on, or false meaning "all" + * @param string $parent if not NULL, this will be the parent task of the launch-task + * @return boolean|string false on error, id of task otherwise + */ + public static function ldadp($command = 'start', $ids = false, $parent = null) { - $np = Trigger::ldadp($this->id(), $parent); - if ($np !== false) { - $parent = $np; + if ($ids === false) { + $ids = self::getActiveModuleIds(); + } elseif (!is_array($ids)) { + $ids = [$ids]; } + $task = Taskmanager::submit('LdadpLauncher', array( + 'ids' => $ids, + 'command' => $command, + 'parentTask' => $parent, + 'failOnParentFail' => false + )); + if (!isset($task['id'])) + return false; + return $task['id']; + } + + protected function generateInternal($tgz, $parent) + { $config = $this->moduleData; if (isset($config['certificate']) && !is_string($config['certificate'])) { unset($config['certificate']); @@ -72,7 +103,7 @@ abstract class ConfigModuleBaseLdap extends ConfigModule $this->preTaskmanagerHook($config); $task = Taskmanager::submit('CreateLdapConfig', $config); if (is_array($task) && isset($task['id'])) { - Trigger::ldadp(null, $task['id']); + self::ldadp('restart', $this->id(), $task['id']); // TODO: Use --restart for this one only } return $task; } diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php index 7f2277dc..64162294 100644 --- a/modules-available/sysconfig/page.inc.php +++ b/modules-available/sysconfig/page.inc.php @@ -343,7 +343,10 @@ class Page_SysConfig extends Page Database::exec("INSERT INTO configtgz_location (locationid, configid) VALUES (:locationid, :configid)" . " ON DUPLICATE KEY UPDATE configid = :configid", compact('locationid', 'configid')); } - Event::activeConfigChanged(); + $task = ConfigModuleBaseLdap::ldadp(); + if ($task !== false) { + TaskmanagerCallback::addCallback($task, 'ldadpStartup'); + } Util::redirect('?do=sysconfig&locationid=' . $this->currentLoc); } diff --git a/modules-available/systemstatus/lang/de/module.json b/modules-available/systemstatus/lang/de/module.json index dd1a115c..22be8a1d 100644 --- a/modules-available/systemstatus/lang/de/module.json +++ b/modules-available/systemstatus/lang/de/module.json @@ -1,6 +1,8 @@ { "module_name": "System-Status", + "page_title": "Systemstatus des Servers", "tab_DmsdLog": "bwLehrpool-Suite Server Log", + "tab_Dnbd3Log": "DNBD3 Server Log", "tab_LdadpLog": "LDAP\/AD", "tab_LighttpdLog": "lighttpd Log", "tab_Netstat": "netstat -tulpn", diff --git a/modules-available/systemstatus/lang/de/template-tags.json b/modules-available/systemstatus/lang/de/template-tags.json index 27760c1e..6641b9e1 100644 --- a/modules-available/systemstatus/lang/de/template-tags.json +++ b/modules-available/systemstatus/lang/de/template-tags.json @@ -5,6 +5,7 @@ "lang_attention": "Achtung!", "lang_average": "Durchschnitt", "lang_capacity": "Kapazit\u00e4t", + "lang_confirmRestart": "Diesen Dienst wirklich neustarten? Dies kann Auswirkungen auf den Betrieb haben.", "lang_cpuLoad": "CPU-Last", "lang_dmsdUnreachable": "dmsd nicht erreichbar", "lang_failure": "Fehler", @@ -19,6 +20,7 @@ "lang_onlyOS": "Nur OS", "lang_overview": "\u00dcbersicht", "lang_ramUsage": "RAM-Nutzung", + "lang_restart": "Neustarten", "lang_runningDownloads": "Aktive Downloads", "lang_runningUploads": "Aktive Uploads", "lang_serverReboot": "Server neustarten", @@ -32,7 +34,6 @@ "lang_systemPartition": "Systempartition", "lang_systemStoreError": "Fehler beim Ermitteln des verf\u00fcgbaren Systemspeichers", "lang_total": "Gesamt", - "lang_unknownState": "Unbekannter Status", "lang_updatedPackages": "Ausstehende Updates", "lang_uptimeOS": "OS Uptime", "lang_vmStore": "VM-Speicher", diff --git a/modules-available/systemstatus/lang/en/module.json b/modules-available/systemstatus/lang/en/module.json index 9f6d937a..f5fec515 100644 --- a/modules-available/systemstatus/lang/en/module.json +++ b/modules-available/systemstatus/lang/en/module.json @@ -1,6 +1,8 @@ { "module_name": "System Status", + "page_title": "System status of server", "tab_DmsdLog": "bwLehrpool-Suite log", + "tab_Dnbd3Log": "DNBD3 server log", "tab_LdadpLog": "LDAP\/AD", "tab_LighttpdLog": "lighttpd log", "tab_Netstat": "netstat -tulpn", diff --git a/modules-available/systemstatus/lang/en/template-tags.json b/modules-available/systemstatus/lang/en/template-tags.json index de210864..9eec08c4 100644 --- a/modules-available/systemstatus/lang/en/template-tags.json +++ b/modules-available/systemstatus/lang/en/template-tags.json @@ -5,6 +5,7 @@ "lang_attention": "Attention!", "lang_average": "Average", "lang_capacity": "Capacity", + "lang_confirmRestart": "Are you sure you want to restart this service? This can leads to interruptions.", "lang_cpuLoad": "CPU Load", "lang_dmsdUnreachable": "dmsd not reachable", "lang_failure": "Failure", @@ -19,6 +20,7 @@ "lang_onlyOS": "OS only", "lang_overview": "Overview", "lang_ramUsage": "RAM usage", + "lang_restart": "Restart", "lang_runningDownloads": "Running downloads", "lang_runningUploads": "Running uploads", "lang_serverReboot": "Reboot Server", @@ -32,7 +34,6 @@ "lang_systemPartition": "System partition", "lang_systemStoreError": "Error querying available system storage", "lang_total": "Total", - "lang_unknownState": "Unknown status", "lang_updatedPackages": "Pending updates", "lang_uptimeOS": "OS uptime", "lang_vmStore": "VM store", diff --git a/modules-available/systemstatus/page.inc.php b/modules-available/systemstatus/page.inc.php index 6d317b47..f94aa4d4 100644 --- a/modules-available/systemstatus/page.inc.php +++ b/modules-available/systemstatus/page.inc.php @@ -3,8 +3,6 @@ class Page_SystemStatus extends Page { - private $rebootTask = false; - protected function doPreprocess() { User::load(); @@ -14,27 +12,51 @@ class Page_SystemStatus extends Page Util::redirect('?do=Main'); } - if (Request::post('action') === 'reboot') { + $action = Request::post('action', false, 'string'); + if ($action === 'reboot') { User::assertPermission("serverreboot"); - $this->rebootTask = Taskmanager::submit('Reboot'); + $task = Taskmanager::submit('Reboot'); + if (Taskmanager::isTask($task)) { + Util::redirect('?do=systemstatus&taskid=' . $task['id']); + } + } elseif ($action === 'service-start' || $action === 'service-restart') { + $this->handleServiceAction(substr($action, 8)); + } + if (Request::isPost()) { + Util::redirect('?do=systemstatus'); } User::assertPermission('*'); } + private function handleServiceAction(string $action) + { + $service = Request::post('service', Request::REQUIRED, 'string'); + $task = Taskmanager::submit('Systemctl', ['operation' => $action, 'service' => $service]); + $extra = ''; + $cmp = preg_replace('/\.service$|@.*$/', '', $service); + if ($cmp === 'dmsd') { + $extra = '#id-DmsdLog_pane'; + } elseif ($cmp === 'ldadp') { + $extra = '#id-LdadpLog_pane'; + } elseif ($cmp === 'dnbd3-server') { + $extra = '#id-Dnbd3Log_pane'; + } + Util::redirect('?do=systemstatus&taskid=' . $task['id'] . '&taskname=' . urlencode($service) . $extra); + } + protected function doRender() { $data = array(); - if (is_array($this->rebootTask) && isset($this->rebootTask['id'])) { - $data['rebootTask'] = $this->rebootTask['id']; - } - $tabs = array('DmsdLog', 'Netstat', 'PsList', 'LdadpLog', 'LighttpdLog'); + $data['taskid'] = Request::get('taskid', '', 'string'); + $data['taskname'] = Request::get('taskname', 'Reboot', 'string'); + $tabs = array('DmsdLog', 'Netstat', 'PsList', 'LdadpLog', 'LighttpdLog', 'Dnbd3Log'); $data['tabs'] = array(); // Dictionary::translate('tab_DmsdLog') Dictionary::translate('tab_LdadpLog') Dictionary::translate('tab_Netstat') - // Dictionary::translate('tab_LighttpdLog') Dictionary::translate('tab_PsList') + // Dictionary::translate('tab_LighttpdLog') Dictionary::translate('tab_PsList') Dictionary::translate('tab_Dnbd3Log') foreach ($tabs as $tab) { $data['tabs'][] = array( 'type' => $tab, - 'name' => Dictionary::translate('tab_' . $tab), + 'name' => Dictionary::translate('tab_' . $tab, true), 'enabled' => User::hasPermission('tab.' . $tab), ); } @@ -238,10 +260,16 @@ class Page_SystemStatus extends Page 'task' => Taskmanager::submit('Systemctl', ['service' => $svc, 'operation' => 'is-active']) ); } - $tasks[] = array( - 'name' => 'LDAP/AD-Proxy', - 'task' => Trigger::ldadp() - ); + $ldapIds = $ldadp = false; + if (Module::isAvailable('sysconfig')) { + $ldapIds = ConfigModuleBaseLdap::getActiveModuleIds(); + if (!empty($ldapIds)) { + $ldadp = array( // No name - no display + 'task' => ConfigModuleBaseLdap::ldadp('check', $ldapIds) // TODO: Proper --check usage + ); + $tasks[] =& $ldadp; + } + } $deadline = time() + 10; do { $done = true; @@ -257,13 +285,40 @@ class Page_SystemStatus extends Page } while (!$done && time() < $deadline); foreach ($tasks as $task) { + if (!isset($task['name'])) + continue; $fail = Taskmanager::isFailed($task['task']); - $data['services'][] = array( + $entry = array( 'name' => $task['name'], + 'service' => $task['name'], 'fail' => $fail, - 'data' => isset($task['data']) ? $task['data'] : null, - 'unknown' => $task['task'] === false ); + if ($fail) { + if (!isset($task['task']['data'])) { + $entry['error'] = 'Taskmanager Error'; + } elseif (isset($task['task']['data']['messages'])) { + $entry['error'] = $task['task']['data']['messages']; + } + } + $data['services'][] = $entry; + } + if ($ldadp !== false) { + //error_log(print_r($ldadp, true)); + preg_match_all('/^ldadp@(\d+)\.service\s+(\S+)$/m', $ldadp['task']['data']['messages'], $out, PREG_SET_ORDER); + $instances = []; + foreach ($out as $instance) { + $instances[$instance[1]] = $instance[2]; + } + foreach ($ldapIds as $id) { + $status = $instances[$id] ?? 'failed'; + $fail = ($status !== 'running'); + $data['services'][] = [ + 'name' => 'LDAP/AD Proxy #' . $id, + 'service' => 'ldadp@' . $id, + 'fail' => $fail, + 'error' => $fail ? $status : false, + ]; + } } echo Render::parse('services', $data); @@ -271,35 +326,23 @@ class Page_SystemStatus extends Page protected function ajaxDmsdLog() { - User::assertPermission("tab.dmsdlog"); - $fh = @fopen('/var/log/dmsd.log', 'r'); - if ($fh === false) { - echo 'Error opening log file'; - return; - } - fseek($fh, -6000, SEEK_END); - $data = fread($fh, 6000); - @fclose($fh); - if ($data === false) { - echo 'Error reading from log file'; - return; - } - // If we could read less, try the .1 file too - $amount = 6000 - strlen($data); - if ($amount > 100) { - $fh = @fopen('/var/log/dmsd.log.1', 'r'); - if ($fh !== false) { - fseek($fh, -$amount, SEEK_END); - $data = fread($fh, $amount) . $data; - @fclose($fh); - } - } - if (strlen($data) < 5990) { - $start = 0; - } else { - $start = strpos($data, "\n") + 1; - } - echo '
', htmlspecialchars(substr($data, $start), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '
'; + $this->showJournal('dmsd.service', 'tab.dmsdlog'); + } + + protected function ajaxDnbd3Log() + { + $this->showJournal('dnbd3-server.service', 'tab.dnbd3log'); + } + + protected function showJournal($service, $permission) + { + User::assertPermission($permission); + $output = [ + 'name' => $service, + 'service' => $service, + 'task' => Taskmanager::submit('Systemctl', ['operation' => 'journal', 'service' => $service]), + ]; + echo Render::parse('ajax-journal', ['modules' => [$output]]); } protected function ajaxLighttpdLog() @@ -338,40 +381,28 @@ class Page_SystemStatus extends Page protected function ajaxLdadpLog() { User::assertPermission("tab.ldadplog"); - $haveSysconfig = Module::isAvailable('sysconfig'); - $files = glob('/var/log/ldadp/*.log', GLOB_NOSORT); - if ($files === false || empty($files)) echo('No logs found'); - $now = time(); - foreach ($files as $file) { - $mod = filemtime($file); - if ($now - $mod > 86400) continue; - // New enough - handle - preg_match(',/(\d+)\.log,', $file, $out); - $module = $haveSysconfig ? ConfigModule::get($out[1]) : false; + if (!Module::isAvailable('sysconfig')) { + die('SysConfig module not enabled'); + } + $ids = ConfigModuleBaseLdap::getActiveModuleIds(); + //error_log(print_r($ids, true)); + $output = []; + foreach ($ids as $id) { + $module = ConfigModule::get($id); if ($module === false) { - echo '

Module ', $out[1], '

'; - } else { - echo '

Module ', htmlspecialchars($module->title()), '

'; - } - $fh = @fopen($file, 'r'); - if ($fh === false) { - echo '
Error opening log file
'; - continue; - } - fseek($fh, -5000, SEEK_END); - $data = fread($fh, 5000); - @fclose($fh); - if ($data === false) { - echo '
Error reading from log file
'; - continue; - } - if (strlen($data) < 4990) { - $start = 0; + $name = "#$id"; } else { - $start = strpos($data, "\n") + 1; + $name = $module->title(); } - echo '
', htmlspecialchars(substr($data, $start), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '
'; + $service = "ldadp@{$id}.service"; + $output[] = [ + 'name' => $name, + 'service' => $service, + 'task' => Taskmanager::submit('Systemctl', ['operation' => 'journal', 'service' => $service]), + ]; } + //error_log(print_r($output, true)); + echo Render::parse('ajax-journal', ['modules' => $output]); } protected function ajaxNetstat() diff --git a/modules-available/systemstatus/templates/_page.html b/modules-available/systemstatus/templates/_page.html index f879ea7a..4b62104c 100644 --- a/modules-available/systemstatus/templates/_page.html +++ b/modules-available/systemstatus/templates/_page.html @@ -1,12 +1,12 @@

{{lang_moduleHeading}}

-{{#rebootTask}} -
Reboot...
-{{/rebootTask}} +{{#taskid}} +
{{taskname}}
+{{/taskid}}