From a21bd0ac1d915f3b0bfe41a7625d3f25b5a43d04 Mon Sep 17 00:00:00 2001 From: redref Date: Sun, 5 Feb 2017 18:55:19 +0100 Subject: [PATCH] Revamp fact pages and tables to datatables --- puppetboard/app.py | 106 ++++++++++++++++++---------- puppetboard/templates/_macros.html | 2 + puppetboard/templates/fact.html | 63 ++++++++--------- puppetboard/templates/fact.json.tpl | 38 ++++++++++ puppetboard/templates/node.html | 17 ++++- 5 files changed, 152 insertions(+), 74 deletions(-) create mode 100644 puppetboard/templates/fact.json.tpl diff --git a/puppetboard/app.py b/puppetboard/app.py index 268320b..615a3a1 100644 --- a/puppetboard/app.py +++ b/puppetboard/app.py @@ -412,11 +412,10 @@ def node(env, node_name): query.add(EqualsOperator("certname", node_name)) node = get_or_abort(puppetdb.node, node_name) - facts = node.facts() + return render_template( 'node.html', node=node, - facts=yield_or_stop(facts), envs=envs, current_env=env, columns=REPORTS_COLUMNS[:2]) @@ -661,73 +660,102 @@ def facts(env): current_env=env) -@app.route('/fact/', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) -@app.route('//fact/') -def fact(env, fact): - """Fetches the specific fact from PuppetDB and displays its value per +@app.route('/fact/', + defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'value': None}) +@app.route('//fact/', defaults={'value': None}) +@app.route('/fact//', + defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) +@app.route('//fact//') +def fact(env, fact, value): + """Fetches the specific fact(/value) from PuppetDB and displays per node for which this fact is known. :param env: Searches for facts in this environment :type env: :obj:`string` :param fact: Find all facts with this name :type fact: :obj:`string` + :param fact: Find all facts with this value + :type fact: :obj:`string` """ envs = environments() check_env(env, envs) - # we can only consume the generator once, lists can be doubly consumed - # om nom nom render_graph = False - if fact in graph_facts: + if fact in graph_facts and not value: render_graph = True - if env == '*': - query = None - else: - query = EqualsOperator("environment", env) - - localfacts = [f for f in yield_or_stop(puppetdb.facts( - name=fact, query=query))] - return Response(stream_with_context(stream_template( + return render_template( 'fact.html', - name=fact, + fact=fact, + value=value, render_graph=render_graph, - facts=localfacts, envs=envs, - current_env=env))) + current_env=env) -@app.route('/fact//', - defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) -@app.route('//fact//') -def fact_value(env, fact, value): - """On asking for fact/value get all nodes with that fact. +@app.route('/fact//json', + defaults={'env': app.config['DEFAULT_ENVIRONMENT'], + 'node': None, 'value': None}) +@app.route('//fact//json', defaults={'node': None, 'value': None}) +@app.route('/fact///json', + defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'node': None}) +@app.route('//fact///json', defaults={'node': None}) +@app.route('/node//facts/json', + defaults={'env': app.config['DEFAULT_ENVIRONMENT'], + 'fact': None, 'value': None}) +@app.route('//node//facts/json', + defaults={'fact': None, 'value': None}) +def fact_ajax(env, node, fact, value): + """Fetches the specific facts matching (node/fact/value) from PuppetDB and + return a JSON table :param env: Searches for facts in this environment :type env: :obj:`string` + :param fact: Find all facts for this node + :type fact: :obj:`string` :param fact: Find all facts with this name :type fact: :obj:`string` - :param value: Filter facts whose value is equal to this - :type value: :obj:`string` + :param fact: Find all facts with this value + :type fact: :obj:`string` """ + draw = int(request.args.get('draw', 0)) + envs = environments() check_env(env, envs) - if env == '*': - query = None - else: - query = EqualsOperator("environment", env) + render_graph = False + if fact in graph_facts and not value and not node: + render_graph = True - facts = get_or_abort(puppetdb.facts, - name=fact, - value=value, - query=query) - localfacts = [f for f in yield_or_stop(facts)] - return render_template( - 'fact.html', + query = AndOperator() + if node: + query.add(EqualsOperator("certname", node)) + + if env != '*': + query.add(EqualsOperator("environment", env)) + + if len(query.operations) == 0: + query = None + + # Generator needs to be converted (graph / total) + facts = [f for f in get_or_abort( + puppetdb.facts, name=fact, value=value, - facts=localfacts, + query=query)] + + total = len(facts) + + return render_template( + 'fact.json.tpl', + fact=fact, + node=node, + value=value, + draw=draw, + total=total, + total_filtered=total, + render_graph=render_graph, + facts=facts, envs=envs, current_env=env) diff --git a/puppetboard/templates/_macros.html b/puppetboard/templates/_macros.html index fe28cc5..e3b31b1 100644 --- a/puppetboard/templates/_macros.html +++ b/puppetboard/templates/_macros.html @@ -76,6 +76,8 @@ // Paging options "lengthMenu": {{ length_selector }}, "pageLength": {{ default_length }}, + // Search as regex (does not apply if serverSide) + "search": {"regex": true}, // Default sort "order": [[ 0, "desc" ]], // Custom options diff --git a/puppetboard/templates/fact.html b/puppetboard/templates/fact.html index ffaa5ce..79bd2c5 100644 --- a/puppetboard/templates/fact.html +++ b/puppetboard/templates/fact.html @@ -1,50 +1,45 @@ {% extends 'layout.html' %} {% import '_macros.html' as macros %} -{% block javascript %} -{% if render_graph %} -var chart = null; -var data = [ -{% for fact in facts|groupby('value') %} - { - label: '{{ fact.grouper.replace("\n", " ") }}', - value: {{ fact.list|length }} - }, -{% endfor %} - { - value: 0, -} -] -var fact_values = data.map(function(item) { return [item.label, item.value]; }).filter(function(item){return item[0];}).sort(function(a,b){return b[1] - a[1];}); -var realdata = fact_values.slice(0, 15); -var otherdata = fact_values.slice(15); -if (otherdata.length > 0) { - realdata.push(["other", otherdata.reduce(function(a,b){return a + b[1];},0)]); -} -{% endif %} -{% endblock javascript %} - {% block onload_script %} - $('table').tablesort(); - {% if render_graph %} - chart = c3.generate({ +{% macro extra_options(caller) %} + // No per page AJAX + 'serverSide': false, +{% endmacro %} +{{ macros.datatable_init(table_html_id="facts_table", ajax_url=url_for('fact_ajax', env=current_env, fact=fact, value=value), default_length=config.NORMAL_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }} + +{% if render_graph %} +table.on('xhr', function(e, settings, json){ + var fact_values = json['chart'].map(function(item) { return [item.label, item.value]; }).filter(function(item){return item[0];}).sort(function(a,b){return b[1] - a[1];}); + var realdata = fact_values.slice(0, 15); + var otherdata = fact_values.slice(15); + if (otherdata.length > 0) { + realdata.push(["other", otherdata.reduce(function(a,b){return a + b[1];},0)]); + } + c3.generate({ bindto: '#factChart', data: { columns: realdata, type : '{{config.GRAPH_TYPE|default('pie')}}', } }); - {% endif %} +}) +{% endif %} {% endblock onload_script %} {% block content %} +{% if render_graph %}
-

{{name}}{% if value %}/{{value}}{% endif %} ({{facts|length}})

- - -{% if value %} -{{macros.facts_table(facts, current_env=current_env, autofocus=True, show_node=True, show_value=False, margin_bottom=10)}} -{% else %} -{{macros.facts_table(facts, current_env=current_env, autofocus=True, show_node=True, link_facts=True, margin_bottom=10)}} {% endif %} +

{{ fact }}{% if value %}/{{ value }}{% endif %}

+ + + + + {% if not value %}{% endif %} + + + + +
NodeValue
{% endblock content %} diff --git a/puppetboard/templates/fact.json.tpl b/puppetboard/templates/fact.json.tpl new file mode 100644 index 0000000..9e38570 --- /dev/null +++ b/puppetboard/templates/fact.json.tpl @@ -0,0 +1,38 @@ +{ + "draw": {{draw}}, + "recordsTotal": {{total}}, + "recordsFiltered": {{total_filtered}}, + "data": [ + {% for fact_h in facts -%} + {%- if not loop.first %},{%- endif -%} + [ + {%- if not fact -%} + {{ fact_h.name | jsonprint }} + {%- if node or value %},{% endif -%} + {%- endif -%} + {%- if not node -%} + {% filter jsonprint %}{{ fact_h.node }}{% endfilter %} + {%- if not value %},{% endif -%} + {%- endif -%} + {%- if not value -%} + {%- if fact_h.value is mapping -%} + {% filter jsonprint %}
{{ fact_h.value | jsonprint }}
{% endfilter %} + {%- else -%} + {% filter jsonprint %}
{{ fact_h.value }}
{% endfilter %} + {%- endif -%} + {%- endif -%} + ] + {% endfor -%} + ] + {%- if render_graph %}, + "chart": [ + {% for fact_h in facts | groupby('value') -%} + {%- if not loop.first %},{%- endif -%} + { + "label": {{ fact_h.grouper.replace("\n", " ") | jsonprint }}, + "value": {{ fact_h.list|length }} + } + {% endfor %} + ] + {% endif %} +} diff --git a/puppetboard/templates/node.html b/puppetboard/templates/node.html index 507e877..5c51f9b 100644 --- a/puppetboard/templates/node.html +++ b/puppetboard/templates/node.html @@ -17,7 +17,13 @@ 'pagingType': 'simple', "bFilter": false, {% endmacro %} +{% macro facts_extra_options(caller) %} + 'paging': false, + // No per page AJAX + 'serverSide': false, +{% endmacro %} {{ macros.datatable_init(table_html_id="reports_table", ajax_url=url_for('reports_ajax', env=current_env, node_name=node.name), default_length=config.LITTLE_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }} +{{ macros.datatable_init(table_html_id="facts_table", ajax_url=url_for('fact_ajax', env=current_env, node=node.name), default_length=config.LITTLE_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=facts_extra_options) }} {% endblock onload_script %} {% block content %} @@ -69,7 +75,16 @@

Facts

- {{macros.facts_table(facts, link_facts=True, condensed=True, current_env=current_env)}} + + + + + + + + + +
NameValue
{% endblock content %}