]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Refactor multiple duplicates of `get_rate()`
authorSebastian Wagner <sebastian.wagner@suse.com>
Fri, 23 Mar 2018 09:29:27 +0000 (10:29 +0100)
committerSebastian Wagner <sebastian.wagner@suse.com>
Mon, 9 Apr 2018 13:20:48 +0000 (15:20 +0200)
... And  `get_latest()`.

* OSD Controller: `.stats_history` now returns the
  derivative.

  Fixes https://tracker.ceph.com/issues/23389

Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/pybind/mgr/dashboard/controllers/cephfs.py
src/pybind/mgr/dashboard/controllers/osd.py
src/pybind/mgr/dashboard/controllers/perf_counters.py
src/pybind/mgr/dashboard/controllers/tcmu_iscsi.py
src/pybind/mgr/dashboard/services/ceph_service.py
src/pybind/mgr/dashboard/tox.ini
src/pybind/mgr/mgr_module.py

index f375a950da4a8fbc7c1f5f12a4713e2db389a458..ddb41cd10bbd3a5293aa2ec33c9473413947d0d3 100644 (file)
@@ -5,10 +5,9 @@ from collections import defaultdict
 
 import cherrypy
 
-from ..services.ceph_service import CephService
-
 from . import ApiController, AuthRequired, BaseController
 from .. import mgr
+from ..services.ceph_service import CephService
 from ..tools import ViewCache
 
 
@@ -98,14 +97,6 @@ class CephFS(BaseController):
 
         return names
 
-    def get_rate(self, daemon_type, daemon_name, stat):
-        data = mgr.get_counter(daemon_type, daemon_name, stat)[stat]
-
-        if data and len(data) > 1:
-            return (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0])
-
-        return 0
-
     # pylint: disable=too-many-locals,too-many-statements,too-many-branches
     def fs_status(self, fs_id):
         mds_versions = defaultdict(list)
@@ -132,17 +123,17 @@ class CephFS(BaseController):
             if up:
                 gid = mdsmap['up']["mds_{0}".format(rank)]
                 info = mdsmap['info']['gid_{0}'.format(gid)]
-                dns = self.get_latest("mds", info['name'], "mds.inodes")
-                inos = self.get_latest("mds", info['name'], "mds_mem.ino")
+                dns = mgr.get_latest("mds", info['name'], "mds.inodes")
+                inos = mgr.get_latest("mds", info['name'], "mds_mem.ino")
 
                 if rank == 0:
-                    client_count = self.get_latest("mds", info['name'],
-                                                   "mds_sessions.session_count")
+                    client_count = mgr.get_latest("mds", info['name'],
+                                                  "mds_sessions.session_count")
                 elif client_count == 0:
                     # In case rank 0 was down, look at another rank's
                     # sessionmap to get an indication of clients.
-                    client_count = self.get_latest("mds", info['name'],
-                                                   "mds_sessions.session_count")
+                    client_count = mgr.get_latest("mds", info['name'],
+                                                  "mds_sessions.session_count")
 
                 laggy = "laggy_since" in info
 
@@ -150,20 +141,15 @@ class CephFS(BaseController):
                 if laggy:
                     state += "(laggy)"
 
-                # if state == "active" and not laggy:
-                #     c_state = self.colorize(state, self.GREEN)
-                # else:
-                #     c_state = self.colorize(state, self.YELLOW)
-
                 # Populate based on context of state, e.g. client
                 # ops for an active daemon, replay progress, reconnect
                 # progress
-                activity = ""
-
                 if state == "active":
-                    activity = self.get_rate("mds",
-                                             info['name'],
-                                             "mds_server.handle_client_request")
+                    activity = CephService.get_rate("mds",
+                                                    info['name'],
+                                                    "mds_server.handle_client_request")
+                else:
+                    activity = 0.0
 
                 metadata = mgr.get_metadata('mds', info['name'])
                 mds_versions[metadata.get('ceph_version', 'unknown')].append(
@@ -185,7 +171,7 @@ class CephFS(BaseController):
                         "rank": rank,
                         "state": "failed",
                         "mds": "",
-                        "activity": "",
+                        "activity": 0.0,
                         "dns": 0,
                         "inos": 0
                     }
@@ -197,10 +183,10 @@ class CephFS(BaseController):
             if daemon_info['state'] != "up:standby-replay":
                 continue
 
-            inos = self.get_latest("mds", daemon_info['name'], "mds_mem.ino")
-            dns = self.get_latest("mds", daemon_info['name'], "mds.inodes")
+            inos = mgr.get_latest("mds", daemon_info['name'], "mds_mem.ino")
+            dns = mgr.get_latest("mds", daemon_info['name'], "mds.inodes")
 
-            activity = self.get_rate(
+            activity = CephService.get_rate(
                 "mds", daemon_info['name'], "mds_log.replay")
 
             rank_table.append(
@@ -292,12 +278,6 @@ class CephFS(BaseController):
             'data': clients
         }
 
-    def get_latest(self, daemon_type, daemon_name, stat):
-        data = mgr.get_counter(daemon_type, daemon_name, stat)[stat]
-        if data:
-            return data[-1][1]
-        return 0
-
 
 class CephFSClients(object):
     def __init__(self, module_inst, fscid):
index f4a4e68c22a9f2503a59b5c98409498ce09a765e..4844ee9857e1d6f6f636710871ec1c5e6b6554da 100644 (file)
@@ -7,28 +7,12 @@ from mgr_module import CommandResult
 
 from . import ApiController, AuthRequired, RESTController
 from .. import logger, mgr
+from ..services.ceph_service import CephService
 
 
 @ApiController('osd')
 @AuthRequired()
 class Osd(RESTController):
-    def get_counter(self, daemon_name, stat):
-        return mgr.get_counter('osd', daemon_name, stat)[stat]
-
-    def get_rate(self, daemon_name, stat):
-        data = self.get_counter(daemon_name, stat)
-        rate = 0
-        if data and len(data) > 1:
-            rate = (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0])
-        return rate
-
-    def get_latest(self, daemon_name, stat):
-        data = self.get_counter(daemon_name, stat)
-        latest = 0
-        if data and data[-1] and len(data[-1]) == 2:
-            latest = data[-1][1]
-        return latest
-
     def list(self):
         osds = self.get_osd_map()
         # Extending by osd stats information
@@ -53,11 +37,11 @@ class Osd(RESTController):
             osd_spec = str(o['osd'])
             for s in ['osd.op_w', 'osd.op_in_bytes', 'osd.op_r', 'osd.op_out_bytes']:
                 prop = s.split('.')[1]
-                o['stats'][prop] = self.get_rate(osd_spec, s)
-                o['stats_history'][prop] = self.get_counter(osd_spec, s)
+                o['stats'][prop] = CephService.get_rate('osd', osd_spec, s)
+                o['stats_history'][prop] = CephService.get_rates('osd', osd_spec, s)
             # Gauge stats
             for s in ['osd.numpg', 'osd.stat_bytes', 'osd.stat_bytes_used']:
-                o['stats'][s.split('.')[1]] = self.get_latest(osd_spec, s)
+                o['stats'][s.split('.')[1]] = mgr.get_latest('osd', osd_spec, s)
         return list(osds.values())
 
     def get_osd_map(self):
index af0631a0a6659795176a2120caf4e2300e6de66a..cced79f1883acf42c9da4d08bffdae8bb4ebcd09 100644 (file)
@@ -3,23 +3,12 @@ from __future__ import absolute_import
 
 from . import ApiController, AuthRequired, RESTController
 from .. import mgr
+from ..services.ceph_service import CephService
 
 
 class PerfCounter(RESTController):
     service_type = None  # type: str
 
-    def _get_rate(self, daemon_type, daemon_name, stat):
-        data = mgr.get_counter(daemon_type, daemon_name, stat)[stat]
-        if data and len(data) > 1:
-            return (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0])
-        return 0
-
-    def _get_latest(self, daemon_type, daemon_name, stat):
-        data = mgr.get_counter(daemon_type, daemon_name, stat)[stat]
-        if data:
-            return data[-1][1]
-        return 0
-
     def get(self, service_id):
         schema_dict = mgr.get_perf_schema(self.service_type, str(service_id))
         schema = schema_dict["{}.{}".format(self.service_type, service_id)]
@@ -31,11 +20,11 @@ class PerfCounter(RESTController):
             counter['description'] = value['description']
             # pylint: disable=W0212
             if mgr._stattype_to_str(value['type']) == 'counter':
-                counter['value'] = self._get_rate(
+                counter['value'] = CephService.get_rate(
                     self.service_type, service_id, key)
                 counter['unit'] = mgr._unit_to_str(value['units'])
             else:
-                counter['value'] = self._get_latest(
+                counter['value'] = mgr.get_latest(
                     self.service_type, service_id, key)
                 counter['unit'] = ''
             counters.append(counter)
index 2b32a9a69aa1d2734e9468dd7617126dcd01ba47..098e4b0d7c49e88f81ba7978f1242088589889d3 100644 (file)
@@ -11,21 +11,6 @@ SERVICE_TYPE = 'tcmu-runner'
 @ApiController('tcmuiscsi')
 @AuthRequired()
 class TcmuIscsi(RESTController):
-    def _get_rate(self, daemon_type, daemon_name, stat):
-        data = mgr.get_counter(daemon_type, daemon_name, stat)[stat]
-
-        if data and len(data) > 1:
-            last_value = data[0][1]
-            last_time = data[0][0]
-            rates = []
-            for datum in data[1:]:
-                rates.append([datum[0], ((datum[1] - last_value) /
-                                         float(datum[0] - last_time))])
-                last_value = datum[1]
-                last_time = datum[0]
-            return rates
-        return [[0, 0]]
-
     # pylint: disable=too-many-locals,too-many-nested-blocks
     def list(self):  # pylint: disable=unused-argument
         daemons = {}
@@ -78,9 +63,9 @@ class TcmuIscsi(RESTController):
                     image['stats_history'] = {}
                     for s in ['rd', 'wr', 'rd_bytes', 'wr_bytes']:
                         perf_key = "{}{}".format(perf_key_prefix, s)
-                        image['stats'][s] = self._get_rate(
-                            'tcmu-runner', service_id, perf_key)[-1][1]
-                        image['stats_history'][s] = self._get_rate(
+                        image['stats'][s] = CephService.get_rate(
+                            'tcmu-runner', service_id, perf_key)
+                        image['stats_history'][s] = CephService.get_rates(
                             'tcmu-runner', service_id, perf_key)
             else:
                 daemon['non_optimized_paths'] += 1
index 9f113cf907d57b6044b375c091f84e93f21b463f..7c1d9928a30732ad03ffb0cb081456226e426c6f 100644 (file)
@@ -7,6 +7,16 @@ from collections import defaultdict
 import json
 
 from mgr_module import CommandResult
+
+try:
+    from more_itertools import pairwise
+except ImportError:
+    def pairwise(iterable):
+        from itertools import tee
+        a, b = tee(iterable)
+        next(b, None)
+        return zip(a, b)
+
 from .. import logger, mgr
 
 
@@ -90,8 +100,7 @@ class CephService(object):
 
             def get_rate(series):
                 if len(series) >= 2:
-                    return (float(series[0][1]) - float(series[1][1])) / \
-                        (float(series[0][0]) - float(series[1][0]))
+                    return differentiate(*series[0:1])
                 return 0
 
             for stat_name, stat_series in stats.items():
@@ -142,3 +151,34 @@ class CephService(object):
                 return json.loads(outb)
             except Exception:  # pylint: disable=broad-except
                 return outb
+
+    @classmethod
+    def get_rates(cls, svc_type, svc_name, path):
+        """
+        :return: the derivative of mgr.get_counter()
+        :rtype: list[tuple[int, float]]"""
+        data = mgr.get_counter(svc_type, svc_name, path)[path]
+        if not data:
+            return [(0, 0)]
+        elif len(data) == 1:
+            return [(data[0][0], 0)]
+        return [(data2[0], differentiate(data1, data2)) for data1, data2 in pairwise(data)]
+
+    @classmethod
+    def get_rate(cls, svc_type, svc_name, path):
+        """returns most recent rate"""
+        data = mgr.get_counter(svc_type, svc_name, path)[path]
+
+        if data and len(data) > 1:
+            return differentiate(*data[-2:])
+        return 0.0
+
+
+def differentiate(data1, data2):
+    """
+    >>> times = [0, 2]
+    >>> values = [100, 101]
+    >>> differentiate(*zip(times, values))
+    0.5
+    """
+    return (data2[1] - data1[1]) / float(data2[0] - data1[0])
index 743a8a6929e5b8c457f72e1943ea9be1f1652893..b9170256efe3506acc0a96d7f41d5ccac1bbfe7d 100644 (file)
@@ -12,7 +12,7 @@ setenv=
     LD_LIBRARY_PATH = {toxinidir}/../../../../build/lib
     PATH = {toxinidir}/../../../../build/bin:$PATH
 commands=
-    {envbindir}/py.test --cov=. --cov-report= --junitxml=junit.{envname}.xml --doctest-modules controllers/rbd.py tests/
+    {envbindir}/py.test --cov=. --cov-report= --junitxml=junit.{envname}.xml --doctest-modules controllers/rbd.py services/ tests/
 
 [testenv:cov-init]
 setenv =
index ce68b459d823ec0e4bad49aa647579400d783904..480fc8586d0f73ac469a83178240b12930ee7d5d 100644 (file)
@@ -578,6 +578,15 @@ class MgrModule(ceph_module.BaseMgrModule):
         """
         return self._ceph_get_osdmap()
 
+    # TODO: improve C++->Python interface to return just
+    # the latest if that's all we want.
+    def get_latest(self, daemon_type, daemon_name, counter):
+        data = self.get_counter(daemon_type, daemon_name, counter)[counter]
+        if data:
+            return data[-1][1]
+        else:
+            return 0
+
     def get_all_perf_counters(self, prio_limit=PRIO_USEFUL):
         """
         Return the perf counters currently known to this ceph-mgr
@@ -593,14 +602,6 @@ class MgrModule(ceph_module.BaseMgrModule):
 
         result = defaultdict(dict)
 
-        # TODO: improve C++->Python interface to return just
-        # the latest if that's all we want.
-        def get_latest(daemon_type, daemon_name, counter):
-            data = self.get_counter(daemon_type, daemon_name, counter)[counter]
-            if data:
-                return data[-1][1]
-            else:
-                return 0
 
         for server in self.list_servers():
             for service in server['services']:
@@ -628,7 +629,8 @@ class MgrModule(ceph_module.BaseMgrModule):
                         continue
 
                     counter_info = counter_schema
-                    counter_info['value'] = get_latest(service['type'], service['id'], counter_path)
+                    counter_info['value'] = self.get_latest(service['type'], service['id'],
+                                                            counter_path)
                     result[svc_full_name][counter_path] = counter_info
 
         self.log.debug("returning {0} counter".format(len(result)))