blob: 1b1dbf9dbf7abade04523ce3c5bb20e86a4bac65 [file] [log] [blame]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03001import os
koder aka kdanilov4a510ee2015-04-21 18:50:42 +03002import bisect
koder aka kdanilova047e1b2015-04-21 23:16:59 +03003import logging
koder aka kdanilov416b87a2015-05-12 00:26:04 +03004import collections
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +03005from cStringIO import StringIO
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03006
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +03007try:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +03008 import numpy
9 import scipy
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +030010 import matplotlib.pyplot as plt
11except ImportError:
12 plt = None
13
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030014import wally
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030015from wally.utils import ssize2b
16from wally.statistic import round_3_digit, data_property
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030017from wally.suits.io.fio_task_parser import get_test_sync_mode
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030018
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030019
koder aka kdanilova047e1b2015-04-21 23:16:59 +030020logger = logging.getLogger("wally.report")
21
22
koder aka kdanilov209e85d2015-04-27 23:11:05 +030023class DiskInfo(object):
24 def __init__(self):
25 self.direct_iops_r_max = 0
26 self.direct_iops_w_max = 0
27 self.rws4k_10ms = 0
28 self.rws4k_30ms = 0
29 self.rws4k_100ms = 0
30 self.bw_write_max = 0
31 self.bw_read_max = 0
32
33
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +030034report_funcs = []
35
36
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030037class Attrmapper(object):
38 def __init__(self, dct):
39 self.__dct = dct
40
41 def __getattr__(self, name):
42 try:
43 return self.__dct[name]
44 except KeyError:
45 raise AttributeError(name)
46
47
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030048class PerfInfo(object):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030049 def __init__(self, name, summary, intervals, params, testnodes_count):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030050 self.name = name
51 self.bw = None
52 self.iops = None
53 self.lat = None
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030054
55 self.raw_bw = []
56 self.raw_iops = []
57 self.raw_lat = []
58
koder aka kdanilov416b87a2015-05-12 00:26:04 +030059 self.params = params
60 self.intervals = intervals
61 self.testnodes_count = testnodes_count
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030062 self.summary = summary
63 self.p = Attrmapper(self.params.vals)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030064
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030065 self.sync_mode = get_test_sync_mode(self.params)
66 self.concurence = self.params.vals.get('numjobs', 1)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030067
68
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +030069# disk_info = None
70# base = None
71# linearity = None
72
73
koder aka kdanilov416b87a2015-05-12 00:26:04 +030074def group_by_name(test_data):
75 name_map = collections.defaultdict(lambda: [])
76
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030077 for data in test_data:
78 name_map[(data.config.name, data.summary())].append(data)
koder aka kdanilov416b87a2015-05-12 00:26:04 +030079
80 return name_map
81
82
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030083def process_disk_info(test_data):
koder aka kdanilov416b87a2015-05-12 00:26:04 +030084 name_map = group_by_name(test_data)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030085 data = {}
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030086 for (name, summary), results in name_map.items():
87 testnodes_count_set = set(dt.vm_count for dt in results)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030088
koder aka kdanilov416b87a2015-05-12 00:26:04 +030089 assert len(testnodes_count_set) == 1
90 testnodes_count, = testnodes_count_set
91 assert len(results) % testnodes_count == 0
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030092
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030093 intervals = [result.run_interval for result in results]
94 p = results[0].config
95 pinfo = PerfInfo(p.name, result.summary(), intervals,
96 p, testnodes_count)
koder aka kdanilov416b87a2015-05-12 00:26:04 +030097
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030098 pinfo.raw_bw = [result.results['bw'] for result in results]
99 pinfo.raw_iops = [result.results['iops'] for result in results]
100 pinfo.raw_lat = [result.results['lat'] for result in results]
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300101
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300102 pinfo.bw = data_property(map(sum, zip(*pinfo.raw_bw)))
103 pinfo.iops = data_property(map(sum, zip(*pinfo.raw_iops)))
104 pinfo.lat = data_property(sum(pinfo.raw_lat, []))
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300105
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300106 data[(p.name, summary)] = pinfo
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300107 return data
108
109
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300110def report(name, required_fields):
111 def closure(func):
112 report_funcs.append((required_fields.split(","), name, func))
113 return func
114 return closure
115
116
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300117def get_test_lcheck_params(pinfo):
118 res = [{
119 's': 'sync',
120 'd': 'direct',
121 'a': 'async',
122 'x': 'sync direct'
123 }[pinfo.sync_mode]]
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300124
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300125 res.append(pinfo.p.rw)
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300126
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300127 return " ".join(res)
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300128
129
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300130def get_emb_data_svg(plt):
131 sio = StringIO()
132 plt.savefig(sio, format='svg')
133 img_start = "<!-- Created with matplotlib (http://matplotlib.org/) -->"
134 return sio.getvalue().split(img_start, 1)[1]
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300135
136
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300137def get_template(templ_name):
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300138 very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
139 templ_dir = os.path.join(very_root_dir, 'report_templates')
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300140 templ_file = os.path.join(templ_dir, templ_name)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300141 return open(templ_file, 'r').read()
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300142
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300143
144@report('linearity', 'linearity_test')
145def linearity_report(processed_results, path, lab_info):
146 labels_and_data = []
147
148 vls = processed_results.values()[0].params.vals.copy()
149 del vls['blocksize']
150
151 for res in processed_results.values():
152 if res.name.startswith('linearity_test'):
153 iotimes = [1000. / val for val in res.iops.raw]
154 labels_and_data.append([res.p.blocksize, res.iops.raw, iotimes])
155 cvls = res.params.vals.copy()
156 del cvls['blocksize']
157 assert cvls == vls
158
159 labels_and_data.sort(key=lambda x: ssize2b(x[0]))
160 _, ax1 = plt.subplots()
161
162 labels, data, iotimes = zip(*labels_and_data)
163 plt.boxplot(iotimes)
164
165 if len(labels_and_data) > 2 and ssize2b(labels_and_data[-2][0]) >= 4096:
166 xt = range(1, len(labels) + 1)
167
168 def io_time(sz, bw, initial_lat):
169 return sz / bw + initial_lat
170
171 x = numpy.array(map(ssize2b, labels))
172 y = numpy.array([sum(dt) / len(dt) for dt in iotimes])
173 popt, _ = scipy.optimize.curve_fit(io_time, x, y, p0=(100., 1.))
174
175 y1 = io_time(x, *popt)
176 plt.plot(xt, y1, linestyle='--', label='LS linear approxomation')
177
178 for idx, (sz, _, _) in enumerate(labels_and_data):
179 if ssize2b(sz) >= 4096:
180 break
181
182 bw = (x[-1] - x[idx]) / (y[-1] - y[idx])
183 lat = y[-1] - x[-1] / bw
184 y2 = io_time(x, bw, lat)
185
186 plt.plot(xt, y2, linestyle='--',
187 label='(4k & max) linear approxomation')
188
189 plt.setp(ax1, xticklabels=labels)
190
191 plt.xlabel("Block size")
192 plt.ylabel("IO time, ms")
193
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300194 plt.subplots_adjust(top=0.85)
195 plt.legend(bbox_to_anchor=(0.5, 1.2), loc='upper center')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300196 plt.grid()
197 iotime_plot = get_emb_data_svg(plt)
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300198 plt.clf()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300199
200 _, ax1 = plt.subplots()
201 plt.boxplot(data)
202 plt.setp(ax1, xticklabels=labels)
203
204 plt.xlabel("Block size")
205 plt.ylabel("IOPS")
206 plt.grid()
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300207 plt.subplots_adjust(top=0.85)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300208
209 iops_plot = get_emb_data_svg(plt)
210
211 res1 = processed_results.values()[0]
212 descr = {
213 'vm_count': res1.testnodes_count,
214 'concurence': res1.concurence,
215 'oper_descr': get_test_lcheck_params(res1).capitalize()
216 }
217
218 params_map = {'iotime_vs_size': iotime_plot,
219 'iops_vs_size': iops_plot,
220 'descr': descr}
221
222 with open(path, 'w') as fd:
223 fd.write(get_template('report_linearity.html').format(**params_map))
224
225
226@report('lat_vs_iops', 'lat_vs_iops')
227def lat_vs_iops(processed_results, path, lab_info):
228 lat_iops = collections.defaultdict(lambda: [])
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300229 requsted_vs_real = collections.defaultdict(lambda: {})
230
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300231 for res in processed_results.values():
232 if res.name.startswith('lat_vs_iops'):
233 lat_iops[res.concurence].append((res.lat.average / 1000.0,
234 res.lat.deviation / 1000.0,
235 res.iops.average,
236 res.iops.deviation))
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300237 requested_iops = res.p.rate_iops * res.concurence
238 requsted_vs_real[res.concurence][requested_iops] = \
239 (res.iops.average, res.iops.deviation)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300240
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300241 colors = ['red', 'green', 'blue', 'orange', 'magenta', "teal"]
242 colors_it = iter(colors)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300243 for conc, lat_iops in sorted(lat_iops.items()):
244 lat, dev, iops, iops_dev = zip(*lat_iops)
245 plt.errorbar(iops, lat, xerr=iops_dev, yerr=dev, fmt='ro',
246 label=str(conc) + " threads",
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300247 color=next(colors_it))
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300248
249 plt.xlabel("IOPS")
250 plt.ylabel("Latency, ms")
251 plt.grid()
252 plt.legend(loc=0)
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300253 plt_iops_vs_lat = get_emb_data_svg(plt)
254 plt.clf()
255
256 colors_it = iter(colors)
257 for conc, req_vs_real in sorted(requsted_vs_real.items()):
258 req, real = zip(*sorted(req_vs_real.items()))
259 iops, dev = zip(*real)
260 plt.errorbar(req, iops, yerr=dev, fmt='ro',
261 label=str(conc) + " threads",
262 color=next(colors_it))
263 plt.xlabel("Requested IOPS")
264 plt.ylabel("Get IOPS")
265 plt.grid()
266 plt.legend(loc=0)
267 plt_iops_vs_requested = get_emb_data_svg(plt)
268
269 res1 = processed_results.values()[0]
270 params_map = {'iops_vs_lat': plt_iops_vs_lat,
271 'iops_vs_requested': plt_iops_vs_requested,
272 'oper_descr': get_test_lcheck_params(res1).capitalize()}
273
274 with open(path, 'w') as fd:
275 fd.write(get_template('report_iops_vs_lat.html').format(**params_map))
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300276
277
278def render_all_html(dest, info, lab_description, images, templ_name):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300279 data = info.__dict__.copy()
280 for name, val in data.items():
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300281 if not name.startswith('__'):
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300282 if val is None:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300283 data[name] = '-'
284 elif isinstance(val, (int, float, long)):
285 data[name] = round_3_digit(val)
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300286
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300287 data['bw_read_max'] = (data['bw_read_max'][0] // 1024,
288 data['bw_read_max'][1])
289 data['bw_write_max'] = (data['bw_write_max'][0] // 1024,
290 data['bw_write_max'][1])
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300291
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300292 images.update(data)
293 report = get_template(templ_name).format(lab_info=lab_description,
294 **images)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300295
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300296 with open(dest, 'w') as fd:
297 fd.write(report)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300298
299
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300300def io_chart(title, concurence,
301 latv, latv_min, latv_max,
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300302 iops_or_bw, iops_or_bw_err,
303 legend, log=False,
304 boxplots=False):
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300305 points = " MiBps" if legend == 'BW' else ""
306 lc = len(concurence)
307 width = 0.35
308 xt = range(1, lc + 1)
309
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300310 op_per_vm = [v / (vm * th) for v, (vm, th) in zip(iops_or_bw, concurence)]
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300311 fig, p1 = plt.subplots()
312 xpos = [i - width / 2 for i in xt]
313
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300314 p1.bar(xpos, iops_or_bw,
315 width=width,
316 yerr=iops_or_bw_err,
317 ecolor='m',
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300318 color='y',
319 label=legend)
320
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300321 p1.grid(True)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300322 p1.plot(xt, op_per_vm, '--', label=legend + "/thread", color='black')
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300323 handles1, labels1 = p1.get_legend_handles_labels()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300324
325 p2 = p1.twinx()
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300326 p2.plot(xt, latv_max, label="lat max")
327 p2.plot(xt, latv, label="lat avg")
328 p2.plot(xt, latv_min, label="lat min")
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300329
330 plt.xlim(0.5, lc + 0.5)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300331 plt.xticks(xt, ["{0} * {1}".format(vm, th) for (vm, th) in concurence])
332 p1.set_xlabel("VM Count * Thread per VM")
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300333 p1.set_ylabel(legend + points)
334 p2.set_ylabel("Latency ms")
335 plt.title(title)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300336 handles2, labels2 = p2.get_legend_handles_labels()
337
338 plt.legend(handles1 + handles2, labels1 + labels2,
339 loc='center left', bbox_to_anchor=(1.1, 0.81))
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300340
341 if log:
342 p1.set_yscale('log')
343 p2.set_yscale('log')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300344 plt.subplots_adjust(right=0.68)
345
346 return get_emb_data_svg(plt)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300347
348
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300349def make_plots(processed_results, plots):
350 files = {}
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300351 for name_pref, fname, desc in plots:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300352 chart_data = []
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300353
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300354 for res in processed_results.values():
355 if res.name.startswith(name_pref):
356 chart_data.append(res)
357
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300358 if len(chart_data) == 0:
359 raise ValueError("Can't found any date for " + name_pref)
360
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300361 use_bw = ssize2b(chart_data[0].p.blocksize) > 16 * 1024
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300362
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300363 chart_data.sort(key=lambda x: x.concurence)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300364
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300365 # if x.lat.average < max_lat]
366 lat = [x.lat.average / 1000 for x in chart_data]
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300367 lat_min = [x.lat.min / 1000 for x in chart_data]
368 lat_max = [x.lat.max / 1000 for x in chart_data]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300369
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300370 testnodes_count = x.testnodes_count
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300371 concurence = [(testnodes_count, x.concurence)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300372 for x in chart_data]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300373
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300374 if use_bw:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300375 data = [x.bw.average / 1000 for x in chart_data]
376 data_dev = [x.bw.confidence / 1000 for x in chart_data]
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300377 name = "BW"
378 else:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300379 data = [x.iops.average for x in chart_data]
380 data_dev = [x.iops.confidence for x in chart_data]
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300381 name = "IOPS"
382
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300383 fc = io_chart(title=desc,
384 concurence=concurence,
385 latv=lat, latv_min=lat_min, latv_max=lat_max,
386 iops_or_bw=data,
387 iops_or_bw_err=data_dev,
388 legend=name)
389 files[fname] = fc
390
391 return files
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300392
393
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300394def find_max_where(processed_results, sync_mode, blocksize, rw, iops=True):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300395 result = None
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300396 attr = 'iops' if iops else 'bw'
397 for measurement in processed_results.values():
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300398 ok = measurement.sync_mode == sync_mode
399 ok = ok and (measurement.p.blocksize == blocksize)
400 ok = ok and (measurement.p.rw == rw)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300401
402 if ok:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300403 field = getattr(measurement, attr)
404
405 if result is None:
406 result = field
407 elif field.average > result.average:
408 result = field
409
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300410 return result
411
412
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300413def get_disk_info(processed_results):
414 di = DiskInfo()
415 rws4k_iops_lat_th = []
416
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300417 di.direct_iops_w_max = find_max_where(processed_results,
418 'd', '4k', 'randwrite')
419 di.direct_iops_r_max = find_max_where(processed_results,
420 'd', '4k', 'randread')
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300421
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300422 di.bw_write_max = find_max_where(processed_results,
423 'd', '16m', 'randwrite', False)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300424 if di.bw_write_max is None:
425 di.bw_write_max = find_max_where(processed_results,
426 'd', '1m', 'write', False)
427
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300428 di.bw_read_max = find_max_where(processed_results,
429 'd', '16m', 'randread', False)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300430 if di.bw_read_max is None:
431 di.bw_read_max = find_max_where(processed_results,
432 'd', '1m', 'read', False)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300433
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300434 for res in processed_results.values():
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300435 if res.sync_mode == 's' and res.p.blocksize == '4k':
436 if res.p.rw != 'randwrite':
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300437 continue
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300438 rws4k_iops_lat_th.append((res.iops.average,
439 res.lat.average,
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300440 res.concurence))
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300441
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300442 rws4k_iops_lat_th.sort(key=lambda (_1, _2, conc): conc)
443
444 latv = [lat for _, lat, _ in rws4k_iops_lat_th]
445
446 for tlatv_ms in [10, 30, 100]:
447 tlat = tlatv_ms * 1000
448 pos = bisect.bisect_left(latv, tlat)
449 if 0 == pos:
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300450 setattr(di, 'rws4k_{}ms'.format(tlatv_ms), 0)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300451 elif pos == len(latv):
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300452 iops3, _, _ = rws4k_iops_lat_th[-1]
453 setattr(di, 'rws4k_{}ms'.format(tlatv_ms), ">=" + str(iops3))
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300454 else:
455 lat1 = latv[pos - 1]
456 lat2 = latv[pos]
457
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300458 iops1, _, th1 = rws4k_iops_lat_th[pos - 1]
459 iops2, _, th2 = rws4k_iops_lat_th[pos]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300460
461 th_lat_coef = (th2 - th1) / (lat2 - lat1)
462 th3 = th_lat_coef * (tlat - lat1) + th1
463
464 th_iops_coef = (iops2 - iops1) / (th2 - th1)
465 iops3 = th_iops_coef * (th3 - th1) + iops1
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300466 setattr(di, 'rws4k_{}ms'.format(tlatv_ms), int(iops3))
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300467
468 hdi = DiskInfo()
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300469
470 def pp(x):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300471 med, conf = x.rounded_average_conf()
472 conf_perc = int(float(conf) / med * 100)
473 return (med, conf_perc)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300474
475 hdi.direct_iops_r_max = pp(di.direct_iops_r_max)
476 hdi.direct_iops_w_max = pp(di.direct_iops_w_max)
477 hdi.bw_write_max = pp(di.bw_write_max)
478 hdi.bw_read_max = pp(di.bw_read_max)
479
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300480 hdi.rws4k_10ms = di.rws4k_10ms if 0 != di.rws4k_10ms else None
481 hdi.rws4k_30ms = di.rws4k_30ms if 0 != di.rws4k_30ms else None
482 hdi.rws4k_100ms = di.rws4k_100ms if 0 != di.rws4k_100ms else None
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300483 return hdi
484
485
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300486@report('HDD', 'hdd_test')
487def make_hdd_report(processed_results, path, lab_info):
488 plots = [
489 ('hdd_test_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
490 ('hdd_test_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS')
491 ]
492 images = make_plots(processed_results, plots)
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300493 di = get_disk_info(processed_results)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300494 render_all_html(path, di, lab_info, images, "report_hdd.html")
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300495
496
497@report('Ceph', 'ceph_test')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300498def make_ceph_report(processed_results, path, lab_info):
499 plots = [
500 ('ceph_test_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
501 ('ceph_test_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS'),
502 ('ceph_test_rrd16m', 'rand_read_16m', 'Random read 16m direct MiBps'),
503 ('ceph_test_rwd16m', 'rand_write_16m',
504 'Random write 16m direct MiBps'),
505 ]
506
507 images = make_plots(processed_results, plots)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300508 di = get_disk_info(processed_results)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300509 render_all_html(path, di, lab_info, images, "report_ceph.html")
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300510
511
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300512def make_io_report(dinfo, results, path, lab_info=None):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300513 lab_info = {
514 "total_disk": "None",
515 "total_memory": "None",
516 "nodes_count": "None",
517 "processor_count": "None"
518 }
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300519
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300520 try:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300521 res_fields = sorted(v.name for v in dinfo.values())
522
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300523 for fields, name, func in report_funcs:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300524 for field in fields:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300525 pos = bisect.bisect_left(res_fields, field)
526
527 if pos == len(res_fields):
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300528 break
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300529
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300530 if not res_fields[pos].startswith(field):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300531 break
532 else:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300533 hpath = path.format(name)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300534 logger.debug("Generatins report " + name + " into " + hpath)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300535 func(dinfo, hpath, lab_info)
koder aka kdanilovafd98742015-04-24 01:27:22 +0300536 break
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300537 else:
538 logger.warning("No report generator found for this load")
koder aka kdanilovafd98742015-04-24 01:27:22 +0300539
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300540 except Exception as exc:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300541 import traceback
542 traceback.print_exc()
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300543 logger.error("Failed to generate html report:" + str(exc))