]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/fsstatus/module.py
74d669b58bac05b3d09742f60ba66f251bc7411b
3 High level status display commands
6 from collections
import defaultdict
7 from prettytable
import PrettyTable
10 from mgr_module
import MgrModule
13 class Module(MgrModule
):
17 "name=fs,type=CephString,req=false",
18 "desc": "Show the status of a CephFS filesystem",
23 "name=bucket,type=CephString,req=false",
24 "desc": "Show the status of OSDs within a bucket, or all",
41 COLOR_SEQ
= "\033[1;%dm"
42 COLOR_DARK_SEQ
= "\033[0;%dm"
44 UNDERLINE_SEQ
= "\033[4m"
46 def colorize(self
, msg
, color
, dark
=False):
48 Decorate `msg` with escape sequences to give the requested color
50 return (self
.COLOR_DARK_SEQ
if dark
else self
.COLOR_SEQ
) % (30 + color
) \
51 + msg
+ self
.RESET_SEQ
55 Decorate `msg` with escape sequences to make it appear bold
57 return self
.BOLD_SEQ
+ msg
+ self
.RESET_SEQ
59 def format_dimless(self
, n
, width
, colored
=True):
61 Format a number without units, so as to fit into `width` characters, substituting
62 an appropriate unit suffix.
64 units
= [' ', 'k', 'M', 'G', 'T', 'P']
66 while len("%s" % (int(n
) // (1000**unit
))) > width
- 1:
70 truncated_float
= ("%f" % (n
/ (1000.0 ** unit
)))[0:width
- 1]
71 if truncated_float
[-1] == '.':
72 truncated_float
= " " + truncated_float
[0:-1]
74 truncated_float
= "%{wid}d".format(wid
=width
-1) % n
75 formatted
= "%s%s" % (truncated_float
, units
[unit
])
79 color
= self
.BLACK
, False
81 color
= self
.YELLOW
, False
82 return self
.bold(self
.colorize(formatted
[0:-1], color
[0], color
[1])) \
83 + self
.bold(self
.colorize(formatted
[-1], self
.BLACK
, False))
87 def get_latest(self
, daemon_type
, daemon_name
, stat
):
88 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
89 #self.log.error("get_latest {0} data={1}".format(stat, data))
95 def get_rate(self
, daemon_type
, daemon_name
, stat
):
96 data
= self
.get_counter(daemon_type
, daemon_name
, stat
)[stat
]
98 #self.log.error("get_latest {0} data={1}".format(stat, data))
99 if data
and len(data
) > 1:
100 return (data
[-1][1] - data
[-2][1]) / float(data
[-1][0] - data
[-2][0])
104 def handle_fs_status(self
, cmd
):
107 mds_versions
= defaultdict(list)
109 fsmap
= self
.get("fs_map")
110 for filesystem
in fsmap
['filesystems']:
111 rank_table
= PrettyTable(
112 ("Rank", "State", "MDS", "Activity", "dns", "inos"),
113 hrules
=prettytable
.FRAME
116 mdsmap
= filesystem
['mdsmap']
120 for rank
in mdsmap
["in"]:
121 up
= "mds_{0}".format(rank
) in mdsmap
["up"]
123 gid
= mdsmap
['up']["mds_{0}".format(rank
)]
124 info
= mdsmap
['info']['gid_{0}'.format(gid
)]
125 dns
= self
.get_latest("mds", info
['name'], "mds_mem.dn")
126 inos
= self
.get_latest("mds", info
['name'], "mds_mem.ino")
129 client_count
= self
.get_latest("mds", info
['name'],
130 "mds_sessions.session_count")
131 elif client_count
== 0:
132 # In case rank 0 was down, look at another rank's
133 # sessionmap to get an indication of clients.
134 client_count
= self
.get_latest("mds", info
['name'],
135 "mds_sessions.session_count")
137 laggy
= "laggy_since" in info
139 state
= info
['state'].split(":")[1]
142 if state
== "active" and not laggy
:
143 c_state
= self
.colorize(state
, self
.GREEN
)
145 c_state
= self
.colorize(state
, self
.YELLOW
)
147 # Populate based on context of state, e.g. client
148 # ops for an active daemon, replay progress, reconnect
152 if state
== "active":
153 activity
= "Reqs: " + self
.format_dimless(
154 self
.get_rate("mds", info
['name'], "mds_server.handle_client_request"),
158 metadata
= self
.get_metadata('mds', info
['name'])
159 mds_versions
[metadata
.get('ceph_version', "unknown")].append(info
['name'])
161 self
.bold(rank
.__str
__()), c_state
, info
['name'],
163 self
.format_dimless(dns
, 5),
164 self
.format_dimless(inos
, 5)
169 rank
, "failed", "", "", ""
172 # Find the standby replays
173 for gid_str
, daemon_info
in mdsmap
['info'].iteritems():
174 if daemon_info
['state'] != "up:standby-replay":
177 inos
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.ino")
178 dns
= self
.get_latest("mds", daemon_info
['name'], "mds_mem.dn")
180 activity
= "Evts: " + self
.format_dimless(
181 self
.get_rate("mds", daemon_info
['name'], "mds_log.replay"),
186 "{0}-s".format(daemon_info
['rank']), "standby-replay",
187 daemon_info
['name'], activity
,
188 self
.format_dimless(dns
, 5),
189 self
.format_dimless(inos
, 5)
193 pool_stats
= dict([(p
['id'], p
['stats']) for p
in df
['pools']])
194 osdmap
= self
.get("osd_map")
195 pools
= dict([(p
['pool'], p
) for p
in osdmap
['pools']])
196 metadata_pool_id
= mdsmap
['metadata_pool']
197 data_pool_ids
= mdsmap
['data_pools']
199 pools_table
= PrettyTable(["Pool", "type", "used", "avail"])
200 for pool_id
in [metadata_pool_id
] + data_pool_ids
:
201 pool_type
= "metadata" if pool_id
== metadata_pool_id
else "data"
202 stats
= pool_stats
[pool_id
]
203 pools_table
.add_row([
204 pools
[pool_id
]['pool_name'], pool_type
,
205 self
.format_dimless(stats
['bytes_used'], 5),
206 self
.format_dimless(stats
['max_avail'], 5)
209 output
+= "{0} - {1} clients\n".format(
210 mdsmap
['fs_name'], client_count
)
211 output
+= "=" * len(mdsmap
['fs_name']) + "\n"
212 output
+= rank_table
.get_string()
213 output
+= "\n" + pools_table
.get_string() + "\n"
215 standby_table
= PrettyTable(["Standby MDS"])
216 for standby
in fsmap
['standbys']:
217 metadata
= self
.get_metadata('mds', standby
['name'])
218 mds_versions
[metadata
.get('ceph_version', "unknown")].append(standby
['name'])
220 standby_table
.add_row([standby
['name']])
222 output
+= "\n" + standby_table
.get_string() + "\n"
224 if len(mds_versions
) == 1:
225 output
+= "MDS version: {0}".format(mds_versions
.keys()[0])
227 version_table
= PrettyTable(["version", "daemons"])
228 for version
, daemons
in mds_versions
.iteritems():
229 version_table
.add_row([
233 output
+= version_table
.get_string() + "\n"
237 def handle_osd_perf(self
, cmd
):
238 osd_table
= PrettyTable(['id', 'host', 'used', 'avail', 'wr ops', 'wr data', 'rd ops', 'rd data'])
239 osdmap
= self
.get("osd_map")
241 # Build dict of OSD ID to stats
242 osd_stats
= dict([(o
['osd'], o
) for o
in self
.get("osd_stats")['osd_stats']])
244 for osd
in osdmap
['osds']:
246 metadata
= self
.get_metadata('osd', "%s" % osd_id
)
247 stats
= osd_stats
[osd_id
]
249 osd_table
.add_row([osd_id
, metadata
['hostname'],
250 self
.format_dimless(stats
['kb_used'] * 1024, 5),
251 self
.format_dimless(stats
['kb_avail'] * 1024, 5),
252 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_w") +
253 self
.get_rate("osd", osd_id
.__str
__(), "osd.op_rw"), 5),
254 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_in_bytes"), 5),
255 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_r"), 5),
256 self
.format_dimless(self
.get_rate("osd", osd_id
.__str
__(), "osd.op_out_bytes"), 5),
259 return 0, "", osd_table
.get_string()
261 def handle_command(self
, cmd
):
262 self
.log
.error("handle_command")
264 if cmd
['prefix'] == "fs status":
265 return self
.handle_fs_status(cmd
)
266 elif cmd
['prefix'] == "osd perf":
267 return self
.handle_osd_perf(cmd
)
269 # mgr should respect our self.COMMANDS and not call us for
270 # any prefix we don't advertise
271 raise NotImplementedError(cmd
['prefix'])