blob: 64156cfccf06ddf27a0aaa0c07cc6048b1063c23 [file] [log] [blame]
""" 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))