From: John Spray Date: Sun, 25 Jun 2017 21:48:26 +0000 (-0400) Subject: pybind/mgr: rename fsstatus->status X-Git-Tag: ses5-milestone8~1^2~17^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=80e1a3c1286b8b6cebc50072b3006a48a14d1b37;p=ceph.git pybind/mgr: rename fsstatus->status Still not exactly the best name but reflects that it isn't just the fs bit. Signed-off-by: John Spray --- diff --git a/src/pybind/mgr/fsstatus/__init__.py b/src/pybind/mgr/fsstatus/__init__.py deleted file mode 100644 index 79f5b86fd504..000000000000 --- a/src/pybind/mgr/fsstatus/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ - -from module import * # NOQA diff --git a/src/pybind/mgr/fsstatus/module.py b/src/pybind/mgr/fsstatus/module.py deleted file mode 100644 index 22f1902404c6..000000000000 --- a/src/pybind/mgr/fsstatus/module.py +++ /dev/null @@ -1,297 +0,0 @@ - -""" -High level status display commands -""" - -from collections import defaultdict -from prettytable import PrettyTable -import prettytable -import fnmatch -import errno - -from mgr_module import MgrModule - - -class Module(MgrModule): - COMMANDS = [ - { - "cmd": "fs status " - "name=fs,type=CephString,req=false", - "desc": "Show the status of a CephFS filesystem", - "perm": "r" - }, - { - "cmd": "osd status " - "name=bucket,type=CephString,req=false", - "desc": "Show the status of OSDs within a bucket, or all", - "perm": "r" - }, - ] - - ( - BLACK, - RED, - GREEN, - YELLOW, - BLUE, - MAGENTA, - CYAN, - GRAY - ) = range(8) - - RESET_SEQ = "\033[0m" - COLOR_SEQ = "\033[1;%dm" - COLOR_DARK_SEQ = "\033[0;%dm" - BOLD_SEQ = "\033[1m" - UNDERLINE_SEQ = "\033[4m" - - def colorize(self, msg, color, dark=False): - """ - Decorate `msg` with escape sequences to give the requested color - """ - return (self.COLOR_DARK_SEQ if dark else self.COLOR_SEQ) % (30 + color) \ - + msg + self.RESET_SEQ - - def bold(self, msg): - """ - Decorate `msg` with escape sequences to make it appear bold - """ - return self.BOLD_SEQ + msg + self.RESET_SEQ - - def format_dimless(self, n, width, colored=True): - """ - Format a number without units, so as to fit into `width` characters, substituting - an appropriate unit suffix. - """ - units = [' ', 'k', 'M', 'G', 'T', 'P'] - unit = 0 - while len("%s" % (int(n) // (1000**unit))) > width - 1: - unit += 1 - - if unit > 0: - truncated_float = ("%f" % (n / (1000.0 ** unit)))[0:width - 1] - if truncated_float[-1] == '.': - truncated_float = " " + truncated_float[0:-1] - else: - truncated_float = "%{wid}d".format(wid=width-1) % n - formatted = "%s%s" % (truncated_float, units[unit]) - - if colored: - if n == 0: - color = self.BLACK, False - else: - color = self.YELLOW, False - return self.bold(self.colorize(formatted[0:-1], color[0], color[1])) \ - + self.bold(self.colorize(formatted[-1], self.BLACK, False)) - else: - return formatted - - def get_latest(self, daemon_type, daemon_name, stat): - data = self.get_counter(daemon_type, daemon_name, stat)[stat] - #self.log.error("get_latest {0} data={1}".format(stat, data)) - if data: - return data[-1][1] - else: - return 0 - - def get_rate(self, daemon_type, daemon_name, stat): - data = self.get_counter(daemon_type, daemon_name, stat)[stat] - - #self.log.error("get_latest {0} data={1}".format(stat, data)) - if data and len(data) > 1: - return (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0]) - else: - return 0 - - def handle_fs_status(self, cmd): - output = "" - - fs_filter = cmd.get('fs', None) - - mds_versions = defaultdict(list) - - fsmap = self.get("fs_map") - for filesystem in fsmap['filesystems']: - if fs_filter and filesystem['mdsmap']['fs_name'] != fs_filter: - continue - - rank_table = PrettyTable( - ("Rank", "State", "MDS", "Activity", "dns", "inos"), - hrules=prettytable.FRAME - ) - - mdsmap = filesystem['mdsmap'] - - client_count = 0 - - for rank in mdsmap["in"]: - up = "mds_{0}".format(rank) in mdsmap["up"] - if up: - gid = mdsmap['up']["mds_{0}".format(rank)] - info = mdsmap['info']['gid_{0}'.format(gid)] - dns = self.get_latest("mds", info['name'], "mds_mem.dn") - inos = self.get_latest("mds", info['name'], "mds_mem.ino") - - if rank == 0: - client_count = self.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") - - laggy = "laggy_since" in info - - state = info['state'].split(":")[1] - 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 = "Reqs: " + self.format_dimless( - self.get_rate("mds", info['name'], "mds_server.handle_client_request"), - 5 - ) + "/s" - - metadata = self.get_metadata('mds', info['name']) - mds_versions[metadata.get('ceph_version', "unknown")].append(info['name']) - rank_table.add_row([ - self.bold(rank.__str__()), c_state, info['name'], - activity, - self.format_dimless(dns, 5), - self.format_dimless(inos, 5) - ]) - - else: - rank_table.add_row([ - rank, "failed", "", "", "" - ]) - - # Find the standby replays - for gid_str, daemon_info in mdsmap['info'].iteritems(): - 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_mem.dn") - - activity = "Evts: " + self.format_dimless( - self.get_rate("mds", daemon_info['name'], "mds_log.replay"), - 5 - ) + "/s" - - rank_table.add_row([ - "{0}-s".format(daemon_info['rank']), "standby-replay", - daemon_info['name'], activity, - self.format_dimless(dns, 5), - self.format_dimless(inos, 5) - ]) - - df = self.get("df") - pool_stats = dict([(p['id'], p['stats']) for p in df['pools']]) - osdmap = self.get("osd_map") - pools = dict([(p['pool'], p) for p in osdmap['pools']]) - metadata_pool_id = mdsmap['metadata_pool'] - data_pool_ids = mdsmap['data_pools'] - - pools_table = PrettyTable(["Pool", "type", "used", "avail"]) - for pool_id in [metadata_pool_id] + data_pool_ids: - pool_type = "metadata" if pool_id == metadata_pool_id else "data" - stats = pool_stats[pool_id] - pools_table.add_row([ - pools[pool_id]['pool_name'], pool_type, - self.format_dimless(stats['bytes_used'], 5), - self.format_dimless(stats['max_avail'], 5) - ]) - - output += "{0} - {1} clients\n".format( - mdsmap['fs_name'], client_count) - output += "=" * len(mdsmap['fs_name']) + "\n" - output += rank_table.get_string() - output += "\n" + pools_table.get_string() + "\n" - - standby_table = PrettyTable(["Standby MDS"]) - for standby in fsmap['standbys']: - metadata = self.get_metadata('mds', standby['name']) - mds_versions[metadata.get('ceph_version', "unknown")].append(standby['name']) - - standby_table.add_row([standby['name']]) - - output += "\n" + standby_table.get_string() + "\n" - - if len(mds_versions) == 1: - output += "MDS version: {0}".format(mds_versions.keys()[0]) - else: - version_table = PrettyTable(["version", "daemons"]) - for version, daemons in mds_versions.iteritems(): - version_table.add_row([ - version, - ", ".join(daemons) - ]) - output += version_table.get_string() + "\n" - - return 0, "", output - - def handle_osd_status(self, cmd): - osd_table = PrettyTable(['id', 'host', 'used', 'avail', 'wr ops', 'wr data', 'rd ops', 'rd data']) - osdmap = self.get("osd_map") - - filter_osds = set() - bucket_filter = None - if 'bucket' in cmd: - self.log.debug("Filtering to bucket '{0}'".format(cmd['bucket'])) - bucket_filter = cmd['bucket'] - crush = self.get("osd_map_crush") - found = False - for bucket in crush['buckets']: - if fnmatch.fnmatch(bucket['name'], bucket_filter): - found = True - filter_osds.update([i['id'] for i in bucket['items']]) - - if not found: - msg = "Bucket '{0}' not found".format(bucket_filter) - return errno.ENOENT, msg, "" - - # Build dict of OSD ID to stats - osd_stats = dict([(o['osd'], o) for o in self.get("osd_stats")['osd_stats']]) - - for osd in osdmap['osds']: - osd_id = osd['osd'] - if bucket_filter and osd_id not in filter_osds: - continue - - metadata = self.get_metadata('osd', "%s" % osd_id) - stats = osd_stats[osd_id] - - osd_table.add_row([osd_id, metadata['hostname'], - self.format_dimless(stats['kb_used'] * 1024, 5), - self.format_dimless(stats['kb_avail'] * 1024, 5), - self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_w") + - self.get_rate("osd", osd_id.__str__(), "osd.op_rw"), 5), - self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_in_bytes"), 5), - self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_r"), 5), - self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_out_bytes"), 5), - ]) - - return 0, "", osd_table.get_string() - - def handle_command(self, cmd): - self.log.error("handle_command") - - if cmd['prefix'] == "fs status": - return self.handle_fs_status(cmd) - elif cmd['prefix'] == "osd status": - return self.handle_osd_status(cmd) - else: - # mgr should respect our self.COMMANDS and not call us for - # any prefix we don't advertise - raise NotImplementedError(cmd['prefix']) diff --git a/src/pybind/mgr/status/__init__.py b/src/pybind/mgr/status/__init__.py new file mode 100644 index 000000000000..79f5b86fd504 --- /dev/null +++ b/src/pybind/mgr/status/__init__.py @@ -0,0 +1,2 @@ + +from module import * # NOQA diff --git a/src/pybind/mgr/status/module.py b/src/pybind/mgr/status/module.py new file mode 100644 index 000000000000..22f1902404c6 --- /dev/null +++ b/src/pybind/mgr/status/module.py @@ -0,0 +1,297 @@ + +""" +High level status display commands +""" + +from collections import defaultdict +from prettytable import PrettyTable +import prettytable +import fnmatch +import errno + +from mgr_module import MgrModule + + +class Module(MgrModule): + COMMANDS = [ + { + "cmd": "fs status " + "name=fs,type=CephString,req=false", + "desc": "Show the status of a CephFS filesystem", + "perm": "r" + }, + { + "cmd": "osd status " + "name=bucket,type=CephString,req=false", + "desc": "Show the status of OSDs within a bucket, or all", + "perm": "r" + }, + ] + + ( + BLACK, + RED, + GREEN, + YELLOW, + BLUE, + MAGENTA, + CYAN, + GRAY + ) = range(8) + + RESET_SEQ = "\033[0m" + COLOR_SEQ = "\033[1;%dm" + COLOR_DARK_SEQ = "\033[0;%dm" + BOLD_SEQ = "\033[1m" + UNDERLINE_SEQ = "\033[4m" + + def colorize(self, msg, color, dark=False): + """ + Decorate `msg` with escape sequences to give the requested color + """ + return (self.COLOR_DARK_SEQ if dark else self.COLOR_SEQ) % (30 + color) \ + + msg + self.RESET_SEQ + + def bold(self, msg): + """ + Decorate `msg` with escape sequences to make it appear bold + """ + return self.BOLD_SEQ + msg + self.RESET_SEQ + + def format_dimless(self, n, width, colored=True): + """ + Format a number without units, so as to fit into `width` characters, substituting + an appropriate unit suffix. + """ + units = [' ', 'k', 'M', 'G', 'T', 'P'] + unit = 0 + while len("%s" % (int(n) // (1000**unit))) > width - 1: + unit += 1 + + if unit > 0: + truncated_float = ("%f" % (n / (1000.0 ** unit)))[0:width - 1] + if truncated_float[-1] == '.': + truncated_float = " " + truncated_float[0:-1] + else: + truncated_float = "%{wid}d".format(wid=width-1) % n + formatted = "%s%s" % (truncated_float, units[unit]) + + if colored: + if n == 0: + color = self.BLACK, False + else: + color = self.YELLOW, False + return self.bold(self.colorize(formatted[0:-1], color[0], color[1])) \ + + self.bold(self.colorize(formatted[-1], self.BLACK, False)) + else: + return formatted + + def get_latest(self, daemon_type, daemon_name, stat): + data = self.get_counter(daemon_type, daemon_name, stat)[stat] + #self.log.error("get_latest {0} data={1}".format(stat, data)) + if data: + return data[-1][1] + else: + return 0 + + def get_rate(self, daemon_type, daemon_name, stat): + data = self.get_counter(daemon_type, daemon_name, stat)[stat] + + #self.log.error("get_latest {0} data={1}".format(stat, data)) + if data and len(data) > 1: + return (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0]) + else: + return 0 + + def handle_fs_status(self, cmd): + output = "" + + fs_filter = cmd.get('fs', None) + + mds_versions = defaultdict(list) + + fsmap = self.get("fs_map") + for filesystem in fsmap['filesystems']: + if fs_filter and filesystem['mdsmap']['fs_name'] != fs_filter: + continue + + rank_table = PrettyTable( + ("Rank", "State", "MDS", "Activity", "dns", "inos"), + hrules=prettytable.FRAME + ) + + mdsmap = filesystem['mdsmap'] + + client_count = 0 + + for rank in mdsmap["in"]: + up = "mds_{0}".format(rank) in mdsmap["up"] + if up: + gid = mdsmap['up']["mds_{0}".format(rank)] + info = mdsmap['info']['gid_{0}'.format(gid)] + dns = self.get_latest("mds", info['name'], "mds_mem.dn") + inos = self.get_latest("mds", info['name'], "mds_mem.ino") + + if rank == 0: + client_count = self.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") + + laggy = "laggy_since" in info + + state = info['state'].split(":")[1] + 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 = "Reqs: " + self.format_dimless( + self.get_rate("mds", info['name'], "mds_server.handle_client_request"), + 5 + ) + "/s" + + metadata = self.get_metadata('mds', info['name']) + mds_versions[metadata.get('ceph_version', "unknown")].append(info['name']) + rank_table.add_row([ + self.bold(rank.__str__()), c_state, info['name'], + activity, + self.format_dimless(dns, 5), + self.format_dimless(inos, 5) + ]) + + else: + rank_table.add_row([ + rank, "failed", "", "", "" + ]) + + # Find the standby replays + for gid_str, daemon_info in mdsmap['info'].iteritems(): + 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_mem.dn") + + activity = "Evts: " + self.format_dimless( + self.get_rate("mds", daemon_info['name'], "mds_log.replay"), + 5 + ) + "/s" + + rank_table.add_row([ + "{0}-s".format(daemon_info['rank']), "standby-replay", + daemon_info['name'], activity, + self.format_dimless(dns, 5), + self.format_dimless(inos, 5) + ]) + + df = self.get("df") + pool_stats = dict([(p['id'], p['stats']) for p in df['pools']]) + osdmap = self.get("osd_map") + pools = dict([(p['pool'], p) for p in osdmap['pools']]) + metadata_pool_id = mdsmap['metadata_pool'] + data_pool_ids = mdsmap['data_pools'] + + pools_table = PrettyTable(["Pool", "type", "used", "avail"]) + for pool_id in [metadata_pool_id] + data_pool_ids: + pool_type = "metadata" if pool_id == metadata_pool_id else "data" + stats = pool_stats[pool_id] + pools_table.add_row([ + pools[pool_id]['pool_name'], pool_type, + self.format_dimless(stats['bytes_used'], 5), + self.format_dimless(stats['max_avail'], 5) + ]) + + output += "{0} - {1} clients\n".format( + mdsmap['fs_name'], client_count) + output += "=" * len(mdsmap['fs_name']) + "\n" + output += rank_table.get_string() + output += "\n" + pools_table.get_string() + "\n" + + standby_table = PrettyTable(["Standby MDS"]) + for standby in fsmap['standbys']: + metadata = self.get_metadata('mds', standby['name']) + mds_versions[metadata.get('ceph_version', "unknown")].append(standby['name']) + + standby_table.add_row([standby['name']]) + + output += "\n" + standby_table.get_string() + "\n" + + if len(mds_versions) == 1: + output += "MDS version: {0}".format(mds_versions.keys()[0]) + else: + version_table = PrettyTable(["version", "daemons"]) + for version, daemons in mds_versions.iteritems(): + version_table.add_row([ + version, + ", ".join(daemons) + ]) + output += version_table.get_string() + "\n" + + return 0, "", output + + def handle_osd_status(self, cmd): + osd_table = PrettyTable(['id', 'host', 'used', 'avail', 'wr ops', 'wr data', 'rd ops', 'rd data']) + osdmap = self.get("osd_map") + + filter_osds = set() + bucket_filter = None + if 'bucket' in cmd: + self.log.debug("Filtering to bucket '{0}'".format(cmd['bucket'])) + bucket_filter = cmd['bucket'] + crush = self.get("osd_map_crush") + found = False + for bucket in crush['buckets']: + if fnmatch.fnmatch(bucket['name'], bucket_filter): + found = True + filter_osds.update([i['id'] for i in bucket['items']]) + + if not found: + msg = "Bucket '{0}' not found".format(bucket_filter) + return errno.ENOENT, msg, "" + + # Build dict of OSD ID to stats + osd_stats = dict([(o['osd'], o) for o in self.get("osd_stats")['osd_stats']]) + + for osd in osdmap['osds']: + osd_id = osd['osd'] + if bucket_filter and osd_id not in filter_osds: + continue + + metadata = self.get_metadata('osd', "%s" % osd_id) + stats = osd_stats[osd_id] + + osd_table.add_row([osd_id, metadata['hostname'], + self.format_dimless(stats['kb_used'] * 1024, 5), + self.format_dimless(stats['kb_avail'] * 1024, 5), + self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_w") + + self.get_rate("osd", osd_id.__str__(), "osd.op_rw"), 5), + self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_in_bytes"), 5), + self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_r"), 5), + self.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_out_bytes"), 5), + ]) + + return 0, "", osd_table.get_string() + + def handle_command(self, cmd): + self.log.error("handle_command") + + if cmd['prefix'] == "fs status": + return self.handle_fs_status(cmd) + elif cmd['prefix'] == "osd status": + return self.handle_osd_status(cmd) + else: + # mgr should respect our self.COMMANDS and not call us for + # any prefix we don't advertise + raise NotImplementedError(cmd['prefix']) diff --git a/src/vstart.sh b/src/vstart.sh index 21d6ea1160c7..6eb3f263c050 100755 --- a/src/vstart.sh +++ b/src/vstart.sh @@ -458,7 +458,7 @@ $CMDSDEBUG mds root ino gid = `id -g` $extra_conf [mgr] - mgr modules = restful fsstatus dashboard + mgr modules = restful status dashboard mgr data = $CEPH_DEV_DIR/mgr.\$id mgr module path = $MGR_PYTHON_PATH mon reweight min pgs per osd = 4