Revamp catalog page: paging + datatables
This commit is contained in:
@@ -40,6 +40,12 @@ REPORTS_COLUMNS = [
|
|||||||
'name': 'Agent version'},
|
'name': 'Agent version'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CATALOGS_COLUMNS = [
|
||||||
|
{'attr': 'certname', 'name': 'Certname', 'type': 'node'},
|
||||||
|
{'attr': 'catalog_timestamp', 'name': 'Compile Time'},
|
||||||
|
{'attr': 'form', 'name': 'Compare'},
|
||||||
|
]
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
app.config.from_object('puppetboard.default_settings')
|
app.config.from_object('puppetboard.default_settings')
|
||||||
@@ -828,9 +834,14 @@ def metric(env, metric):
|
|||||||
current_env=env)
|
current_env=env)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/catalogs', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
@app.route('/catalogs',
|
||||||
@app.route('/<env>/catalogs')
|
defaults={'env': app.config['DEFAULT_ENVIRONMENT'],
|
||||||
def catalogs(env):
|
'compare': None})
|
||||||
|
@app.route('/<env>/catalogs', defaults={'compare': None})
|
||||||
|
@app.route('/catalogs/compare/<compare>',
|
||||||
|
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
||||||
|
@app.route('/<env>/catalogs/compare/<compare>')
|
||||||
|
def catalogs(env, compare):
|
||||||
"""Lists all nodes with a compiled catalog.
|
"""Lists all nodes with a compiled catalog.
|
||||||
|
|
||||||
:param env: Find the nodes with this catalog_environment value
|
:param env: Find the nodes with this catalog_environment value
|
||||||
@@ -839,52 +850,78 @@ def catalogs(env):
|
|||||||
envs = environments()
|
envs = environments()
|
||||||
check_env(env, envs)
|
check_env(env, envs)
|
||||||
|
|
||||||
if app.config['ENABLE_CATALOG']:
|
if not app.config['ENABLE_CATALOG']:
|
||||||
nodenames = []
|
log.warn('Access to catalog interface disabled by administrator')
|
||||||
catalog_list = []
|
abort(403)
|
||||||
query = AndOperator()
|
return
|
||||||
|
|
||||||
if env != '*':
|
|
||||||
query.add(EqualsOperator("catalog_environment", env))
|
|
||||||
|
|
||||||
query.add(NullOperator("catalog_timestamp", False))
|
|
||||||
|
|
||||||
order_by_str = '[{"field": "certname", "order": "asc"}]'
|
|
||||||
nodes = get_or_abort(puppetdb.nodes,
|
|
||||||
query=query,
|
|
||||||
with_status=False,
|
|
||||||
order_by=order_by_str)
|
|
||||||
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(
|
return render_template(
|
||||||
'catalogs.html',
|
'catalogs.html',
|
||||||
nodes=catalog_list,
|
compare=compare,
|
||||||
|
columns=CATALOGS_COLUMNS)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/catalogs/json',
|
||||||
|
defaults={'env': app.config['DEFAULT_ENVIRONMENT'],
|
||||||
|
'compare': None})
|
||||||
|
@app.route('/<env>/catalogs/json', defaults={'compare': None})
|
||||||
|
@app.route('/catalogs/compare/<compare>/json',
|
||||||
|
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
||||||
|
@app.route('/<env>/catalogs/compare/<compare>/json')
|
||||||
|
def catalogs_ajax(env, compare):
|
||||||
|
"""Server data to catalogs as JSON to Jquery datatables
|
||||||
|
"""
|
||||||
|
draw = int(request.args.get('draw', 0))
|
||||||
|
start = int(request.args.get('start', 0))
|
||||||
|
length = int(request.args.get('length', app.config['NORMAL_TABLE_COUNT']))
|
||||||
|
paging_args = {'limit': length, 'offset': start}
|
||||||
|
search_arg = request.args.get('search[value]')
|
||||||
|
order_column = int(request.args.get('order[0][column]', 0))
|
||||||
|
order_filter = CATALOGS_COLUMNS[order_column].get(
|
||||||
|
'filter', CATALOGS_COLUMNS[order_column]['attr'])
|
||||||
|
order_dir = request.args.get('order[0][dir]', 'asc')
|
||||||
|
order_args = '[{"field": "%s", "order": "%s"}]' % (order_filter, order_dir)
|
||||||
|
|
||||||
|
envs = environments()
|
||||||
|
check_env(env, envs)
|
||||||
|
|
||||||
|
query = AndOperator()
|
||||||
|
if env != '*':
|
||||||
|
query.add(EqualsOperator("catalog_environment", env))
|
||||||
|
if search_arg:
|
||||||
|
query.add(RegexOperator("certname", r"%s" % search_arg))
|
||||||
|
query.add(NullOperator("catalog_timestamp", False))
|
||||||
|
|
||||||
|
nodes = get_or_abort(puppetdb.nodes,
|
||||||
|
query=query,
|
||||||
|
include_total=True,
|
||||||
|
order_by=order_args,
|
||||||
|
**paging_args)
|
||||||
|
|
||||||
|
catalog_list = []
|
||||||
|
total = None
|
||||||
|
for node in nodes:
|
||||||
|
if total is None:
|
||||||
|
total = puppetdb.total
|
||||||
|
|
||||||
|
catalog_list.append({
|
||||||
|
'certname': node.name,
|
||||||
|
'catalog_timestamp': node.catalog_timestamp,
|
||||||
|
'form': compare,
|
||||||
|
})
|
||||||
|
|
||||||
|
if total is None:
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'catalogs.json.tpl',
|
||||||
|
total=total,
|
||||||
|
total_filtered=total,
|
||||||
|
draw=draw,
|
||||||
|
columns=CATALOGS_COLUMNS,
|
||||||
|
catalogs=catalog_list,
|
||||||
envs=envs,
|
envs=envs,
|
||||||
current_env=env)
|
current_env=env)
|
||||||
else:
|
|
||||||
log.warn('Access to catalog interface disabled by administrator')
|
|
||||||
abort(403)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/catalog/<node_name>',
|
@app.route('/catalog/<node_name>',
|
||||||
@@ -911,40 +948,6 @@ def catalog_node(env, node_name):
|
|||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/catalog/submit', methods=['POST'],
|
|
||||||
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
|
||||||
@app.route('/<env>/catalog/submit', methods=['POST'])
|
|
||||||
def catalog_submit(env):
|
|
||||||
"""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.
|
|
||||||
|
|
||||||
:param env: This parameter only directs the response page to the right
|
|
||||||
environment. If this environment does not exist return the use to the
|
|
||||||
catalogs page with the right environment.
|
|
||||||
:type env: :obj:`string`
|
|
||||||
"""
|
|
||||||
envs = environments()
|
|
||||||
check_env(env, envs)
|
|
||||||
|
|
||||||
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',
|
|
||||||
env=env,
|
|
||||||
compare=compare,
|
|
||||||
against=against))
|
|
||||||
return redirect(url_for('catalogs', env=env))
|
|
||||||
else:
|
|
||||||
log.warn('Access to catalog interface disabled by administrator')
|
|
||||||
abort(403)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/catalogs/compare/<compare>...<against>',
|
@app.route('/catalogs/compare/<compare>...<against>',
|
||||||
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
|
||||||
@app.route('/<env>/catalogs/compare/<compare>...<against>')
|
@app.route('/<env>/catalogs/compare/<compare>...<against>')
|
||||||
|
|||||||
@@ -1,40 +1,21 @@
|
|||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% import '_macros.html' as macros %}
|
{% import '_macros.html' as macros %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="ui fluid icon input hide" style="margin-bottom:20px">
|
<table id="catalogs_table" class='ui very basic table stackable'>
|
||||||
<input autofocus="autofocus" class="filter-table" placeholder="Type here to filter...">
|
|
||||||
</div>
|
|
||||||
<table class='ui very basic very compact table nodes'>
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
{% for column in columns %}
|
||||||
<th>Certname</th>
|
<th>{{ column.name }}</th>
|
||||||
<th>Compile Time</th>
|
{% endfor %}
|
||||||
<th>Compare With</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="searchable">
|
<tbody>
|
||||||
{% for node in nodes %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td><a href="{{url_for('node', env=current_env, node_name=node.name)}}">{{node.name}}</a></td>
|
|
||||||
<td><a rel="utctimestamp" href="{{url_for('catalog_node', env=current_env, node_name=node.name)}}">{{node.catalog_timestamp}}</a></td>
|
|
||||||
<td>
|
|
||||||
{% if node.form %}
|
|
||||||
<div class="ui action input">
|
|
||||||
<form method="POST" action="{{url_for('catalog_submit', env=current_env)}}">
|
|
||||||
{{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>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
{% block onload_script %}
|
||||||
|
{% macro extra_options(caller) %}
|
||||||
|
"order": [[ 0, "asc" ]],
|
||||||
|
{% endmacro %}
|
||||||
|
{{ macros.datatable_init(table_html_id="catalogs_table", ajax_url=url_for('catalogs_ajax', env=current_env, compare=compare), default_length=config.NORMAL_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }}
|
||||||
|
{% endblock onload_script %}
|
||||||
|
|||||||
42
puppetboard/templates/catalogs.json.tpl
Normal file
42
puppetboard/templates/catalogs.json.tpl
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"draw": {{draw}},
|
||||||
|
"recordsTotal": {{total}},
|
||||||
|
"recordsFiltered": {{total_filtered}},
|
||||||
|
"data": [
|
||||||
|
{% for catalog in catalogs -%}
|
||||||
|
{%- if catalog_flag %},{%- endif %}
|
||||||
|
{%- set catalog_flag = True -%}
|
||||||
|
[
|
||||||
|
{%- for column in columns -%}
|
||||||
|
{%- if column_flag %},{%- endif -%}
|
||||||
|
{%- set column_flag = True -%}
|
||||||
|
{%- if column.attr == 'catalog_timestamp' -%}
|
||||||
|
"<a rel=\"utctimestamp\" href=\"{{url_for('catalog_node', env=current_env, node_name=catalog.certname)}}\">{{ catalog.catalog_timestamp }}</a>"
|
||||||
|
{%- elif column.type == 'node' -%}
|
||||||
|
{% filter jsonprint %}<a href="{{url_for('node', env=current_env, node_name=catalog.certname)}}">{{ catalog.certname }}</a>{% endfilter %}
|
||||||
|
{%- elif column.attr == 'form' -%}
|
||||||
|
{% filter jsonprint -%}
|
||||||
|
<div class="ui action input">
|
||||||
|
{%- if catalog.form -%}
|
||||||
|
<form method="GET" action="{{url_for('catalog_compare', env=current_env, compare=catalog.form, against=catalog.certname)}}">
|
||||||
|
{%- else -%}
|
||||||
|
<form method="GET" action="{{url_for('catalogs', env=current_env, compare=catalog.certname)}}">
|
||||||
|
{%- endif -%}
|
||||||
|
<div class="field inline">
|
||||||
|
{%- if catalog.form -%}
|
||||||
|
<input type="submit" class="ui submit button" style="height:auto;" value="Compare with {{ catalog.form }}"/>
|
||||||
|
{%- else -%}
|
||||||
|
<input type="submit" class="ui submit button" style="height:auto;" value="Compare with ..."/>
|
||||||
|
{%- endif -%}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{%- endfilter -%}
|
||||||
|
{%- else -%}
|
||||||
|
""
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
]
|
||||||
|
{% endfor %}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user