2013-08-01 09:54:00 +00:00
|
|
|
''' Classes to build full console screen '''
|
2012-09-17 18:07:54 +00:00
|
|
|
from Tank.Plugins import Codes
|
2012-09-23 17:01:28 +00:00
|
|
|
import copy
|
2012-09-17 18:07:54 +00:00
|
|
|
import fcntl
|
|
|
|
import logging
|
2012-09-23 17:01:28 +00:00
|
|
|
import math
|
2012-09-17 18:07:54 +00:00
|
|
|
import os
|
|
|
|
import struct
|
|
|
|
import termios
|
2012-09-23 13:21:13 +00:00
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
def get_terminal_size():
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Gets width and height of terminal viewport
|
|
|
|
'''
|
2012-11-20 11:32:50 +00:00
|
|
|
default_size = (60, 140)
|
2012-09-17 18:07:54 +00:00
|
|
|
env = os.environ
|
2012-09-23 13:21:13 +00:00
|
|
|
def ioctl_gwinsz(file_d):
|
2012-09-24 15:04:24 +00:00
|
|
|
'''
|
|
|
|
Helper to get console size
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
try:
|
2012-09-23 13:21:13 +00:00
|
|
|
sizes = struct.unpack('hh', fcntl.ioctl(file_d, termios.TIOCGWINSZ, '1234'))
|
|
|
|
except Exception:
|
|
|
|
sizes = default_size
|
|
|
|
return sizes
|
|
|
|
sizes = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2)
|
|
|
|
if not sizes:
|
2012-09-17 18:07:54 +00:00
|
|
|
try:
|
2012-09-23 13:21:13 +00:00
|
|
|
file_d = os.open(os.ctermid(), os.O_RDONLY)
|
|
|
|
sizes = ioctl_gwinsz(file_d)
|
|
|
|
os.close(file_d)
|
|
|
|
except Exception:
|
2012-09-17 18:07:54 +00:00
|
|
|
pass
|
2012-09-23 13:21:13 +00:00
|
|
|
if not sizes:
|
2012-09-17 18:07:54 +00:00
|
|
|
try:
|
2012-09-23 13:21:13 +00:00
|
|
|
sizes = (env['LINES'], env['COLUMNS'])
|
2012-09-24 15:04:24 +00:00
|
|
|
except Exception:
|
2012-09-23 13:21:13 +00:00
|
|
|
sizes = default_size
|
|
|
|
return int(sizes[1]), int(sizes[0])
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
|
2012-12-21 09:56:38 +00:00
|
|
|
def krutilka():
|
|
|
|
pos=0
|
|
|
|
chars="|/-\\"
|
|
|
|
while True:
|
|
|
|
yield chars[pos]
|
|
|
|
pos+=1
|
|
|
|
if pos>=len(chars):
|
|
|
|
pos=0
|
|
|
|
|
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
class Screen(object):
|
2013-03-12 10:09:44 +00:00
|
|
|
''' Console screen renderer class '''
|
2012-09-17 18:07:54 +00:00
|
|
|
RIGHT_PANEL_SEPARATOR = ' . '
|
|
|
|
|
|
|
|
def __init__(self, info_panel_width, markup_provider):
|
|
|
|
self.log = logging.getLogger(__name__)
|
|
|
|
self.info_panel_percent = int(info_panel_width)
|
|
|
|
self.info_widgets = {}
|
|
|
|
self.markup = markup_provider
|
2012-11-16 12:05:21 +00:00
|
|
|
self.term_height = 60
|
|
|
|
self.term_width = 120
|
2012-09-17 18:07:54 +00:00
|
|
|
self.right_panel_width = 10
|
|
|
|
self.left_panel_width = self.term_width - self.right_panel_width - len(self.RIGHT_PANEL_SEPARATOR)
|
2012-09-23 13:21:13 +00:00
|
|
|
|
2012-11-16 12:54:31 +00:00
|
|
|
block1 = VerticalBlock(CurrentHTTPBlock(self), CurrentNetBlock(self))
|
|
|
|
block2 = VerticalBlock(block1, CasesBlock(self))
|
|
|
|
block3 = VerticalBlock(block2, TotalQuantilesBlock(self))
|
|
|
|
block4 = VerticalBlock(block3, AnswSizesBlock(self))
|
|
|
|
block5 = VerticalBlock(block4, AvgTimesBlock(self))
|
|
|
|
|
|
|
|
self.block_rows = [[CurrentTimesDistBlock(self), block5]]
|
2012-10-02 11:16:10 +00:00
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
def __get_right_line(self, widget_output):
|
2013-03-12 10:09:44 +00:00
|
|
|
''' Gets next line for right panel '''
|
2012-09-17 18:07:54 +00:00
|
|
|
right_line = ''
|
|
|
|
if widget_output:
|
|
|
|
right_line = widget_output.pop(0)
|
|
|
|
if len(right_line) > self.right_panel_width:
|
|
|
|
right_line_plain = self.markup.clean_markup(right_line)
|
|
|
|
if len(right_line_plain) > self.right_panel_width:
|
|
|
|
right_line = right_line[:self.right_panel_width] + self.markup.RESET
|
|
|
|
return right_line
|
|
|
|
|
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
def __render_left_panel(self):
|
2013-03-08 18:33:14 +00:00
|
|
|
''' Render left blocks '''
|
|
|
|
self.log.debug("Rendering left blocks")
|
2012-09-17 18:07:54 +00:00
|
|
|
lines = []
|
|
|
|
for row in self.block_rows:
|
2012-09-19 07:34:31 +00:00
|
|
|
space_left = self.left_panel_width
|
2012-09-17 18:07:54 +00:00
|
|
|
# render blocks
|
|
|
|
for block in row:
|
|
|
|
block.render()
|
|
|
|
space_left -= block.width
|
|
|
|
|
|
|
|
# merge blocks output into row
|
|
|
|
space = ' ' * int(math.floor(float(space_left) / (len(row) + 1)))
|
|
|
|
had_lines = True
|
|
|
|
while had_lines:
|
|
|
|
had_lines = False
|
|
|
|
line = space
|
|
|
|
for block in row:
|
|
|
|
if block.lines:
|
|
|
|
block_line = block.lines.pop(0)
|
|
|
|
line += block_line + ' ' * (block.width - len(self.markup.clean_markup(block_line)))
|
|
|
|
had_lines = True
|
|
|
|
else:
|
|
|
|
line += ' ' * block.width
|
|
|
|
line += space
|
|
|
|
lines.append(line)
|
2012-09-24 15:36:17 +00:00
|
|
|
#lines.append(". " * (1 + self.left_panel_width / 3))
|
|
|
|
#lines.append("")
|
2012-09-17 18:07:54 +00:00
|
|
|
return lines
|
|
|
|
|
2012-09-21 08:41:02 +00:00
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
def render_screen(self):
|
2013-03-12 10:09:44 +00:00
|
|
|
''' Main method to render screen view '''
|
2012-09-17 18:07:54 +00:00
|
|
|
self.term_width, self.term_height = get_terminal_size()
|
|
|
|
self.log.debug("Terminal size: %sx%s", self.term_width, self.term_height)
|
|
|
|
self.right_panel_width = int((self.term_width - len(self.RIGHT_PANEL_SEPARATOR)) * (float(self.info_panel_percent) / 100)) - 1
|
|
|
|
if self.right_panel_width > 0:
|
|
|
|
self.left_panel_width = self.term_width - self.right_panel_width - len(self.RIGHT_PANEL_SEPARATOR) - 2
|
|
|
|
else:
|
|
|
|
self.right_panel_width = 0
|
|
|
|
self.left_panel_width = self.term_width - 1
|
|
|
|
self.log.debug("Left/right panels width: %s/%s", self.left_panel_width, self.right_panel_width)
|
|
|
|
|
|
|
|
if self.right_panel_width:
|
|
|
|
widget_output = []
|
|
|
|
for index, widget in sorted(self.info_widgets.iteritems(), key=lambda(k, v): (v.get_index(), k)):
|
|
|
|
self.log.debug("Rendering info widget #%s: %s", index, widget)
|
2012-10-01 11:39:27 +00:00
|
|
|
widget_out = widget.render(self).strip()
|
2012-09-21 08:41:02 +00:00
|
|
|
if widget_out:
|
2012-10-01 11:39:27 +00:00
|
|
|
widget_output += widget_out.split("\n")
|
2012-09-21 08:41:02 +00:00
|
|
|
widget_output += [""]
|
2012-09-17 18:07:54 +00:00
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
left_lines = self.__render_left_panel()
|
2012-09-17 18:07:54 +00:00
|
|
|
|
2013-03-08 18:33:14 +00:00
|
|
|
self.log.debug("Composing final screen output")
|
2012-09-17 18:07:54 +00:00
|
|
|
output = []
|
2012-09-23 13:21:13 +00:00
|
|
|
for line_no in range(1, self.term_height):
|
2012-09-17 18:07:54 +00:00
|
|
|
line = " "
|
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
if line_no > 1 and left_lines:
|
2012-09-17 18:07:54 +00:00
|
|
|
left_line = left_lines.pop(0)
|
2013-03-12 12:56:59 +00:00
|
|
|
left_line_plain = self.markup.clean_markup(left_line)
|
2012-09-17 18:07:54 +00:00
|
|
|
if len(left_line) > self.left_panel_width:
|
|
|
|
if len(left_line_plain) > self.left_panel_width:
|
|
|
|
left_line = left_line[:self.left_panel_width] + self.markup.RESET
|
|
|
|
|
2013-03-12 12:56:59 +00:00
|
|
|
left_line += (' ' * (self.left_panel_width - len(left_line_plain)))
|
2012-09-17 18:07:54 +00:00
|
|
|
line += left_line
|
|
|
|
else:
|
|
|
|
line += ' ' * self.left_panel_width
|
|
|
|
if self.right_panel_width:
|
|
|
|
line += (self.RIGHT_PANEL_SEPARATOR)
|
2012-09-23 13:21:13 +00:00
|
|
|
right_line = self.__get_right_line(widget_output)
|
2012-09-17 18:07:54 +00:00
|
|
|
line += right_line
|
|
|
|
|
|
|
|
output.append(line)
|
|
|
|
return self.markup.new_line.join(output) + self.markup.new_line
|
|
|
|
|
|
|
|
|
|
|
|
def add_info_widget(self, widget):
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Add widget string to right panel of the screen
|
|
|
|
'''
|
2012-10-18 10:46:50 +00:00
|
|
|
index = widget.get_index()
|
|
|
|
while index in self.info_widgets.keys():
|
|
|
|
index += 1
|
|
|
|
self.info_widgets[widget.get_index()] = widget
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
def add_second_data(self, data):
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Notification method about new aggregator data
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
for row in self.block_rows:
|
|
|
|
for block in row:
|
|
|
|
block.add_second(data)
|
|
|
|
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
class AbstractBlock:
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Parent class for all left panel blocks
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
def __init__(self, screen):
|
|
|
|
self.log = logging.getLogger(__name__)
|
|
|
|
self.lines = []
|
|
|
|
self.width = 0
|
|
|
|
self.screen = screen
|
|
|
|
|
|
|
|
def add_second(self, data):
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Notification about new aggregate data
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
def render(self):
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Render method, fills .lines and .width properties with rendered data
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
raise RuntimeError("Abstract method needs to be overridden")
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
class VerticalBlock(AbstractBlock):
|
|
|
|
'''
|
|
|
|
Block to merge two other blocks vertically
|
|
|
|
'''
|
|
|
|
def __init__(self, top_block, bottom_block):
|
2012-09-23 13:21:13 +00:00
|
|
|
AbstractBlock.__init__(self, None)
|
2012-09-19 07:17:56 +00:00
|
|
|
self.top = top_block
|
|
|
|
self.bottom = bottom_block
|
|
|
|
|
|
|
|
def render(self):
|
|
|
|
self.top.render()
|
|
|
|
self.bottom.render()
|
|
|
|
self.width = max(self.top.width, self.bottom.width)
|
|
|
|
|
|
|
|
self.lines = []
|
|
|
|
for line in self.top.lines:
|
|
|
|
self.lines.append(line + ' ' * (self.width - self.top.width))
|
|
|
|
|
|
|
|
if self.top.lines and self.bottom.lines:
|
|
|
|
self.lines.append(' ' * self.width)
|
|
|
|
|
|
|
|
for line in self.bottom.lines:
|
|
|
|
self.lines.append(line + ' ' * (self.width - self.bottom.width))
|
|
|
|
|
|
|
|
def add_second(self, data):
|
|
|
|
self.top.add_second(data)
|
|
|
|
self.bottom.add_second(data)
|
|
|
|
|
|
|
|
# ======================================================
|
2012-09-23 16:45:05 +00:00
|
|
|
class CurrentTimesDistBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
'''
|
|
|
|
Detailed distribution for current RPS
|
|
|
|
'''
|
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
def __init__(self, screen):
|
|
|
|
AbstractBlock.__init__(self, screen)
|
|
|
|
self.current_codes = {}
|
2012-09-23 16:45:05 +00:00
|
|
|
self.current_rps = 0
|
2012-09-17 18:07:54 +00:00
|
|
|
self.current_duration = 0
|
|
|
|
self.current_count = 0
|
|
|
|
self.current_max_rt = 0
|
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
def add_second(self, data):
|
|
|
|
self.log.debug("Arrived times dist: %s", data.overall.times_dist)
|
|
|
|
rps = data.overall.planned_requests
|
|
|
|
if not self.current_rps == rps:
|
|
|
|
self.current_rps = rps
|
|
|
|
self.current_count = 0
|
|
|
|
self.current_max_rt = 0
|
|
|
|
self.current_codes = {}
|
|
|
|
self.current_duration = 0
|
|
|
|
for item in data.overall.times_dist:
|
|
|
|
self.current_count += item['count']
|
|
|
|
self.current_max_rt = max(self.current_max_rt, item['to'])
|
|
|
|
if item['from'] in self.current_codes.keys():
|
|
|
|
self.current_codes[item['from']]['count'] += item['count']
|
|
|
|
else:
|
|
|
|
self.current_codes[item['from']] = copy.deepcopy(item)
|
|
|
|
self.current_duration += 1
|
|
|
|
self.log.debug("Current times dist: %s", self.current_codes)
|
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
def render(self):
|
|
|
|
self.lines = []
|
|
|
|
quan = 0
|
|
|
|
current_times = sorted(self.current_codes.iteritems())
|
|
|
|
while current_times:
|
2012-09-23 13:21:13 +00:00
|
|
|
line, quan = self.__format_line(current_times, quan)
|
2012-09-17 18:07:54 +00:00
|
|
|
self.width = max(self.width, len(line))
|
|
|
|
self.lines.append(line)
|
|
|
|
self.lines.reverse()
|
2012-09-23 16:45:05 +00:00
|
|
|
self.lines = [self.screen.markup.WHITE + 'Times for %s RPS:' % self.current_rps + self.screen.markup.RESET] + self.lines
|
2012-09-17 18:07:54 +00:00
|
|
|
self.lines.append("")
|
|
|
|
|
|
|
|
count_len = str(len(str(self.current_count)))
|
2012-09-23 16:45:05 +00:00
|
|
|
tpl = ' %' + count_len + 'd %6.2f%%: Total'
|
|
|
|
if self.current_count:
|
|
|
|
self.lines.append(tpl % (self.current_count, 100))
|
2012-09-17 18:07:54 +00:00
|
|
|
self.width = max(self.width, len(self.lines[0]))
|
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
def __format_line(self, current_times, quan):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Format dist line '''
|
2012-09-17 18:07:54 +00:00
|
|
|
left_line = ''
|
|
|
|
if current_times:
|
2012-10-25 14:12:24 +00:00
|
|
|
item = current_times.pop(0)[1]
|
2012-09-17 18:07:54 +00:00
|
|
|
if self.current_count:
|
|
|
|
perc = float(item['count']) / self.current_count
|
|
|
|
else:
|
|
|
|
perc = 1
|
|
|
|
quan += perc
|
|
|
|
# 30691 9.26%: 010 -- 025 68.03% < 025
|
|
|
|
count_len = str(len(str(self.current_count)))
|
|
|
|
timing_len = str(len(str(self.current_max_rt)))
|
2012-09-23 18:31:29 +00:00
|
|
|
tpl = ' %' + count_len + 'd %6.2f%%: %' + timing_len + 'd -- %' + timing_len + 'd %6.2f%% < %' + timing_len + 'd'
|
2012-09-17 18:07:54 +00:00
|
|
|
data = (item['count'], perc * 100, item['from'], item['to'], quan * 100, item['to'])
|
|
|
|
left_line = tpl % data
|
|
|
|
return left_line, quan
|
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
class CurrentHTTPBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Http codes with highlight'''
|
2012-09-23 16:45:05 +00:00
|
|
|
TITLE = 'HTTP for %s RPS: '
|
2012-09-17 18:07:54 +00:00
|
|
|
def __init__(self, screen):
|
|
|
|
AbstractBlock.__init__(self, screen)
|
2012-09-19 07:17:56 +00:00
|
|
|
self.times_dist = {}
|
2012-09-23 16:45:05 +00:00
|
|
|
self.current_rps = 0
|
2012-09-19 07:17:56 +00:00
|
|
|
self.total_count = 0
|
2012-09-23 13:21:13 +00:00
|
|
|
self.highlight_codes = []
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
def process_dist(self, rps, codes_dist):
|
2012-09-23 13:21:13 +00:00
|
|
|
'''
|
|
|
|
Analyze arrived codes distribution and highlight arrived
|
|
|
|
'''
|
2012-09-17 18:07:54 +00:00
|
|
|
self.log.debug("Arrived codes data: %s", codes_dist)
|
|
|
|
self.highlight_codes = []
|
|
|
|
if not self.current_rps == rps:
|
|
|
|
self.current_rps = rps
|
2012-09-19 07:17:56 +00:00
|
|
|
self.total_count = 0
|
|
|
|
for key in self.times_dist.keys():
|
|
|
|
self.times_dist[key] = 0
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
for code, count in codes_dist.items():
|
2012-09-19 07:17:56 +00:00
|
|
|
self.total_count += count
|
2012-09-17 18:07:54 +00:00
|
|
|
self.highlight_codes.append(code)
|
2012-09-19 07:17:56 +00:00
|
|
|
if code in self.times_dist.keys():
|
|
|
|
self.times_dist[code] += count
|
2012-09-17 18:07:54 +00:00
|
|
|
else:
|
2012-09-19 07:17:56 +00:00
|
|
|
self.times_dist[code] = count
|
2012-09-17 18:07:54 +00:00
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
self.log.debug("Current codes dist: %s", self.times_dist)
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
def add_second(self, data):
|
|
|
|
rps = data.overall.planned_requests
|
|
|
|
codes_dist = data.overall.http_codes
|
|
|
|
self.process_dist(rps, codes_dist)
|
|
|
|
|
|
|
|
def render(self):
|
2012-09-23 16:45:05 +00:00
|
|
|
self.lines = [self.screen.markup.WHITE + self.TITLE % self.current_rps + self.screen.markup.RESET]
|
2012-09-17 18:07:54 +00:00
|
|
|
#self.width = len(self.lines[0])
|
2012-09-19 07:17:56 +00:00
|
|
|
for code, count in sorted(self.times_dist.iteritems()):
|
2012-09-17 18:07:54 +00:00
|
|
|
line = self.format_line(code, count)
|
|
|
|
self.width = max(self.width, len(self.screen.markup.clean_markup(line)))
|
|
|
|
self.lines.append(line)
|
|
|
|
|
|
|
|
def format_line(self, code, count):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' format line for display '''
|
2012-09-19 07:17:56 +00:00
|
|
|
if self.total_count:
|
|
|
|
perc = float(count) / self.total_count
|
2012-09-17 18:07:54 +00:00
|
|
|
else:
|
|
|
|
perc = 1
|
|
|
|
# 11083 5.07%: 304 Not Modified
|
2012-09-19 07:17:56 +00:00
|
|
|
count_len = str(len(str(self.total_count)))
|
2012-09-17 18:07:54 +00:00
|
|
|
if int(code) in Codes.HTTP:
|
|
|
|
code_desc = Codes.HTTP[int(code)]
|
|
|
|
else:
|
|
|
|
code_desc = "N/A"
|
2012-09-23 13:36:34 +00:00
|
|
|
tpl = ' %' + count_len + 'd %6.2f%%: %s %s'
|
2012-09-17 18:07:54 +00:00
|
|
|
data = (count, perc * 100, code, code_desc)
|
|
|
|
left_line = tpl % data
|
|
|
|
|
|
|
|
if code in self.highlight_codes:
|
2012-09-23 13:21:13 +00:00
|
|
|
code = str(code)
|
2012-09-17 18:07:54 +00:00
|
|
|
if code[0] == '2':
|
|
|
|
left_line = self.screen.markup.GREEN + left_line + self.screen.markup.RESET
|
|
|
|
elif code[0] == '3':
|
|
|
|
left_line = self.screen.markup.CYAN + left_line + self.screen.markup.RESET
|
|
|
|
elif code[0] == '4':
|
|
|
|
left_line = self.screen.markup.YELLOW + left_line + self.screen.markup.RESET
|
|
|
|
elif code[0] == '5':
|
|
|
|
left_line = self.screen.markup.RED + left_line + self.screen.markup.RESET
|
|
|
|
else:
|
|
|
|
left_line = self.screen.markup.MAGENTA + left_line + self.screen.markup.RESET
|
|
|
|
|
|
|
|
return left_line
|
|
|
|
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
class CurrentNetBlock(CurrentHTTPBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' NET codes with highlight'''
|
2012-09-23 16:45:05 +00:00
|
|
|
TITLE = ' NET for %s RPS: '
|
2012-09-17 18:07:54 +00:00
|
|
|
|
|
|
|
def add_second(self, data):
|
|
|
|
rps = data.overall.planned_requests
|
2012-09-21 10:15:06 +00:00
|
|
|
codes_dist = copy.deepcopy(data.overall.net_codes)
|
2012-09-17 18:07:54 +00:00
|
|
|
self.process_dist(rps, codes_dist)
|
|
|
|
|
|
|
|
def format_line(self, code, count):
|
2012-09-19 07:17:56 +00:00
|
|
|
if self.total_count:
|
|
|
|
perc = float(count) / self.total_count
|
2012-09-17 18:07:54 +00:00
|
|
|
else:
|
|
|
|
perc = 1
|
|
|
|
# 11083 5.07%: 304 Not Modified
|
2012-09-19 07:17:56 +00:00
|
|
|
count_len = str(len(str(self.total_count)))
|
2012-09-17 18:07:54 +00:00
|
|
|
if int(code) in Codes.NET:
|
|
|
|
code_desc = Codes.NET[int(code)]
|
|
|
|
else:
|
|
|
|
code_desc = "N/A"
|
2012-09-23 13:36:34 +00:00
|
|
|
tpl = ' %' + count_len + 'd %6.2f%%: %s %s'
|
2012-09-17 18:07:54 +00:00
|
|
|
data = (count, perc * 100, code, code_desc)
|
|
|
|
left_line = tpl % data
|
|
|
|
|
|
|
|
if code in self.highlight_codes:
|
|
|
|
if code == '0':
|
|
|
|
left_line = self.screen.markup.GREEN + left_line + self.screen.markup.RESET
|
|
|
|
else:
|
|
|
|
left_line = self.screen.markup.RED + left_line + self.screen.markup.RESET
|
|
|
|
|
|
|
|
return left_line
|
|
|
|
|
|
|
|
# ======================================================
|
2012-09-19 07:17:56 +00:00
|
|
|
|
|
|
|
class TotalQuantilesBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Total test quantiles '''
|
2012-09-17 18:07:54 +00:00
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
def __init__(self, screen):
|
|
|
|
AbstractBlock.__init__(self, screen)
|
|
|
|
self.total_count = 0
|
|
|
|
self.current_max_rt = 0
|
2013-03-14 12:19:06 +00:00
|
|
|
self.quantiles = {}
|
2012-09-17 18:07:54 +00:00
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
def add_second(self, data):
|
2013-03-14 12:19:06 +00:00
|
|
|
self.quantiles=data.cumulative.quantiles
|
2012-09-19 07:17:56 +00:00
|
|
|
|
2012-09-17 18:07:54 +00:00
|
|
|
def render(self):
|
|
|
|
self.lines = []
|
2013-03-14 12:19:06 +00:00
|
|
|
for quant in sorted(self.quantiles):
|
|
|
|
line = self.__format_line(quant, self.quantiles[quant])
|
2012-10-02 13:22:22 +00:00
|
|
|
self.width = max(self.width, len(line))
|
|
|
|
self.lines.append(line)
|
|
|
|
|
2012-09-19 07:17:56 +00:00
|
|
|
self.lines.reverse()
|
2012-09-23 16:45:05 +00:00
|
|
|
self.lines = [self.screen.markup.WHITE + 'Cumulative Percentiles:' + self.screen.markup.RESET] + self.lines
|
|
|
|
self.width = max(self.width, len(self.screen.markup.clean_markup(self.lines[0])))
|
2012-09-19 07:17:56 +00:00
|
|
|
|
2012-09-23 13:21:13 +00:00
|
|
|
def __format_line(self, quan, timing):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Format line '''
|
2012-09-19 07:17:56 +00:00
|
|
|
timing_len = str(len(str(self.current_max_rt)))
|
|
|
|
tpl = ' %3d%% < %' + timing_len + 'd ms'
|
2013-03-14 12:19:06 +00:00
|
|
|
data = (quan, timing)
|
2012-09-19 07:17:56 +00:00
|
|
|
left_line = tpl % data
|
|
|
|
return left_line
|
2012-09-23 13:21:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
|
|
|
|
class AnswSizesBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Answer sizes, if available '''
|
2012-09-23 13:21:13 +00:00
|
|
|
|
2012-09-23 13:36:34 +00:00
|
|
|
def __init__(self, screen):
|
2012-09-23 13:21:13 +00:00
|
|
|
AbstractBlock.__init__(self, screen)
|
|
|
|
self.sum_in = 0
|
|
|
|
self.current_rps = -1
|
|
|
|
self.sum_out = 0
|
|
|
|
self.count = 0
|
2012-09-23 16:45:05 +00:00
|
|
|
self.header = screen.markup.WHITE + 'Request/Response Sizes:' + screen.markup.RESET
|
2012-10-02 07:46:17 +00:00
|
|
|
self.cur_count = 0
|
2012-10-02 13:22:22 +00:00
|
|
|
self.cur_in = 0
|
|
|
|
self.cur_out = 0
|
2012-09-23 13:21:13 +00:00
|
|
|
|
|
|
|
def render(self):
|
|
|
|
self.lines = [self.header]
|
|
|
|
if self.count:
|
2012-09-23 13:36:34 +00:00
|
|
|
self.lines.append(" Avg Request at %s RPS: %d bytes" % (self.current_rps, self.sum_out / self.count))
|
|
|
|
self.lines.append(" Avg Response at %s RPS: %d bytes" % (self.current_rps, self.sum_in / self.count))
|
|
|
|
self.lines.append("")
|
2012-10-02 07:46:17 +00:00
|
|
|
if self.cur_count:
|
2012-09-23 13:36:34 +00:00
|
|
|
self.lines.append(" Last Avg Request: %d bytes" % (self.cur_out / self.cur_count))
|
|
|
|
self.lines.append(" Last Avg Response: %d bytes" % (self.cur_in / self.cur_count))
|
2012-11-16 12:54:31 +00:00
|
|
|
else:
|
|
|
|
self.lines.append("")
|
|
|
|
self.lines.append("")
|
2012-09-23 13:21:13 +00:00
|
|
|
for line in self.lines:
|
2012-09-23 16:45:05 +00:00
|
|
|
self.width = max(self.width, len(self.screen.markup.clean_markup(line)))
|
2012-09-23 13:21:13 +00:00
|
|
|
|
|
|
|
def add_second(self, data):
|
2012-09-23 13:36:34 +00:00
|
|
|
if data.overall.planned_requests != self.current_rps:
|
2012-09-23 13:21:13 +00:00
|
|
|
self.current_rps = data.overall.planned_requests
|
|
|
|
self.sum_in = 0
|
|
|
|
self.sum_out = 0
|
|
|
|
self.count = 0
|
|
|
|
|
|
|
|
self.count += data.overall.RPS
|
|
|
|
self.sum_in += data.overall.input
|
|
|
|
self.sum_out += data.overall.output
|
|
|
|
|
2012-09-23 13:36:34 +00:00
|
|
|
self.cur_in = data.overall.input
|
|
|
|
self.cur_out = data.overall.output
|
|
|
|
self.cur_count = data.overall.RPS
|
2012-09-23 16:45:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
|
|
|
|
class AvgTimesBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Average times breakdown '''
|
|
|
|
|
2012-09-24 15:04:24 +00:00
|
|
|
def __init__(self, screen):
|
2012-09-23 16:45:05 +00:00
|
|
|
AbstractBlock.__init__(self, screen)
|
2012-09-24 15:04:24 +00:00
|
|
|
self.rps_connect = 0
|
|
|
|
self.rps_send = 0
|
|
|
|
self.rps_latency = 0
|
|
|
|
self.rps_receive = 0
|
|
|
|
self.rps_overall = 0
|
|
|
|
self.rps_count = 0
|
2012-09-23 16:45:05 +00:00
|
|
|
self.current_rps = 0
|
2012-09-24 15:04:24 +00:00
|
|
|
|
|
|
|
self.all_connect = 0
|
|
|
|
self.all_send = 0
|
|
|
|
self.all_latency = 0
|
|
|
|
self.all_receive = 0
|
|
|
|
self.all_overall = 0
|
|
|
|
self.all_count = 0
|
|
|
|
|
|
|
|
self.last_connect = 0
|
|
|
|
self.last_send = 0
|
|
|
|
self.last_latency = 0
|
|
|
|
self.last_receive = 0
|
|
|
|
self.last_overall = 0
|
|
|
|
self.last_count = 0
|
|
|
|
|
2012-10-22 11:41:59 +00:00
|
|
|
self.header = 'Avg Times (all / %s RPS / last):'
|
2012-09-23 16:45:05 +00:00
|
|
|
|
|
|
|
def add_second(self, data):
|
2012-09-24 15:04:24 +00:00
|
|
|
if self.current_rps != data.overall.planned_requests:
|
2012-09-23 16:45:05 +00:00
|
|
|
self.current_rps = data.overall.planned_requests
|
2012-09-24 15:04:24 +00:00
|
|
|
self.rps_connect = 0
|
|
|
|
self.rps_send = 0
|
|
|
|
self.rps_latency = 0
|
|
|
|
self.rps_receive = 0
|
|
|
|
self.rps_overall = 0
|
|
|
|
self.rps_count = 0
|
2012-09-23 16:45:05 +00:00
|
|
|
|
2012-09-24 15:04:24 +00:00
|
|
|
self.rps_connect += data.overall.avg_connect_time * data.overall.RPS
|
|
|
|
self.rps_send += data.overall.avg_send_time * data.overall.RPS
|
|
|
|
self.rps_latency += data.overall.avg_latency * data.overall.RPS
|
|
|
|
self.rps_receive += data.overall.avg_receive_time * data.overall.RPS
|
|
|
|
self.rps_overall += data.overall.avg_response_time * data.overall.RPS
|
|
|
|
self.rps_count += data.overall.RPS
|
|
|
|
|
|
|
|
self.all_connect += data.overall.avg_connect_time * data.overall.RPS
|
|
|
|
self.all_send += data.overall.avg_send_time * data.overall.RPS
|
|
|
|
self.all_latency += data.overall.avg_latency * data.overall.RPS
|
|
|
|
self.all_receive += data.overall.avg_receive_time * data.overall.RPS
|
|
|
|
self.all_overall += data.overall.avg_response_time * data.overall.RPS
|
|
|
|
self.all_count += data.overall.RPS
|
2012-09-23 16:45:05 +00:00
|
|
|
|
2012-09-24 15:04:24 +00:00
|
|
|
self.last_connect = data.overall.avg_connect_time * data.overall.RPS
|
|
|
|
self.last_send = data.overall.avg_send_time * data.overall.RPS
|
|
|
|
self.last_latency = data.overall.avg_latency * data.overall.RPS
|
|
|
|
self.last_receive = data.overall.avg_receive_time * data.overall.RPS
|
|
|
|
self.last_overall = data.overall.avg_response_time * data.overall.RPS
|
|
|
|
self.last_count = data.overall.RPS
|
|
|
|
|
2012-09-23 16:45:05 +00:00
|
|
|
def render(self):
|
2012-09-24 15:04:24 +00:00
|
|
|
self.lines = [self.screen.markup.WHITE + self.header % self.current_rps + self.screen.markup.RESET]
|
|
|
|
if self.last_count:
|
|
|
|
len_all = str(len(str(max([self.all_connect, self.all_latency, self.all_overall, self.all_receive, self.all_send]))))
|
|
|
|
len_rps = str(len(str(max([self.rps_connect, self.rps_latency, self.rps_overall, self.rps_receive, self.rps_send]))))
|
|
|
|
len_last = str(len(str(max([self.last_connect, self.last_latency, self.last_overall, self.last_receive, self.last_send]))))
|
|
|
|
tpl = "%" + len_all + "d / %" + len_rps + "d / %" + len_last + "d"
|
|
|
|
self.lines.append(" Overall: " + tpl % (float(self.all_overall) / self.all_count, float(self.rps_overall) / self.rps_count, float(self.last_overall) / self.last_count))
|
|
|
|
self.lines.append(" Connect: " + tpl % (float(self.all_connect) / self.all_count, float(self.rps_connect) / self.rps_count, float(self.last_connect) / self.last_count))
|
|
|
|
self.lines.append(" Send: " + tpl % (float(self.all_send) / self.all_count, float(self.rps_send) / self.rps_count, float(self.rps_send) / self.rps_count))
|
|
|
|
self.lines.append(" Latency: " + tpl % (float(self.all_latency) / self.all_count, float(self.rps_latency) / self.rps_count, float(self.rps_latency) / self.rps_count))
|
|
|
|
self.lines.append(" Receive: " + tpl % (float(self.all_receive) / self.all_count, float(self.rps_receive) / self.rps_count, float(self.rps_receive) / self.rps_count))
|
2012-11-16 12:54:31 +00:00
|
|
|
else:
|
|
|
|
self.lines.append("")
|
|
|
|
self.lines.append("")
|
|
|
|
self.lines.append("")
|
|
|
|
self.lines.append("")
|
|
|
|
self.lines.append("")
|
2012-09-23 16:45:05 +00:00
|
|
|
for line in self.lines:
|
|
|
|
self.width = max(self.width, len(self.screen.markup.clean_markup(line)))
|
|
|
|
|
|
|
|
|
|
|
|
# ======================================================
|
|
|
|
|
|
|
|
|
|
|
|
class CasesBlock(AbstractBlock):
|
2012-10-02 13:22:22 +00:00
|
|
|
''' Cases info '''
|
2012-09-23 16:45:05 +00:00
|
|
|
def __init__(self, screen):
|
|
|
|
AbstractBlock.__init__(self, screen)
|
|
|
|
self.cases = {}
|
|
|
|
self.count = 0
|
|
|
|
self.header = "Cumulative Cases Info:"
|
|
|
|
self.highlight_cases = []
|
|
|
|
self.max_case_len = 0
|
|
|
|
|
|
|
|
def add_second(self, data):
|
|
|
|
self.highlight_cases = []
|
|
|
|
for name, case in data.cases.iteritems():
|
|
|
|
self.highlight_cases.append(name)
|
|
|
|
if not name in self.cases.keys():
|
|
|
|
self.cases[name] = [0, 0]
|
|
|
|
self.max_case_len = max(self.max_case_len, len(name))
|
|
|
|
self.cases[name][0] += case.RPS
|
2013-02-21 12:58:00 +00:00
|
|
|
self.cases[name][1] += case.avg_response_time * case.RPS
|
2012-09-23 16:45:05 +00:00
|
|
|
self.count += case.RPS
|
|
|
|
|
|
|
|
def render(self):
|
|
|
|
self.lines = [self.screen.markup.WHITE + self.header + self.screen.markup.RESET]
|
|
|
|
tpl = " %s: %" + str(len(str(self.count))) + "d %5.2f%% / avg %.1f ms"
|
2012-09-23 16:51:42 +00:00
|
|
|
for name, (count, resp_time) in sorted(self.cases.iteritems()):
|
2012-09-23 16:45:05 +00:00
|
|
|
line = tpl % (" "*(self.max_case_len - len(name)) + name, count, 100 * float(count) / self.count, float(resp_time) / count)
|
|
|
|
if name in self.highlight_cases:
|
|
|
|
self.lines.append(self.screen.markup.CYAN + line + self.screen.markup.RESET)
|
|
|
|
else:
|
|
|
|
self.lines.append(line)
|
|
|
|
|
|
|
|
for line in self.lines:
|
|
|
|
self.width = max(self.width, len(self.screen.markup.clean_markup(line)))
|
|
|
|
|