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:
@@ -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
|
||||
)
|
||||
|
||||
42
puppetboard/static/css/radiator.css
Normal file
42
puppetboard/static/css/radiator.css
Normal 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;}
|
||||
21
puppetboard/static/js/radiator.js
Normal file
21
puppetboard/static/js/radiator.js
Normal 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');
|
||||
})
|
||||
@@ -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 %}
|
||||
|
||||
103
puppetboard/templates/radiator.html
Normal file
103
puppetboard/templates/radiator.html
Normal 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
BIN
screenshots/radiator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
Reference in New Issue
Block a user