summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlejandro Sirgo Rica <asirgo@soleta.eu>2024-06-21 13:14:51 +0200
committerAlejandro Sirgo Rica <asirgo@soleta.eu>2024-06-25 13:53:04 +0200
commit0ed9ecdae9a7e4c7fbb30af30ef4310cd45f2143 (patch)
tree0abf1330b55891c18ba62c39a39977cfc0566ab3
parentaf8236b18948b083c408df39306fc36bc26b51b7 (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.html74
-rw-r--r--ogcp/templates/commands.html2
-rw-r--r--ogcp/views.py42
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: