puppetboard: Adding a more intuitive catalog view
A new endpoint in the header, Catalogs, takes the user to a page with a node table similar to that in the nodes page. This table shows the node with a link to the node page, the catalog timestamp with a link to the catalog page and a small form with a select field to be used to compare the catalog of this row's node with that of another node.
This commit is contained in:
@@ -18,7 +18,7 @@ from flask_wtf.csrf import CsrfProtect
|
||||
|
||||
from pypuppetdb import connect
|
||||
|
||||
from puppetboard.forms import QueryForm
|
||||
from puppetboard.forms import (CatalogForm, QueryForm)
|
||||
from puppetboard.utils import (
|
||||
get_or_abort, yield_or_stop,
|
||||
limit_reports, jsonprint
|
||||
@@ -404,6 +404,45 @@ def metric(metric):
|
||||
name=name,
|
||||
metric=sorted(metric.items()))
|
||||
|
||||
@app.route('/catalogs')
|
||||
def catalogs():
|
||||
if app.config['ENABLE_CATALOG']:
|
||||
nodenames = []
|
||||
catalog_list = []
|
||||
nodes = get_or_abort(puppetdb.nodes,
|
||||
query='["null?", "catalog_timestamp", false]',
|
||||
with_status=False,
|
||||
order_by='[{"field": "certname", "order": "asc"}]')
|
||||
nodes, temp = tee(nodes)
|
||||
|
||||
for node in temp:
|
||||
nodenames.append(node.name)
|
||||
|
||||
for node in nodes:
|
||||
table_row = {
|
||||
'name': node.name,
|
||||
'catalog_timestamp': node.catalog_timestamp
|
||||
}
|
||||
|
||||
if len(nodenames) > 1:
|
||||
form = CatalogForm()
|
||||
|
||||
form.compare.data = node.name
|
||||
form.against.choices = [(x, x) for x in nodenames
|
||||
if x != node.name]
|
||||
table_row['form'] = form
|
||||
else:
|
||||
table_row['form'] = None
|
||||
|
||||
catalog_list.append(table_row)
|
||||
|
||||
return render_template(
|
||||
'catalogs.html',
|
||||
nodes=catalog_list)
|
||||
else:
|
||||
log.warn('Access to catalogs endpoint disabled by administrator')
|
||||
abort(403)
|
||||
|
||||
@app.route('/catalog/<node_name>')
|
||||
def catalog_node(node_name):
|
||||
"""Fetches from PuppetDB the compiled catalog of a given node."""
|
||||
@@ -413,3 +452,43 @@ def catalog_node(node_name):
|
||||
else:
|
||||
log.warn('Access to catalog interface disabled by administrator')
|
||||
abort(403)
|
||||
|
||||
@app.route('/catalog/submit', methods=['POST'])
|
||||
def catalog_submit():
|
||||
"""Receives the submitted form data from the catalogs page and directs
|
||||
the users to the comparison page. Directs users back to the catalogs
|
||||
page if no form submission data is found.
|
||||
"""
|
||||
if app.config['ENABLE_CATALOG']:
|
||||
form = CatalogForm(request.form)
|
||||
|
||||
form.against.choices = [(form.against.data, form.against.data)]
|
||||
if form.validate_on_submit():
|
||||
compare = form.compare.data
|
||||
against = form.against.data
|
||||
return redirect(
|
||||
url_for('catalog_compare',
|
||||
compare=compare,
|
||||
against=against))
|
||||
return redirect(url_for('catalogs'))
|
||||
else:
|
||||
log.warn('Access to catalog interface disabled by administrator')
|
||||
abort(403)
|
||||
|
||||
@app.route('/catalogs/compare/<compare>...<against>')
|
||||
def catalog_compare(compare, against):
|
||||
"""Compares the catalog of one node, parameter compare, with that of
|
||||
with that of another node, parameter against.
|
||||
"""
|
||||
if app.config['ENABLE_CATALOG']:
|
||||
compare_cat = get_or_abort(puppetdb.catalog,
|
||||
node=compare)
|
||||
against_cat = get_or_abort(puppetdb.catalog,
|
||||
node=against)
|
||||
|
||||
return render_template('catalog_compare.html',
|
||||
compare=compare_cat,
|
||||
against=against_cat)
|
||||
else:
|
||||
log.warn('Access to catalog interface disabled by administrator')
|
||||
abort(403)
|
||||
|
||||
@@ -2,7 +2,10 @@ from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
|
||||
from flask.ext.wtf import Form
|
||||
from wtforms import RadioField, TextAreaField, validators
|
||||
from wtforms import (
|
||||
HiddenField, RadioField, SelectField,
|
||||
TextAreaField, validators
|
||||
)
|
||||
|
||||
|
||||
class QueryForm(Form):
|
||||
@@ -18,3 +21,8 @@ class QueryForm(Form):
|
||||
('reports', 'Reports'),
|
||||
('events', 'Events'),
|
||||
])
|
||||
|
||||
class CatalogForm(Form):
|
||||
"""The form used to compare the catalogs of different nodes."""
|
||||
compare = HiddenField('compare')
|
||||
against = SelectField(u'against')
|
||||
|
||||
87
puppetboard/templates/catalog_compare.html
Normal file
87
puppetboard/templates/catalog_compare.html
Normal file
@@ -0,0 +1,87 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% block content %}
|
||||
<table class="ui basic table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th><h1>Comparing</h1></th>
|
||||
<th><h1>Against</h1></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{compare.node}}</td>
|
||||
<td>{{against.node}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="ui basic table compact catalog">
|
||||
<thead>
|
||||
<tr><th>Resources</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for resource in compare.get_resources() %}
|
||||
<tr>
|
||||
<td>{{resource.type_}}[{{resource.name}}]</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table class="ui basic table compact catalog">
|
||||
<thead>
|
||||
<tr><th>Resources</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for resource in against.get_resources() %}
|
||||
<tr>
|
||||
<td>{{resource.type_}}[{{resource.name}}]</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="ui basic table compact catalog">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Edges</th>
|
||||
<th>-></th>
|
||||
<th>Target</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for edge in compare.get_edges() %}
|
||||
<tr>
|
||||
<td>{{edge.source}}</td>
|
||||
<td>{{edge.relationship}}</td>
|
||||
<td>{{edge.target}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table class="ui basic table compact catalog">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Edge</th>
|
||||
<th>-></th>
|
||||
<th>Target</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for edge in against.get_edges() %}
|
||||
<tr>
|
||||
<td>{{edge.source}}</td>
|
||||
<td>{{edge.relationship}}</td>
|
||||
<td>{{edge.target}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
40
puppetboard/templates/catalogs.html
Normal file
40
puppetboard/templates/catalogs.html
Normal file
@@ -0,0 +1,40 @@
|
||||
{% extends 'layout.html' %}
|
||||
{% import '_macros.html' as macros %}
|
||||
{% block content %}
|
||||
<div class="ui fluid icon input hide" style="margin-bottom:20px">
|
||||
<input autofocus="autofocus" class="filter-table" placeholder="Type here to filter...">
|
||||
</div>
|
||||
<table class='ui compact basic table nodes'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Hostname</th>
|
||||
<th>Compile Time</th>
|
||||
<th>Compare With</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
{% for node in nodes %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><a href="{{url_for('node', node_name=node.name)}}">{{node.name}}</a></td>
|
||||
<td><a rel="utctimestamp" href="{{url_for('catalog_node', node_name=node.name)}}">{{node.catalog_timestamp}}</a></td>
|
||||
<td>
|
||||
{% if node.form %}
|
||||
<div class="ui form">
|
||||
<form method="POST" action="{{url_for('catalog_submit')}}">
|
||||
{{node.form.csrf_token}}
|
||||
<div class="field inline">
|
||||
{{node.form.compare}}
|
||||
{{node.form.against}}
|
||||
<input type="submit" class="ui submit button" style="height:auto;" value="Compare"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
@@ -34,6 +34,7 @@
|
||||
('reports', 'Reports'),
|
||||
('metrics', 'Metrics'),
|
||||
('inventory', 'Inventory'),
|
||||
('catalogs', 'Catalogs'),
|
||||
('query', 'Query'),
|
||||
] %}
|
||||
<a {% if endpoint == request.endpoint %} class="active item" {% else %} class="item" {% endif %}
|
||||
|
||||
Reference in New Issue
Block a user