]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/status/module.py
3 High level status display commands
6 from collections
import defaultdict
7 from prettytable
import PrettyTable
8 from typing
import Any
, Dict
, List
, Optional
, Tuple
, Union
14 from mgr_module
import CLIReadCommand
, MgrModule
, HandleCommandResult
17 class Module(MgrModule
):
18 def get_latest(self
, daemon_type
: str, daemon_name
: str, stat
: str) -> int:
19 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
25 def get_rate(self
, daemon_type
: str, daemon_name
: str, stat
: str) -> int:
26 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
27 if data
and len(data
) > 1 and (int(data
[-1][0] - data
[-2][0]) != 0):
28 return (data
[-1][1] - data
[-2][1]) // int(data
[-1][0] - data
[-2][0])
32 @CLIReadCommand("fs status")
33 def handle_fs_status(self
,
34 fs
: Optional
[str] = None,
35 format
: str = 'plain') -> Tuple
[int, str, str]:
37 Show the status of a CephFS filesystem
40 json_output
: Dict
[str, List
[Dict
[str, Union
[int, str, List
[str]]]]] = \
45 output_format
= format
49 mds_versions
= defaultdict(list)
51 fsmap
= self
.get("fs_map")
52 for filesystem
in fsmap
['filesystems']:
53 if fs_filter
and filesystem
['mdsmap']['fs_name'] != fs_filter
:
56 rank_table
= PrettyTable(
57 ("RANK", "STATE", "MDS", "ACTIVITY", "DNS", "INOS", "DIRS", "CAPS"),
60 rank_table
.left_padding_width
= 0
61 rank_table
.right_padding_width
= 2
63 mdsmap
= filesystem
['mdsmap']
67 for rank
in mdsmap
["in"]:
68 up
= "mds_{0}".format(rank
) in mdsmap
["up"]
70 gid
= mdsmap
['up']["mds_{0}".format(rank
)]
71 info
= mdsmap
['info']['gid_{0}'.format(gid
)]
72 dns
= self
.get_latest("mds", info
['name'], "mds_mem.dn")
73 inos
= self
.get_latest("mds", info
['name'], "mds_mem.ino")
74 dirs
= self
.get_latest("mds", info
['name'], "mds_mem.dir")
75 caps
= self
.get_latest("mds", info
['name'], "mds_mem.cap")
78 client_count
= self
.get_latest("mds", info
['name'],
79 "mds_sessions.session_count")
80 elif client_count
== 0:
81 # In case rank 0 was down, look at another rank's
82 # sessionmap to get an indication of clients.
83 client_count
= self
.get_latest("mds", info
['name'],
84 "mds_sessions.session_count")
86 laggy
= "laggy_since" in info
88 state
= info
['state'].split(":")[1]
91 if state
== "active" and not laggy
:
92 c_state
= mgr_util
.colorize(state
, mgr_util
.GREEN
)
94 c_state
= mgr_util
.colorize(state
, mgr_util
.YELLOW
)
96 # Populate based on context of state, e.g. client
97 # ops for an active daemon, replay progress, reconnect
101 if state
== "active":
102 rate
= self
.get_rate("mds", info
['name'],
103 "mds_server.handle_client_request")
104 if output_format
not in ('json', 'json-pretty'):
105 activity
= "Reqs: " + mgr_util
.format_dimless(rate
, 5) + "/s"
107 metadata
= self
.get_metadata('mds', info
['name'],
108 default
=defaultdict(lambda: 'unknown'))
110 mds_versions
[metadata
['ceph_version']].append(info
['name'])
112 if output_format
in ('json', 'json-pretty'):
113 json_output
['mdsmap'].append({
115 'name': info
['name'],
117 'rate': rate
if state
== "active" else "0",
125 mgr_util
.bold(rank
.__str
__()), c_state
, info
['name'],
127 mgr_util
.format_dimless(dns
, 5),
128 mgr_util
.format_dimless(inos
, 5),
129 mgr_util
.format_dimless(dirs
, 5),
130 mgr_util
.format_dimless(caps
, 5)
133 if output_format
in ('json', 'json-pretty'):
134 json_output
['mdsmap'].append({
140 rank
, "failed", "", "", "", "", "", ""
143 # Find the standby replays
144 for gid_str
, daemon_info
in mdsmap
['info'].items():
145 if daemon_info
['state'] != "up:standby-replay":
148 inos
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.ino")
149 dns
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.dn")
150 dirs
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.dir")
151 caps
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.cap")
153 events
= self
.get_rate("mds", daemon_info
['name'], "mds_log.replayed")
154 if output_format
not in ('json', 'json-pretty'):
155 activity
= "Evts: " + mgr_util
.format_dimless(events
, 5) + "/s"
157 metadata
= self
.get_metadata('mds', daemon_info
['name'],
158 default
=defaultdict(lambda: 'unknown'))
160 mds_versions
[metadata
['ceph_version']].append(daemon_info
['name'])
162 if output_format
in ('json', 'json-pretty'):
163 json_output
['mdsmap'].append({
165 'name': daemon_info
['name'],
166 'state': 'standby-replay',
175 "{0}-s".format(daemon_info
['rank']), "standby-replay",
176 daemon_info
['name'], activity
,
177 mgr_util
.format_dimless(dns
, 5),
178 mgr_util
.format_dimless(inos
, 5),
179 mgr_util
.format_dimless(dirs
, 5),
180 mgr_util
.format_dimless(caps
, 5)
184 pool_stats
= dict([(p
['id'], p
['stats']) for p
in df
['pools']])
185 osdmap
= self
.get("osd_map")
186 pools
= dict([(p
['pool'], p
) for p
in osdmap
['pools']])
187 metadata_pool_id
= mdsmap
['metadata_pool']
188 data_pool_ids
= mdsmap
['data_pools']
190 pools_table
= PrettyTable(["POOL", "TYPE", "USED", "AVAIL"],
192 pools_table
.left_padding_width
= 0
193 pools_table
.right_padding_width
= 2
194 for pool_id
in [metadata_pool_id
] + data_pool_ids
:
195 pool_type
= "metadata" if pool_id
== metadata_pool_id
else "data"
196 stats
= pool_stats
[pool_id
]
198 if output_format
in ('json', 'json-pretty'):
199 json_output
['pools'].append({
201 'name': pools
[pool_id
]['pool_name'],
203 'used': stats
['bytes_used'],
204 'avail': stats
['max_avail']
207 pools_table
.add_row([
208 pools
[pool_id
]['pool_name'], pool_type
,
209 mgr_util
.format_bytes(stats
['bytes_used'], 5),
210 mgr_util
.format_bytes(stats
['max_avail'], 5)
213 if output_format
in ('json', 'json-pretty'):
214 json_output
['clients'].append({
215 'fs': mdsmap
['fs_name'],
216 'clients': client_count
,
219 output
+= "{0} - {1} clients\n".format(
220 mdsmap
['fs_name'], client_count
)
221 output
+= "=" * len(mdsmap
['fs_name']) + "\n"
222 output
+= rank_table
.get_string()
223 output
+= "\n" + pools_table
.get_string() + "\n"
225 if not output
and not json_output
and fs_filter
is not None:
226 return errno
.EINVAL
, "", "Invalid filesystem: " + fs_filter
228 standby_table
= PrettyTable(["STANDBY MDS"], border
=False)
229 standby_table
.left_padding_width
= 0
230 standby_table
.right_padding_width
= 2
231 for standby
in fsmap
['standbys']:
232 metadata
= self
.get_metadata('mds', standby
['name'],
233 default
=defaultdict(lambda: 'unknown'))
235 mds_versions
[metadata
['ceph_version']].append(standby
['name'])
237 if output_format
in ('json', 'json-pretty'):
238 json_output
['mdsmap'].append({
239 'name': standby
['name'],
243 standby_table
.add_row([standby
['name']])
245 if output_format
not in ('json', 'json-pretty'):
246 output
+= "\n" + standby_table
.get_string() + "\n"
248 if len(mds_versions
) == 1:
249 if output_format
in ('json', 'json-pretty'):
250 json_output
['mds_version'] = [dict(version
=k
, daemon
=v
)
251 for k
, v
in mds_versions
.items()]
253 output
+= "MDS version: {0}".format([*mds_versions
][0])
255 version_table
= PrettyTable(["VERSION", "DAEMONS"],
257 version_table
.left_padding_width
= 0
258 version_table
.right_padding_width
= 2
259 for version
, daemons
in mds_versions
.items():
260 if output_format
in ('json', 'json-pretty'):
261 json_output
['mds_version'].append({
266 version_table
.add_row([
270 if output_format
not in ('json', 'json-pretty'):
271 output
+= version_table
.get_string() + "\n"
273 if output_format
== "json":
274 return HandleCommandResult(stdout
=json
.dumps(json_output
, sort_keys
=True))
275 elif output_format
== "json-pretty":
276 return HandleCommandResult(stdout
=json
.dumps(json_output
, sort_keys
=True, indent
=4,
277 separators
=(',', ': ')))
279 return HandleCommandResult(stdout
=output
)
281 @CLIReadCommand("osd status")
282 def handle_osd_status(self
, bucket
: Optional
[str] = None, format
: str = 'plain') -> Tuple
[int, str, str]:
284 Show the status of OSDs within a bucket, or all
286 json_output
: Dict
[str, List
[Any
]] = \
288 output_format
= format
290 osd_table
= PrettyTable(['ID', 'HOST', 'USED', 'AVAIL', 'WR OPS',
291 'WR DATA', 'RD OPS', 'RD DATA', 'STATE'],
293 osd_table
.align
['ID'] = 'r'
294 osd_table
.align
['HOST'] = 'l'
295 osd_table
.align
['USED'] = 'r'
296 osd_table
.align
['AVAIL'] = 'r'
297 osd_table
.align
['WR OPS'] = 'r'
298 osd_table
.align
['WR DATA'] = 'r'
299 osd_table
.align
['RD OPS'] = 'r'
300 osd_table
.align
['RD DATA'] = 'r'
301 osd_table
.align
['STATE'] = 'l'
302 osd_table
.left_padding_width
= 0
303 osd_table
.right_padding_width
= 2
304 osdmap
= self
.get("osd_map")
308 if bucket
is not None:
309 self
.log
.debug(f
"Filtering to bucket '{bucket}'")
310 bucket_filter
= bucket
311 crush
= self
.get("osd_map_crush")
313 for bucket_
in crush
['buckets']:
314 if fnmatch
.fnmatch(bucket_
['name'], bucket_filter
):
316 filter_osds
.update([i
['id'] for i
in bucket_
['items']])
319 msg
= "Bucket '{0}' not found".format(bucket_filter
)
320 return errno
.ENOENT
, msg
, ""
322 # Build dict of OSD ID to stats
323 osd_stats
= dict([(o
['osd'], o
) for o
in self
.get("osd_stats")['osd_stats']])
325 for osd
in osdmap
['osds']:
327 if bucket_filter
and osd_id
not in filter_osds
:
334 if osd_id
in osd_stats
:
335 metadata
= self
.get_metadata('osd', str(osd_id
), default
=defaultdict(str))
336 stats
= osd_stats
[osd_id
]
338 hostname
= metadata
['hostname']
339 kb_used
= stats
['kb_used'] * 1024
340 kb_avail
= stats
['kb_avail'] * 1024
342 wr_ops_rate
= (self
.get_rate("osd", osd_id
.__str
__(), "osd.op_w") +
343 self
.get_rate("osd", osd_id
.__str
__(), "osd.op_rw"))
344 wr_byte_rate
= self
.get_rate("osd", osd_id
.__str
__(), "osd.op_in_bytes")
345 rd_ops_rate
= self
.get_rate("osd", osd_id
.__str
__(), "osd.op_r")
346 rd_byte_rate
= self
.get_rate("osd", osd_id
.__str
__(), "osd.op_out_bytes")
347 osd_table
.add_row([osd_id
, hostname
,
348 mgr_util
.format_bytes(kb_used
, 5),
349 mgr_util
.format_bytes(kb_avail
, 5),
350 mgr_util
.format_dimless(wr_ops_rate
, 5),
351 mgr_util
.format_bytes(wr_byte_rate
, 5),
352 mgr_util
.format_dimless(rd_ops_rate
, 5),
353 mgr_util
.format_bytes(rd_byte_rate
, 5),
354 ','.join(osd
['state']),
356 if output_format
in ('json', 'json-pretty'):
357 json_output
['OSDs'].append({
359 'host name': hostname
,
361 'kb available':kb_avail
,
362 'write ops rate': wr_ops_rate
,
363 'write byte rate': wr_byte_rate
,
364 'read ops rate': rd_ops_rate
,
365 'read byte rate': rd_byte_rate
,
366 'state': osd
['state']
369 if output_format
== "json":
370 return 0, json
.dumps(json_output
, sort_keys
=True) , ""
371 elif output_format
== "json-pretty":
372 return 0, json
.dumps(json_output
, sort_keys
=True,indent
=4,separators
=(',', ': ')) , ""
374 return 0, osd_table
.get_string(), ""