Improve display of task results, records and host facts

Pass the text, json, lists and dicts to pygments in order to get
the nice formatting and highlighting from it.

Change-Id: I3846fc55a70ea5a0f0487ee8afc6b13d55b50f4b
This commit is contained in:
David Moreau-Simard 2017-07-15 20:01:04 -04:00
parent 788bbac413
commit cbd3ae1e9b
8 changed files with 107 additions and 26 deletions

View File

@ -24,6 +24,8 @@ from os import path
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import YamlLexer
from pygments.lexers import JsonLexer
from pygments.lexers.special import TextLexer
def configure_template_filters(app):
@ -87,6 +89,26 @@ def configure_template_filters(app):
YamlLexer(),
formatter)
@app.template_filter('pygments_formatter')
def jinja_pygments_formatter(data):
formatter = HtmlFormatter(cssclass='codehilite')
if isinstance(data, str) or isinstance(data, unicode):
data.rstrip()
try:
data = json.loads(data)
data = json.dumps(data, indent=4, sort_keys=True)
lexer = JsonLexer()
except (ValueError, TypeError):
lexer = TextLexer()
elif isinstance(data, dict) or isinstance(data, list):
data = json.dumps(data, indent=4, sort_keys=True)
lexer = JsonLexer()
else:
lexer = TextLexer()
return highlight(Markup(data).unescape(), lexer, formatter)
@app.template_filter('fast_count')
def jinja_fast_count(query):
return fast_count(query)

View File

@ -57,7 +57,7 @@
/* Table styling */
/* Wrap pre tags so they don't print endlessly horizontally */
.table pre, .results pre {
.records pre, .facts pre, .results .codehilite pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;

View File

@ -1,5 +1,5 @@
{% if record.type == 'json' or record.type == 'dict' -%}
<pre>{{ record.value | to_nice_json | escape }}</pre>
{{ record.value | pygments_formatter | safe }}
{% elif record.type == 'list' -%}
<ul class="text-left">
{% for item in record.value -%}

View File

@ -1 +1 @@
<a href="#" data-toggle="modal" data-target="#task_result_modal" data-load="{{ result.id }}/"><span class="{{ result.derived_status }} label">{{ result.derived_status |upper }}</span></a>
<a href="#" data-toggle="modal" data-target="#task_result_modal" data-load="{{ result.id }}/"><span class="{{ result.derived_status }} label">{{ result.derived_status | upper }}</span></a>

View File

@ -6,7 +6,7 @@
<h2>Host facts: <strong>{{ host.name }}</strong></h2>
<p>Last updated: {{ host.facts.timestamp | datefmt }}</p>
<p>Ansible version: <strong>{{ host.playbook.ansible_version }}</strong></p>
<table class="table table-striped table-bordered table-hover table-condensed">
<table class="table table-striped table-bordered table-hover table-condensed facts">
<thead>
<tr>
<th>Fact</th>
@ -17,7 +17,7 @@
{% for fact, value in facts -%}
<tr>
<td id="{{ fact }}"><a href="#{{ fact }}">{{ fact }}</a></td>
<td><pre>{{ value | to_nice_json | escape }}</pre></td>
<td>{{ value | pygments_formatter | safe }}</td>
</tr>
{% endfor -%}
</tbody>

View File

@ -239,7 +239,7 @@
<span class="pficon pficon-help pull-right" data-toggle="tooltip" data-placement="bottom" data-html="true" title="{% include "tips/records.html" %}"></span>
</h3>
{% if record_count -%}
<table class="table table-striped table-bordered table-hover table-condensed" id="records_{{ playbook.id }}">
<table class="table table-striped table-bordered table-hover table-condensed records" id="records_{{ playbook.id }}">
<thead>
<tr>
<th><span class="pull-right">Key</span></th>
@ -313,7 +313,7 @@
</div>
<div class="modal-body embed-responsive embed-responsive-item">
<div class="row text-center"><strong>Loading task details...</strong></div>
<div class="spinner spinner-lg iframe-spinner"></div>
<div class="spinner spinner-lg"></div>
<iframe id="result_iframe" class="iframe" data-src="{{ url_for('result.index') }}"></iframe>
</div>
</div>

View File

@ -4,19 +4,13 @@
{% if result is mapping -%}
{% if 'item' in result -%}
<{{ hdr }}>Item</{{ hdr }}>
<pre>{{ result.item | to_nice_json | escape }}</pre>
{{ result.item | pygments_formatter | safe }}
{% endif %}
{% for attr in result.keys()|sort if attr not in ['item', 'changed', 'stdout_lines'] -%}
{% if result[attr]|default(False) -%}
<{{ hdr }}>{{ attr|title }}</{{ hdr }}>
{% if result[attr] is string -%}
<pre>{{ result[attr] }}</pre>
{% elif result[attr] is mapping or result[attr] is iterable -%}
<pre>{{ result[attr] | to_nice_json | escape }}</pre>
{% else -%}
<pre>{{ result | to_nice_json | escape }}</pre>
{% endif -%}
{% for attr in result.keys() | sort if attr not in ['item', 'changed', 'stdout_lines'] -%}
{% if result[attr] | default(False) -%}
<{{ hdr }}>{{ attr | title }}</{{ hdr }}>
{{ result[attr] | pygments_formatter | safe }}
{% endif -%}
{% endfor -%}
{% elif result is iterable and result is not string %}
@ -29,10 +23,11 @@
</div>
{% endfor %}
{% else -%}
<pre>{{ result | to_nice_json | escape }}</pre>
{{ result | pygments_formatter | safe }}
{% endif -%}
{% endmacro %}
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-12 results">
@ -44,15 +39,16 @@
<p>Time: <strong>{{ task_result.time_start | datefmt }}</strong></p>
<p>Ansible version: <strong>{{ task_result.task.playbook.ansible_version }}</strong></p>
{% set result = task_result.result | from_json %}
{% if 'results' in result -%}
{% for item in result.results -%}
<h2>Result {{loop.index}}</h2>
{% if 'results' in result %}
{% for item in result.results %}
<h2>Result #{{ loop.index }}</h2>
{{ display_result(item, 'h3') }}
{% endfor -%}
{% else -%}
{% endfor %}
{% else %}
{{ display_result(result, 'h2') }}
{% endif -%}
{% endif %}
</div>
</div>
</div>
</body>
</html>

View File

@ -107,6 +107,69 @@ class TestFilters(TestAra):
self.assertEqual(res, u'"definitely not json"')
def test_jinja_pygments_formatter_string_simple(self):
data = "string"
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span>string\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_string_json(self):
data = '{"one": "value", "two": "value"}'
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span><span class="p">{</span>\n <span class="nt">&quot;one&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span><span class="p">,</span> \n <span class="nt">&quot;two&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span>\n<span class="p">}</span>\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_unicode(self):
data = u"string"
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span>string\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_list(self):
data = ['one', 'two']
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span><span class="p">[</span>\n <span class="s2">&quot;one&quot;</span><span class="p">,</span> \n <span class="s2">&quot;two&quot;</span>\n<span class="p">]</span>\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_dict(self):
data = {'one': 'value', 'two': 'value'}
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span><span class="p">{</span>\n <span class="nt">&quot;one&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span><span class="p">,</span> \n <span class="nt">&quot;two&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span>\n<span class="p">}</span>\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_integer(self):
data = 1
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span>1\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_pygments_formatter_boolean(self):
data = True
t = self.env.from_string('{{ data | pygments_formatter | safe }}')
res = t.render(data=data)
# This is ugly, sorry
expected = u'''<div class="codehilite"><pre><span></span>True\n</pre></div>\n''' # flake8: noqa
self.assertEqual(res, expected)
def test_jinja_yamlhighlight(self):
data = """- name: Test thing
hosts: localhost