From ff6e86e8e5db2728d3b34c10f561cfdb533afa87 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Thu, 6 Jul 2017 13:11:54 +0200 Subject: [runmode] New module for managing special boot modes of clients --- modules-available/runmode/config.json | 4 + modules-available/runmode/inc/runmode.inc.php | 185 +++++++++++++++++++++ modules-available/runmode/install.inc.php | 36 ++++ modules-available/runmode/page.inc.php | 115 +++++++++++++ modules-available/runmode/style.css | 36 ++++ .../runmode/templates/machine-selector.html | 127 ++++++++++++++ 6 files changed, 503 insertions(+) create mode 100644 modules-available/runmode/config.json create mode 100644 modules-available/runmode/inc/runmode.inc.php create mode 100644 modules-available/runmode/install.inc.php create mode 100644 modules-available/runmode/page.inc.php create mode 100644 modules-available/runmode/style.css create mode 100644 modules-available/runmode/templates/machine-selector.html (limited to 'modules-available/runmode') diff --git a/modules-available/runmode/config.json b/modules-available/runmode/config.json new file mode 100644 index 00000000..e3c07d48 --- /dev/null +++ b/modules-available/runmode/config.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ "statistics", "js_selectize" ], + "permission":"0" +} diff --git a/modules-available/runmode/inc/runmode.inc.php b/modules-available/runmode/inc/runmode.inc.php new file mode 100644 index 00000000..5b59f1c1 --- /dev/null +++ b/modules-available/runmode/inc/runmode.inc.php @@ -0,0 +1,185 @@ + $machineuuid, + 'module' => $moduleId, + 'modeid' => $modeId, + 'modedata' => $modeData, + )); + } + return true; + } + + /** + * @param string|\Module $module + * @return array + */ + public static function getForModule($module, $groupByModeId = false) + { + if (is_object($module)) { + $module = $module->getIdentifier(); + } + $res = Database::simpleQuery('SELECT machineuuid, modeid, modedata FROM runmode WHERE module = :module', + compact('module')); + $ret = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($groupByModeId) { + if (!isset($ret[$row['modeid']])) { + $ret[$row['modeid']] = array(); + } + $ret[$row['modeid']][] = $row; + } else { + $ret[$row['machineuuid']] = $row; + } + } + return $ret; + } + + /** + * @param string|\Module $module + * @param string $modeId + * @param bool $detailed whether to return meta data about machine, not just machineuuid + * @return array + */ + public static function getForMode($module, $modeId, $detailed = false) + { + if (is_object($module)) { + $module = $module->getIdentifier(); + } + if ($detailed) { + $sel = ', m.hostname, m.clientip, m.macaddr, m.locationid'; + $join = 'INNER JOIN machine m USING (machineuuid)'; + } else { + $join = $sel = ''; + } + $res = Database::simpleQuery( + "SELECT r.machineuuid, r.modedata $sel + FROM runmode r $join + WHERE module = :module AND modeid = :modeId", + compact('module', 'modeId')); + $ret = array(); + while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + if ($detailed && empty($row['hostname'])) { + $row['hostname'] = $row['clientip']; + } + $ret[] = $row; + } + return $ret; + } + + /** + * Get display name of a module's mode. If the module doesn't have a getModeName + * method configured, the modeId is simply returned. Otherwise the return value of + * that method is passed through. getModeName by contract should return false if + * the module doesn't think the given modeId exists. + * + * @param string|\Module $module + * @param string $modeId + * @return string|bool mode name if known, modeId as fallback, or false if mode is not known by module + */ + public static function getModeName($module, $modeId) + { + if (is_object($module)) { + $module = $module->getIdentifier(); + } + $conf = self::getModuleConfig($module); + if ($conf === false || $conf->getModeName === false || !Module::isAvailable($module)) + return $modeId; + return call_user_func($conf->getModeName, $modeId); + } + +} + +/* *\ +|* Helper classes *| +\* */ + +/** + * Class RunModeModuleConfig represents desired config of a runmode + */ +class RunModeModuleConfig +{ + /** + * @var string|false + */ + public $systemdDefaultTarget = false; + /** + * @var string[] + */ + public $systemdDisableTargets = []; + /** + * @var string[] + */ + public $systemdEnableTargets = []; + /** + * @var string Name of function that turns a modeId into a string + */ + public $getModeName = false; + /** + * @var bool Consider this a normal client that should e.g. be shown in client statistics by default + */ + public $isClient = false; + + public function __construct($file) + { + $data = json_decode(file_get_contents($file), true); + if (!is_array($data)) + return; + $this->loadType($data, 'systemdDefaultTarget', 'string'); + $this->loadType($data, 'systemdDisableTargets', 'array'); + $this->loadType($data, 'systemdEnableTargets', 'array'); + $this->loadType($data, 'getModeName', 'string'); + $this->loadType($data, 'isClient', 'string'); + } + + private function loadType($data, $key, $type) + { + if (!isset($data[$key])) + return false; + if (is_string($type) && gettype($data[$key]) !== $type) + return false; + if (is_array($type) && !in_array(gettype($data[$key]), $type)) + return false; + $this->{$key} = $data[$key]; + return true; + } +} diff --git a/modules-available/runmode/install.inc.php b/modules-available/runmode/install.inc.php new file mode 100644 index 00000000..962a0cc9 --- /dev/null +++ b/modules-available/runmode/install.inc.php @@ -0,0 +1,36 @@ +handleAction($action); + Util::redirect('?do=runmode'); + } + } + + private function handleAction($action) + { + if ($action === 'save-mode') { + $machines = array_filter(Request::post('machines', [], 'array'), 'is_string'); + $module = Request::post('module', false, 'string'); + $modeId = Request::post('modeid', false, 'string'); + // TODO Validate + foreach ($machines as $machine) { + Database::exec("INSERT IGNORE INTO runmode (machineuuid, module, modeid) + VALUES (:machine, :module, :modeId)", compact('machine', 'module', 'modeId')); + } + Database::exec('DELETE FROM runmode + WHERE module = :module AND modeid = :modeId AND machineuuid NOT IN (:machines)', + compact('module', 'modeId', 'machines')); + Util::redirect('?do=runmode&module=' . $module . '&modeid=' . $modeId); + } + } + + protected function doRender() + { + $moduleId = Request::get('module', false, 'string'); + if ($moduleId !== false) { + $this->renderModule($moduleId); + return; + } + // TODO + Message::addInfo('OMGhai2U'); + } + + private function renderModule($moduleId) + { + $module = Module::get($moduleId); + if ($module === false) { + Message::addError('main.no-such-module', $moduleId); + Util::redirect('?do=runmode'); + } + $modeId = Request::get('modeid', false, 'string'); + if ($modeId !== false) { + $this->renderModuleMode($module, $modeId); + return; + } + // TODO + Message::addError('main.parameter-missing', 'modeid'); + Util::redirect('?do=runmode'); + } + + /** + * @param \Module $module + * @param string $modeId + */ + private function renderModuleMode($module, $modeId) + { + $moduleId = $module->getIdentifier(); + $modeName = RunMode::getModeName($moduleId, $modeId); + if ($modeName === false) { + Message::addError('invalid-modeid', $modeId); + Util::redirect('?do=runmode'); + } + Render::addTemplate('machine-selector', [ + 'module' => $moduleId, + 'modeid' => $modeId, + 'moduleName' => $module->getDisplayName(), + 'modeName' => $modeName, + 'machines' => json_encode(RunMode::getForMode($module, $modeId, true)) + ]); + } + + protected function doAjax() + { + $action = Request::any('action', false, 'string'); + + if ($action === 'getmachines') { + $query = Request::get('query', false, 'string'); + + $result = Database::simpleQuery('SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, m.locationid, ' + . 'r.module, r.modeid ' + . 'FROM machine m ' + . 'LEFT JOIN runmode r USING (machineuuid) ' + . 'WHERE machineuuid LIKE :query ' + . ' OR macaddr LIKE :query ' + . ' OR clientip LIKE :query ' + . ' OR hostname LIKE :query ' + . ' LIMIT 100', ['query' => "%$query%"]); + + $returnObject = [ + 'machines' => $result->fetchAll(PDO::FETCH_ASSOC) + ]; + + echo json_encode($returnObject); + } + } + +} \ No newline at end of file diff --git a/modules-available/runmode/style.css b/modules-available/runmode/style.css new file mode 100644 index 00000000..655292db --- /dev/null +++ b/modules-available/runmode/style.css @@ -0,0 +1,36 @@ +/* select popup */ +.machine-entry { + width: 99%; + width: calc(100% - 5px); + border: 1px solid #999; + border-radius: 5px; + margin: 2px; + padding: 2px 4px; +} + +/* in case it is already in the sketchboard */ +.machine-entry.used { + color: #666; +} + +.machine-entry table { + font-size: 12px; + margin-bottom: -5px; + width: 100%; +} +.machine-entry table tr { + border-top: 1px solid #bbb; +} + +.machine-entry-header { + font-weight: bolder; + font-size: 18px; +} + +.used .mode { + color: #f00; +} + +.selectize-dropdown-content { + max-height : 600px; +} diff --git a/modules-available/runmode/templates/machine-selector.html b/modules-available/runmode/templates/machine-selector.html new file mode 100644 index 00000000..95b255b5 --- /dev/null +++ b/modules-available/runmode/templates/machine-selector.html @@ -0,0 +1,127 @@ +

{{lang_assignRunmodeToMachine}}

+

{{moduleName}} // {{modeName}}

+

{{lang_assignMachineIntroText}}

+ + + +

{{lang_addNewMachines}}

+
+ + + + + +
+ +
+
+ + \ No newline at end of file -- cgit v1.2.3-55-g7522