puppetboard/app.py: Adding Radiator view (#232)

* puppetboard/app.py: Adding Radiator view

Part 2 of https://github.com/voxpupuli/puppetboard/issues/70

This is a refactoring of the changes in https://github.com/voxpupuli/puppetboard/pull/100.
Adds a simple Heads-Up Display of the last run statuses of managed nodes,
either environment filtered or not.

* puppetboard/app.py: Fixing environment filters and metric strings.

* The names of the population metrics have changes in PuppetDB 4.0, the initial
  commit used the metric names from PuppetDB 2.x.
* The main nodes query did not include a query string to filter on environments.
This commit is contained in:
Corey Hammerton
2016-05-16 19:59:40 -04:00
parent f22ea84c68
commit 3833bbf0a1
6 changed files with 247 additions and 0 deletions

View File

@@ -949,3 +949,83 @@ def catalog_compare(env, compare, against):
else:
log.warn('Access to catalog interface disabled by administrator')
abort(403)
@app.route('/radiator', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/radiator')
def radiator(env):
"""This view generates a simplified monitoring page
akin to the radiator view in puppet dashboard
"""
envs = environments()
check_env(env, envs)
if env == '*':
query = None
metrics = get_or_abort(
puppetdb.metric,
'puppetlabs.puppetdb.population:name=num-nodes')
num_nodes = metrics['Value']
else:
query = '["and", {0}]]'.format(
",".join('["=", "{0}", "{1}"]'.format(field, env)
for field in ['catalog_environment', 'facts_environment']))
metrics = get_or_abort(
puppetdb._query,
'nodes',
query='["extract", [["function", "count"]],{0}'.format(
query))
num_nodes = metrics[0]['count']
nodes = puppetdb.nodes(
query=query,
unreported=app.config['UNRESPONSIVE_HOURS'],
with_status=True
)
stats = {
'changed_percent': 0,
'changed': 0,
'failed_percent': 0,
'failed': 0,
'noop_percent': 0,
'noop': 0,
'skipped_percent': 0,
'skipped': 0,
'unchanged_percent': 0,
'unchanged': 0,
'unreported_percent': 0,
'unreported': 0,
}
for node in nodes:
if node.status == 'unreported':
stats['unreported'] += 1
elif node.status == 'changed':
stats['changed'] += 1
elif node.status == 'failed':
stats['failed'] += 1
elif node.status == 'noop':
stats['noop'] += 1
elif node.status == 'skipped':
stats['skipped'] +=1
else:
stats['unchanged'] += 1
stats['changed_percent'] = int(100 * stats['changed'] / float(num_nodes))
stats['failed_percent'] = int(100 * stats['failed'] / float(num_nodes))
stats['noop_percent'] = int(100 * stats['noop'] / float(num_nodes))
stats['skipped_percent'] = int(100 * stats['skipped'] / float(num_nodes))
stats['unchanged_percent'] = int(100 * stats['unchanged'] / float(num_nodes))
stats['unreported_percent'] = int(100 * stats['unreported'] / float(num_nodes))
return render_template(
'radiator.html',
stats=stats,
total=num_nodes
)

View File

@@ -0,0 +1,42 @@
html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,code,del,dfn,em,img,q,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;}
body{line-height:20px;}
table{border-collapse:separate;border-spacing:0;}
caption,th,td {text-align:left;font-weight:normal;}
table,td,th {vertical-align:middle;}
blockquote:before,blockquote:after,q:before,q:after {content:"";}
blockquote,q {quotes:"" "";}
a img {border:none;}
ul {list-style-position:inside;}
body.radiator_controller {font-size:1000%;line-height:1;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif;background-color:#000;}
body.radiator_controller table.node_summary {padding:20px;position:absolute;height:100%;width:100%;}
body.radiator_controller table.node_summary .count_column {min-width:2em;width:2em;}
body.radiator_controller table.node_summary tr:last-child td {border-bottom:none;}
body.radiator_controller table.node_summary tr:last-child td .label {border-left:0px #000 solid;}
body.radiator_controller table.node_summary tr.unreported .percent {background-color:#ee7722;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.unreported .label,body.radiator_controller table.node_summary tr.unreported .count {color:#ee7722;}
body.radiator_controller table.node_summary tr.unreported .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.failed .percent {background-color:#cc2211;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.failed .label,body.radiator_controller table.node_summary tr.failed .count {color:#cc2211;}
body.radiator_controller table.node_summary tr.failed .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.noop .percent {background-color:#eddc21;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.noop .label,body.radiator_controller table.node_summary tr.noop .count {color:#eddc21;}
body.radiator_controller table.node_summary tr.noop .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.changed .percent {background-color:#009933;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.changed .label,body.radiator_controller table.node_summary tr.changed .count {color:#009933;}
body.radiator_controller table.node_summary tr.changed .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.unchanged .percent {background-color:#2198ed;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.unchanged .label,body.radiator_controller table.node_summary tr.unchanged .count {color:#2198ed;}
body.radiator_controller table.node_summary tr.unchanged .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.total {color:#fff;background-color:#181818;}
body.radiator_controller table.node_summary tr.total .percent {background-color:white;border-radius:0 3px 3px 0;}
body.radiator_controller table.node_summary tr.total .label,body.radiator_controller table.node_summary tr.total .count {color:white;}
body.radiator_controller table.node_summary tr.total .label {border-left:1px #333 dashed;}
body.radiator_controller table.node_summary tr.total .percent {display:none;}
body.radiator_controller table.node_summary tr.total td {border-top:1px solid #fff;}
body.radiator_controller table.node_summary tr td {color:#ccc;font-weight:normal;position:relative;border-bottom:1px solid #333;vertical-align:baseline;}
body.radiator_controller table.node_summary tr td div {position:relative;height:100%;}
body.radiator_controller table.node_summary tr td .percent {color:#000;position:absolute;top:0;left:0;height:100%;overflow:hidden;}
body.radiator_controller table.node_summary tr td .percent span {margin-left:0.1em;}
body.radiator_controller table.node_summary tr td .label {position:relative;height:100%;}
body.radiator_controller table.node_summary tr td .label span {margin-left:0.1em;}
body.radiator_controller table.node_summary tr td .count {text-align:right;width:1.75em;display:inline-block;font-weight:bold;margin-top:-0.12em;}

View File

@@ -0,0 +1,21 @@
function resizeMe() {
var preferredHeight = 944;
var displayHeight = $(window).height();
var percentageHeight = displayHeight / preferredHeight;
var preferredWidth = 1100;
var displayWidth = $(window).width();
var percentageWidth = displayWidth / preferredWidth;
var newFontSize;
if (percentageHeight < percentageWidth) {
newFontSize = Math.floor("960" * percentageHeight) - 30;
} else {
newFontSize = Math.floor("960" * percentageWidth) - 30;
}
$("body").css("font-size", newFontSize + "%")
}
$(document).ready(function() {
$(window).on('resize', resizeMe).trigger('resize');
})

View File

@@ -32,6 +32,7 @@
('metrics', 'Metrics'),
('inventory', 'Inventory'),
('catalogs', 'Catalogs'),
('radiator', 'Radiator'),
('query', 'Query'),
] %}
<a {% if endpoint == request.endpoint %} class="active item" {% else %} class="item" {% endif %}

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Puppetboard</title>
{% if config.REFRESH_RATE > 0 %}
<meta http-equiv='refresh' content="{{config.REFRESH_RATE}}"/>
{% endif %}
<link href="{{ url_for('static', filename='css/radiator.css')}}" media="screen" rel="stylesheet" type="text/css" />
<script src="{{ url_for('static', filename='js/jquery.min.js')}}" type="text/javascript"></script>
<script src="{{ url_for('static', filename='js/radiator.js')}}" type="text/javascript"></script>
</head>
<body class='radiator_controller radiator_index_action no-sidebar'>
<table class='node_summary'>
<tr class='failed '>
<td class='count_column'>
<span class='count'>{{stats['failed']}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Failed</span>
</p>
<p class='percent' style='width:{{stats['failed_percent']}}%'>
<span>Failed</span>
</p>
</div>
</td>
</tr>
<tr class='unreported '>
<td class='count_column'>
<span class='count'>{{stats['unreported']}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Unreported</span>
</p>
<p class='percent' style='width:{{stats['unreported_percent']}}%'>
<span>Unreported</span>
</p>
</div>
</td>
</tr>
<tr class='noop '>
<td class='count_column'>
<span class='count'>{{stats['noop']}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Pending</span>
</p>
<p class='percent' style='width:{{stats['noop_percent']}}%'>
<span>Pending</span>
</p>
</div>
</td>
</tr>
<tr class='changed '>
<td class='count_column'>
<span class='count'>{{stats['changed']}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Changed</span>
</p>
<p class='percent' style='width:{{stats['changed_percent']}}%'>
<span>Changed</span>
</p>
</div>
</td>
</tr>
<tr class='unchanged '>
<td class='count_column'>
<span class='count'>{{stats['unchanged']}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Unchanged</span>
</p>
<p class='percent' style='width:{{stats['unchanged_percent']}}%'>
<span>Unchanged</span>
</p>
</div>
</td>
</tr>
<tr class='total '>
<td class='count_column'>
<span class='count'>{{total}}</span>
</td>
<td>
<div>
<p class='label'>
<span>Total</span>
</p>
</div>
</td>
</tr>
</table>
</body>
</html>

BIN
screenshots/radiator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB