]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cephfs-top: add read/write io speed support
authorXiubo Li <xiubli@redhat.com>
Mon, 19 Apr 2021 11:26:09 +0000 (19:26 +0800)
committerXiubo Li <xiubli@redhat.com>
Tue, 19 Oct 2021 01:30:58 +0000 (09:30 +0800)
Calculate the speeds in cephfs-top.

Fixes: https://tracker.ceph.com/issues/49811
Signed-off-by: Xiubo Li <xiubli@redhat.com>
doc/man/8/cephfs-top.rst
src/tools/cephfs/top/cephfs-top

index 3a01bb7cdc9e531615335a05c453da4645bc4b1e..936e02e201a27c39bf17bbe27e85a96529024177 100644 (file)
@@ -79,6 +79,14 @@ Descriptions of fields
 
    total size of write IOs
 
+.. describe:: rsp
+
+   speed of read IOs compared with the last refresh
+
+.. describe:: wsp
+
+   speed of write IOs compared with the last refresh
+
 
 Availability
 ============
index 43019af706a4f4fb18e435f47d661d0304853f61..73bb411b7bed586771d75979a6aa6dcb3cd216d3 100755 (executable)
@@ -6,6 +6,7 @@ import curses
 import errno
 import json
 import signal
+import time
 
 from collections import OrderedDict
 from datetime import datetime
@@ -82,6 +83,10 @@ CLIENT_METADATA_VALID_METRICS_KEY = "valid_metrics"
 GLOBAL_METRICS_KEY = "global_metrics"
 GLOBAL_COUNTERS_KEY = "global_counters"
 
+last_time = time.time()
+last_read_size = {}
+last_write_size = {}
+
 
 def calc_perc(c):
     if c[0] == 0 and c[1] == 0:
@@ -98,6 +103,13 @@ def calc_size(c):
     return round(c[1] / (1024 * 1024), 2)
 
 
+# in MB/s
+def calc_speed(size, duration):
+    if duration == 0:
+        return 0.0
+    return round(size / (duration * 1024 * 1024), 2)
+
+
 def wrap(s, sl):
     """return a '+' suffixed wrapped string"""
     if len(s) < sl:
@@ -224,6 +236,22 @@ class FSTop(object):
             # return empty string for none type
             return ''
 
+    def speed_items(self, item):
+        if item == "READ_IO_SIZES":
+            return "rsp"
+        if item == "WRITE_IO_SIZES":
+            return "wsp"
+        else:
+            # return empty string for none type
+            return ''
+
+    def speed_mtype(self, typ):
+        if typ == MetricType.METRIC_TYPE_SIZE:
+            return "(MB/s)"
+        else:
+            # return empty string for none type
+            return ''
+
     def refresh_top_line_and_build_coord(self):
         if self.topl is None:
             return
@@ -245,6 +273,16 @@ class FSTop(object):
             x_coord_map[item] = (xp, nlen)
             xp += nlen
 
+            if item == "READ_IO_SIZES" or item == "WRITE_IO_SIZES":
+                it = f'{self.speed_items(item)}{self.speed_mtype(typ)}'
+                heading.append(it)
+                nlen = len(it) + len(ITEMS_PAD)
+                if item == "READ_IO_SIZES":
+                    x_coord_map["READ_IO_SPEED"] = (xp, nlen)
+                if item == "WRITE_IO_SIZES":
+                    x_coord_map["WRITE_IO_SPEED"] = (xp, nlen)
+                xp += nlen
+
         for item in MAIN_WINDOW_TOP_LINE_ITEMS_END:
             heading.append(item)
             nlen = len(item) + len(ITEMS_PAD)
@@ -268,6 +306,10 @@ class FSTop(object):
         return True
 
     def refresh_client(self, client_id, metrics, counters, client_meta, x_coord_map, y_coord):
+        global last_time
+        cur_time = time.time()
+        duration = cur_time - last_time
+        last_time = cur_time
         remaining_hlen = self.width - 1
         for item in MAIN_WINDOW_TOP_LINE_ITEMS_START:
             coord = x_coord_map[item]
@@ -293,6 +335,7 @@ class FSTop(object):
                 return
 
         cidx = 0
+        client_id = x_coord_map[FS_TOP_MAIN_WINDOW_COL_CLIENT_ID]
         for item in counters:
             coord = x_coord_map[item]
             hlen = coord[1] - len(ITEMS_PAD)
@@ -302,7 +345,8 @@ class FSTop(object):
             else:
                 remaining_hlen -= coord[1]
             m = metrics[cidx]
-            typ = MAIN_WINDOW_TOP_LINE_METRICS[MGR_STATS_COUNTERS[cidx]]
+            key = MGR_STATS_COUNTERS[cidx]
+            typ = MAIN_WINDOW_TOP_LINE_METRICS[key]
             if item.lower() in client_meta.get(CLIENT_METADATA_VALID_METRICS_KEY, []):
                 if typ == MetricType.METRIC_TYPE_PERCENTAGE:
                     self.mainw.addnstr(y_coord, coord[0], f'{calc_perc(m)}', hlen)
@@ -310,6 +354,31 @@ class FSTop(object):
                     self.mainw.addnstr(y_coord, coord[0], f'{calc_lat(m)}', hlen)
                 elif typ == MetricType.METRIC_TYPE_SIZE:
                     self.mainw.addnstr(y_coord, coord[0], f'{calc_size(m)}', hlen)
+                    if remaining_hlen == 0:
+                        return
+                    if key == "READ_IO_SIZES":
+                        coord = x_coord_map["READ_IO_SPEED"]
+                    elif key == "WRITE_IO_SIZES":
+                        coord = x_coord_map["WRITE_IO_SPEED"]
+                    hlen = coord[1] - len(ITEMS_PAD)
+                    hlen = min(hlen, remaining_hlen)
+                    if remaining_hlen < coord[1]:
+                        remaining_hlen = 0
+                    else:
+                        remaining_hlen -= coord[1]
+                    if key == "READ_IO_SIZES":
+                        global last_read_size
+                        last_size = last_read_size.get(client_id, 0)
+                        size = m[1] - last_size
+                        last_read_size[client_id] = m[1]
+                    if key == "WRITE_IO_SIZES":
+                        global last_write_size
+                        last_size = last_write_size.get(client_id, 0)
+                        size = m[1] - last_size
+                        last_write_size[client_id] = m[1]
+                    self.mainw.addnstr(y_coord, coord[0],
+                                       f'{calc_speed(size, duration)}',
+                                       hlen)
                 else:
                     # display 0th element from metric tuple
                     self.mainw.addnstr(y_coord, coord[0], f'{m[0]}', hlen)