blob: 260c031244a8168ab445d9263bdaa53ff03dc50b [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 kdanilovcff7b2e2015-04-18 20:48:15 +03004
koder aka kdanilov4a510ee2015-04-21 18:50:42 +03005import wally
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +03006from wally import charts
koder aka kdanilov63ad2062015-04-27 13:11:40 +03007from wally.utils import parse_creds, ssize_to_b
koder aka kdanilov4a510ee2015-04-21 18:50:42 +03008from wally.suits.io.results_loader import process_disk_info
koder aka kdanilovea7eac72015-04-21 21:37:27 +03009from wally.meta_info import total_lab_info, collect_lab_data
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030010
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030011
koder aka kdanilova047e1b2015-04-21 23:16:59 +030012logger = logging.getLogger("wally.report")
13
14
koder aka kdanilov63ad2062015-04-27 13:11:40 +030015def render_hdd_html(dest, info, lab_description):
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030016 very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
17 templ_dir = os.path.join(very_root_dir, 'report_templates')
koder aka kdanilov63ad2062015-04-27 13:11:40 +030018 templ_file = os.path.join(templ_dir, "report_hdd.html")
19 templ = open(templ_file, 'r').read()
20 report = templ.format(lab_info=lab_description, **info.__dict__)
21 open(dest, 'w').write(report)
22
23
24def render_ceph_html(dest, info, lab_description):
25 very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
26 templ_dir = os.path.join(very_root_dir, 'report_templates')
27 templ_file = os.path.join(templ_dir, "report_ceph.html")
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030028 templ = open(templ_file, 'r').read()
29 report = templ.format(lab_info=lab_description, **info.__dict__)
30 open(dest, 'w').write(report)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030031
32
33def io_chart(title, concurence, latv, iops_or_bw, iops_or_bw_dev,
34 legend, fname):
35 bar_data, bar_dev = iops_or_bw, iops_or_bw_dev
36 legend = [legend]
37
38 iops_or_bw_per_vm = []
39 for i in range(len(concurence)):
40 iops_or_bw_per_vm.append(iops_or_bw[i] / concurence[i])
41
42 bar_dev_bottom = []
43 bar_dev_top = []
44 for i in range(len(bar_data)):
45 bar_dev_top.append(bar_data[i] + bar_dev[i])
46 bar_dev_bottom.append(bar_data[i] - bar_dev[i])
47
48 latv = [lat / 1000 for lat in latv]
49 ch = charts.render_vertical_bar(title, legend, [bar_data], [bar_dev_top],
50 [bar_dev_bottom], file_name=fname,
51 scale_x=concurence,
52 lines=[
53 (latv, "msec", "rr", "lat"),
54 (iops_or_bw_per_vm, None, None,
koder aka kdanilov63ad2062015-04-27 13:11:40 +030055 legend[0] + " per thread")
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030056 ])
57 return str(ch)
58
59
koder aka kdanilov63ad2062015-04-27 13:11:40 +030060def make_hdd_plots(processed_results, path):
61 plots = [
62 ('hdd_test_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
koder aka kdanilovea7eac72015-04-21 21:37:27 +030063 ('hdd_test_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS')
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030064 ]
koder aka kdanilov63ad2062015-04-27 13:11:40 +030065 make_plots(processed_results, path, plots)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030066
koder aka kdanilov63ad2062015-04-27 13:11:40 +030067
68def make_ceph_plots(processed_results, path):
69 plots = [
70 ('ceph_test_rrd4k', 'rand_read_4k', 'Random read 4k direct IOPS'),
71 ('ceph_test_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS'),
72 ('ceph_test_rrd16m', 'rand_read_16m', 'Random read 16m direct MiBps'),
73 ('ceph_test_swd1m', 'seq_write_1m',
74 'Sequential write 1m direct MiBps'),
75 ]
76 make_plots(processed_results, path, plots)
77
78
79def make_plots(processed_results, path, plots):
80 for name_pref, fname, desc in plots:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030081 chart_data = []
koder aka kdanilov63ad2062015-04-27 13:11:40 +030082
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030083 for res in processed_results.values():
84 if res.name.startswith(name_pref):
85 chart_data.append(res)
86
koder aka kdanilov63ad2062015-04-27 13:11:40 +030087 if len(chart_data) == 0:
88 raise ValueError("Can't found any date for " + name_pref)
89
90 use_bw = ssize_to_b(chart_data[0].raw['blocksize']) > 16 * 1024
91
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030092 chart_data.sort(key=lambda x: x.raw['concurence'])
93
94 lat = [x.lat for x in chart_data]
95 concurence = [x.raw['concurence'] for x in chart_data]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030096
koder aka kdanilov63ad2062015-04-27 13:11:40 +030097 if use_bw:
98 data = [x.bw for x in chart_data]
99 data_dev = [x.bw * x.dev for x in chart_data]
100 name = "BW"
101 else:
102 data = [x.iops for x in chart_data]
103 data_dev = [x.iops * x.dev for x in chart_data]
104 name = "IOPS"
105
106 io_chart(desc, concurence, lat, data, data_dev, name, fname)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300107
108
109class DiskInfo(object):
110 def __init__(self):
111 self.direct_iops_r_max = 0
112 self.direct_iops_w_max = 0
113 self.rws4k_10ms = 0
114 self.rws4k_30ms = 0
115 self.rws4k_100ms = 0
116 self.bw_write_max = 0
117 self.bw_read_max = 0
118
119
120def get_disk_info(processed_results):
121 di = DiskInfo()
122 rws4k_iops_lat_th = []
123
124 for res in processed_results.values():
125 if res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '4k':
126 if res.raw['rw'] == 'randwrite':
127 di.direct_iops_w_max = max(di.direct_iops_w_max, res.iops)
128 elif res.raw['rw'] == 'randread':
129 di.direct_iops_r_max = max(di.direct_iops_r_max, res.iops)
130 elif res.raw['sync_mode'] == 's' and res.raw['blocksize'] == '4k':
131 if res.raw['rw'] != 'randwrite':
132 continue
133
134 rws4k_iops_lat_th.append((res.iops, res.lat,
135 res.raw['concurence']))
136
137 elif res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '1m':
138
139 if res.raw['rw'] == 'write':
140 di.bw_write_max = max(di.bw_write_max, res.bw)
141 elif res.raw['rw'] == 'read':
142 di.bw_read_max = max(di.bw_read_max, res.bw)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300143 elif res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '16m':
144 if res.raw['rw'] == 'write' or res.raw['rw'] == 'randwrite':
145 di.bw_write_max = max(di.bw_write_max, res.bw)
146 elif res.raw['rw'] == 'read' or res.raw['rw'] == 'randread':
147 di.bw_read_max = max(di.bw_read_max, res.bw)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300148
149 di.bw_write_max /= 1000
150 di.bw_read_max /= 1000
151
152 rws4k_iops_lat_th.sort(key=lambda (_1, _2, conc): conc)
153
154 latv = [lat for _, lat, _ in rws4k_iops_lat_th]
155
156 for tlatv_ms in [10, 30, 100]:
157 tlat = tlatv_ms * 1000
158 pos = bisect.bisect_left(latv, tlat)
159 if 0 == pos:
160 iops3 = 0
161 elif pos == len(latv):
162 iops3 = latv[-1]
163 else:
164 lat1 = latv[pos - 1]
165 lat2 = latv[pos]
166
167 th1 = rws4k_iops_lat_th[pos - 1][2]
168 th2 = rws4k_iops_lat_th[pos][2]
169
170 iops1 = rws4k_iops_lat_th[pos - 1][0]
171 iops2 = rws4k_iops_lat_th[pos][0]
172
173 th_lat_coef = (th2 - th1) / (lat2 - lat1)
174 th3 = th_lat_coef * (tlat - lat1) + th1
175
176 th_iops_coef = (iops2 - iops1) / (th2 - th1)
177 iops3 = th_iops_coef * (th3 - th1) + iops1
178 setattr(di, 'rws4k_{}ms'.format(tlatv_ms), int(iops3))
179
180 hdi = DiskInfo()
181 hdi.direct_iops_r_max = di.direct_iops_r_max
182 hdi.direct_iops_w_max = di.direct_iops_w_max
183 hdi.rws4k_10ms = di.rws4k_10ms if 0 != di.rws4k_10ms else '-'
184 hdi.rws4k_30ms = di.rws4k_30ms if 0 != di.rws4k_30ms else '-'
185 hdi.rws4k_100ms = di.rws4k_100ms if 0 != di.rws4k_100ms else '-'
186 hdi.bw_write_max = di.bw_write_max
187 hdi.bw_read_max = di.bw_read_max
188 return hdi
189
190
koder aka kdanilovafd98742015-04-24 01:27:22 +0300191report_funcs = []
192
193
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300194def report(name, required_fields):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300195 def closure(func):
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300196 report_funcs.append((required_fields.split(","), name, func))
koder aka kdanilovafd98742015-04-24 01:27:22 +0300197 return func
198 return closure
199
200
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300201@report('HDD', 'hdd_test_rrd4k,hdd_test_rws4k')
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300202def make_hdd_report(processed_results, path, lab_info):
203 make_plots(processed_results, path)
204 di = get_disk_info(processed_results)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300205 render_hdd_html(path, di, lab_info)
206
207
208@report('Ceph', 'ceph_test')
209def make_ceph_report(processed_results, path, lab_info):
210 make_ceph_plots(processed_results, path)
211 di = get_disk_info(processed_results)
212 render_ceph_html(path, di, lab_info)
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300213
214
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300215def make_io_report(results, path, lab_url=None, creds=None):
216 if lab_url is not None:
217 username, password, tenant_name = parse_creds(creds)
218 creds = {'username': username,
219 'password': password,
220 "tenant_name": tenant_name}
221 data = collect_lab_data(lab_url, creds)
222 lab_info = total_lab_info(data)
223 else:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300224 lab_info = {
225 "total_disk": "None",
226 "total_memory": "None",
227 "nodes_count": "None",
228 "processor_count": "None"
229 }
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300230
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300231 try:
232 processed_results = process_disk_info(results)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300233 res_fields = sorted(processed_results.keys())
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300234 for fields, name, func in report_funcs:
koder aka kdanilovafd98742015-04-24 01:27:22 +0300235 for field in fields:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300236 pos = bisect.bisect_left(res_fields, field)
237
238 if pos == len(res_fields):
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300239 break
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300240
241 if not res_fields[pos + 1].startswith(field):
koder aka kdanilovafd98742015-04-24 01:27:22 +0300242 break
243 else:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300244 hpath = path.format(name)
koder aka kdanilov63ad2062015-04-27 13:11:40 +0300245 logger.debug("Generatins report " + name + " into " + hpath)
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300246 func(processed_results, hpath, lab_info)
koder aka kdanilovafd98742015-04-24 01:27:22 +0300247 break
koder aka kdanilova4a570f2015-04-23 22:11:40 +0300248 else:
249 logger.warning("No report generator found for this load")
koder aka kdanilovafd98742015-04-24 01:27:22 +0300250
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300251 except Exception as exc:
koder aka kdanilov57ce4db2015-04-25 21:25:51 +0300252 import traceback
253 traceback.print_exc()
koder aka kdanilovec1b9732015-04-23 20:43:29 +0300254 logger.error("Failed to generate html report:" + str(exc))