diff --git a/README.rst b/README.rst index 4137c42..f936eb1 100644 --- a/README.rst +++ b/README.rst @@ -219,6 +219,9 @@ Other settings that might be interesting in no particular order: * ``ENABLE_QUERY``: Defaults to ``True`` causing a Query tab to show up in the web interface allowing users to write and execute arbitrary queries against a set of endpoints in PuppetDB. Change this to ``False`` to disable this. +* ``GRAPH_TYPE```: Specify the type of graph to display. Default is + pie, other good option is donut. Other choices can be found here: + `_C3JS_documentation` * ``GRAPH_FACTS``: A list of fact names to tell PuppetBoard to generate a pie-chart on the fact page. With some fact values being unique per node, like ipaddress, uuid, and serial number, as well as structured facts it was @@ -241,6 +244,7 @@ Other settings that might be interesting in no particular order: .. _pypuppetdb documentation: http://pypuppetdb.readthedocs.org/en/v0.1.0/quickstart.html#ssl .. _Flask documentation: http://flask.pocoo.org/docs/0.10/quickstart/#sessions +.. _C3JS_documentation: http://c3js.org/examples.html#chart Puppet Enterprise ----------------- diff --git a/puppetboard/default_settings.py b/puppetboard/default_settings.py index 9019488..717925b 100644 --- a/puppetboard/default_settings.py +++ b/puppetboard/default_settings.py @@ -21,6 +21,7 @@ TABLE_COUNT_SELECTOR = [10, 20, 50, 100, 500] OFFLINE_MODE = False ENABLE_CATALOG = False OVERVIEW_FILTER = None +GRAPH_TYPE = 'pie' GRAPH_FACTS = ['architecture', 'clientversion', 'domain', diff --git a/puppetboard/docker_settings.py b/puppetboard/docker_settings.py index fa0055c..e32ac2a 100644 --- a/puppetboard/docker_settings.py +++ b/puppetboard/docker_settings.py @@ -47,6 +47,8 @@ GRAPH_FACTS = [x.strip() for x in os.getenv('GRAPH_FACTS', GRAPH_FACTS_DEFAULT).split(',')] +GRAPH_TYPE = os.getenv('GRAPH_TYPE', 'pie') + # Tuples are hard to express as an environment variable, so here # the tupple can be listed as a list of items # export INVENTORY_FACTS="Hostname, fqdn, IP Address, ipaddress,.. etc" diff --git a/puppetboard/static/coffeescript/lists.coffee b/puppetboard/static/coffeescript/lists.coffee index 86e89ec..5a4968e 100644 --- a/puppetboard/static/coffeescript/lists.coffee +++ b/puppetboard/static/coffeescript/lists.coffee @@ -1,21 +1,28 @@ $ = jQuery -$ -> -$('input.filter-list').parent('div').removeClass('hide') -$("input.filter-list").on "keyup", (e) -> - rex = new RegExp($(this).val(), "i") - +filter_list = (val) -> + rex = new RegExp(val, "i") $(".searchable li").hide() - $(".searchable li").parent().parent().hide() + $(".searchable li").parent().parent('.list_hide_segment').hide() $(".searchable li").filter( -> rex.test $(this).text() ).show() $(".searchable li").filter( -> rex.test $(this).text() ).parent().parent().show() - +$("input.filter-list").on "keyup", (e) -> + # If key is escape, reset value if e.keyCode is 27 $(e.currentTarget).val "" ev = $.Event("keyup") ev.keyCode = 13 $(e.currentTarget).trigger(ev) e.currentTarget.blur() + else + filter_list($(this).val()) +$("input.filter-list").ready -> + elem = $("input.filter-list") + elem.focus() + val = elem.val() + filter_list(val) + # Force cursor at the end + elem.val('').val(val) diff --git a/puppetboard/static/css/puppetboard.css b/puppetboard/static/css/puppetboard.css index 4f7f478..19506f5 100644 --- a/puppetboard/static/css/puppetboard.css +++ b/puppetboard/static/css/puppetboard.css @@ -14,7 +14,7 @@ h1.ui.header.no-margin-bottom { } .ui.grid.padding-bottom { - padding-bottom: 40px !important; + padding-bottom: 4em !important; } .status { diff --git a/puppetboard/static/jquery-tablesort-v.0.0.7/jquery.tablesort.min.js b/puppetboard/static/jquery-tablesort-v.0.0.7/jquery.tablesort.min.js new file mode 100644 index 0000000..d9b4041 --- /dev/null +++ b/puppetboard/static/jquery-tablesort-v.0.0.7/jquery.tablesort.min.js @@ -0,0 +1,6 @@ +/* + A simple, lightweight jQuery plugin for creating sortable tables. + https://github.com/kylefox/jquery-tablesort + Version 0.0.7 +*/ +!function(t){t.tablesort=function(e,s){var i=this;this.$table=e,this.$thead=this.$table.find("thead"),this.settings=t.extend({},t.tablesort.defaults,s),this.$sortCells=this.$thead.length>0?this.$thead.find("th:not(.no-sort)"):this.$table.find("th:not(.no-sort)"),this.$sortCells.bind("click.tablesort",function(){i.sort(t(this))}),this.index=null,this.$th=null,this.direction=null},t.tablesort.prototype={sort:function(e,s){var i=new Date,n=this,o=this.$table,l=this.$thead.length>0?o.find("tbody tr"):o.find("tr").has("td"),a=o.find("tr td:nth-of-type("+(e.index()+1)+")"),r=e.data().sortBy,d=[],h=a.map(function(s,i){return r?"function"==typeof r?r(t(e),t(i),n):r:null!=t(this).data().sortValue?t(this).data().sortValue:t(this).text()});0!==h.length&&("asc"!==s&&"desc"!==s?this.direction="asc"===this.direction?"desc":"asc":this.direction=s,s="asc"==this.direction?1:-1,n.$table.trigger("tablesort:start",[n]),n.log("Sorting by "+this.index+" "+this.direction),n.$table.css("display"),setTimeout(function(){n.$sortCells.removeClass(n.settings.asc+" "+n.settings.desc);for(var r=0,c=h.length;c>r;r++)d.push({index:r,cell:a[r],row:l[r],value:h[r]});d.sort(function(t,e){return t.value>e.value?1*s:t.value2e3?200:10))},log:function(e){(t.tablesort.DEBUG||this.settings.debug)&&console&&console.log&&console.log("[tablesort] "+e)},destroy:function(){return this.$sortCells.unbind("click.tablesort"),this.$table.data("tablesort",null),null}},t.tablesort.DEBUG=!1,t.tablesort.defaults={debug:t.tablesort.DEBUG,asc:"sorted ascending",desc:"sorted descending"},t.fn.tablesort=function(e){var s,i;return this.each(function(){s=t(this),i=s.data("tablesort"),i&&i.destroy(),s.data("tablesort",new t.tablesort(s,e))})}}(window.Zepto||window.jQuery); \ No newline at end of file diff --git a/puppetboard/static/js/lists.js b/puppetboard/static/js/lists.js index afaee34..9f8d455 100644 --- a/puppetboard/static/js/lists.js +++ b/puppetboard/static/js/lists.js @@ -1,31 +1,42 @@ // Generated by CoffeeScript 1.9.3 (function() { - var $; + var $, filter_list; $ = jQuery; - $(function() {}); - - $('input.filter-list').parent('div').removeClass('hide'); - - $("input.filter-list").on("keyup", function(e) { - var ev, rex; - rex = new RegExp($(this).val(), "i"); + filter_list = function(val) { + var rex; + rex = new RegExp(val, "i"); $(".searchable li").hide(); - $(".searchable li").parent().parent().hide(); + $(".searchable li").parent().parent('.list_hide_segment').hide(); $(".searchable li").filter(function() { return rex.test($(this).text()); }).show(); - $(".searchable li").filter(function() { + return $(".searchable li").filter(function() { return rex.test($(this).text()); }).parent().parent().show(); + }; + + $("input.filter-list").on("keyup", function(e) { + var ev; if (e.keyCode === 27) { $(e.currentTarget).val(""); ev = $.Event("keyup"); ev.keyCode = 13; $(e.currentTarget).trigger(ev); return e.currentTarget.blur(); + } else { + return filter_list($(this).val()); } }); + $("input.filter-list").ready(function() { + var elem, val; + elem = $("input.filter-list"); + elem.focus(); + val = elem.val(); + filter_list(val); + return elem.val('').val(val); + }); + }).call(this); diff --git a/puppetboard/static/js/tables.js b/puppetboard/static/js/tables.js new file mode 100644 index 0000000..25c4434 --- /dev/null +++ b/puppetboard/static/js/tables.js @@ -0,0 +1,35 @@ +// Generated by CoffeeScript 1.4.0 +(function() { + var $; + + $ = jQuery; + + $(function() {}); + + if ($('th.default-sort').data()) { + $('table.sortable').tablesort().data('tablesort').sort($("th.default-sort"), "desc"); + } + + $('thead th.date').data('sortBy', function(th, td, tablesort) { + return moment.utc(td.text()).unix(); + }); + + $('input.filter-table').parent('div').removeClass('hide'); + + $("input.filter-table").on("keyup", function(e) { + var ev, rex; + rex = new RegExp($(this).val(), "i"); + $(".searchable tr").hide(); + $(".searchable tr").filter(function() { + return rex.test($(this).text()); + }).show(); + if (e.keyCode === 27) { + $(e.currentTarget).val(""); + ev = $.Event("keyup"); + ev.keyCode = 13; + $(e.currentTarget).trigger(ev); + return e.currentTarget.blur(); + } + }); + +}).call(this); diff --git a/puppetboard/templates/_macros.html b/puppetboard/templates/_macros.html index 3817470..fe28cc5 100644 --- a/puppetboard/templates/_macros.html +++ b/puppetboard/templates/_macros.html @@ -46,39 +46,6 @@ {%- endmacro %} -{% macro facts_graph(facts, autofocus=False, condensed=False, show_node=False, margin_top=20, margin_bottom=20) -%} - - -
- -{%- endmacro %} - {% macro status_counts(caller, status, node_name, events, current_env, unreported_time=False, report_hash=False) -%} {{ status|upper }} {% if status == 'unreported' %} @@ -90,7 +57,7 @@ {% endif %} {%- endmacro %} -{% macro datatable_init(table_html_id, ajax_url, default_length, length_selector, extra_options=None, columns) -%} +{% macro datatable_init(table_html_id, ajax_url, default_length, length_selector, extra_options=None) -%} // Init datatable $.fn.dataTable.ext.errMode = 'throw'; var table = $('#{{ table_html_id }}').DataTable({ diff --git a/puppetboard/templates/fact.html b/puppetboard/templates/fact.html index dbef184..ffaa5ce 100644 --- a/puppetboard/templates/fact.html +++ b/puppetboard/templates/fact.html @@ -1,10 +1,47 @@ {% extends 'layout.html' %} {% import '_macros.html' as macros %} -{% block content %} -

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

+ +{% block javascript %} {% if render_graph %} -{{macros.facts_graph(facts, autofocus=True, show_node=True, margin_bottom=10)}} +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({ + bindto: '#factChart', + data: { + columns: realdata, + type : '{{config.GRAPH_TYPE|default('pie')}}', + } + }); + {% endif %} +{% endblock onload_script %} + +{% block content %} +
+

{{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 %} diff --git a/puppetboard/templates/facts.html b/puppetboard/templates/facts.html index 345b293..ce3fe77 100644 --- a/puppetboard/templates/facts.html +++ b/puppetboard/templates/facts.html @@ -1,29 +1,29 @@ {% extends 'layout.html' %} {% block content %} -
+
-
- {%- set facts_count = 0 -%} - {%- set break = facts_len//4 + 1 -%} - {%- for key,facts_list in facts_dict %} -
- {{key}} -
    - {%- for fact in facts_list %} -
  • {{fact}}
  • - {%- endfor %} -
-
- {%- set facts_count = facts_count + facts_list|length -%} - {%- if facts_count >= break -%} -
-
- {%- set break = facts_len//4 + 1 + break -%} - {%- endif -%} - {%- set facts_count = facts_count + 5 -%} - {% endfor %} -
+
+ {%- set facts_count = 0 -%} + {%- set break = facts_len//4 + 1 -%} + {%- for key,facts_list in facts_dict %} +
+ {{key}} +
    + {%- for fact in facts_list %} +
  • {{fact}}
  • + {%- endfor %} +
+
+ {%- set facts_count = facts_count + facts_list|length -%} + {%- if facts_count >= break -%} +
+
+ {%- set break = facts_len//4 + 1 + break -%} + {%- endif -%} + {%- set facts_count = facts_count + 5 -%} + {% endfor %} +
{% endblock content %} diff --git a/puppetboard/templates/index.html b/puppetboard/templates/index.html index d77195a..20e028e 100644 --- a/puppetboard/templates/index.html +++ b/puppetboard/templates/index.html @@ -12,6 +12,7 @@ {% endblock script %} {% endif %} {% endblock head %} + {% block content %} {% if config.REFRESH_RATE > 0 %} diff --git a/puppetboard/templates/layout.html b/puppetboard/templates/layout.html index ab9f649..7bd1a3c 100644 --- a/puppetboard/templates/layout.html +++ b/puppetboard/templates/layout.html @@ -41,14 +41,20 @@ + + + + {% block script %} {% endblock script %} - {% block script %} {% endblock script %} {% block head %} {% endblock head %} diff --git a/puppetboard/templates/metrics.html b/puppetboard/templates/metrics.html index e3d4245..844ad41 100644 --- a/puppetboard/templates/metrics.html +++ b/puppetboard/templates/metrics.html @@ -1,7 +1,7 @@ {% extends 'layout.html' %} {% block content %}

Metrics

-
+
    diff --git a/puppetboard/templates/node.html b/puppetboard/templates/node.html index c35e150..507e877 100644 --- a/puppetboard/templates/node.html +++ b/puppetboard/templates/node.html @@ -13,7 +13,11 @@ {% endblock script %} {% endblock head %} {% block onload_script %} -{{ 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, columns=columns) }} +{% macro extra_options(caller) %} + 'pagingType': 'simple', + "bFilter": 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) }} {% endblock onload_script %} {% block content %} diff --git a/puppetboard/templates/reports.html b/puppetboard/templates/reports.html index 1d16d03..d8fe86a 100644 --- a/puppetboard/templates/reports.html +++ b/puppetboard/templates/reports.html @@ -33,7 +33,7 @@ // No initial loading "deferLoading": true, {% 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.NORMAL_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options, columns=columns) }} + {{ macros.datatable_init(table_html_id="reports_table", ajax_url=url_for('reports_ajax', env=current_env, node_name=node_name), default_length=config.NORMAL_TABLE_COUNT, length_selector=config.TABLE_COUNT_SELECTOR, extra_options=extra_options) }} // Event listener for status filters function status_filter_change(){