diff options
author | Dan Mick <dan.mick@redhat.com> | 2017-04-19 00:24:22 +0200 |
---|---|---|
committer | Dan Mick <dan.mick@redhat.com> | 2017-04-26 01:19:02 +0200 |
commit | 814aae9c134d4f2da9bb9372e9bf42170c606991 (patch) | |
tree | 32865358e7fb7ccdc008e501ec93a4b1dd0d41a4 /src/pybind | |
parent | ceph.in: Move daemonperf to its own function (diff) | |
download | ceph-814aae9c134d4f2da9bb9372e9bf42170c606991.tar.xz ceph-814aae9c134d4f2da9bb9372e9bf42170c606991.zip |
pybind/ceph_daemon.py: DaemonWatcher: fit display to window size
Keep track of terminal width/height, and output as many
candidate statistics as will fit in the window.
Signed-off-by: Dan Mick <dan.mick@redhat.com>
Diffstat (limited to 'src/pybind')
-rwxr-xr-x | src/pybind/ceph_daemon.py | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/src/pybind/ceph_daemon.py b/src/pybind/ceph_daemon.py index c3293d66f57..c039f064700 100755 --- a/src/pybind/ceph_daemon.py +++ b/src/pybind/ceph_daemon.py @@ -16,6 +16,9 @@ import socket import struct import time from collections import defaultdict, OrderedDict +from fcntl import ioctl +from signal import signal, SIGWINCH +from termios import TIOCGWINSZ from ceph_argparse import parse_json_funcsigs, validate_command @@ -82,6 +85,32 @@ def admin_socket(asok_path, cmd, format=''): return ret +def _gettermsize(): + return struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, 8*'\x00'))[0:2] + +class Termsize(object): + + def __init__(self): + self.rows, self.cols = _gettermsize() + self.changed = False + + def update(self): + rows, cols = _gettermsize() + self.changed = self.changed or ( + (self.rows != rows) or (self.cols != cols) + ) + self.rows, self.cols = rows, cols + + def reset_changed(self): + self.changed = False + + def __str__(self): + return '%s(%dx%d, changed %s)' % (self.__class__, self.rows, self.cols, self.changed) + + def __repr__(self): + return 'Termsize(%d,%d,%s)' % (self.__class__, self.rows, self.cols, self.changed) + + class DaemonWatcher(object): """ Given a Ceph daemon's admin socket path, poll its performance counters @@ -111,6 +140,8 @@ class DaemonWatcher(object): self._stats = None self._schema = None + self._stats_that_fit = dict() + self.termsize = Termsize() def supports_color(self, ostr): """ @@ -173,13 +204,36 @@ class DaemonWatcher(object): """ return max(len(nick), 4) + def get_stats_that_fit(self): + ''' + Truncated list of stats to display based on current terminal width + ''' + current_fit = {} + if self.termsize.changed or not self._stats_that_fit: + width = 0 + for section_name, names in self._stats.items(): + section_width = \ + sum([self.col_width(x) + 1 for x in names.values()]) - 1 + if width + section_width > self.termsize.cols: + break + width += section_width + current_fit[section_name] = names + + self.termsize.reset_changed() + changed = current_fit and (current_fit != self._stats_that_fit) + if changed: + self._stats_that_fit = current_fit + return self._stats_that_fit, changed + def _print_headers(self, ostr): """ Print a header row to `ostr` """ header = "" - for section_name, names in self._stats.items(): - section_width = sum([self.col_width(x)+1 for x in names.values()]) - 1 + stats, _ = self.get_stats_that_fit() + for section_name, names in stats.items(): + section_width = \ + sum([self.col_width(x) + 1 for x in names.values()]) - 1 pad = max(section_width - len(section_name), 0) pad_prefix = pad // 2 header += (pad_prefix * '-') @@ -190,7 +244,7 @@ class DaemonWatcher(object): ostr.write(self.colorize(header, self.BLUE, True)) sub_header = "" - for section_name, names in self._stats.items(): + for section_name, names in stats.items(): for stat_name, stat_nick in names.items(): sub_header += self.UNDERLINE_SEQ \ + self.colorize( @@ -207,7 +261,10 @@ class DaemonWatcher(object): `last_dump`. """ val_row = "" - for section_name, names in self._stats.items(): + fit, changed = self.get_stats_that_fit() + if changed: + self._print_headers(ostr) + for section_name, names in fit.items(): for stat_name, stat_nick in names.items(): stat_type = self._schema[section_name][stat_name]['type'] if bool(stat_type & COUNTER): @@ -252,6 +309,9 @@ class DaemonWatcher(object): self._stats[section_name] = OrderedDict() self._stats[section_name][name] = schema_data['nick'] + def _handle_sigwinch(self, signo, frame): + self.termsize.update() + def run(self, interval, count=None, ostr=sys.stdout): """ Print output at regular intervals until interrupted. @@ -266,12 +326,12 @@ class DaemonWatcher(object): last_dump = json.loads(admin_socket(self.asok_path, ["perf", "dump"]).decode('utf-8')) rows_since_header = 0 - term_height = 25 try: + signal(SIGWINCH, self._handle_sigwinch) while True: dump = json.loads(admin_socket(self.asok_path, ["perf", "dump"]).decode('utf-8')) - if rows_since_header > term_height - 2: + if rows_since_header > self.termsize.rows - 2: self._print_headers(ostr) rows_since_header = 0 self._print_vals(ostr, dump, last_dump) |