diff --git a/src/pybind/mgr/dashboard/module.py b/src/pybind/mgr/dashboard/module.py index de9b889a2ee..257abfed05c 100644 --- a/src/pybind/mgr/dashboard/module.py +++ b/src/pybind/mgr/dashboard/module.py @@ -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), } diff --git a/src/pybind/mgr/dashboard/monitors.html b/src/pybind/mgr/dashboard/monitors.html index 40a26551d4c..60b34ad62c5 100644 --- a/src/pybind/mgr/dashboard/monitors.html +++ b/src/pybind/mgr/dashboard/monitors.html @@ -67,7 +67,7 @@ Open Sessions - {mon.name} + {mon.name} {mon.rank} {mon.public_addr} { mon.stats.num_sessions | sparkline_data } @@ -87,7 +87,7 @@ Public Addr - {mon.name} + {mon.name} {mon.rank} {mon.public_addr} diff --git a/src/pybind/mgr/dashboard/osd_perf.html b/src/pybind/mgr/dashboard/osd_perf.html index b13ad17e1fd..2c36613517f 100644 --- a/src/pybind/mgr/dashboard/osd_perf.html +++ b/src/pybind/mgr/dashboard/osd_perf.html @@ -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); + }); @@ -116,11 +117,12 @@

osd.{osd.osd} +

- + - +
@@ -150,7 +152,7 @@
{item.key}{item.value}{item.value}
diff --git a/src/pybind/mgr/dashboard/perf_counters.html b/src/pybind/mgr/dashboard/perf_counters.html new file mode 100644 index 00000000000..40638b6b2d8 --- /dev/null +++ b/src/pybind/mgr/dashboard/perf_counters.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} + +{% block content %} + + + + +
+

+ {service_type}.{service_id} +

+
+ +
+
+
+

Performance Counters

+
+
+ + + + + + + + + + + + + + + +
NameDescriptionValue
{item.name}{item.description}{item.value | dimless} {item.unit}
+
+
+ +
+ + +{% endblock %} diff --git a/src/pybind/mgr/dashboard/rgw_detail.html b/src/pybind/mgr/dashboard/rgw_detail.html index 986dc90da07..3f224ac4c59 100644 --- a/src/pybind/mgr/dashboard/rgw_detail.html +++ b/src/pybind/mgr/dashboard/rgw_detail.html @@ -20,6 +20,7 @@
+

diff --git a/src/pybind/mgr/dashboard/servers.html b/src/pybind/mgr/dashboard/servers.html index 421d3389c35..dcecac40943 100644 --- a/src/pybind/mgr/dashboard/servers.html +++ b/src/pybind/mgr/dashboard/servers.html @@ -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 += "" + svc.type + "." + svc.id + ", " ; }); - return strings.join(", "); + result = result.slice(0, -2); + return result; }; rivets.bind($("#content"), content_data); @@ -55,8 +56,7 @@ {server.hostname} - - {server.services | service_list} + {server.ceph_version | short_version} diff --git a/src/pybind/mgr/mgr_module.py b/src/pybind/mgr/mgr_module.py index ea72df830a0..60cc2202847 100644 --- a/src/pybind/mgr/mgr_module.py +++ b/src/pybind/mgr/mgr_module.py @@ -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 diff --git a/src/pybind/mgr/prometheus/module.py b/src/pybind/mgr/prometheus/module.py index 5396d685175..74aceeaad44 100644 --- a/src/pybind/mgr/prometheus/module.py +++ b/src/pybind/mgr/prometheus/module.py @@ -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(