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:
|
else:
|
||||||
log.warn('Access to catalog interface disabled by administrator')
|
log.warn('Access to catalog interface disabled by administrator')
|
||||||
abort(403)
|
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'),
|
('metrics', 'Metrics'),
|
||||||
('inventory', 'Inventory'),
|
('inventory', 'Inventory'),
|
||||||
('catalogs', 'Catalogs'),
|
('catalogs', 'Catalogs'),
|
||||||
|
('radiator', 'Radiator'),
|
||||||
('query', 'Query'),
|
('query', 'Query'),
|
||||||
] %}
|
] %}
|
||||||
<a {% if endpoint == request.endpoint %} class="active item" {% else %} class="item" {% endif %}
|
<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