Merge pull request #19922 from Rubab-Syed/performance_counter_browser

mgr/dashboard: performance counter browsers

Reviewed-by: John Spray <john.spray@redhat.com>
This commit is contained in:
John Spray 2018-01-26 11:05:28 +00:00 committed by GitHub
commit 367c462882
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 128 additions and 27 deletions

View File

@ -776,6 +776,7 @@ class Module(MgrModule):
mon_status = global_instance().get_sync_object(MonStatus).data
for mon in mon_status["monmap"]["mons"]:
mon["stats"] = {}
mon["url_perf"] = "/perf_counters/mon/" + mon["name"]
for counter in counters:
data = global_instance().get_counter("mon", mon["name"], counter)
if data is not None:
@ -803,6 +804,43 @@ class Module(MgrModule):
def servers_data(self):
return self._servers()
@cherrypy.expose
def perf_counters(self, service_type, service_id):
template = env.get_template("perf_counters.html")
toplevel_data = self._toplevel_data()
return template.render(
url_prefix = global_instance().url_prefix,
ceph_version=global_instance().version,
path_info=cherrypy.request.path_info,
toplevel_data=json.dumps(toplevel_data, indent=2),
content_data=json.dumps(self.perf_counters_data(service_type, service_id), indent=2)
)
@cherrypy.expose
@cherrypy.tools.json_out()
def perf_counters_data(self, service_type, service_id):
schema = global_instance().get_perf_schema(service_type, str(service_id)).values()[0]
counters = []
for key, value in sorted(schema.items()):
counter = dict()
counter["name"] = str(key)
counter["description"] = value["description"]
if global_instance()._stattype_to_str(value["type"]) == 'counter':
counter["value"] = global_instance().get_rate(service_type, service_id, key)
counter["unit"] = "/s"
else:
counter["value"] = global_instance().get_latest(service_type, service_id, key)
counter["unit"] = ""
counters.append(counter)
return {
'service_type': service_type,
'service_id': service_id,
'counters': counters,
}
def _health(self):
# Fuse osdmap with pg_summary to get description of pools
# including their PG states
@ -1022,7 +1060,8 @@ class Module(MgrModule):
return {
"osd": osd,
"osd_metadata": osd_metadata,
"osd_histogram": histogram
"osd_histogram": histogram,
"url_perf": "/perf_counters/osd/" + str(osd_id)
}
@cherrypy.expose
@ -1196,6 +1235,7 @@ class Module(MgrModule):
return {
"rgw_id": rgw_id,
"url_perf": "/perf_counters/rgw/" + str(rgw_id),
"rgw_metadata": to_sorted_array(rgw_metadata),
"rgw_status": to_sorted_array(rgw_status),
}

View File

@ -67,7 +67,7 @@
<th>Open Sessions</th>
</thead>
<tr rv-each-mon="in_quorum" style="font-size:15px;height:80px">
<td style="font-weight:bold">{mon.name}</td>
<td style="font-weight:bold"><a rv-href="mon.url_perf">{mon.name}</a></td>
<td>{mon.rank}</td>
<td>{mon.public_addr}</td>
<td><span class="inlinesparkline" style="font-size:30px">{ mon.stats.num_sessions | sparkline_data }</span></td>
@ -87,7 +87,7 @@
<th>Public Addr</th>
</thead>
<tr rv-each-mon="out_quorum" class="font-size:15px;height:80px">
<td style="font-weight:bold">{mon.name}</td>
<td style="font-weight:bold"><a rv-href="mon.url_perf">{mon.name}</a></td>
<td>{mon.rank}</td>
<td>{mon.public_addr}</td>
</tr>

View File

@ -104,11 +104,12 @@
post_load();
setTimeout(refresh, 3000);
});
};
};
// Wait 1s to load irrespective of normal frequency,
// to promptly load our first delta
setTimeout(refresh, 1000);
setTimeout(refresh, 1000);
});
</script>
@ -116,11 +117,12 @@
<section class="content-header">
<h1>
osd.{osd.osd}
<button class="pull-right btn btn-default"><a rv-href="url_perf">Performance Counters</a></button>
</h1>
</section>
<section class="content">
<table>
<tr>
<td>
@ -150,7 +152,7 @@
<tbody>
<tr rv-each-item="osd_list">
<td>{item.key}</td>
<td>{item.value}</td>
<td>{item.value}</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,58 @@
{% extends "base.html" %}
{% block content %}
<script>
$(document).ready(function(){
// Pre-populated initial data at page load
var content_data = {{ content_data }};
rivets.bind($("div#content"), content_data);
var refresh = function() {
$.get("{{ url_prefix }}/perf_counters/" + content_data.service_type + "/" + content_data.service_id, function(data) {
_.extend(content_data, data);
setTimeout(refresh, 3000);
});
};
setTimeout(refresh, 1000);
});
</script>
<section class="content-header">
<h1>
{service_type}.{service_id}
</h1>
</section>
<section class="content">
<div class="box">
<div class="box-header">
<h3 class="box-title">Performance Counters</h3>
</div>
<div class="box-body">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr rv-each-item="counters">
<td>{item.name}</td>
<td>{item.description}</td>
<td>{item.value | dimless} {item.unit}</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- /.content -->
{% endblock %}

View File

@ -20,6 +20,7 @@
</script>
<section class="content-header">
<button class="pull-right btn btn-default"><a rv-href="url_perf">Performance Counters</a></button>
<h1 rv-text="rgw_id"></h1>
</section>

View File

@ -16,11 +16,12 @@
setTimeout(refresh, 5000);
rivets.formatters.service_list = function(services) {
var strings = [];
var result = ""
$.each(services, function(i, svc) {
strings.push(svc.type + "." + svc.id);
result += "<a href={{url_prefix}}/perf_counters/" + svc.type + "/" + svc.id + ">" + svc.type + "." + svc.id + "</a>, " ;
});
return strings.join(", ");
result = result.slice(0, -2);
return result;
};
rivets.bind($("#content"), content_data);
@ -55,8 +56,7 @@
<td>
{server.hostname}
</td>
<td>
{server.services | service_list}
<td rv-html="server.services | service_list">
</td>
<td>
{server.ceph_version | short_version}

View File

@ -308,6 +308,21 @@ class MgrModule(ceph_module.BaseMgrModule):
"""
return self._ceph_get(data_name)
def _stattype_to_str(self, stattype):
typeonly = stattype & self.PERFCOUNTER_TYPE_MASK
if typeonly == 0:
return 'gauge'
if typeonly == self.PERFCOUNTER_LONGRUNAVG:
# this lie matches the DaemonState decoding: only val, no counts
return 'counter'
if typeonly == self.PERFCOUNTER_COUNTER:
return 'counter'
if typeonly == self.PERFCOUNTER_HISTOGRAM:
return 'histogram'
return ''
def get_server(self, hostname):
"""
Called by the plugin to fetch metadata about a particular hostname from

View File

@ -144,21 +144,6 @@ class Module(MgrModule):
self.schema = OrderedDict()
_global_instance['plugin'] = self
def _stattype_to_str(self, stattype):
typeonly = stattype & self.PERFCOUNTER_TYPE_MASK
if typeonly == 0:
return 'gauge'
if typeonly == self.PERFCOUNTER_LONGRUNAVG:
# this lie matches the DaemonState decoding: only val, no counts
return 'counter'
if typeonly == self.PERFCOUNTER_COUNTER:
return 'counter'
if typeonly == self.PERFCOUNTER_HISTOGRAM:
return 'histogram'
return ''
def _setup_static_metrics(self):
metrics = {}
metrics['health_status'] = Metric(