]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/osd_perf_query/module.py
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / pybind / mgr / osd_perf_query / module.py
1
2 """
3 osd_perf_query module
4 """
5
6 from itertools import groupby
7 from time import time
8 import errno
9 import prettytable
10
11 from mgr_module import MgrModule
12
13 def get_human_readable(bytes, precision=2):
14 suffixes = ['', 'Ki', 'Mi', 'Gi', 'Ti']
15 suffix_index = 0
16 while bytes > 1024 and suffix_index < 4:
17 # increment the index of the suffix
18 suffix_index += 1
19 # apply the division
20 bytes = bytes / 1024.0
21 return '%.*f%s' % (precision, bytes, suffixes[suffix_index])
22
23 class OSDPerfQuery(MgrModule):
24 COMMANDS = [
25 {
26 "cmd": "osd perf query add "
27 "name=query,type=CephChoices,"
28 "strings=client_id|rbd_image_id|all_subkeys",
29 "desc": "add osd perf query",
30 "perm": "w"
31 },
32 {
33 "cmd": "osd perf query remove "
34 "name=query_id,type=CephInt,req=true",
35 "desc": "remove osd perf query",
36 "perm": "w"
37 },
38 {
39 "cmd": "osd perf counters get "
40 "name=query_id,type=CephInt,req=true",
41 "desc": "fetch osd perf counters",
42 "perm": "w"
43 },
44 ]
45
46 CLIENT_ID_QUERY = {
47 'key_descriptor': [
48 {'type': 'client_id', 'regex': '^(.+)$'},
49 ],
50 'performance_counter_descriptors': [
51 'bytes', 'write_ops', 'read_ops', 'write_bytes', 'read_bytes',
52 'write_latency', 'read_latency',
53 ],
54 'limit': {'order_by': 'bytes', 'max_count': 10},
55 }
56
57 RBD_IMAGE_ID_QUERY = {
58 'key_descriptor': [
59 {'type': 'pool_id', 'regex': '^(.+)$'},
60 {'type': 'object_name',
61 'regex': '^(?:rbd|journal)_data\.(?:([0-9]+)\.)?([^.]+)\.'},
62 ],
63 'performance_counter_descriptors': [
64 'bytes', 'write_ops', 'read_ops', 'write_bytes', 'read_bytes',
65 'write_latency', 'read_latency',
66 ],
67 'limit': {'order_by': 'bytes', 'max_count': 10},
68 }
69
70 ALL_SUBKEYS_QUERY = {
71 'key_descriptor': [
72 {'type': 'client_id', 'regex': '^(.*)$'},
73 {'type': 'client_address', 'regex': '^(.*)$'},
74 {'type': 'pool_id', 'regex': '^(.*)$'},
75 {'type': 'namespace', 'regex': '^(.*)$'},
76 {'type': 'osd_id', 'regex': '^(.*)$'},
77 {'type': 'pg_id', 'regex': '^(.*)$'},
78 {'type': 'object_name', 'regex': '^(.*)$'},
79 {'type': 'snap_id', 'regex': '^(.*)$'},
80 ],
81 'performance_counter_descriptors': [
82 'write_ops', 'read_ops',
83 ],
84 }
85
86 queries = {}
87
88 def handle_command(self, inbuf, cmd):
89 if cmd['prefix'] == "osd perf query add":
90 if cmd['query'] == 'rbd_image_id':
91 query = self.RBD_IMAGE_ID_QUERY
92 elif cmd['query'] == 'client_id':
93 query = self.CLIENT_ID_QUERY
94 else:
95 query = self.ALL_SUBKEYS_QUERY
96 query_id = self.add_osd_perf_query(query)
97 if query_id is None:
98 return -errno.EINVAL, "", "Invalid query"
99 self.queries[query_id] = [query, time()]
100 return 0, str(query_id), "added query " + cmd['query'] + " with id " + str(query_id)
101 elif cmd['prefix'] == "osd perf query remove":
102 if cmd['query_id'] not in self.queries:
103 return -errno.ENOENT, "", "unknown query id " + str(cmd['query_id'])
104 self.remove_osd_perf_query(cmd['query_id'])
105 del self.queries[cmd['query_id']]
106 return 0, "", "removed query with id " + str(cmd['query_id'])
107 elif cmd['prefix'] == "osd perf counters get":
108 if cmd['query_id'] not in self.queries:
109 return -errno.ENOENT, "", "unknown query id " + str(cmd['query_id'])
110
111 query = self.queries[cmd['query_id']][0]
112 res = self.get_osd_perf_counters(cmd['query_id'])
113 now = time()
114 last_update = self.queries[cmd['query_id']][1]
115 descriptors = query['performance_counter_descriptors']
116
117 if query == self.RBD_IMAGE_ID_QUERY:
118 column_names = ["pool_id", "rbd image_id"]
119 else:
120 column_names = [sk['type'] for sk in query['key_descriptor']]
121 for d in descriptors:
122 desc = d
123 if d in ['bytes']:
124 continue
125 elif d in ['write_bytes', 'read_bytes']:
126 desc += '/sec'
127 elif d in ['write_latency', 'read_latency']:
128 desc += '(msec)'
129 column_names.append(desc)
130
131 table = prettytable.PrettyTable(tuple(column_names),
132 hrules=prettytable.FRAME)
133
134 if query == self.RBD_IMAGE_ID_QUERY:
135 # typical output:
136 # {'k': [['3'], ['', '16fe5b5a8435e']],
137 # 'c': [[1024, 0], [1, 0], ...]}
138 # pool id fixup: if the object_name regex has matched pool id
139 # use it as the image pool id
140 for c in res['counters']:
141 if c['k'][1][0]:
142 c['k'][0][0] = c['k'][1][0]
143 # group by (pool_id, image_id)
144 processed = []
145 res['counters'].sort(key=lambda c: [c['k'][0][0], c['k'][1][1]])
146 for key, group in groupby(res['counters'],
147 lambda c: [c['k'][0][0], c['k'][1][1]]):
148 counters = [[0, 0] for x in descriptors]
149 for c in group:
150 for i in range(len(counters)):
151 counters[i][0] += c['c'][i][0]
152 counters[i][1] += c['c'][i][1]
153 processed.append({'k' : key, 'c' : counters})
154 else:
155 # typical output:
156 # {'k': [['client.94348']], 'c': [[1024, 0], [1, 0], ...]}
157 processed = res['counters']
158
159 max_count = len(processed)
160 if 'limit' in query:
161 if 'max_count' in query['limit']:
162 max_count = query['limit']['max_count']
163 if 'order_by' in query['limit']:
164 i = descriptors.index(query['limit']['order_by'])
165 processed.sort(key=lambda x: x['c'][i][0], reverse=True)
166 for c in processed[:max_count]:
167 if query == self.RBD_IMAGE_ID_QUERY:
168 row = c['k']
169 else:
170 row = [sk[0] for sk in c['k']]
171 counters = c['c']
172 for i in range(len(descriptors)):
173 if descriptors[i] in ['bytes']:
174 continue
175 elif descriptors[i] in ['write_bytes', 'read_bytes']:
176 bps = counters[i][0] / (now - last_update)
177 row.append(get_human_readable(bps))
178 elif descriptors[i] in ['write_latency', 'read_latency']:
179 lat = 0
180 if counters[i][1] > 0:
181 lat = 1.0 * counters[i][0] / counters[i][1] / 1000000
182 row.append("%.2f" % lat)
183 else:
184 row.append("%d" % counters[i][0])
185 table.add_row(row)
186
187 msg = "counters for the query id %d for the last %d sec" % \
188 (cmd['query_id'], now - last_update)
189 self.queries[cmd['query_id']][1] = now
190
191 return 0, table.get_string() + "\n", msg
192 else:
193 raise NotImplementedError(cmd['prefix'])
194