blob: f27491b34dc37321c3511f2972b4a464128b73c6 [file] [log] [blame]
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03001import os
koder aka kdanilov88407ff2015-05-26 15:35:57 +03002import csv
koder aka kdanilov4a510ee2015-04-21 18:50:42 +03003import bisect
koder aka kdanilova047e1b2015-04-21 23:16:59 +03004import logging
koder aka kdanilov88407ff2015-05-26 15:35:57 +03005import itertools
koder aka kdanilov416b87a2015-05-12 00:26:04 +03006import collections
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +03007from cStringIO import StringIO
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03008
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +03009try:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030010 import numpy
11 import scipy
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030012 import matplotlib
koder aka kdanilov9e0512a2015-08-10 14:51:59 +030013 matplotlib.use('svg')
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +030014 import matplotlib.pyplot as plt
15except ImportError:
16 plt = None
17
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030018import wally
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030019from wally.utils import ssize2b
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +030020from wally.statistic import round_3_digit
koder aka kdanilov88407ff2015-05-26 15:35:57 +030021from wally.suits.io.fio_task_parser import (get_test_sync_mode,
22 get_test_summary,
23 parse_all_in_1,
24 abbv_name_to_full)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030025
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030026
koder aka kdanilova047e1b2015-04-21 23:16:59 +030027logger = logging.getLogger("wally.report")
28
29
koder aka kdanilov209e85d2015-04-27 23:11:05 +030030class DiskInfo(object):
31 def __init__(self):
32 self.direct_iops_r_max = 0
33 self.direct_iops_w_max = 0
koder aka kdanilov88407ff2015-05-26 15:35:57 +030034
35 # 64 used instead of 4k to faster feed caches
36 self.direct_iops_w64_max = 0
37
koder aka kdanilov209e85d2015-04-27 23:11:05 +030038 self.rws4k_10ms = 0
39 self.rws4k_30ms = 0
40 self.rws4k_100ms = 0
41 self.bw_write_max = 0
42 self.bw_read_max = 0
43
44
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +030045report_funcs = []
46
47
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030048class Attrmapper(object):
49 def __init__(self, dct):
50 self.__dct = dct
51
52 def __getattr__(self, name):
53 try:
54 return self.__dct[name]
55 except KeyError:
56 raise AttributeError(name)
57
58
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030059class PerfInfo(object):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030060 def __init__(self, name, summary, intervals, params, testnodes_count):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030061 self.name = name
62 self.bw = None
63 self.iops = None
64 self.lat = None
koder aka kdanilov88407ff2015-05-26 15:35:57 +030065 self.lat_50 = None
66 self.lat_95 = None
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030067
68 self.raw_bw = []
69 self.raw_iops = []
70 self.raw_lat = []
71
koder aka kdanilov416b87a2015-05-12 00:26:04 +030072 self.params = params
73 self.intervals = intervals
74 self.testnodes_count = testnodes_count
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030075 self.summary = summary
76 self.p = Attrmapper(self.params.vals)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030077
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030078 self.sync_mode = get_test_sync_mode(self.params)
79 self.concurence = self.params.vals.get('numjobs', 1)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030080
81
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +030082# disk_info = None
83# base = None
84# linearity = None
85
86
koder aka kdanilov416b87a2015-05-12 00:26:04 +030087def group_by_name(test_data):
88 name_map = collections.defaultdict(lambda: [])
89
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030090 for data in test_data:
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +030091 name_map[(data.name, data.summary())].append(data)
koder aka kdanilov416b87a2015-05-12 00:26:04 +030092
93 return name_map
94
95
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +030096def report(name, required_fields):
97 def closure(func):
98 report_funcs.append((required_fields.split(","), name, func))
99 return func
100 return closure
101
102
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300103def get_test_lcheck_params(pinfo):
104 res = [{
105 's': 'sync',
106 'd': 'direct',
107 'a': 'async',
108 'x': 'sync direct'
109 }[pinfo.sync_mode]]
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300110
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300111 res.append(pinfo.p.rw)
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300112
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300113 return " ".join(res)
koder aka kdanilov63e9c5a2015-04-28 23:06:07 +0300114
115
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300116def get_emb_data_svg(plt):
117 sio = StringIO()
118 plt.savefig(sio, format='svg')
119 img_start = "<!-- Created with matplotlib (http://matplotlib.org/) -->"
120 return sio.getvalue().split(img_start, 1)[1]
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300121
122
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300123def get_template(templ_name):
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300124 very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
125 templ_dir = os.path.join(very_root_dir, 'report_templates')
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300126 templ_file = os.path.join(templ_dir, templ_name)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300127 return open(templ_file, 'r').read()
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300128
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300129
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300130def group_by(data, func):
131 if len(data) < 2:
132 yield data
133 return
134
135 ndata = [(func(dt), dt) for dt in data]
136 ndata.sort(key=func)
137 pkey, dt = ndata[0]
138 curr_list = [dt]
139
140 for key, val in ndata[1:]:
141 if pkey != key:
142 yield curr_list
143 curr_list = [val]
144 else:
145 curr_list.append(val)
146 pkey = key
147
148 yield curr_list
149
150
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300151@report('linearity', 'linearity_test')
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300152def linearity_report(processed_results, lab_info, comment):
153 labels_and_data_mp = collections.defaultdict(lambda: [])
154 vls = {}
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300155
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300156 # plot io_time = func(bsize)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300157 for res in processed_results.values():
158 if res.name.startswith('linearity_test'):
159 iotimes = [1000. / val for val in res.iops.raw]
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300160
161 op_summ = get_test_summary(res.params)[:3]
162
163 labels_and_data_mp[op_summ].append(
164 [res.p.blocksize, res.iops.raw, iotimes])
165
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300166 cvls = res.params.vals.copy()
167 del cvls['blocksize']
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300168 del cvls['rw']
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300169
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300170 cvls.pop('sync', None)
171 cvls.pop('direct', None)
172 cvls.pop('buffered', None)
173
174 if op_summ not in vls:
175 vls[op_summ] = cvls
176 else:
177 assert cvls == vls[op_summ]
178
179 all_labels = None
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300180 _, ax1 = plt.subplots()
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300181 for name, labels_and_data in labels_and_data_mp.items():
182 labels_and_data.sort(key=lambda x: ssize2b(x[0]))
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300183
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300184 labels, _, iotimes = zip(*labels_and_data)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300185
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300186 if all_labels is None:
187 all_labels = labels
188 else:
189 assert all_labels == labels
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300190
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300191 plt.boxplot(iotimes)
192 if len(labels_and_data) > 2 and \
193 ssize2b(labels_and_data[-2][0]) >= 4096:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300194
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300195 xt = range(1, len(labels) + 1)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300196
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300197 def io_time(sz, bw, initial_lat):
198 return sz / bw + initial_lat
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300199
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300200 x = numpy.array(map(ssize2b, labels))
201 y = numpy.array([sum(dt) / len(dt) for dt in iotimes])
202 popt, _ = scipy.optimize.curve_fit(io_time, x, y, p0=(100., 1.))
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300203
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300204 y1 = io_time(x, *popt)
205 plt.plot(xt, y1, linestyle='--',
206 label=name + ' LS linear approx')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300207
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300208 for idx, (sz, _, _) in enumerate(labels_and_data):
209 if ssize2b(sz) >= 4096:
210 break
211
212 bw = (x[-1] - x[idx]) / (y[-1] - y[idx])
213 lat = y[-1] - x[-1] / bw
214 y2 = io_time(x, bw, lat)
215 plt.plot(xt, y2, linestyle='--',
216 label=abbv_name_to_full(name) +
217 ' (4k & max) linear approx')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300218
219 plt.setp(ax1, xticklabels=labels)
220
221 plt.xlabel("Block size")
222 plt.ylabel("IO time, ms")
223
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300224 plt.subplots_adjust(top=0.85)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300225 plt.legend(bbox_to_anchor=(0.5, 1.15),
226 loc='upper center',
227 prop={'size': 10}, ncol=2)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300228 plt.grid()
229 iotime_plot = get_emb_data_svg(plt)
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300230 plt.clf()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300231
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300232 # plot IOPS = func(bsize)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300233 _, ax1 = plt.subplots()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300234
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300235 for name, labels_and_data in labels_and_data_mp.items():
236 labels_and_data.sort(key=lambda x: ssize2b(x[0]))
237 _, data, _ = zip(*labels_and_data)
238 plt.boxplot(data)
239 avg = [float(sum(arr)) / len(arr) for arr in data]
240 xt = range(1, len(data) + 1)
241 plt.plot(xt, avg, linestyle='--',
242 label=abbv_name_to_full(name) + " avg")
243
244 plt.setp(ax1, xticklabels=labels)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300245 plt.xlabel("Block size")
246 plt.ylabel("IOPS")
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300247 plt.legend(bbox_to_anchor=(0.5, 1.15),
248 loc='upper center',
249 prop={'size': 10}, ncol=2)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300250 plt.grid()
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300251 plt.subplots_adjust(top=0.85)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300252
253 iops_plot = get_emb_data_svg(plt)
254
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300255 res = set(get_test_lcheck_params(res) for res in processed_results.values())
256 ncount = list(set(res.testnodes_count for res in processed_results.values()))
257 conc = list(set(res.concurence for res in processed_results.values()))
258
259 assert len(conc) == 1
260 assert len(ncount) == 1
261
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300262 descr = {
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300263 'vm_count': ncount[0],
264 'concurence': conc[0],
265 'oper_descr': ", ".join(res).capitalize()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300266 }
267
268 params_map = {'iotime_vs_size': iotime_plot,
269 'iops_vs_size': iops_plot,
270 'descr': descr}
271
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300272 return get_template('report_linearity.html').format(**params_map)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300273
274
275@report('lat_vs_iops', 'lat_vs_iops')
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300276def lat_vs_iops(processed_results, lab_info, comment):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300277 lat_iops = collections.defaultdict(lambda: [])
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300278 requsted_vs_real = collections.defaultdict(lambda: {})
279
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300280 for res in processed_results.values():
281 if res.name.startswith('lat_vs_iops'):
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300282 lat_iops[res.concurence].append((res.lat,
283 0,
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300284 res.iops.average,
285 res.iops.deviation))
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300286 # lat_iops[res.concurence].append((res.lat.average / 1000.0,
287 # res.lat.deviation / 1000.0,
288 # res.iops.average,
289 # res.iops.deviation))
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300290 requested_iops = res.p.rate_iops * res.concurence
291 requsted_vs_real[res.concurence][requested_iops] = \
292 (res.iops.average, res.iops.deviation)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300293
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300294 colors = ['red', 'green', 'blue', 'orange', 'magenta', "teal"]
295 colors_it = iter(colors)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300296 for conc, lat_iops in sorted(lat_iops.items()):
297 lat, dev, iops, iops_dev = zip(*lat_iops)
298 plt.errorbar(iops, lat, xerr=iops_dev, yerr=dev, fmt='ro',
299 label=str(conc) + " threads",
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300300 color=next(colors_it))
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300301
302 plt.xlabel("IOPS")
303 plt.ylabel("Latency, ms")
304 plt.grid()
305 plt.legend(loc=0)
koder aka kdanilovfd2cfa52015-05-20 03:17:42 +0300306 plt_iops_vs_lat = get_emb_data_svg(plt)
307 plt.clf()
308
309 colors_it = iter(colors)
310 for conc, req_vs_real in sorted(requsted_vs_real.items()):
311 req, real = zip(*sorted(req_vs_real.items()))
312 iops, dev = zip(*real)
313 plt.errorbar(req, iops, yerr=dev, fmt='ro',
314 label=str(conc) + " threads",
315 color=next(colors_it))
316 plt.xlabel("Requested IOPS")
317 plt.ylabel("Get IOPS")
318 plt.grid()
319 plt.legend(loc=0)
320 plt_iops_vs_requested = get_emb_data_svg(plt)
321
322 res1 = processed_results.values()[0]
323 params_map = {'iops_vs_lat': plt_iops_vs_lat,
324 'iops_vs_requested': plt_iops_vs_requested,
325 'oper_descr': get_test_lcheck_params(res1).capitalize()}
326
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300327 return get_template('report_iops_vs_lat.html').format(**params_map)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300328
329
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300330def render_all_html(comment, info, lab_description, images, templ_name):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300331 data = info.__dict__.copy()
332 for name, val in data.items():
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300333 if not name.startswith('__'):
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300334 if val is None:
koder aka kdanilov765920a2016-04-12 00:35:48 +0300335 if name in ('direct_iops_w64_max', 'direct_iops_w_max'):
336 data[name] = ('-', '-', '-')
337 else:
338 data[name] = '-'
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300339 elif isinstance(val, (int, float, long)):
340 data[name] = round_3_digit(val)
koder aka kdanilov209e85d2015-04-27 23:11:05 +0300341
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300342 data['bw_read_max'] = (data['bw_read_max'][0] // 1024,
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300343 data['bw_read_max'][1],
344 data['bw_read_max'][2])
345
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300346 data['bw_write_max'] = (data['bw_write_max'][0] // 1024,
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300347 data['bw_write_max'][1],
348 data['bw_write_max'][2])
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300349
koder aka kdanilov765920a2016-04-12 00:35:48 +0300350 # templ_name = 'report_ceph_1.html'
351
352 # import pprint
353 # pprint.pprint(data)
354 # pprint.pprint(info.__dict__)
355
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300356 images.update(data)
koder aka kdanilov765920a2016-04-12 00:35:48 +0300357 templ = get_template(templ_name)
358 return templ.format(lab_info=lab_description,
359 comment=comment,
360 **images)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300361
362
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300363def io_chart(title, concurence,
364 latv, latv_min, latv_max,
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300365 iops_or_bw, iops_or_bw_err,
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300366 legend,
367 log_iops=False,
368 log_lat=False,
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300369 boxplots=False,
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300370 latv_50=None,
371 latv_95=None,
372 error2=None):
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300373
374 matplotlib.rcParams.update({'font.size': 10})
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300375 points = " MiBps" if legend == 'BW' else ""
376 lc = len(concurence)
377 width = 0.35
378 xt = range(1, lc + 1)
379
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300380 op_per_vm = [v / (vm * th) for v, (vm, th) in zip(iops_or_bw, concurence)]
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300381 fig, p1 = plt.subplots()
382 xpos = [i - width / 2 for i in xt]
383
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300384 p1.bar(xpos, iops_or_bw,
385 width=width,
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300386 color='y',
387 label=legend)
388
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300389 err1_leg = None
390 for pos, y, err in zip(xpos, iops_or_bw, iops_or_bw_err):
391 err1_leg = p1.errorbar(pos + width / 2,
392 y,
393 err,
394 color='magenta')
395
396 err2_leg = None
397 if error2 is not None:
398 for pos, y, err in zip(xpos, iops_or_bw, error2):
399 err2_leg = p1.errorbar(pos + width / 2 + 0.08,
400 y,
401 err,
402 lw=2,
403 alpha=0.5,
404 color='teal')
405
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300406 p1.grid(True)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300407 p1.plot(xt, op_per_vm, '--', label=legend + "/thread", color='black')
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300408 handles1, labels1 = p1.get_legend_handles_labels()
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300409
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300410 handles1 += [err1_leg]
411 labels1 += ["95% conf"]
412
413 if err2_leg is not None:
414 handles1 += [err2_leg]
415 labels1 += ["95% dev"]
416
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300417 p2 = p1.twinx()
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300418
419 if latv_50 is None:
420 p2.plot(xt, latv_max, label="lat max")
421 p2.plot(xt, latv, label="lat avg")
422 p2.plot(xt, latv_min, label="lat min")
423 else:
424 p2.plot(xt, latv_50, label="lat med")
425 p2.plot(xt, latv_95, label="lat 95%")
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300426
427 plt.xlim(0.5, lc + 0.5)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300428 plt.xticks(xt, ["{0} * {1}".format(vm, th) for (vm, th) in concurence])
429 p1.set_xlabel("VM Count * Thread per VM")
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300430 p1.set_ylabel(legend + points)
431 p2.set_ylabel("Latency ms")
432 plt.title(title)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300433 handles2, labels2 = p2.get_legend_handles_labels()
434
435 plt.legend(handles1 + handles2, labels1 + labels2,
436 loc='center left', bbox_to_anchor=(1.1, 0.81))
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300437
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300438 if log_iops:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300439 p1.set_yscale('log')
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300440
441 if log_lat:
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300442 p2.set_yscale('log')
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300443
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300444 plt.subplots_adjust(right=0.68)
445
446 return get_emb_data_svg(plt)
koder aka kdanilovd5ed4da2015-05-07 23:33:23 +0300447
448
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300449def make_plots(processed_results, plots):
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300450 """
451 processed_results: [PerfInfo]
452 plots = [(test_name_prefix:str, fname:str, description:str)]
453 """
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300454 files = {}
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300455 for name_pref, fname, desc in plots:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300456 chart_data = []
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300457
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300458 for res in processed_results:
459 summ = res.name + "_" + res.summary
460 if summ.startswith(name_pref):
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300461 chart_data.append(res)
462
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300463 if len(chart_data) == 0:
464 raise ValueError("Can't found any date for " + name_pref)
465
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300466 use_bw = ssize2b(chart_data[0].p.blocksize) > 16 * 1024
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300467
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300468 chart_data.sort(key=lambda x: x.params['vals']['numjobs'])
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300469
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300470 lat = None
471 lat_min = None
472 lat_max = None
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300473
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300474 lat_50 = [x.lat_50 for x in chart_data]
475 lat_95 = [x.lat_95 for x in chart_data]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300476
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300477 lat_diff_max = max(x.lat_95 / x.lat_50 for x in chart_data)
478 lat_log_scale = (lat_diff_max > 10)
479
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300480 testnodes_count = x.testnodes_count
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300481 concurence = [(testnodes_count, x.concurence)
koder aka kdanilov416b87a2015-05-12 00:26:04 +0300482 for x in chart_data]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300483
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300484 if use_bw:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300485 data = [x.bw.average / 1000 for x in chart_data]
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300486 data_conf = [x.bw.confidence / 1000 for x in chart_data]
487 data_dev = [x.bw.deviation * 2.5 / 1000 for x in chart_data]
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300488 name = "BW"
489 else:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300490 data = [x.iops.average for x in chart_data]
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300491 data_conf = [x.iops.confidence for x in chart_data]
492 data_dev = [x.iops.deviation * 2 for x in chart_data]
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300493 name = "IOPS"
494
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300495 fc = io_chart(title=desc,
496 concurence=concurence,
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300497
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300498 latv=lat,
499 latv_min=lat_min,
500 latv_max=lat_max,
501
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300502 iops_or_bw=data,
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300503 iops_or_bw_err=data_conf,
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300504
505 legend=name,
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300506 log_lat=lat_log_scale,
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300507
508 latv_50=lat_50,
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300509 latv_95=lat_95,
510
511 error2=data_dev)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300512 files[fname] = fc
513
514 return files
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300515
516
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300517def find_max_where(processed_results, sync_mode, blocksize, rw, iops=True):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300518 result = None
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300519 attr = 'iops' if iops else 'bw'
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300520 for measurement in processed_results:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300521 ok = measurement.sync_mode == sync_mode
522 ok = ok and (measurement.p.blocksize == blocksize)
523 ok = ok and (measurement.p.rw == rw)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300524
525 if ok:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300526 field = getattr(measurement, attr)
527
528 if result is None:
529 result = field
530 elif field.average > result.average:
531 result = field
532
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300533 return result
534
535
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300536def get_disk_info(processed_results):
537 di = DiskInfo()
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300538 di.direct_iops_w_max = find_max_where(processed_results,
539 'd', '4k', 'randwrite')
540 di.direct_iops_r_max = find_max_where(processed_results,
541 'd', '4k', 'randread')
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300542
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300543 di.direct_iops_w64_max = find_max_where(processed_results,
544 'd', '64k', 'randwrite')
545
546 for sz in ('16m', '64m'):
547 di.bw_write_max = find_max_where(processed_results,
548 'd', sz, 'randwrite', False)
549 if di.bw_write_max is not None:
550 break
551
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300552 if di.bw_write_max is None:
koder aka kdanilov170936a2015-06-27 22:51:17 +0300553 for sz in ('1m', '2m', '4m', '8m'):
554 di.bw_write_max = find_max_where(processed_results,
555 'd', sz, 'write', False)
556 if di.bw_write_max is not None:
557 break
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300558
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300559 for sz in ('16m', '64m'):
560 di.bw_read_max = find_max_where(processed_results,
561 'd', sz, 'randread', False)
562 if di.bw_read_max is not None:
563 break
564
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300565 if di.bw_read_max is None:
566 di.bw_read_max = find_max_where(processed_results,
567 'd', '1m', 'read', False)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300568
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300569 rws4k_iops_lat_th = []
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300570 for res in processed_results:
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300571 if res.sync_mode in 'xs' and res.p.blocksize == '4k':
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300572 if res.p.rw != 'randwrite':
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300573 continue
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300574 rws4k_iops_lat_th.append((res.iops.average,
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300575 res.lat,
576 # res.lat.average,
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300577 res.concurence))
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300578
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300579 rws4k_iops_lat_th.sort(key=lambda (_1, _2, conc): conc)
580
581 latv = [lat for _, lat, _ in rws4k_iops_lat_th]
582
koder aka kdanilov170936a2015-06-27 22:51:17 +0300583 for tlat in [10, 30, 100]:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300584 pos = bisect.bisect_left(latv, tlat)
585 if 0 == pos:
koder aka kdanilov170936a2015-06-27 22:51:17 +0300586 setattr(di, 'rws4k_{}ms'.format(tlat), 0)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300587 elif pos == len(latv):
koder aka kdanilovbb5fe072015-05-21 02:50:23 +0300588 iops3, _, _ = rws4k_iops_lat_th[-1]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300589 iops3 = int(round_3_digit(iops3))
koder aka kdanilov170936a2015-06-27 22:51:17 +0300590 setattr(di, 'rws4k_{}ms'.format(tlat), ">=" + str(iops3))
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300591 else:
592 lat1 = latv[pos - 1]
593 lat2 = latv[pos]
594
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300595 iops1, _, th1 = rws4k_iops_lat_th[pos - 1]
596 iops2, _, th2 = rws4k_iops_lat_th[pos]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300597
598 th_lat_coef = (th2 - th1) / (lat2 - lat1)
599 th3 = th_lat_coef * (tlat - lat1) + th1
600
601 th_iops_coef = (iops2 - iops1) / (th2 - th1)
602 iops3 = th_iops_coef * (th3 - th1) + iops1
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300603 iops3 = int(round_3_digit(iops3))
604 setattr(di, 'rws4k_{}ms'.format(tlat), iops3)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300605
606 hdi = DiskInfo()
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300607
608 def pp(x):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300609 med, conf = x.rounded_average_conf()
610 conf_perc = int(float(conf) / med * 100)
koder aka kdanilovc0c97e22015-07-21 00:08:33 +0300611 dev_perc = int(float(x.deviation) / med * 100)
612 return (round_3_digit(med), conf_perc, dev_perc)
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300613
614 hdi.direct_iops_r_max = pp(di.direct_iops_r_max)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300615
616 if di.direct_iops_w_max is not None:
617 hdi.direct_iops_w_max = pp(di.direct_iops_w_max)
618 else:
619 hdi.direct_iops_w_max = None
620
621 if di.direct_iops_w64_max is not None:
622 hdi.direct_iops_w64_max = pp(di.direct_iops_w64_max)
623 else:
624 hdi.direct_iops_w64_max = None
625
koder aka kdanilov7e0f7cf2015-05-01 17:24:35 +0300626 hdi.bw_write_max = pp(di.bw_write_max)
627 hdi.bw_read_max = pp(di.bw_read_max)
628
koder aka kdanilovc368eb62015-04-28 18:22:01 +0300629 hdi.rws4k_10ms = di.rws4k_10ms if 0 != di.rws4k_10ms else None
630 hdi.rws4k_30ms = di.rws4k_30ms if 0 != di.rws4k_30ms else None
631 hdi.rws4k_100ms = di.rws4k_100ms if 0 != di.rws4k_100ms else None
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300632 return hdi
633
634
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300635@report('hdd', 'hdd')
636def make_hdd_report(processed_results, lab_info, comment):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300637 plots = [
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300638 ('hdd_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
639 ('hdd_rwx4k', 'rand_write_4k', 'Random write 4k sync IOPS')
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300640 ]
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300641 perf_infos = [res.disk_perf_info() for res in processed_results]
642 images = make_plots(perf_infos, plots)
643 di = get_disk_info(perf_infos)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300644 return render_all_html(comment, di, lab_info, images, "report_hdd.html")
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300645
646
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300647@report('cinder_iscsi', 'cinder_iscsi')
648def make_cinder_iscsi_report(processed_results, lab_info, comment):
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300649 plots = [
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300650 ('cinder_iscsi_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
651 ('cinder_iscsi_rwx4k', 'rand_write_4k', 'Random write 4k sync IOPS')
652 ]
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300653 perf_infos = [res.disk_perf_info() for res in processed_results]
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300654 try:
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300655 images = make_plots(perf_infos, plots)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300656 except ValueError:
657 plots = [
658 ('cinder_iscsi_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
659 ('cinder_iscsi_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS')
660 ]
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300661 images = make_plots(perf_infos, plots)
662 di = get_disk_info(perf_infos)
koder aka kdanilov170936a2015-06-27 22:51:17 +0300663
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300664 return render_all_html(comment, di, lab_info, images, "report_cinder_iscsi.html")
665
666
667@report('ceph', 'ceph')
668def make_ceph_report(processed_results, lab_info, comment):
669 plots = [
670 ('ceph_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
671 ('ceph_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS'),
672 ('ceph_rrd16m', 'rand_read_16m', 'Random read 16m direct MiBps'),
673 ('ceph_rwd16m', 'rand_write_16m',
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300674 'Random write 16m direct MiBps'),
675 ]
676
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300677 perf_infos = [res.disk_perf_info() for res in processed_results]
678 images = make_plots(perf_infos, plots)
679 di = get_disk_info(perf_infos)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300680 return render_all_html(comment, di, lab_info, images, "report_ceph.html")
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300681
682
koder aka kdanilov7248c7b2015-05-31 22:53:03 +0300683@report('mixed', 'mixed')
684def make_mixed_report(processed_results, lab_info, comment):
685 #
686 # IOPS(X% read) = 100 / ( X / IOPS_W + (100 - X) / IOPS_R )
687 #
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300688
689 perf_infos = [res.disk_perf_info() for res in processed_results]
koder aka kdanilov7248c7b2015-05-31 22:53:03 +0300690 mixed = collections.defaultdict(lambda: [])
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300691
692 is_ssd = False
693 for res in perf_infos:
koder aka kdanilov7248c7b2015-05-31 22:53:03 +0300694 if res.name.startswith('mixed'):
695 if res.name.startswith('mixed-ssd'):
696 is_ssd = True
697 mixed[res.concurence].append((res.p.rwmixread,
koder aka kdanilovf236b9c2015-06-24 18:17:22 +0300698 res.lat,
699 0,
700 # res.lat.average / 1000.0,
701 # res.lat.deviation / 1000.0,
koder aka kdanilov7248c7b2015-05-31 22:53:03 +0300702 res.iops.average,
703 res.iops.deviation))
704
705 if len(mixed) == 0:
706 raise ValueError("No mixed load found")
707
708 fig, p1 = plt.subplots()
709 p2 = p1.twinx()
710
711 colors = ['red', 'green', 'blue', 'orange', 'magenta', "teal"]
712 colors_it = iter(colors)
713 for conc, mix_lat_iops in sorted(mixed.items()):
714 mix_lat_iops = sorted(mix_lat_iops)
715 read_perc, lat, dev, iops, iops_dev = zip(*mix_lat_iops)
716 p1.errorbar(read_perc, iops, color=next(colors_it),
717 yerr=iops_dev, label=str(conc) + " th")
718
719 p2.errorbar(read_perc, lat, color=next(colors_it),
720 ls='--', yerr=dev, label=str(conc) + " th lat")
721
722 if is_ssd:
723 p1.set_yscale('log')
724 p2.set_yscale('log')
725
726 p1.set_xlim(-5, 105)
727
728 read_perc = set(read_perc)
729 read_perc.add(0)
730 read_perc.add(100)
731 read_perc = sorted(read_perc)
732
733 plt.xticks(read_perc, map(str, read_perc))
734
735 p1.grid(True)
736 p1.set_xlabel("% of reads")
737 p1.set_ylabel("Mixed IOPS")
738 p2.set_ylabel("Latency, ms")
739
740 handles1, labels1 = p1.get_legend_handles_labels()
741 handles2, labels2 = p2.get_legend_handles_labels()
742 plt.subplots_adjust(top=0.85)
743 plt.legend(handles1 + handles2, labels1 + labels2,
744 bbox_to_anchor=(0.5, 1.15),
745 loc='upper center',
746 prop={'size': 12}, ncol=3)
747 plt.show()
748
749
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300750def make_load_report(idx, results_dir, fname):
751 dpath = os.path.join(results_dir, "io_" + str(idx))
752 files = sorted(os.listdir(dpath))
753 gf = lambda x: "_".join(x.rsplit(".", 1)[0].split('_')[:3])
754
755 for key, group in itertools.groupby(files, gf):
756 fname = os.path.join(dpath, key + ".fio")
757
758 cfgs = list(parse_all_in_1(open(fname).read(), fname))
759
760 fname = os.path.join(dpath, key + "_lat.log")
761
762 curr = []
763 arrays = []
764
765 with open(fname) as fd:
766 for offset, lat, _, _ in csv.reader(fd):
767 offset = int(offset)
768 lat = int(lat)
769 if len(curr) > 0 and curr[-1][0] > offset:
770 arrays.append(curr)
771 curr = []
772 curr.append((offset, lat))
773 arrays.append(curr)
774 conc = int(cfgs[0].vals.get('numjobs', 1))
775
776 if conc != 5:
777 continue
778
779 assert len(arrays) == len(cfgs) * conc
780
781 garrays = [[(0, 0)] for _ in range(conc)]
782
783 for offset in range(len(cfgs)):
784 for acc, new_arr in zip(garrays, arrays[offset * conc:(offset + 1) * conc]):
785 last = acc[-1][0]
786 for off, lat in new_arr:
787 acc.append((off / 1000. + last, lat / 1000.))
788
789 for cfg, arr in zip(cfgs, garrays):
790 plt.plot(*zip(*arr[1:]))
791 plt.show()
792 exit(1)
793
794
795def make_io_report(dinfo, comment, path, lab_info=None):
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300796 lab_info = {
797 "total_disk": "None",
798 "total_memory": "None",
799 "nodes_count": "None",
800 "processor_count": "None"
801 }
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300802
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300803 try:
koder aka kdanilovbb6d6cd2015-06-20 02:55:07 +0300804 res_fields = sorted(v.name for v in dinfo)
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300805
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300806 found = False
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300807 for fields, name, func in report_funcs:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300808 for field in fields:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300809 pos = bisect.bisect_left(res_fields, field)
810
811 if pos == len(res_fields):
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300812 break
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300813
koder aka kdanilovbe8f89f2015-04-28 14:51:51 +0300814 if not res_fields[pos].startswith(field):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300815 break
816 else:
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300817 found = True
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300818 hpath = path.format(name)
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300819
820 try:
821 report = func(dinfo, lab_info, comment)
822 except:
823 logger.exception("Diring {0} report generation".format(name))
824 continue
825
koder aka kdanilov7248c7b2015-05-31 22:53:03 +0300826 if report is not None:
827 try:
828 with open(hpath, "w") as fd:
829 fd.write(report)
830 except:
831 logger.exception("Diring saving {0} report".format(name))
832 continue
833 logger.info("Report {0} saved into {1}".format(name, hpath))
834 else:
835 logger.warning("No report produced by {0!r}".format(name))
koder aka kdanilov88407ff2015-05-26 15:35:57 +0300836
837 if not found:
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300838 logger.warning("No report generator found for this load")
koder aka kdanilovafd98742015-04-24 01:27:22 +0300839
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300840 except Exception as exc:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300841 import traceback
842 traceback.print_exc()
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300843 logger.error("Failed to generate html report:" + str(exc))