]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/status/module.py
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / pybind / mgr / status / module.py
CommitLineData
7c673cae
FG
1
2"""
3High level status display commands
4"""
5
6from collections import defaultdict
7from prettytable import PrettyTable
224ce89b 8import errno
1adf2230 9import fnmatch
11fdf7f2 10import mgr_util
1adf2230
AA
11import prettytable
12import six
7c673cae
FG
13
14from mgr_module import MgrModule
15
16
17class Module(MgrModule):
18 COMMANDS = [
19 {
20 "cmd": "fs status "
21 "name=fs,type=CephString,req=false",
22 "desc": "Show the status of a CephFS filesystem",
23 "perm": "r"
24 },
25 {
224ce89b 26 "cmd": "osd status "
7c673cae
FG
27 "name=bucket,type=CephString,req=false",
28 "desc": "Show the status of OSDs within a bucket, or all",
29 "perm": "r"
30 },
31 ]
32
3efd9988 33
7c673cae
FG
34 def get_latest(self, daemon_type, daemon_name, stat):
35 data = self.get_counter(daemon_type, daemon_name, stat)[stat]
36 #self.log.error("get_latest {0} data={1}".format(stat, data))
37 if data:
38 return data[-1][1]
39 else:
40 return 0
41
42 def get_rate(self, daemon_type, daemon_name, stat):
43 data = self.get_counter(daemon_type, daemon_name, stat)[stat]
44
45 #self.log.error("get_latest {0} data={1}".format(stat, data))
46 if data and len(data) > 1:
47 return (data[-1][1] - data[-2][1]) / float(data[-1][0] - data[-2][0])
48 else:
49 return 0
50
51 def handle_fs_status(self, cmd):
52 output = ""
53
224ce89b
WB
54 fs_filter = cmd.get('fs', None)
55
7c673cae
FG
56 mds_versions = defaultdict(list)
57
58 fsmap = self.get("fs_map")
59 for filesystem in fsmap['filesystems']:
224ce89b
WB
60 if fs_filter and filesystem['mdsmap']['fs_name'] != fs_filter:
61 continue
62
7c673cae
FG
63 rank_table = PrettyTable(
64 ("Rank", "State", "MDS", "Activity", "dns", "inos"),
65 hrules=prettytable.FRAME
66 )
67
68 mdsmap = filesystem['mdsmap']
69
70 client_count = 0
71
72 for rank in mdsmap["in"]:
73 up = "mds_{0}".format(rank) in mdsmap["up"]
74 if up:
75 gid = mdsmap['up']["mds_{0}".format(rank)]
76 info = mdsmap['info']['gid_{0}'.format(gid)]
31f18b77 77 dns = self.get_latest("mds", info['name'], "mds_mem.dn")
7c673cae
FG
78 inos = self.get_latest("mds", info['name'], "mds_mem.ino")
79
80 if rank == 0:
81 client_count = self.get_latest("mds", info['name'],
82 "mds_sessions.session_count")
83 elif client_count == 0:
84 # In case rank 0 was down, look at another rank's
85 # sessionmap to get an indication of clients.
86 client_count = self.get_latest("mds", info['name'],
87 "mds_sessions.session_count")
88
89 laggy = "laggy_since" in info
90
91 state = info['state'].split(":")[1]
92 if laggy:
93 state += "(laggy)"
94 if state == "active" and not laggy:
11fdf7f2 95 c_state = mgr_util.colorize(state, mgr_util.GREEN)
7c673cae 96 else:
11fdf7f2 97 c_state = mgr_util.colorize(state, mgr_util.YELLOW)
7c673cae
FG
98
99 # Populate based on context of state, e.g. client
100 # ops for an active daemon, replay progress, reconnect
101 # progress
102 activity = ""
103
104 if state == "active":
11fdf7f2 105 activity = "Reqs: " + mgr_util.format_dimless(
7c673cae
FG
106 self.get_rate("mds", info['name'], "mds_server.handle_client_request"),
107 5
108 ) + "/s"
109
110 metadata = self.get_metadata('mds', info['name'])
111 mds_versions[metadata.get('ceph_version', "unknown")].append(info['name'])
112 rank_table.add_row([
11fdf7f2 113 mgr_util.bold(rank.__str__()), c_state, info['name'],
7c673cae 114 activity,
11fdf7f2
TL
115 mgr_util.format_dimless(dns, 5),
116 mgr_util.format_dimless(inos, 5)
7c673cae
FG
117 ])
118
119 else:
120 rank_table.add_row([
c07f9fc5 121 rank, "failed", "", "", "", ""
7c673cae
FG
122 ])
123
124 # Find the standby replays
1adf2230 125 for gid_str, daemon_info in six.iteritems(mdsmap['info']):
7c673cae
FG
126 if daemon_info['state'] != "up:standby-replay":
127 continue
128
129 inos = self.get_latest("mds", daemon_info['name'], "mds_mem.ino")
31f18b77 130 dns = self.get_latest("mds", daemon_info['name'], "mds_mem.dn")
7c673cae 131
11fdf7f2 132 activity = "Evts: " + mgr_util.format_dimless(
f64942e4 133 self.get_rate("mds", daemon_info['name'], "mds_log.replayed"),
7c673cae
FG
134 5
135 ) + "/s"
136
11fdf7f2
TL
137 metadata = self.get_metadata('mds', daemon_info['name'])
138 mds_versions[metadata.get('ceph_version', "unknown")].append(daemon_info['name'])
139
7c673cae
FG
140 rank_table.add_row([
141 "{0}-s".format(daemon_info['rank']), "standby-replay",
142 daemon_info['name'], activity,
11fdf7f2
TL
143 mgr_util.format_dimless(dns, 5),
144 mgr_util.format_dimless(inos, 5)
7c673cae
FG
145 ])
146
147 df = self.get("df")
148 pool_stats = dict([(p['id'], p['stats']) for p in df['pools']])
149 osdmap = self.get("osd_map")
150 pools = dict([(p['pool'], p) for p in osdmap['pools']])
151 metadata_pool_id = mdsmap['metadata_pool']
152 data_pool_ids = mdsmap['data_pools']
153
154 pools_table = PrettyTable(["Pool", "type", "used", "avail"])
155 for pool_id in [metadata_pool_id] + data_pool_ids:
156 pool_type = "metadata" if pool_id == metadata_pool_id else "data"
157 stats = pool_stats[pool_id]
158 pools_table.add_row([
159 pools[pool_id]['pool_name'], pool_type,
11fdf7f2
TL
160 mgr_util.format_bytes(stats['bytes_used'], 5),
161 mgr_util.format_bytes(stats['max_avail'], 5)
7c673cae
FG
162 ])
163
164 output += "{0} - {1} clients\n".format(
165 mdsmap['fs_name'], client_count)
166 output += "=" * len(mdsmap['fs_name']) + "\n"
167 output += rank_table.get_string()
168 output += "\n" + pools_table.get_string() + "\n"
169
11fdf7f2
TL
170 if not output and fs_filter is not None:
171 return errno.EINVAL, "", "Invalid filesystem: " + fs_filter
172
7c673cae
FG
173 standby_table = PrettyTable(["Standby MDS"])
174 for standby in fsmap['standbys']:
175 metadata = self.get_metadata('mds', standby['name'])
176 mds_versions[metadata.get('ceph_version', "unknown")].append(standby['name'])
177
178 standby_table.add_row([standby['name']])
179
180 output += "\n" + standby_table.get_string() + "\n"
181
182 if len(mds_versions) == 1:
f64942e4 183 output += "MDS version: {0}".format(list(mds_versions)[0])
7c673cae
FG
184 else:
185 version_table = PrettyTable(["version", "daemons"])
1adf2230 186 for version, daemons in six.iteritems(mds_versions):
7c673cae
FG
187 version_table.add_row([
188 version,
189 ", ".join(daemons)
190 ])
191 output += version_table.get_string() + "\n"
192
11fdf7f2 193 return 0, output, ""
7c673cae 194
224ce89b 195 def handle_osd_status(self, cmd):
b32b8144 196 osd_table = PrettyTable(['id', 'host', 'used', 'avail', 'wr ops', 'wr data', 'rd ops', 'rd data', 'state'])
7c673cae
FG
197 osdmap = self.get("osd_map")
198
224ce89b
WB
199 filter_osds = set()
200 bucket_filter = None
201 if 'bucket' in cmd:
202 self.log.debug("Filtering to bucket '{0}'".format(cmd['bucket']))
203 bucket_filter = cmd['bucket']
204 crush = self.get("osd_map_crush")
205 found = False
206 for bucket in crush['buckets']:
207 if fnmatch.fnmatch(bucket['name'], bucket_filter):
208 found = True
209 filter_osds.update([i['id'] for i in bucket['items']])
210
211 if not found:
212 msg = "Bucket '{0}' not found".format(bucket_filter)
213 return errno.ENOENT, msg, ""
214
7c673cae
FG
215 # Build dict of OSD ID to stats
216 osd_stats = dict([(o['osd'], o) for o in self.get("osd_stats")['osd_stats']])
217
218 for osd in osdmap['osds']:
219 osd_id = osd['osd']
224ce89b
WB
220 if bucket_filter and osd_id not in filter_osds:
221 continue
222
b32b8144
FG
223 hostname = ""
224 kb_used = 0
225 kb_avail = 0
7c673cae 226
b32b8144
FG
227 if osd_id in osd_stats:
228 metadata = self.get_metadata('osd', "%s" % osd_id)
229 stats = osd_stats[osd_id]
230 hostname = metadata['hostname']
231 kb_used = stats['kb_used'] * 1024
232 kb_avail = stats['kb_avail'] * 1024
233
234 osd_table.add_row([osd_id, hostname,
11fdf7f2
TL
235 mgr_util.format_bytes(kb_used, 5),
236 mgr_util.format_bytes(kb_avail, 5),
237 mgr_util.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_w") +
7c673cae 238 self.get_rate("osd", osd_id.__str__(), "osd.op_rw"), 5),
11fdf7f2
TL
239 mgr_util.format_bytes(self.get_rate("osd", osd_id.__str__(), "osd.op_in_bytes"), 5),
240 mgr_util.format_dimless(self.get_rate("osd", osd_id.__str__(), "osd.op_r"), 5),
241 mgr_util.format_bytes(self.get_rate("osd", osd_id.__str__(), "osd.op_out_bytes"), 5),
b32b8144 242 ','.join(osd['state']),
7c673cae
FG
243 ])
244
11fdf7f2 245 return 0, osd_table.get_string(), ""
7c673cae 246
11fdf7f2 247 def handle_command(self, inbuf, cmd):
7c673cae
FG
248 self.log.error("handle_command")
249
250 if cmd['prefix'] == "fs status":
251 return self.handle_fs_status(cmd)
224ce89b
WB
252 elif cmd['prefix'] == "osd status":
253 return self.handle_osd_status(cmd)
7c673cae
FG
254 else:
255 # mgr should respect our self.COMMANDS and not call us for
256 # any prefix we don't advertise
257 raise NotImplementedError(cmd['prefix'])