diff options
author | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-06-21 13:14:51 +0200 |
---|---|---|
committer | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-06-25 13:53:04 +0200 |
commit | 0ed9ecdae9a7e4c7fbb30af30ef4310cd45f2143 (patch) | |
tree | 0abf1330b55891c18ba62c39a39977cfc0566ab3 | |
parent | af8236b18948b083c408df39306fc36bc26b51b7 (diff) |
ogcp: add support to view script output
Add view at /action/script/output to visualize the result of
/shell/run for multiple clients.
Use shell/output to request the execution data of the selected clients.
Each client element has execution timestamip (UTC), client ip, cmd,
return code and stdout of the executed command.
-rw-r--r-- | ogcp/templates/actions/script_output.html | 74 | ||||
-rw-r--r-- | ogcp/templates/commands.html | 2 | ||||
-rw-r--r-- | ogcp/views.py | 42 |
3 files changed, 118 insertions, 0 deletions
diff --git a/ogcp/templates/actions/script_output.html b/ogcp/templates/actions/script_output.html new file mode 100644 index 0000000..c7ffdcb --- /dev/null +++ b/ogcp/templates/actions/script_output.html @@ -0,0 +1,74 @@ +{% extends 'commands.html' %} +{% import "bootstrap/wtf.html" as wtf %} +{% import "macros.html" as macros %} + +{% set sidebar_state = 'disabled' %} +{% set btn_back = true %} + +{% block nav_client %} active {% endblock %} +{% block nav_script_output %} active {% endblock %} +{% block content %} + +<h1 class="m-5"> + {{ _('Script output') }} +</h1> + +{{ macros.cmd_selected_clients(selected_clients) }} + +<br> + +<style> +.card-header { + padding: 0; + margin-bottom: 0; + border: 0; +} + +.btn-link { + padding: 0; + border: 0; +} + +.card-header button { + width: 100%; + text-align: center; +} + +.bg-failed { + background-color: rgba(255,0,0,0.2); +} + +.bg-ok { + background-color: rgba(0,255,0,0.2); +} +</style> + +<div class="accordion" id="shellAccordion"> + {% for client in client_data %} + <div class="card"> + <div class="card-header" id="heading_1"> + <h2 class="mb-0"> + <button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapse_{{ loop.index }}" aria-expanded="true" aria-controls="collapse_{{ loop.index }}"> + <table class="table table-bordered mb-0"> + <thead class="thead"> + <tr> + <th class="col-2">{{ client['tstamp'] }}</th> + <th class="col-2"><b>{{ client['addr'] }}</b></th> + <th>{{ client['cmd'] }}</th> + <th class="col-2 {% if client['retcode'] == 0 %}bg-ok{% else %}bg-failed{% endif %}">return: {{ client['retcode'] }}</th> + </tr> + </thead> + </table> + </button> + </h2> + </div> + <div id="collapse_{{ loop.index }}" class="collapse" aria-labelledby="heading_{{ loop.index }}"> + <div class="card-body"> + <p><samp>{{ client['output'] }}</samp></p> + </div> + </div> + </div> + {% endfor %} +</div> + +{% endblock %} diff --git a/ogcp/templates/commands.html b/ogcp/templates/commands.html index eab9f29..7a63c38 100644 --- a/ogcp/templates/commands.html +++ b/ogcp/templates/commands.html @@ -94,6 +94,8 @@ <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <input class="btn btn-light dropdown-item{% block nav_run_script %}{% endblock %}" type="submit" value="{{ _('Run') }}" form="scopesForm" formaction="{{ url_for('action_run_script') }}" formmethod="get"> + <input class="btn btn-light dropdown-item{% block nav_display_output %}{% endblock %}" type="submit" value="{{ _('Display output') }}" + form="scopesForm" formaction="{{ url_for('action_script_display_output') }}" formmethod="get"> </div> <div class="dropdown btn"> diff --git a/ogcp/views.py b/ogcp/views.py index 109957a..45a0f73 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -1881,6 +1881,48 @@ def action_run_script(): selected_clients=selected_clients, scopes=scopes) +@app.route('/action/script/output', methods=['GET']) +@login_required +def action_script_display_output(): + ips = parse_elements(request.args.to_dict()) + + if not validate_elements(ips): + return redirect(url_for('commands')) + + server = get_server_from_clients(ips) + + r = server.get('/shell/output', payload={'clients': list(ips)}) + if not r: + return ogserver_down('commands') + if r.status_code != requests.codes.ok: + return ogserver_error('commands') + + client_data = r.json()['clients'] + + for client in client_data: + if not 'output' in client: + client['output'] = _('No output') + + if not 'retcode' in client: + client['retcode'] = 0 + + if not 'cmd' in client: + client['cmd'] = ['Null'] + + if not 'tstamp' in client: + client['tstamp'] = _('No time available') + else: + timestamp_utc = datetime.datetime.utcfromtimestamp(client['tstamp']) + client['tstamp'] = timestamp_utc.strftime('%Y-%m-%d %H:%M:%S') + + client_data.sort(key=lambda x:x['tstamp'], reverse=True) + + scopes, clients = get_scopes(set(ips)) + selected_clients = list(get_selected_clients(scopes['scope']).items()) + return render_template('actions/script_output.html', + selected_clients=selected_clients, + scopes=scopes, client_data=client_data) + def get_clients_modes(ips, server): modes = {} for ip in ips: |