|  | """ Analize test results for finding bottlenecks """ | 
|  |  | 
|  | import sys | 
|  | import argparse | 
|  |  | 
|  | import texttable as TT | 
|  |  | 
|  | from collections import namedtuple | 
|  |  | 
|  |  | 
|  | Record = namedtuple("Record", ['name', 'max_value']) | 
|  | MetricValue = namedtuple("MetricValue", ['value', 'time']) | 
|  | Bottleneck = namedtuple("Bottleneck", ['node', 'value', 'count']) | 
|  |  | 
|  | sortRuleByValue = lambda x: x.value | 
|  | sortRuleByMaxValue = lambda x: x.max_value | 
|  | sortRuleByCount = lambda x: x.count | 
|  | sortRuleByTime = lambda x: x.time | 
|  |  | 
|  | critical_values = [ | 
|  | Record("io_queue", 1), | 
|  | Record("procs_blocked", 1), | 
|  | Record("mem_usage_percent", 0.8) | 
|  | ] | 
|  |  | 
|  |  | 
|  | def get_name_from_sourceid(source_id): | 
|  | """ Cut port """ | 
|  | pos = source_id.rfind(":") | 
|  | return source_id[:pos] | 
|  |  | 
|  |  | 
|  | def create_table(header, rows, signs=1): | 
|  | """ Return texttable view """ | 
|  | tab = TT.Texttable() | 
|  | tab.set_deco(tab.VLINES) | 
|  | tab.set_precision(signs) | 
|  | tab.add_row(header) | 
|  | tab.header = header | 
|  |  | 
|  | for row in rows: | 
|  | tab.add_row(row) | 
|  |  | 
|  | return tab.draw() | 
|  |  | 
|  |  | 
|  | def load_results(period, rfile): | 
|  | """ Read raw results from dir and return | 
|  | data from provided period""" | 
|  | results = {} | 
|  | if period is not None: | 
|  | begin_time, end_time = period | 
|  | with open(rfile, "r") as f: | 
|  | for line in f: | 
|  |  | 
|  | if len(line) <= 1: | 
|  | continue | 
|  | if " : " in line: | 
|  | # old format | 
|  | ttime, _, raw_data = line.partition(" : ") | 
|  | raw_data = raw_data.strip('"\n\r') | 
|  | itime = float(ttime) | 
|  | else: | 
|  | # new format without time | 
|  | raw_data = line.strip('"\n\r') | 
|  |  | 
|  | _, data = eval(raw_data) | 
|  | sid = get_name_from_sourceid(data.pop("source_id")) | 
|  | itime = data.pop("time") | 
|  |  | 
|  | if period is None or (itime >= begin_time and itime <= end_time): | 
|  | serv_data = results.setdefault(sid, {}) | 
|  | for key, value in data.items(): | 
|  | # select device and metric names | 
|  | dev, _, metric = key.partition(".") | 
|  | # create dict for metric | 
|  | metric_dict = serv_data.setdefault(metric, {}) | 
|  | # set value for metric on dev | 
|  | cur_val = metric_dict.setdefault(dev, []) | 
|  | cur_val.append(MetricValue(value, itime)) | 
|  |  | 
|  | # sort by time | 
|  | for ms in results.values(): | 
|  | for dev in ms.values(): | 
|  | for d in dev.keys(): | 
|  | dev[d] = sorted(dev[d], key=sortRuleByTime) | 
|  |  | 
|  | return results | 
|  |  | 
|  |  | 
|  | def find_time_load_percent(data, params): | 
|  | """ Find avg load of components by time | 
|  | and return sorted table """ | 
|  |  | 
|  | header = ["Component", "Avg load %"] | 
|  | name_fmt = "{0}.{1}" | 
|  | value_fmt = "{0:.1f}" | 
|  | loads = [] | 
|  | for node, metrics in data.items(): | 
|  | for metric, max_value in params: | 
|  | if metric in metrics: | 
|  | item = metrics[metric] | 
|  | # count time it was > max_value | 
|  | # count times it was > max_value | 
|  | for dev, vals in item.items(): | 
|  | num_l = 0 | 
|  | times = [] | 
|  | i = 0 | 
|  | while i < len(vals): | 
|  | if vals[i].value >= max_value: | 
|  | num_l += 1 | 
|  | b_time = vals[i].time | 
|  | while i < len(vals) and \ | 
|  | vals[i].value >= max_value: | 
|  | i += 1 | 
|  | times.append(vals[i-1].time - b_time) | 
|  | i += 1 | 
|  | if num_l > 0: | 
|  | avg_time = sum(times) / float(num_l) | 
|  | total_time = vals[-1].time - vals[0].time | 
|  | avg_load = (avg_time / total_time) * 100 | 
|  | loads.append(Record(name_fmt.format(node, dev), avg_load)) | 
|  |  | 
|  | rows = [[name, value_fmt.format(value)] | 
|  | for name, value in sorted(loads, key=sortRuleByMaxValue, reverse=True)] | 
|  | return create_table(header, rows) | 
|  |  | 
|  |  | 
|  |  | 
|  | def print_bottlenecks(data, params, max_bottlenecks=3): | 
|  | """ Print bottlenecks in table format, | 
|  | search in data by fields in params""" | 
|  | # all bottlenecks | 
|  | rows = [] | 
|  | val_format = "{0}: {1}, {2} times it was >= {3}" | 
|  |  | 
|  | # max_bottlenecks most slowests places | 
|  | # Record metric : [Bottleneck nodes (max 3)] | 
|  | max_values = {} | 
|  |  | 
|  | for node, metrics in data.items(): | 
|  | node_rows = [] | 
|  | for metric, max_value in params: | 
|  | if metric in metrics: | 
|  | item = metrics[metric] | 
|  | # find max val for dev | 
|  | # count times it was > max_value | 
|  | for dev, vals in item.items(): | 
|  | num_l = 0 | 
|  | max_v = -1 | 
|  | for val in vals: | 
|  | if val >= max_value: | 
|  | num_l += 1 | 
|  | if max_v < val: | 
|  | max_v = val | 
|  | if num_l > 0: | 
|  | key = Record(metric, max_value) | 
|  | # add to most slowest | 
|  | btnk = max_values.setdefault(key, []) | 
|  | # just add all data at first | 
|  | btnk.append(Bottleneck(node, max_v, num_l)) | 
|  | #add to common table | 
|  | c_val = val_format.format(metric, max_v, | 
|  | num_l, max_value) | 
|  | node_rows.append([dev, c_val]) | 
|  | if len(node_rows) > 0: | 
|  | rows.append([node, ""]) | 
|  | rows.extend(node_rows) | 
|  |  | 
|  | tab = TT.Texttable() | 
|  | #tab.set_deco(tab.VLINES) | 
|  |  | 
|  | header = ["Server, device", "Critical value"] | 
|  | tab.add_row(header) | 
|  | tab.header = header | 
|  |  | 
|  | for row in rows: | 
|  | tab.add_row(row) | 
|  |  | 
|  | most_slowest_header = [metric for metric, max_value in max_values.keys()] | 
|  | most_slowest = [] | 
|  | # select most slowest | 
|  | for metric, btnks in max_values.items(): | 
|  | m_data = [] | 
|  | worst = sorted(btnks, key=sortRuleByValue, reverse=True)[:max_bottlenecks] | 
|  | longest = sorted(btnks, key=sortRuleByCount, reverse=True)[:max_bottlenecks] | 
|  | m_data.append("{0} worst by value: ".format(max_bottlenecks)) | 
|  | for btnk in worst: | 
|  | m_data.append(val_format.format(btnk.node, btnk.value, | 
|  | btnk.count, | 
|  | metric.max_value)) | 
|  | m_data.append("{0} worst by times it was bad: ".format(max_bottlenecks)) | 
|  | for btnk in longest: | 
|  | m_data.append(val_format.format(btnk.node, btnk.value, | 
|  | btnk.count, | 
|  | metric.max_value)) | 
|  | most_slowest.append(m_data) | 
|  |  | 
|  |  | 
|  | rows2 = zip(*most_slowest) | 
|  |  | 
|  | tab2 = TT.Texttable() | 
|  | #tab2.set_deco(tab.VLINES) | 
|  |  | 
|  | tab2.add_row(most_slowest_header) | 
|  | tab2.header = most_slowest_header | 
|  |  | 
|  | for row in rows2: | 
|  | tab2.add_row(row) | 
|  | return tab.draw(), tab2.draw() | 
|  |  | 
|  |  | 
|  |  | 
|  | def parse_args(args): | 
|  | parser = argparse.ArgumentParser() | 
|  | parser.add_argument('-t', '--time_period', nargs=2, | 
|  | type=float, default=None, | 
|  | help="Begin and end time for tests") | 
|  | parser.add_argument('-d', '--debug-ver', action='store_true', | 
|  | help="Full report with original data") | 
|  | parser.add_argument('-u', '--user-ver', action='store_true', | 
|  | default=True, | 
|  | help="Avg load report") | 
|  | parser.add_argument('sensors_result', type=str, | 
|  | default=None, nargs='?') | 
|  | return parser.parse_args(args[1:]) | 
|  |  | 
|  |  | 
|  | def main(argv): | 
|  | opts = parse_args(argv) | 
|  |  | 
|  | results = load_results(opts.time_period, opts.sensors_result) | 
|  |  | 
|  | if opts.debug_ver: | 
|  | tab_all, tab_max = print_bottlenecks(results, critical_values) | 
|  | print "Maximum values on provided metrics" | 
|  | print tab_max | 
|  | print "All loaded values" | 
|  | print tab_all | 
|  |  | 
|  | else: | 
|  | print find_time_load_percent(results, critical_values) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | exit(main(sys.argv)) |