mirror of
https://github.com/ceph/ceph
synced 2024-12-29 15:03:33 +00:00
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:
commit
367c462882
@ -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),
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
58
src/pybind/mgr/dashboard/perf_counters.html
Normal file
58
src/pybind/mgr/dashboard/perf_counters.html
Normal 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 %}
|
@ -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>
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user