diff options
author | Simon Rettberg | 2024-09-20 17:08:51 +0200 |
---|---|---|
committer | Simon Rettberg | 2024-09-20 17:08:51 +0200 |
commit | 52b3a02fb2dbb0eca3fb4effbe7c6b60a5ecf168 (patch) | |
tree | a4b4b63b19c7660b4bdf4b4e9c09466da9d9a151 | |
parent | [statistics] Ignore HDDS (block devices) that are too small (diff) | |
download | slx-admin-52b3a02fb2dbb0eca3fb4effbe7c6b60a5ecf168.tar.gz slx-admin-52b3a02fb2dbb0eca3fb4effbe7c6b60a5ecf168.tar.xz slx-admin-52b3a02fb2dbb0eca3fb4effbe7c6b60a5ecf168.zip |
[rebootcontrol/statistics] Add templates for useful remote exec commands
13 files changed, 193 insertions, 17 deletions
diff --git a/modules-available/rebootcontrol/inc/exectemplate.inc.php b/modules-available/rebootcontrol/inc/exectemplate.inc.php new file mode 100644 index 00000000..483dc31f --- /dev/null +++ b/modules-available/rebootcontrol/inc/exectemplate.inc.php @@ -0,0 +1,106 @@ +<?php +declare(strict_types=1); + +class ExecTemplate +{ + + /** @var string */ + public $id; + /** @var string */ + public $title; + /** @var string */ + public $command; + /** @var ExecTemplateField[] */ + public $args; + + public function __construct(string $id, string $title, string $command, array $args) + { + $this->id = $id; + $this->title = $title; + $this->command = $command; + $this->args = $args; + } + + public function buildFromPost(): string + { + $args = []; + foreach ($this->args as $arg) { + $args[$arg->id] = Request::post('param-' . $arg->id, '', 'string'); + } + return preg_replace_callback('/%([0-9]+)([a-z]*)%/', function($out) use ($args) { + if (!isset($args[$out[1]])) { + ErrorHandler::traceError('Invalid Argument Index: ' . $out[1]); + } + $str = preg_replace('/\r\n?/', "\n", $args[$out[1]]); + if (strpos($out[2], 'r') === false) { + $str = $this->bashString($str); + } + return $str; + }, $this->command); + } + + private function bashString(string $string): string + { + if (strpos($string, "'") === false) { + return "'$string'"; + } + return "'" . str_replace("'", "'\\''", $string) . "'"; + } + + // ## STATIC ## + + public static function get(string $id): ?ExecTemplate + { + // TODO: some day, maybe put these in a json file? Or DB and allow user defined ones... + // XXX: Add to list() too if you add something here + if ($id === '1') { + return new ExecTemplate('1', Dictionary::translateFileModule('rebootcontrol', 'module', 'exec_debug_report'), + 'debug_report --message %1%', + [new ExecTemplateField('1', Dictionary::translateFileModule('rebootcontrol', 'module', 'exec_debug_report_message'), 'string')], + ); + } + if ($id === '2') { + return new ExecTemplate('2', Dictionary::translateFileModule('rebootcontrol', 'module', 'exec_systemd_plot'), + 'systemd-analyze plot', + [], + ); + } + return null; + } + + /** + * @return ExecTemplate[] + */ + public static function list(): array + { + return [self::get('1'), self::get('2')]; + } + +} + +class ExecTemplateField +{ + + /** @var string */ + private $type; + /** @var string */ + public $title; + /** @var string */ + public $id; + + public function __construct(string $id, string $title, string $type) + { + $this->id = $id; + $this->title = $title; + $this->type = $type; + } + + public function render(): string + { + if ($this->type === 'string') { + return '<input type="text" class="form-control" name="param-' . $this->id . '">'; + } + return '<div>???</div>'; + } + +}
\ 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 107c2a50..b6ad7a16 100644 --- a/modules-available/rebootcontrol/inc/rebootcontrol.inc.php +++ b/modules-available/rebootcontrol/inc/rebootcontrol.inc.php @@ -446,7 +446,7 @@ class RebootControl $subnet['iclients'] = array_slice($subnet['iclients'], 0, 3); } - public static function prepareExec() + public static function prepareExec(string $presetId = null) { User::assertPermission('.rebootcontrol.action.exec'); $uuids = array_values(Request::post('uuid', Request::REQUIRED, 'array')); @@ -455,7 +455,11 @@ class RebootControl return; $id = mt_rand(); Session::set('exec-' . $id, $machines, 60); - Util::redirect('?do=rebootcontrol&show=exec&what=prepare&id=' . $id); + $q = $id; + if ($presetId !== null) { + $q .= '&preset=' . $presetId; + } + Util::redirect('?do=rebootcontrol&show=exec&what=prepare&id=' . $q); } /** diff --git a/modules-available/rebootcontrol/lang/de/messages.json b/modules-available/rebootcontrol/lang/de/messages.json index b481d64a..9a460301 100644 --- a/modules-available/rebootcontrol/lang/de/messages.json +++ b/modules-available/rebootcontrol/lang/de/messages.json @@ -1,4 +1,5 @@ { + "exec-template-not-found": "Ung\u00fcltiges Exec-Template: {{0}}", "invalid-cidr": "Ung\u00fcltige CIDR-Angabe: {{0}}", "invalid-port": "Ung\u00fcltiger Port: {{0}}", "invalid-subnet": "Ung\u00fcltiges Subnetz: {{0}}", diff --git a/modules-available/rebootcontrol/lang/de/module.json b/modules-available/rebootcontrol/lang/de/module.json index 2488fc81..d7e8978c 100644 --- a/modules-available/rebootcontrol/lang/de/module.json +++ b/modules-available/rebootcontrol/lang/de/module.json @@ -1,4 +1,7 @@ { + "exec_debug_report": "Fehlerbericht senden", + "exec_debug_report_message": "Beizuf\u00fcgender Hinweistext", + "exec_systemd_plot": "systemd plot erzeugen", "jumphosts": "Sprung-Hosts", "module_name": "Fernsteuerung \/ WakeOnLAN", "page_title": "WakeOnLAN", diff --git a/modules-available/rebootcontrol/lang/de/template-tags.json b/modules-available/rebootcontrol/lang/de/template-tags.json index b54adbcd..df0a4875 100644 --- a/modules-available/rebootcontrol/lang/de/template-tags.json +++ b/modules-available/rebootcontrol/lang/de/template-tags.json @@ -11,6 +11,7 @@ "lang_checkingJumpHost": "Teste Sprung-Host", "lang_client": "Client", "lang_clientCount": "# Clients", + "lang_command": "Befehl", "lang_confirmDeleteSubnet": "Dieses Subnetz wirklich l\u00f6schen?", "lang_connecting": "Verbinde...", "lang_directedBroadcastAddress": "Ziel-Adresse", diff --git a/modules-available/rebootcontrol/lang/en/messages.json b/modules-available/rebootcontrol/lang/en/messages.json index f125e944..8cfafaba 100644 --- a/modules-available/rebootcontrol/lang/en/messages.json +++ b/modules-available/rebootcontrol/lang/en/messages.json @@ -1,4 +1,5 @@ { + "exec-template-not-found": "Invalid exec template: {{0}}", "invalid-cidr": "Invalid CIDR notion: {{0}}", "invalid-port": "Invalid port: {{0}}", "invalid-subnet": "Invalid subnet: {{0}}", diff --git a/modules-available/rebootcontrol/lang/en/module.json b/modules-available/rebootcontrol/lang/en/module.json index 1308976b..14ad50b0 100644 --- a/modules-available/rebootcontrol/lang/en/module.json +++ b/modules-available/rebootcontrol/lang/en/module.json @@ -1,4 +1,7 @@ { + "exec_debug_report": "Send debug report", + "exec_debug_report_message": "Message to add", + "exec_systemd_plot": "Generate systemd plot", "jumphosts": "Jump hosts", "module_name": "Remote \/ WakeOnLAN", "page_title": "WakeOnLAN", diff --git a/modules-available/rebootcontrol/lang/en/template-tags.json b/modules-available/rebootcontrol/lang/en/template-tags.json index 5740b208..4f951641 100644 --- a/modules-available/rebootcontrol/lang/en/template-tags.json +++ b/modules-available/rebootcontrol/lang/en/template-tags.json @@ -11,6 +11,7 @@ "lang_checkingJumpHost": "Check jump host", "lang_client": "Client", "lang_clientCount": "# clients", + "lang_command": "Command", "lang_confirmDeleteSubnet": "Delete this subnet?", "lang_connecting": "Connecting...", "lang_directedBroadcastAddress": "Destination address", diff --git a/modules-available/rebootcontrol/pages/exec.inc.php b/modules-available/rebootcontrol/pages/exec.inc.php index 6b5ea407..422f88f8 100644 --- a/modules-available/rebootcontrol/pages/exec.inc.php +++ b/modules-available/rebootcontrol/pages/exec.inc.php @@ -15,11 +15,19 @@ class SubPage { $uuids = array_values(Request::post('uuid', Request::REQUIRED, 'array')); $machines = RebootUtils::getFilteredMachineList($uuids, 'action.exec'); - if (empty($machines)) - return; + if (empty($machines)) { + ErrorHandler::traceError('No machines'); + } RebootUtils::sortRunningFirst($machines); - $script = preg_replace('/\r\n?/', "\n", Request::post('script', Request::REQUIRED, 'string')); - $task = RebootControl::runScript($machines, $script); + $preset = self::presetFromRequest(); + if ($preset !== null) { + $script = $preset->buildFromPost(); + } + if (empty($script)) { + $script = preg_replace('/\r\n?/', "\n", + Request::post('script', Request::REQUIRED, 'string')); + } + $task = RebootControl::runScript($machines, $script, 15); if (Taskmanager::isTask($task)) { Util::redirect("?do=rebootcontrol&show=task&what=task&taskid=" . $task["id"]); } @@ -46,7 +54,20 @@ class SubPage return; } Session::set('exec-' . $id, false); - Render::addTemplate('exec-enter-command', ['clients' => $machines, 'id' => $id]); + $preset = self::presetFromRequest(); + Render::addTemplate('exec-enter-command', ['clients' => $machines, 'id' => $id, 'preset' => $preset]); + } + + private static function presetFromRequest(): ?ExecTemplate + { + $presetId = Request::any('preset', null, 'string'); + if ($presetId === null) + return null; + $preset = ExecTemplate::get($presetId); + if ($preset === null) { + Message::addError('exec-template-not-found', $presetId); + } + return $preset; } public static function doAjax() diff --git a/modules-available/rebootcontrol/templates/exec-enter-command.html b/modules-available/rebootcontrol/templates/exec-enter-command.html index 8bf81605..09eb32f5 100644 --- a/modules-available/rebootcontrol/templates/exec-enter-command.html +++ b/modules-available/rebootcontrol/templates/exec-enter-command.html @@ -28,12 +28,26 @@ </tbody> </table> - <h3>{{lang_enterCommand}}</h3> + {{^preset}} + <h3>{{lang_enterCommand}}</h3> + <div> + <label for="script-text">{{lang_scriptOrCommand}}</label> + <textarea id="script-text" class="form-control" name="script" rows="10"></textarea> + </div> + {{/preset}} + + {{#preset}} + <input type="hidden" name="preset" value="{{id}}"> + <h3>{{title}}</h3> + <div class="slx-space">{{lang_command}}: <i>{{command}}</i></div> + {{#args}} + <div class="row"> + <div class="col-md-5">{{title}}</div> + <div class="col-md-7">{{{render}}}</div> + </div> + {{/args}} + {{/preset}} - <div> - <label for="script-text">{{lang_scriptOrCommand}}</label> - <textarea id="script-text" class="form-control" name="script" rows="10"></textarea> - </div> <div class="text-right slx-space"> <button type="submit" class="btn btn-primary" name="action" value="exec"> <span class="glyphicon glyphicon-play"></span> diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index 4f11e835..f07dca56 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -97,6 +97,10 @@ class Page_Statistics extends Page if (Module::isAvailable('rebootcontrol')) { RebootControl::prepareExec(); } + } elseif (substr($action, 0, 12) === 'exec-preset-') { + if (Module::isAvailable('rebootcontrol')) { + RebootControl::prepareExec(substr($action, 12)); + } } // Make sure we don't render any content for POST requests - should be handled above and then diff --git a/modules-available/statistics/pages/machine.inc.php b/modules-available/statistics/pages/machine.inc.php index 34ed63da..77d9b28e 100644 --- a/modules-available/statistics/pages/machine.inc.php +++ b/modules-available/statistics/pages/machine.inc.php @@ -164,12 +164,13 @@ class SubPage } } // Rebootcontrol - if (Module::get('rebootcontrol') !== false) { + if (Module::isAvailable('rebootcontrol')) { $client['canReboot'] = (User::hasPermission('.rebootcontrol.action.reboot', (int)$client['locationid'])); $client['canShutdown'] = (User::hasPermission('.rebootcontrol.action.shutdown', (int)$client['locationid'])); $client['canWol'] = (User::hasPermission('.rebootcontrol.action.wol', (int)$client['locationid'])); $client['canExec'] = (User::hasPermission('.rebootcontrol.action.exec', (int)$client['locationid'])); $client['rebootcontrol'] = $client['canReboot'] || $client['canShutdown'] || $client['canWol'] || $client['canExec']; + $client['execList'] = ExecTemplate::list(); } // Baseconfig if (Module::get('baseconfig') !== false diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html index be32f9c7..1139d5b9 100644 --- a/modules-available/statistics/templates/machine-main.html +++ b/modules-available/statistics/templates/machine-main.html @@ -153,10 +153,26 @@ </button> {{/canWol}} {{#canExec}} - <button type="submit" name="action" value="prepare-exec" class="btn btn-sm btn-primary btn-machine-action"> - <span class="glyphicon glyphicon-play"></span> - {{lang_remoteExec}} - </button> + <div class="btn-group"> + <button type="submit" name="action" value="prepare-exec" class="btn btn-sm btn-primary btn-machine-action"> + <span class="glyphicon glyphicon-play"></span> + {{lang_remoteExec}} + </button> + <button type="button" class="btn btn-sm btn-primary btn-machine-action dropdown-toggle" + data-toggle="dropdown" aria-haspopup="true"> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <div class="dropdown-menu" style="padding:0"> + <div class="btn-group-vertical"> + {{#execList}} + <button type="submit" name="action" value="exec-preset-{{id}}" class="btn btn-danger btn-machine-action"> + {{title}} + </button> + {{/execList}} + </div> + </div> + </div> {{/canExec}} </div> <div class="modal fade" id="reboot-confirm" tabindex="-1" role="dialog"> |