blob: 85ac38888ba0b905ffbeb4713a565536f6332714 [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 kdanilovcff7b2e2015-04-18 20:48:15 +03007from wally.utils import parse_creds
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 kdanilov4a510ee2015-04-21 18:50:42 +030015def render_html(dest, info, lab_description):
16 very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
17 templ_dir = os.path.join(very_root_dir, 'report_templates')
18 templ_file = os.path.join(templ_dir, "report.html")
19 templ = open(templ_file, 'r').read()
20 report = templ.format(lab_info=lab_description, **info.__dict__)
21 open(dest, 'w').write(report)
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030022
23
24def io_chart(title, concurence, latv, iops_or_bw, iops_or_bw_dev,
25 legend, fname):
26 bar_data, bar_dev = iops_or_bw, iops_or_bw_dev
27 legend = [legend]
28
29 iops_or_bw_per_vm = []
30 for i in range(len(concurence)):
31 iops_or_bw_per_vm.append(iops_or_bw[i] / concurence[i])
32
33 bar_dev_bottom = []
34 bar_dev_top = []
35 for i in range(len(bar_data)):
36 bar_dev_top.append(bar_data[i] + bar_dev[i])
37 bar_dev_bottom.append(bar_data[i] - bar_dev[i])
38
39 latv = [lat / 1000 for lat in latv]
40 ch = charts.render_vertical_bar(title, legend, [bar_data], [bar_dev_top],
41 [bar_dev_bottom], file_name=fname,
42 scale_x=concurence,
43 lines=[
44 (latv, "msec", "rr", "lat"),
45 (iops_or_bw_per_vm, None, None,
koder aka kdanilovea7eac72015-04-21 21:37:27 +030046 "IOPS per vm")
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +030047 ])
48 return str(ch)
49
50
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030051def make_plots(processed_results, path):
52 name_filters = [
koder aka kdanilovea7eac72015-04-21 21:37:27 +030053 ('hdd_test_rrd4k', 'rand_read_4k', 'Random read 4k sync IOPS'),
54 ('hdd_test_rws4k', 'rand_write_4k', 'Random write 4k sync IOPS')
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030055 ]
56
koder aka kdanilovea7eac72015-04-21 21:37:27 +030057 for name_pref, fname, desc in name_filters:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030058 chart_data = []
59 for res in processed_results.values():
60 if res.name.startswith(name_pref):
61 chart_data.append(res)
62
63 chart_data.sort(key=lambda x: x.raw['concurence'])
64
65 lat = [x.lat for x in chart_data]
66 concurence = [x.raw['concurence'] for x in chart_data]
koder aka kdanilovea7eac72015-04-21 21:37:27 +030067 iops = [x.iops for x in chart_data]
68 iops_dev = [x.iops * x.dev for x in chart_data]
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030069
koder aka kdanilovea7eac72015-04-21 21:37:27 +030070 io_chart(desc, concurence, lat, iops, iops_dev, 'bw', fname)
koder aka kdanilov4a510ee2015-04-21 18:50:42 +030071
72
73class DiskInfo(object):
74 def __init__(self):
75 self.direct_iops_r_max = 0
76 self.direct_iops_w_max = 0
77 self.rws4k_10ms = 0
78 self.rws4k_30ms = 0
79 self.rws4k_100ms = 0
80 self.bw_write_max = 0
81 self.bw_read_max = 0
82
83
84def get_disk_info(processed_results):
85 di = DiskInfo()
86 rws4k_iops_lat_th = []
87
88 for res in processed_results.values():
89 if res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '4k':
90 if res.raw['rw'] == 'randwrite':
91 di.direct_iops_w_max = max(di.direct_iops_w_max, res.iops)
92 elif res.raw['rw'] == 'randread':
93 di.direct_iops_r_max = max(di.direct_iops_r_max, res.iops)
94 elif res.raw['sync_mode'] == 's' and res.raw['blocksize'] == '4k':
95 if res.raw['rw'] != 'randwrite':
96 continue
97
98 rws4k_iops_lat_th.append((res.iops, res.lat,
99 res.raw['concurence']))
100
101 elif res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '1m':
102
103 if res.raw['rw'] == 'write':
104 di.bw_write_max = max(di.bw_write_max, res.bw)
105 elif res.raw['rw'] == 'read':
106 di.bw_read_max = max(di.bw_read_max, res.bw)
107
108 di.bw_write_max /= 1000
109 di.bw_read_max /= 1000
110
111 rws4k_iops_lat_th.sort(key=lambda (_1, _2, conc): conc)
112
113 latv = [lat for _, lat, _ in rws4k_iops_lat_th]
114
115 for tlatv_ms in [10, 30, 100]:
116 tlat = tlatv_ms * 1000
117 pos = bisect.bisect_left(latv, tlat)
118 if 0 == pos:
119 iops3 = 0
120 elif pos == len(latv):
121 iops3 = latv[-1]
122 else:
123 lat1 = latv[pos - 1]
124 lat2 = latv[pos]
125
126 th1 = rws4k_iops_lat_th[pos - 1][2]
127 th2 = rws4k_iops_lat_th[pos][2]
128
129 iops1 = rws4k_iops_lat_th[pos - 1][0]
130 iops2 = rws4k_iops_lat_th[pos][0]
131
132 th_lat_coef = (th2 - th1) / (lat2 - lat1)
133 th3 = th_lat_coef * (tlat - lat1) + th1
134
135 th_iops_coef = (iops2 - iops1) / (th2 - th1)
136 iops3 = th_iops_coef * (th3 - th1) + iops1
137 setattr(di, 'rws4k_{}ms'.format(tlatv_ms), int(iops3))
138
139 hdi = DiskInfo()
140 hdi.direct_iops_r_max = di.direct_iops_r_max
141 hdi.direct_iops_w_max = di.direct_iops_w_max
142 hdi.rws4k_10ms = di.rws4k_10ms if 0 != di.rws4k_10ms else '-'
143 hdi.rws4k_30ms = di.rws4k_30ms if 0 != di.rws4k_30ms else '-'
144 hdi.rws4k_100ms = di.rws4k_100ms if 0 != di.rws4k_100ms else '-'
145 hdi.bw_write_max = di.bw_write_max
146 hdi.bw_read_max = di.bw_read_max
147 return hdi
148
149
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300150def make_io_report(results, path, lab_url=None, creds=None):
151 if lab_url is not None:
152 username, password, tenant_name = parse_creds(creds)
153 creds = {'username': username,
154 'password': password,
155 "tenant_name": tenant_name}
156 data = collect_lab_data(lab_url, creds)
157 lab_info = total_lab_info(data)
158 else:
koder aka kdanilov4a510ee2015-04-21 18:50:42 +0300159 lab_info = {
160 "total_disk": "None",
161 "total_memory": "None",
162 "nodes_count": "None",
163 "processor_count": "None"
164 }
koder aka kdanilovcff7b2e2015-04-18 20:48:15 +0300165
koder aka kdanilova047e1b2015-04-21 23:16:59 +0300166 try:
167 processed_results = process_disk_info(results)
168 make_plots(processed_results, path)
169 di = get_disk_info(processed_results)
170 render_html(path, di, lab_info)
171 except Exception as exc:
172 logger.error("Failed to generate html report:" + exc.message)
173 else:
174 logger.info("Html report were stored in " + path)