Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 1 | """ Analize test results for finding bottlenecks """ |
| 2 | |
| 3 | import sys |
| 4 | import argparse |
| 5 | |
| 6 | import texttable as TT |
| 7 | |
| 8 | from collections import namedtuple |
| 9 | |
| 10 | |
| 11 | Record = namedtuple("Record", ['name', 'max_value']) |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 12 | MetricValue = namedtuple("MetricValue", ['value', 'time']) |
| 13 | Bottleneck = namedtuple("Bottleneck", ['node', 'value', 'count']) |
| 14 | |
| 15 | sortRuleByValue = lambda x: x.value |
| 16 | sortRuleByMaxValue = lambda x: x.max_value |
| 17 | sortRuleByCount = lambda x: x.count |
| 18 | sortRuleByTime = lambda x: x.time |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 19 | |
| 20 | critical_values = [ |
| 21 | Record("io_queue", 1), |
| 22 | Record("procs_blocked", 1), |
| 23 | Record("mem_usage_percent", 0.8) |
| 24 | ] |
| 25 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 26 | |
| 27 | def get_name_from_sourceid(source_id): |
| 28 | """ Cut port """ |
| 29 | pos = source_id.rfind(":") |
| 30 | return source_id[:pos] |
| 31 | |
| 32 | |
| 33 | def create_table(header, rows, signs=1): |
| 34 | """ Return texttable view """ |
| 35 | tab = TT.Texttable() |
| 36 | tab.set_deco(tab.VLINES) |
| 37 | tab.set_precision(signs) |
| 38 | tab.add_row(header) |
| 39 | tab.header = header |
| 40 | |
| 41 | for row in rows: |
| 42 | tab.add_row(row) |
| 43 | |
| 44 | return tab.draw() |
| 45 | |
| 46 | |
| 47 | def load_results(period, rfile): |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 48 | """ Read raw results from dir and return |
| 49 | data from provided period""" |
| 50 | results = {} |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 51 | if period is not None: |
| 52 | begin_time, end_time = period |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 53 | with open(rfile, "r") as f: |
| 54 | for line in f: |
| 55 | |
| 56 | if len(line) <= 1: |
| 57 | continue |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 58 | if " : " in line: |
| 59 | # old format |
| 60 | ttime, _, raw_data = line.partition(" : ") |
| 61 | raw_data = raw_data.strip('"\n\r') |
| 62 | itime = float(ttime) |
| 63 | else: |
| 64 | # new format without time |
| 65 | raw_data = line.strip('"\n\r') |
| 66 | |
| 67 | _, data = eval(raw_data) |
| 68 | sid = get_name_from_sourceid(data.pop("source_id")) |
| 69 | itime = data.pop("time") |
| 70 | |
| 71 | if period is None or (itime >= begin_time and itime <= end_time): |
| 72 | serv_data = results.setdefault(sid, {}) |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 73 | for key, value in data.items(): |
| 74 | # select device and metric names |
| 75 | dev, _, metric = key.partition(".") |
| 76 | # create dict for metric |
| 77 | metric_dict = serv_data.setdefault(metric, {}) |
| 78 | # set value for metric on dev |
| 79 | cur_val = metric_dict.setdefault(dev, []) |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 80 | cur_val.append(MetricValue(value, itime)) |
| 81 | |
| 82 | # sort by time |
| 83 | for ms in results.values(): |
| 84 | for dev in ms.values(): |
| 85 | for d in dev.keys(): |
| 86 | dev[d] = sorted(dev[d], key=sortRuleByTime) |
| 87 | |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 88 | return results |
| 89 | |
| 90 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 91 | def find_time_load_percent(data, params): |
| 92 | """ Find avg load of components by time |
| 93 | and return sorted table """ |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 94 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 95 | header = ["Component", "Avg load %"] |
| 96 | name_fmt = "{0}.{1}" |
| 97 | value_fmt = "{0:.1f}" |
| 98 | loads = [] |
| 99 | for node, metrics in data.items(): |
| 100 | for metric, max_value in params: |
| 101 | if metric in metrics: |
| 102 | item = metrics[metric] |
| 103 | # count time it was > max_value |
| 104 | # count times it was > max_value |
| 105 | for dev, vals in item.items(): |
| 106 | num_l = 0 |
| 107 | times = [] |
| 108 | i = 0 |
| 109 | while i < len(vals): |
| 110 | if vals[i].value >= max_value: |
| 111 | num_l += 1 |
| 112 | b_time = vals[i].time |
| 113 | while i < len(vals) and \ |
| 114 | vals[i].value >= max_value: |
| 115 | i += 1 |
| 116 | times.append(vals[i-1].time - b_time) |
| 117 | i += 1 |
| 118 | if num_l > 0: |
| 119 | avg_time = sum(times) / float(num_l) |
| 120 | total_time = vals[-1].time - vals[0].time |
| 121 | avg_load = (avg_time / total_time) * 100 |
| 122 | loads.append(Record(name_fmt.format(node, dev), avg_load)) |
| 123 | |
| 124 | rows = [[name, value_fmt.format(value)] |
| 125 | for name, value in sorted(loads, key=sortRuleByMaxValue, reverse=True)] |
| 126 | return create_table(header, rows) |
| 127 | |
| 128 | |
| 129 | |
| 130 | def print_bottlenecks(data, params, max_bottlenecks=3): |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 131 | """ Print bottlenecks in table format, |
| 132 | search in data by fields in params""" |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 133 | # all bottlenecks |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 134 | rows = [] |
| 135 | val_format = "{0}: {1}, {2} times it was >= {3}" |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 136 | |
| 137 | # max_bottlenecks most slowests places |
| 138 | # Record metric : [Bottleneck nodes (max 3)] |
| 139 | max_values = {} |
| 140 | |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 141 | for node, metrics in data.items(): |
| 142 | node_rows = [] |
| 143 | for metric, max_value in params: |
| 144 | if metric in metrics: |
| 145 | item = metrics[metric] |
| 146 | # find max val for dev |
| 147 | # count times it was > max_value |
| 148 | for dev, vals in item.items(): |
| 149 | num_l = 0 |
| 150 | max_v = -1 |
| 151 | for val in vals: |
| 152 | if val >= max_value: |
| 153 | num_l += 1 |
| 154 | if max_v < val: |
| 155 | max_v = val |
| 156 | if num_l > 0: |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 157 | key = Record(metric, max_value) |
| 158 | # add to most slowest |
| 159 | btnk = max_values.setdefault(key, []) |
| 160 | # just add all data at first |
| 161 | btnk.append(Bottleneck(node, max_v, num_l)) |
| 162 | #add to common table |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 163 | c_val = val_format.format(metric, max_v, |
| 164 | num_l, max_value) |
| 165 | node_rows.append([dev, c_val]) |
| 166 | if len(node_rows) > 0: |
| 167 | rows.append([node, ""]) |
| 168 | rows.extend(node_rows) |
| 169 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 170 | tab = TT.Texttable() |
| 171 | #tab.set_deco(tab.VLINES) |
| 172 | |
| 173 | header = ["Server, device", "Critical value"] |
| 174 | tab.add_row(header) |
| 175 | tab.header = header |
| 176 | |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 177 | for row in rows: |
| 178 | tab.add_row(row) |
| 179 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 180 | most_slowest_header = [metric for metric, max_value in max_values.keys()] |
| 181 | most_slowest = [] |
| 182 | # select most slowest |
| 183 | for metric, btnks in max_values.items(): |
| 184 | m_data = [] |
| 185 | worst = sorted(btnks, key=sortRuleByValue, reverse=True)[:max_bottlenecks] |
| 186 | longest = sorted(btnks, key=sortRuleByCount, reverse=True)[:max_bottlenecks] |
| 187 | m_data.append("{0} worst by value: ".format(max_bottlenecks)) |
| 188 | for btnk in worst: |
| 189 | m_data.append(val_format.format(btnk.node, btnk.value, |
| 190 | btnk.count, |
| 191 | metric.max_value)) |
| 192 | m_data.append("{0} worst by times it was bad: ".format(max_bottlenecks)) |
| 193 | for btnk in longest: |
| 194 | m_data.append(val_format.format(btnk.node, btnk.value, |
| 195 | btnk.count, |
| 196 | metric.max_value)) |
| 197 | most_slowest.append(m_data) |
| 198 | |
| 199 | |
| 200 | rows2 = zip(*most_slowest) |
| 201 | |
| 202 | tab2 = TT.Texttable() |
| 203 | #tab2.set_deco(tab.VLINES) |
| 204 | |
| 205 | tab2.add_row(most_slowest_header) |
| 206 | tab2.header = most_slowest_header |
| 207 | |
| 208 | for row in rows2: |
| 209 | tab2.add_row(row) |
| 210 | return tab.draw(), tab2.draw() |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 211 | |
| 212 | |
| 213 | |
| 214 | def parse_args(args): |
| 215 | parser = argparse.ArgumentParser() |
| 216 | parser.add_argument('-t', '--time_period', nargs=2, |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 217 | type=float, default=None, |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 218 | help="Begin and end time for tests") |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 219 | parser.add_argument('-d', '--debug-ver', action='store_true', |
| 220 | help="Full report with original data") |
| 221 | parser.add_argument('-u', '--user-ver', action='store_true', |
| 222 | default=True, |
| 223 | help="Avg load report") |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 224 | parser.add_argument('sensors_result', type=str, |
| 225 | default=None, nargs='?') |
| 226 | return parser.parse_args(args[1:]) |
| 227 | |
| 228 | |
| 229 | def main(argv): |
| 230 | opts = parse_args(argv) |
| 231 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 232 | results = load_results(opts.time_period, opts.sensors_result) |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 233 | |
Ved-vampir | fcea072 | 2015-04-27 14:06:13 +0300 | [diff] [blame^] | 234 | if opts.debug_ver: |
| 235 | tab_all, tab_max = print_bottlenecks(results, critical_values) |
| 236 | print "Maximum values on provided metrics" |
| 237 | print tab_max |
| 238 | print "All loaded values" |
| 239 | print tab_all |
| 240 | |
| 241 | else: |
| 242 | print find_time_load_percent(results, critical_values) |
| 243 | |
Ved-vampir | 5c7b614 | 2015-04-24 19:49:59 +0300 | [diff] [blame] | 244 | |
| 245 | if __name__ == "__main__": |
| 246 | exit(main(sys.argv)) |