This repository has been archived by the owner on Mar 31, 2024. It is now read-only.
forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from rashidkpc/master
Compare nodes button and scripted dashboard
- Loading branch information
Showing
3 changed files
with
284 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* global _ */ | ||
|
||
/* | ||
* Node statistics scripted dashboard | ||
* This script generates a dashboard object that Kibana can load. | ||
* | ||
* Parameters (all optional) | ||
* nodes :: By default, a comma seperated list of queries to run. Default: * | ||
* show :: The names of the rows to expand | ||
* from :: Search this amount of time back, eg 15m, 1h, 2d. Default: 1d | ||
* | ||
*/ | ||
|
||
'use strict'; | ||
|
||
// Setup some variables | ||
var dashboard, queries, _d_timespan; | ||
|
||
// All url parameters are available via the ARGS object | ||
var ARGS; | ||
|
||
// Set a default timespan if one isn't specified | ||
_d_timespan = '1d'; | ||
|
||
// Intialize a skeleton with nothing but a rows array and service object | ||
dashboard = { | ||
rows : [], | ||
services : {} | ||
}; | ||
|
||
// Set a title | ||
dashboard.title = 'Node Statistics'; | ||
|
||
// And the index options | ||
dashboard.failover = false; | ||
dashboard.index = { | ||
default: 'ADD_A_TIME_FILTER', | ||
pattern: '[es_monitor-]YYYY.MM.DD', | ||
interval: 'day' | ||
}; | ||
|
||
// In this dashboard we let users pass nodes as comma seperated list to the query parameter. | ||
// If nodes are defined, split into a list of query objects, otherwise, show all | ||
// NOTE: ids must be integers, hence the parseInt()s | ||
if(!_.isUndefined(ARGS.nodes)) { | ||
queries = _.object(_.map(ARGS.nodes.split(','), function(v,k) { | ||
return [k,{ | ||
query: 'node.transport_address:"'+v+'"', | ||
id: parseInt(k,10), | ||
alias: v | ||
}]; | ||
})); | ||
} else { | ||
// No queries passed? Initialize a single query to match everything | ||
queries = { | ||
0: { | ||
query: '*', | ||
id: 0, | ||
} | ||
}; | ||
} | ||
|
||
var show = ARGS.show.split(',') || []; | ||
|
||
// Now populate the query service with our objects | ||
dashboard.services.query = { | ||
list : queries, | ||
ids : _.map(_.keys(queries),function(v){return parseInt(v,10);}) | ||
}; | ||
|
||
// Lets also add a default time filter, the value of which can be specified by the user | ||
dashboard.services.filter = { | ||
list: { | ||
0: { | ||
from: "now-"+(ARGS.from||_d_timespan), | ||
to: "now", | ||
field: "@timestamp", | ||
type: "time", | ||
active: true, | ||
id: 0, | ||
} | ||
}, | ||
ids: [0] | ||
}; | ||
|
||
// Ok, lets make some rows. Since all of our panels are similar, we can abstract this. | ||
// Obviously this is a partial list, feel free to expand on this. | ||
var rows = [ | ||
{ | ||
name:'OS', | ||
charts: [{ | ||
field: 'os.cpu.user', | ||
derivative: false, | ||
},{ | ||
field: 'os.mem.used_percent', | ||
derivative: false | ||
},{ | ||
field: 'os.swap.used_in_bytes', | ||
derivative: true | ||
}] | ||
}, | ||
{ | ||
name: 'JVM', | ||
charts: [{ | ||
field: 'jvm.gc.collectors.ParNew.collection_time_in_millis', | ||
derivative: true | ||
},{ | ||
field: 'jvm.gc.collectors.ParNew.collection_count', | ||
derivative: true | ||
},{ | ||
field: 'jvm.gc.collectors.ConcurrentMarkSweep.collection_time_in_millis', | ||
derivative: true | ||
}] | ||
} | ||
]; | ||
|
||
dashboard.rows = _.map(rows, function(r) { | ||
return { | ||
title: r.name, | ||
height: '150px', | ||
collapse: !_.contains(show,r.name), | ||
panels: _.map(r.charts,function(c) { | ||
// A bunch of histogram panels, with similar defaults | ||
return { | ||
title: c.field, | ||
type: 'histogram', | ||
span: 4, | ||
time_field: '@timestamp', | ||
value_field: c.field, | ||
derivative: c.derivative, | ||
bars: false, | ||
lines: true, | ||
stack: false, | ||
linewidth:2, | ||
mode: 'max', // Pretty sure we want max for all of these? No? Average for some? | ||
zoomlinks: false, | ||
options: false, | ||
legend: false, // Might want to enable this, cleaner without it though | ||
interactive: false // Because the filter pulldown is hidden | ||
}; | ||
}) | ||
}; | ||
}); | ||
|
||
// No pulldowns shown, and they can't be enabled. | ||
dashboard.pulldowns = []; | ||
|
||
// Now return the object and we're good! | ||
return dashboard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,93 @@ | ||
<div ng-controller='marvel.nodes_health' ng-init="init()"> | ||
<style> | ||
.marvel-table { | ||
vertical-align: middle; | ||
} | ||
.marvel-mean { | ||
font-size: 20pt; | ||
font-weight: 200; | ||
display: inline-block; | ||
vertical-align: middle; | ||
} | ||
.marvel-extended { | ||
display: inline-block; | ||
font-size:9pt; | ||
margin-left: 5px; | ||
vertical-align: middle; | ||
} | ||
.marvel-header .nodes{ | ||
font-size: 20pt; | ||
font-weight: bold; | ||
margin-left: 10px; | ||
} | ||
.marvel-nodes-health-chart { | ||
margin-top: 5px; | ||
display: inline-block; | ||
height: 10px; | ||
width: 50px; | ||
} | ||
</style> | ||
<style> | ||
.marvel-table { | ||
vertical-align: middle; | ||
} | ||
.marvel-mean { | ||
font-size: 20pt; | ||
font-weight: 200; | ||
display: inline-block; | ||
vertical-align: middle; | ||
} | ||
.marvel-extended { | ||
display: inline-block; | ||
font-size:9pt; | ||
margin-left: 5px; | ||
vertical-align: middle; | ||
} | ||
.marvel-header { | ||
margin-bottom: 10px; | ||
} | ||
.marvel-header .nodes{ | ||
font-size: 20pt; | ||
font-weight: bold; | ||
margin-left: 10px; | ||
} | ||
.marvel-nodes-health-chart { | ||
margin-top: 5px; | ||
display: inline-block; | ||
height: 10px; | ||
width: 50px; | ||
} | ||
</style> | ||
|
||
<div class="pull-left marvel-header marvel-table" ng-show="nodes.length > 0"> | ||
<span class="nodes">{{nodes.length}} nodes</span> / Last 10m</span> | ||
</div> | ||
<div class="pull-right"> | ||
<a href="" ng-class="{strong:!panel.compact}" ng-click="panel.compact=false">Full</a> / <a href="" ng-class="{strong:panel.compact}" | ||
ng-click="panel.compact=true">Compact</a> | ||
</div> | ||
<div class="pull-left marvel-header marvel-table" ng-show="nodes.length > 0"> | ||
<span class="nodes">{{nodes.length}} nodes</span> / Last 10m </span> | ||
</div> | ||
<div class="pull-right"> | ||
<a href="" ng-class="{strong:!panel.compact}" ng-click="panel.compact=false">Full</a> / | ||
<a href="" ng-class="{strong:panel.compact}" ng-click="panel.compact=true">Compact</a> | ||
</div> | ||
|
||
<table class="table table-bordered" ng-if="!panel.compact"> | ||
<thead> | ||
<th>node</th> | ||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th> | ||
</thead> | ||
<tr ng-repeat="node in nodes"> | ||
<td>{{node}}</td> | ||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node][metric.name])"> | ||
<div class="marvel-mean"> | ||
{{data[node+"_"+metric.name].mean / metric.scale | number:metric.decimals}}<br> | ||
<table class="table table-bordered" ng-if="!panel.compact"> | ||
<thead> | ||
<th>node <a ng-href="{{compareLink()}}" target="_blank" class="btn btn-mini btn-info" ng-disabled="!hasSelected(nodes)" bs-tooltip="compareTip()" data-placement="right">Compare</a></th> | ||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th> | ||
</thead> | ||
<tr ng-repeat="node in nodes"> | ||
<td> | ||
<div class="checkbox"> | ||
<label> | ||
<input type="checkbox" ng-model="nodes[$index].selected" ng-checked="nodes[$index].selected"> | ||
{{node.name}} | ||
</label> | ||
</div> | ||
</td> | ||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node][metric.name])"> | ||
<div class="marvel-mean"> | ||
{{data[node.name+"_"+metric.name].mean / metric.scale | number:metric.decimals}}<br> | ||
|
||
<div class="marvel-nodes-health-chart" series="data[node+'_'+metric.name+'_history']"></div> | ||
</div> | ||
<div class="marvel-extended"> | ||
<span>min: {{data[node+"_"+metric.name].min / metric.scale | number:metric.decimals}}</span><br> | ||
<span>max: {{data[node+"_"+metric.name].max / metric.scale | number:metric.decimals}}</span> | ||
<div class="marvel-nodes-health-chart" series="data[node.name+'_'+metric.name+'_history']"></div> | ||
</div> | ||
<div class="marvel-extended"> | ||
<span>min: {{data[node.name+"_"+metric.name].min / metric.scale | number:metric.decimals}}</span><br> | ||
<span>max: {{data[node.name+"_"+metric.name].max / metric.scale | number:metric.decimals}}</span> | ||
|
||
</div> | ||
</td> | ||
</tr> | ||
</table> | ||
</div> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
<table class="table table-bordered table-condensed marvel-table" ng-if="panel.compact"> | ||
<thead> | ||
<th>node</th> | ||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th> | ||
</thead> | ||
<tr ng-repeat="node in nodes"> | ||
<td>{{node}}</td> | ||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node][metric.name])"> | ||
<div>{{data[node+"_"+metric.name].mean / metric.scale | number:metric.decimals}} | ||
<div class="marvel-nodes-health-chart" series="data[node+'_'+metric.name+'_history']"></div> | ||
</div> | ||
</td> | ||
</tr> | ||
</table> | ||
<table class="table table-bordered table-condensed marvel-table" ng-if="panel.compact"> | ||
<thead> | ||
<th>node <a ng-href="{{compareLink()}}" class="btn btn-mini btn-info" ng-disabled="!hasSelected(nodes)" bs-tooltip="compareTip()" data-placement="right">Compare</a></th> | ||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th> | ||
</thead> | ||
<tr ng-repeat="node in nodes"> | ||
<td> | ||
<div class="checkbox"> | ||
<label> | ||
<input type="checkbox" ng-model="nodes[$index].selected" ng-checked="nodes[$index].selected"> | ||
{{node.name}} | ||
</label> | ||
</div> | ||
</td> | ||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node.name][metric.name])"> | ||
<div>{{data[node.name+"_"+metric.name].mean / metric.scale | number:metric.decimals}} | ||
<div class="marvel-nodes-health-chart" series="data[node.name+'_'+metric.name+'_history']"></div> | ||
</div> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
</div> |
Oops, something went wrong.