Merge pull request #28 from digitalmediacenter/improve-nodestatus
Enhance node status feature in overview and nodes
This commit is contained in:
@@ -101,40 +101,35 @@ def index():
|
|||||||
'avg_resources_node': "{0:10.6f}".format(avg_resources_node['Value']),
|
'avg_resources_node': "{0:10.6f}".format(avg_resources_node['Value']),
|
||||||
}
|
}
|
||||||
|
|
||||||
latest_event_count = puppetdb._query(
|
nodes = puppetdb.nodes(with_status=True)
|
||||||
'aggregate-event-counts',
|
|
||||||
query='["=", "latest-report?", true]',
|
|
||||||
summarize_by='certname')
|
|
||||||
|
|
||||||
latest_event_count['noopskip'] = (
|
nodes_overview = []
|
||||||
latest_event_count['noops'] + latest_event_count['skips'])
|
stats = {
|
||||||
|
'changed': 0,
|
||||||
|
'unchanged': 0,
|
||||||
|
'failed': 0,
|
||||||
|
'unreported': 0,
|
||||||
|
}
|
||||||
|
|
||||||
latest_events = puppetdb._query(
|
for node in nodes:
|
||||||
'event-counts',
|
if node.status == 'unreported':
|
||||||
query='["=", "latest-report?", true]',
|
stats['unreported'] += 1
|
||||||
summarize_by='certname')
|
elif node.status == 'changed':
|
||||||
|
stats['changed'] += 1
|
||||||
|
elif node.status == 'failed':
|
||||||
|
stats['failed'] += 1
|
||||||
|
else:
|
||||||
|
stats['unchanged'] += 1
|
||||||
|
|
||||||
unreported = []
|
if node.status != 'unchanged':
|
||||||
unresponsive_window = datetime.utcnow() - (
|
nodes_overview.append(node)
|
||||||
timedelta(hours=app.config['UNRESPONSIVE_HOURS']))
|
|
||||||
for node in puppetdb.nodes():
|
|
||||||
try:
|
|
||||||
node_last_seen = node.report_timestamp.replace(tzinfo=None)
|
|
||||||
if node_last_seen < unresponsive_window:
|
|
||||||
delta = (datetime.utcnow() - node_last_seen)
|
|
||||||
node.noresponse = str(delta.days) + "d "
|
|
||||||
node.noresponse += str(int(delta.seconds / 3600)) + "h "
|
|
||||||
node.noresponse += str(int((delta.seconds % 3600) / 60)) + "m"
|
|
||||||
unreported.append(node)
|
|
||||||
except AttributeError:
|
|
||||||
unreported.append(node)
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'index.html',
|
'index.html',
|
||||||
metrics=metrics,
|
metrics=metrics,
|
||||||
latest_event_count=latest_event_count,
|
nodes=nodes_overview,
|
||||||
latest_events=latest_events,
|
stats=stats
|
||||||
unreported=unreported)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/nodes')
|
@app.route('/nodes')
|
||||||
@@ -149,27 +144,13 @@ def nodes():
|
|||||||
provide a search feature instead.
|
provide a search feature instead.
|
||||||
"""
|
"""
|
||||||
status_arg = request.args.get('status', '')
|
status_arg = request.args.get('status', '')
|
||||||
latest_events = puppetdb._query(
|
|
||||||
'event-counts',
|
|
||||||
query='["=", "latest-report?", true]',
|
|
||||||
summarize_by='certname')
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in yield_or_stop(puppetdb.nodes()):
|
for node in yield_or_stop(puppetdb.nodes(with_status=True)):
|
||||||
# check if node name is contained in any of the
|
|
||||||
# event-counts (grouped by certname)
|
|
||||||
status = [
|
|
||||||
s for s in latest_events if
|
|
||||||
s['subject']['title'] == node.name]
|
|
||||||
if status:
|
|
||||||
node.status = status[0]
|
|
||||||
else:
|
|
||||||
node.status = {}
|
|
||||||
if status_arg:
|
if status_arg:
|
||||||
if node.status.has_key(status_arg) and node.status[status_arg]:
|
if node.status == status_arg:
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
else:
|
else:
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
return Response(stream_with_context(
|
return Response(stream_with_context(
|
||||||
stream_template('nodes.html', nodes=nodes)))
|
stream_template('nodes.html', nodes=nodes)))
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,22 @@ $ = jQuery
|
|||||||
$ ->
|
$ ->
|
||||||
$('.nodes').tablesorter(
|
$('.nodes').tablesorter(
|
||||||
headers:
|
headers:
|
||||||
3:
|
4:
|
||||||
sorter: false
|
sorter: false
|
||||||
sortList: [[0,0]]
|
sortList: [[1,0]]
|
||||||
)
|
)
|
||||||
|
|
||||||
$('.facts').tablesorter(
|
$('.facts').tablesorter(
|
||||||
sortList: [[0,0]]
|
sortList: [[0,0]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
$('.dashboard').tablesorter(
|
||||||
|
headers:
|
||||||
|
2:
|
||||||
|
sorter: false
|
||||||
|
sortList: [[0, 1]]
|
||||||
|
)
|
||||||
|
|
||||||
$('input.filter-table').parent('div').removeClass('hide')
|
$('input.filter-table').parent('div').removeClass('hide')
|
||||||
$("input.filter-table").on "keyup", (e) ->
|
$("input.filter-table").on "keyup", (e) ->
|
||||||
rex = new RegExp($(this).val(), "i")
|
rex = new RegExp($(this).val(), "i")
|
||||||
|
|||||||
@@ -61,12 +61,29 @@ h1.success {
|
|||||||
h1.noop {
|
h1.noop {
|
||||||
color:#aaa;
|
color:#aaa;
|
||||||
}
|
}
|
||||||
.numtag {
|
.label-count {
|
||||||
width:20px;
|
width:25px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.label-time {
|
||||||
|
width:73px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.label-status {
|
||||||
|
width:100px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
.label-nothing {
|
.label-nothing {
|
||||||
background-color:#ddd;
|
background-color:#ddd;
|
||||||
width:20px;
|
|
||||||
color:#ddd;
|
color:#ddd;
|
||||||
}
|
}
|
||||||
|
.label.label-failed {
|
||||||
|
background-color: rgb(231, 76, 60);
|
||||||
|
}
|
||||||
|
.label.label-changed {
|
||||||
|
background-color: rgb(24, 188, 156);
|
||||||
|
}
|
||||||
|
.label.label-unreported {
|
||||||
|
background-color: rgb(231, 76, 60);
|
||||||
|
background-color: rgb(129, 145, 146);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,13 @@
|
|||||||
sortList: [[0, 0]]
|
sortList: [[0, 0]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.dashboard').tablesorter({
|
||||||
|
headers: {
|
||||||
|
2: { sorter: false }
|
||||||
|
},
|
||||||
|
sortList: [[0, 1]]
|
||||||
|
});
|
||||||
|
|
||||||
$('input.filter-table').parent('div').removeClass('hide');
|
$('input.filter-table').parent('div').removeClass('hide');
|
||||||
|
|
||||||
$("input.filter-table").on("keyup", function(e) {
|
$("input.filter-table").on("keyup", function(e) {
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
{% extends 'layout.html' %}
|
{% extends 'layout.html' %}
|
||||||
{% block row_fluid %}
|
{% block row_fluid %}
|
||||||
<div class="container" style="margin-bottom:55px;">
|
<div class="container" style="margin-bottom:55px;">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="span4 stat">
|
<div class="span4 stat">
|
||||||
<a href="nodes?status=failures">
|
<a href="nodes?status=failed">
|
||||||
<h1 class="error">{{latest_event_count['failures']}} <small>node(s)</small></h1>
|
<h1 class="error">{{stats['failed']}}
|
||||||
|
<small>{% if stats['failed']== 1 %} node {% else %} nodes {% endif %}</small>
|
||||||
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
<span>with failures in latest reports</span>
|
<span>with status failed</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="span4 stat">
|
<div class="span4 stat">
|
||||||
<a href="nodes?status=successes">
|
<a href="nodes?status=changed">
|
||||||
<h1 class="success">{{latest_event_count['successes']}} <small>node(s)</small></h1>
|
<h1 class="success">{{stats['changed']}}
|
||||||
|
<small>{% if stats['changed']== 1 %} node {% else %} nodes {% endif %}</small>
|
||||||
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
<span>with successes in latest reports</span>
|
<span>with status changed</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="span4 stat">
|
<div class="span4 stat">
|
||||||
<h1 class="noop">{{ unreported|length }} <small>unreported</small></h1>
|
<a href="nodes?status=unreported">
|
||||||
|
<h1 class="noop">{{ stats['unreported'] }}
|
||||||
|
<small>{% if stats['unreported']== 1 %} node {% else %} nodes {% endif %}</small>
|
||||||
|
</h1>
|
||||||
|
</a>
|
||||||
<span>
|
<span>
|
||||||
{% if unreported|length == 1 %} node {% else %} nodes {% endif %}
|
unreported in the last {{ config.UNRESPONSIVE_HOURS }} hours
|
||||||
in the last {{ config.UNRESPONSIVE_HOURS }} hours
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,34 +52,38 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<h2>Nodes unresponsive in the last {{ config.UNRESPONSIVE_HOURS }} hours</h2>
|
<h2>Node Status Detail</h2>
|
||||||
<table class='nodes table table-striped table-condensed'>
|
<table class='dashboard table table-striped table-condensed'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:220px;">Status</th>
|
||||||
|
<th style="width:600px;">Hostname</th>
|
||||||
|
<th style="width:120px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody class="searchable">
|
<tbody class="searchable">
|
||||||
{% for node in unreported %}
|
{% for node in nodes %}
|
||||||
|
{% if node.status != 'unchanged' %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:110px;"><span class="label label-important">No Report</span></td>
|
<td>
|
||||||
<td>{{ node.noresponse }}</td>
|
<a href="{{url_for('report_latest', node_name=node.name)}}">
|
||||||
<td><a href="{{url_for('node', node_name=node.name)}}">{{ node.name }}</a></td>
|
<span class="label label-status label-{{node.status}}">{{node.status}}</span>
|
||||||
<td style="width:120px;"><a class="btn btn-small btn-primary" href="{{url_for('report_latest', node_name=node.name)}}">Latest Report</a></td>
|
</a>
|
||||||
</tr>
|
{% if node.status=='unreported'%}
|
||||||
{% endfor %}
|
<span class="label label-time label-unreported"> {{ node.unreported_time }} </label>
|
||||||
</tbody>
|
{% else %}
|
||||||
</table>
|
{% if node.events['failures'] %}<span class="label label-important label-count">{{node.events['failures']}}</span>{% else %}<span class="label label-count">0</span>{% endif%}
|
||||||
<h2>Latest reports with events</h2>
|
{% if node.events['successes'] %}<span class="label label-success label-count">{{node.events['successes']}}</span>{% else %}<span class="label label-count">0</span>{% endif%}
|
||||||
<table class='nodes table table-striped table-condensed'>
|
{% endif %}
|
||||||
<tbody class="searchable">
|
|
||||||
{% for event in latest_events %}
|
|
||||||
<tr>
|
|
||||||
<td style="width:110px;">
|
|
||||||
{% if event['failures'] %}<span class="label label-important numtag">{{event['failures']}}</span>{% else %}<span class="label numtag">0</span>{% endif%}
|
|
||||||
{% if event['successes'] %}<span class="label label-success numtag">{{event['successes']}}</span>{% else %}<span class="label numtag">0</span>{% endif%}
|
|
||||||
</td>
|
</td>
|
||||||
<td style="width:700px;"><a href="{{url_for('node', node_name=event['subject']['title'])}}">{{event['subject']['title']}}</a></td>
|
<td><a href="{{url_for('node', node_name=node.name)}}">{{ node.name }}</a></td>
|
||||||
<td style="width:120px;"><a class="btn btn-small btn-primary" href="{{url_for('report_latest', node_name=event['subject']['title'])}}">Latest Report</a></td>
|
<td><a class="btn btn-small btn-primary" href="{{url_for('report_latest', node_name=node.name)}}">Latest Report</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -29,10 +29,14 @@
|
|||||||
{% for node in nodes %}
|
{% for node in nodes %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{url_for('report_latest', node_name=node.name)}}">
|
<td><a href="{{url_for('report_latest', node_name=node.name)}}">
|
||||||
{% if node.status['failures'] %}<span class="label numtag label-important">{{node.status['failures']}}</span>{% else %}<span class="label label-nothing">0</span>{% endif %}
|
<span class="label label-status label-{{ node.status }}">{{ node.status }}</span>
|
||||||
{% if node.status['successes'] %}<span class="label numtag label-success">{{node.status['successes']}}</span>{% else %}<span class="label label-nothing">0</span>{% endif %}
|
|
||||||
{% if node.status['noops'] or node.status['skips'] %}<span class="label numtag">{{node.status['skips']+node.status['noops']}}</span>{% else %}<span class="label label-nothing">0</span>{% endif %}
|
|
||||||
</a>
|
</a>
|
||||||
|
{% if node.status=='unreported'%}
|
||||||
|
<span class="label label-time label-unreported"> {{ node.unreported_time }} </label>
|
||||||
|
{% else %}
|
||||||
|
{% if node.events['failures'] %}<span class="label label-count label-important">{{node.events['failures']}}</span>{% else %}<span class="label label-count">0</span>{% endif%}
|
||||||
|
{% if node.events['successes'] %}<span class="label label-count label-success">{{node.events['successes']}}</span>{% else %}<span class="label label-count">0</span>{% endif%}
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td><a href="{{url_for('node', node_name=node.name)}}">{{node.name}}</a></td>
|
<td><a href="{{url_for('node', node_name=node.name)}}">{{node.name}}</a></td>
|
||||||
<td rel="utctimestamp">{{node.catalog_timestamp}}</td>
|
<td rel="utctimestamp">{{node.catalog_timestamp}}</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user