]>
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
12 from mgr_module
import MgrModule
15 class Module(MgrModule
):
19 "name=fs,type=CephString,req=false",
20 "desc": "Show the status of a CephFS filesystem",
25 "name=bucket,type=CephString,req=false",
26 "desc": "Show the status of OSDs within a bucket, or all",
43 COLOR_SEQ
= "\033[1;%dm"
44 COLOR_DARK_SEQ
= "\033[0;%dm"
46 UNDERLINE_SEQ
= "\033[4m"
48 def colorize(self
, msg
, color
, dark
=False):
50 Decorate `msg` with escape sequences to give the requested color
52 return (self
.COLOR_DARK_SEQ
if dark
else self
.COLOR_SEQ
) % (30 + color
) \
53 + msg
+ self
.RESET_SEQ
57 Decorate `msg` with escape sequences to make it appear bold
59 return self
.BOLD_SEQ
+ msg
+ self
.RESET_SEQ
61 def format_dimless(self
, n
, width
, colored
=True):
63 Format a number without units, so as to fit into `width` characters, substituting
64 an appropriate unit suffix.
66 units
= [' ', 'k', 'M', 'G', 'T', 'P']
68 while len("%s" % (int(n
) // (1000**unit
))) > width
- 1:
72 truncated_float
= ("%f" % (n
/ (1000.0 ** unit
)))[0:width
- 1]
73 if truncated_float
[-1] == '.':
74 truncated_float
= " " + truncated_float
[0:-1]
76 truncated_float
= "%{wid}d".format(wid
=width
-1) % n
77 formatted
= "%s%s" % (truncated_float
, units
[unit
])
81 color
= self
.BLACK
, False
83 color
= self
.YELLOW
, False
84 return self
.bold(self
.colorize(formatted
[0:-1], color
[0], color
[1])) \
85 + self
.bold(self
.colorize(formatted
[-1], self
.BLACK
, False))
89 def get_latest(self
, daemon_type
, daemon_name
, stat
):
90 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
91 #self.log.error("get_latest {0} data={1}".format(stat, data))
97 def get_rate(self
, daemon_type
, daemon_name
, stat
):
98 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
100 #self.log.error("get_latest {0} data={1}".format(stat, data))
101 if data
and len(data
) > 1:
102 return (data
[-1][1] - data
[-2][1]) / float(data
[-1][0] - data
[-2][0])
106 def handle_fs_status(self
, cmd
):
109 fs_filter
= cmd
.get('fs', None)
111 mds_versions
= defaultdict(list)
113 fsmap
= self
.get("fs_map")
114 for filesystem
in fsmap
['filesystems']:
115 if fs_filter
and filesystem
['mdsmap']['fs_name'] != fs_filter
:
118 rank_table
= PrettyTable(
119 ("Rank", "State", "MDS", "Activity", "dns", "inos"),
120 hrules
=prettytable
.FRAME
123 mdsmap
= filesystem
['mdsmap']
127 for rank
in mdsmap
["in"]:
128 up
= "mds_{0}".format(rank
) in mdsmap
["up"]
130 gid
= mdsmap
['up']["mds_{0}".format(rank
)]
131 info
= mdsmap
['info']['gid_{0}'.format(gid
)]
132 dns
= self
.get_latest("mds", info
['name'], "mds_mem.dn")
133 inos
= self
.get_latest("mds", info
['name'], "mds_mem.ino")
136 client_count
= self
.get_latest("mds", info
['name'],
137 "mds_sessions.session_count")
138 elif client_count
== 0:
139 # In case rank 0 was down, look at another rank's
140 # sessionmap to get an indication of clients.
141 client_count
= self
.get_latest("mds", info
['name'],
142 "mds_sessions.session_count")
144 laggy
= "laggy_since" in info
146 state
= info
['state'].split(":")[1]
149 if state
== "active" and not laggy
:
150 c_state
= self
.colorize(state
, self
.GREEN
)
152 c_state
= self
.colorize(state
, self
.YELLOW
)
154 # Populate based on context of state, e.g. client
155 # ops for an active daemon, replay progress, reconnect
159 if state
== "active":
160 activity
= "Reqs: " + self
.format_dimless(
161 self
.get_rate("mds", info
['name'], "mds_server.handle_client_request"),
165 metadata
= self
.get_metadata('mds', info
['name'])
166 mds_versions
[metadata
.get('ceph_version', "unknown")].append(info
['name'])
168 self
.bold(rank
.__str
__()), c_state
, info
['name'],
170 self
.format_dimless(dns
, 5),
171 self
.format_dimless(inos
, 5)
176 rank
, "failed", "", "", ""
179 # Find the standby replays
180 for gid_str
, daemon_info
in mdsmap
['info'].iteritems():
181 if daemon_info
['state'] != "up:standby-replay":
184 inos
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.ino")
185 dns
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.dn")
187 activity
= "Evts: " + self
.format_dimless(
188 self
.get_rate("mds", daemon_info
['name'], "mds_log.replay"),
193 "{0}-s".format(daemon_info
['rank']), "standby-replay",
194 daemon_info
['name'], activity
,
195 self
.format_dimless(dns
, 5),
196 self
.format_dimless(inos
, 5)
200 pool_stats
= dict([(p
['id'], p
['stats']) for p
in df
['pools']])
201 osdmap
= self
.get("osd_map")
202 pools
= dict([(p
['pool'], p
) for p
in osdmap
['pools']])
203 metadata_pool_id
= mdsmap
['metadata_pool']
204 data_pool_ids
= mdsmap
['data_pools']
206 pools_table
= PrettyTable(["Pool", "type", "used", "avail"])
207 for pool_id
in [metadata_pool_id
] + data_pool_ids
:
208 pool_type
= "metadata" if pool_id
== metadata_pool_id
else "data"
209 stats
= pool_stats
[pool_id
]
210 pools_table
.add_row([
211 pools
[pool_id
]['pool_name'], pool_type
,
212 self
.format_dimless(stats
['bytes_used'], 5),
213 self
.format_dimless(stats
['max_avail'], 5)
216 output
+= "{0} - {1} clients\n".format(
217 mdsmap
['fs_name'], client_count
)
218 output
+= "=" * len(mdsmap
['fs_name']) + "\n"
219 output
+= rank_table
.get_string()
220 output
+= "\n" + pools_table
.get_string() + "\n"
222 standby_table
= PrettyTable(["Standby MDS"])
223 for standby
in fsmap
['standbys']:
224 metadata
= self
.get_metadata('mds', standby
['name'])
225 mds_versions
[metadata
.get('ceph_version', "unknown")].append(standby
['name'])
227 standby_table
.add_row([standby
['name']])
229 output
+= "\n" + standby_table
.get_string() + "\n"
231 if len(mds_versions
) == 1:
232 output
+= "MDS version: {0}".format(mds_versions
.keys()[0])
234 version_table
= PrettyTable(["version", "daemons"])
235 for version
, daemons
in mds_versions
.iteritems():
236 version_table
.add_row([
240 output
+= version_table
.get_string() + "\n"
244 def handle_osd_status(self
, cmd
):
245 osd_table
= PrettyTable(['id', 'host', 'used', 'avail', 'wr ops', 'wr data', 'rd ops', 'rd data'])
246 osdmap
= self
.get("osd_map")
251 self
.log
.debug("Filtering to bucket '{0}'".format(cmd
['bucket']))
252 bucket_filter
= cmd
['bucket']
253 crush
= self
.get("osd_map_crush")
255 for bucket
in crush
['buckets']:
256 if fnmatch
.fnmatch(bucket
['name'], bucket_filter
):
258 filter_osds
.update([i
['id'] for i
in bucket
['items']])
261 msg
= "Bucket '{0}' not found".format(bucket_filter
)
262 return errno
.ENOENT
, msg
, ""
264 # Build dict of OSD ID to stats
265 osd_stats
= dict([(o
['osd'], o
) for o
in self
.get("osd_stats")['osd_stats']])
267 for osd
in osdmap
['osds']:
269 if bucket_filter
and osd_id
not in filter_osds
:
272 metadata
= self
.get_metadata('osd', "%s" % osd_id
)
273 stats
= osd_stats
[osd_id
]
275 osd_table
.add_row([osd_id
, metadata
['hostname'],
276 self
.format_dimless(stats
['kb_used'] * 1024, 5),
277 self
.format_dimless(stats
['kb_avail'] * 1024, 5),
278 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_w") +
279 self
.get_rate("osd", osd_id
.__str
__(), "osd.op_rw"), 5),
280 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_in_bytes"), 5),
281 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_r"), 5),
282 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_out_bytes"), 5),
285 return 0, "", osd_table
.get_string()
287 def handle_command(self
, cmd
):
288 self
.log
.error("handle_command")
290 if cmd
['prefix'] == "fs status":
291 return self
.handle_fs_status(cmd
)
292 elif cmd
['prefix'] == "osd status":
293 return self
.handle_osd_status(cmd
)
295 # mgr should respect our self.COMMANDS and not call us for
296 # any prefix we don't advertise
297 raise NotImplementedError(cmd
['prefix'])