]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/iostat: print output as a table 21338/head
authorMohamad Gebai <mgebai@suse.com>
Tue, 10 Apr 2018 20:37:39 +0000 (16:37 -0400)
committerMohamad Gebai <mgebai@suse.com>
Tue, 24 Apr 2018 14:59:40 +0000 (10:59 -0400)
Signed-off-by: Mohamad Gebai <mgebai@suse.com>
src/ceph.in
src/pybind/mgr/iostat/module.py
src/pybind/mgr/mgr_module.py

index b4a75b09ff9d9a5f9fe492435ec910d32c1a0c70..82ee08e39381f44fb15fd56f00aecb48a67ba6cb 100755 (executable)
@@ -533,16 +533,22 @@ def do_command(parsed_args, target, cmdargs, sigdict, inbuf, verbose):
     else:
         return -errno.EINVAL, '', 'invalid command'
 
+    next_header_print = 0
+    # Set extra options for polling commands only:
+    if valid_dict.get('poll', False):
+        valid_dict['width'] = Termsize().cols
     while True:
         try:
-            if next_header_print == 0:
+            # Only print the header for polling commands
+            if next_header_print == 0 and valid_dict.get('poll', False):
                 valid_dict['print_header'] = True
                 next_header_print = Termsize().rows - 3
             next_header_print -= 1
             ret, outbuf, outs = json_command(cluster_handle, target=target,
                 argdict=valid_dict, inbuf=inbuf)
-            valid_dict['print_header'] = False
-            if 'poll' not in valid_dict or not valid_dict['poll']:
+            if valid_dict.get('poll', False):
+                valid_dict['print_header'] = False
+            if not valid_dict.get('poll', False):
                 # Don't print here if it's not a polling command
                 break
             if ret:
index e42925c8bf8c0ef57428a5dc05571c534cb049d3..e9f7c3eb92b95ee4692e034c7b4f8d07129918a3 100644 (file)
@@ -25,7 +25,10 @@ class Module(MgrModule):
     def handle_command(self, command):
         rd = 0
         wr = 0
-        ops = 0
+        total = 0
+        rd_ops = 0
+        wr_ops = 0
+        total_ops = 0
         ret = ''
 
         if command['prefix'] == 'iostat':
@@ -35,9 +38,33 @@ class Module(MgrModule):
             if (stamp_delta > 0):
                 rd = int(r['pg_stats_delta']['stat_sum']['num_read_kb']) / stamp_delta
                 wr = int(r['pg_stats_delta']['stat_sum']['num_write_kb']) / stamp_delta
-                ops = ( int(r['pg_stats_delta']['stat_sum']['num_write']) + int(r['pg_stats_delta']['stat_sum']['num_read']) ) / stamp_delta
+                # The values are in kB, but to_pretty_iec() requires them to be in bytes
+                rd = int(rd) << 10
+                wr = int(wr) << 10
+                total = rd + wr
 
-            ret = "wr: {0} kB/s, rd: {1} kB/s, iops: {2}".format(int(wr), int(rd), int(ops))
+                rd_ops = int(r['pg_stats_delta']['stat_sum']['num_read']) / stamp_delta
+                wr_ops = int(r['pg_stats_delta']['stat_sum']['num_write']) / stamp_delta
+                total_ops = rd_ops + wr_ops
+
+            if 'width' in command:
+                width = command['width']
+            else:
+                width = 80
+
+            if command.get('print_header', False):
+                elems = ['Read', 'Write', 'Total', 'Read IOPS', 'Write IOPS', 'Total IOPS']
+                ret += self.get_pretty_header(elems, width)
+
+            elems = [
+                self.to_pretty_iec(rd) + 'B/s',
+                self.to_pretty_iec(wr) + 'B/s',
+                self.to_pretty_iec(total) + 'B/s',
+                int(rd_ops),
+                int(wr_ops),
+                int(total_ops)
+            ]
+            ret += self.get_pretty_row(elems, width)
 
         elif command['prefix'] == 'iostat self-test':
             r = self.get('io_rate')
index b51952ebd18e08b5220284e7a44020c5176476dc..84e7826a3a24cd91c8fee342f853edd3f8b8756e 100644 (file)
@@ -342,7 +342,59 @@ class MgrModule(ceph_module.BaseMgrModule):
             return "/s"
         elif unit == self.BYTES:
             return "B/s"  
-    
+
+    def to_pretty_iec(self, n):
+        for bits, suffix in [(60, 'Ei'), (50, 'Pi'), (40, 'Ti'), (30, 'Gi'),
+                (20, 'Mi'), (10, 'Ki')]:
+            if n > 10 << bits:
+                return str(n >> bits) + ' ' + suffix
+        return str(n) + ' '
+
+    def get_pretty_row(self, elems, width):
+        """
+        Takes an array of elements and returns a string with those elements
+        formatted as a table row. Useful for polling modules.
+
+        :param elems: the elements to be printed
+        :param width: the width of the terminal
+        """
+        n = len(elems)
+        column_width = width / n
+
+        ret = '|'
+        for elem in elems:
+            ret += '{0:>{w}} |'.format(elem, w=column_width - 2)
+
+        return ret
+
+    def get_pretty_header(self, elems, width):
+        """
+        Like ``get_pretty_row`` but adds dashes, to be used as a table title.
+
+        :param elems: the elements to be printed
+        :param width: the width of the terminal
+        """
+        n = len(elems)
+        column_width = width / n
+
+        # dash line
+        ret = '+'
+        for i in range(0, n):
+            ret += '-' * (column_width - 1) + '+'
+        ret += '\n'
+
+        # title
+        ret += self.get_pretty_row(elems, width)
+        ret += '\n'
+
+        # dash line
+        ret += '+'
+        for i in range(0, n):
+            ret += '-' * (column_width - 1) + '+'
+        ret += '\n'
+
+        return ret
+
     def get_server(self, hostname):
         """
         Called by the plugin to fetch metadata about a particular hostname from