yandex-tank/Tank/Plugins/WebOnline.py
Alexey Lavrenuke (load testing) 2c115d3e33 make sure error code is a string
2014-06-06 20:17:15 +04:00

235 lines
8.3 KiB
Python

''' local webserver with online graphs '''
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from threading import Thread
import json
import logging
import os.path
import socket
import time
from Tank.Plugins.Aggregator import AggregatorPlugin, AggregateResultListener
from tankcore import AbstractPlugin
import tankcore
class WebOnlinePlugin(AbstractPlugin, Thread, AggregateResultListener):
''' web online plugin '''
SECTION = "web"
@staticmethod
def get_key():
return __file__
def __init__(self, core):
AbstractPlugin.__init__(self, core)
Thread.__init__(self)
self.daemon = True # Thread auto-shutdown
self.port = 8080
self.last_sec = None
self.server = None
self.interval = 60
self.quantiles_data = []
self.codes_data = []
self.avg_data = []
self.redirect = ''
self.manual_stop = 0
def get_available_options(self):
return ["port", "interval", "redirect", "manual_stop"]
def configure(self):
self.port = int(self.get_option("port", self.port))
self.interval = int(tankcore.expand_to_seconds(self.get_option("interval", '1m')))
self.redirect = self.get_option("redirect", self.redirect)
self.manual_stop = int(self.get_option('manual_stop', self.manual_stop))
def prepare_test(self):
try:
self.server = OnlineServer(('', self.port), WebOnlineHandler)
self.server.owner = self
aggregator = self.core.get_plugin_of_type(AggregatorPlugin)
aggregator.add_result_listener(self)
except Exception, ex:
self.log.warning("Failed to start web results server: %s", ex)
def start_test(self):
self.start()
def end_test(self, retcode):
# self.log.info("Shutting down local server")
# self.server.shutdown() don't enable it since it leads to random deadlocks
if self.manual_stop:
raw_input('Press Enter, to close webserver.')
if self.redirect:
time.sleep(2)
del self.server
self.server = None
return retcode
def run(self):
if (self.server):
address = socket.gethostname()
self.log.info("Starting local HTTP server for online view at port: http://%s:%s/", address, self.port)
self.server.serve_forever()
def __calculate_quantiles(self, data):
''' prepare response quantiles data '''
if not self.quantiles_data:
header = ["timeStamp", "requestCount"]
quantiles = [int(x) for x in sorted(data.overall.quantiles.keys(), reverse=True)]
header += quantiles
self.quantiles_data = [header, []]
item_data = {"timeStamp": time.mktime(data.time.timetuple()),
"requestCount": data.overall.planned_requests if data.overall.planned_requests else data.overall.RPS}
for level, timing in data.overall.quantiles.iteritems():
item_data[str(int(level))] = timing
self.quantiles_data[1] += [item_data]
while len(self.quantiles_data[1]) > self.interval:
self.quantiles_data[1].pop(0)
def __calculate_avg(self, data):
''' prepare response avg times data '''
if not self.avg_data:
header = ["timeStamp", "connect", "send", "latency", "receive"]
self.avg_data = [header, []]
item_data = {
"timeStamp": time.mktime(data.time.timetuple()),
'connect': data.overall.avg_connect_time,
'send': data.overall.avg_send_time, 'latency': data.overall.avg_latency,
'receive': data.overall.avg_receive_time
}
self.avg_data[1] += [item_data]
while len(self.avg_data[1]) > self.interval:
self.avg_data[1].pop(0)
def __calculate_codes(self, data):
''' prepare response codes data '''
if not self.codes_data:
header = ["timeStamp", "net", "2xx", "3xx", "4xx", "5xx", "Non-HTTP"]
self.codes_data = [header, []]
item_data = {"timeStamp": time.mktime(data.time.timetuple()), "net": 0, "2xx": 0, "3xx": 0, "4xx": 0, "5xx": 0,
"Non-HTTP": 0}
net = 0
for code, count in data.overall.net_codes.iteritems():
code = str(code) # make sure its a string
if code != "0":
net += count
item_data['net'] = net
for code, count in data.overall.http_codes.iteritems():
code = str(code) # make sure its a string
if code[0] == '2':
item_data['2xx'] += count
elif code[0] == '3':
item_data['3xx'] += count
elif code[0] == '4':
item_data['4xx'] += count
elif code[0] == '5':
item_data['5xx'] += count
else:
item_data['Non-HTTP'] += count
self.codes_data[1] += [item_data]
while len(self.codes_data[1]) > self.interval:
self.codes_data[1].pop(0)
def aggregate_second(self, data):
self.last_sec = data
self.__calculate_quantiles(data)
self.__calculate_avg(data)
self.__calculate_codes(data)
# http://fragments.turtlemeat.com/pythonwebserver.php
class OnlineServer(HTTPServer):
''' web server starter '''
def __init__(self, server_address, handler_class, bind_and_activate=True):
HTTPServer.allow_reuse_address = True
HTTPServer.__init__(self, server_address, handler_class, bind_and_activate)
self.owner = None
class WebOnlineHandler(BaseHTTPRequestHandler):
''' request handler '''
def __init__(self, request, client_address, server):
self.log = logging.getLogger(__name__)
BaseHTTPRequestHandler.__init__(self, request, client_address, server)
def log_error(self, fmt, *args):
self.log.error(fmt % args)
def log_message(self, fmt, *args):
self.log.debug(fmt % args)
def do_GET(self):
''' handle GET request '''
try:
if self.path == '/':
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
fhandle = open(os.path.dirname(__file__) + '/online.html')
self.wfile.write(fhandle.read())
fhandle.close()
elif self.path.endswith(".ico"):
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write("")
elif self.path.endswith(".json"):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
if self.path == '/Q.json':
self.wfile.write(json.dumps(self.server.owner.quantiles_data))
if self.path == '/HTTP.json':
self.wfile.write(json.dumps(self.server.owner.codes_data))
if self.path == '/Avg.json':
self.wfile.write(json.dumps(self.server.owner.avg_data))
elif self.path == '/redirect.json':
self.wfile.write('["%s"]' % self.server.owner.redirect)
elif self.path == '/numbers.json':
sec = self.server.owner.last_sec
net = 0
if sec:
for code, count in sec.overall.net_codes.iteritems():
if code != "0":
net += count
data = (sec.overall.active_threads, sec.overall.planned_requests, sec.overall.RPS,
sec.overall.avg_response_time, net)
else:
data = (0, 0, 0, 0, 0)
self.wfile.write('{"instances": %s, "planned": %s, "actual": %s, "avg": %s, "net": %s}' % data)
else:
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
fhandle = open(os.path.dirname(__file__) + self.path)
self.wfile.write(fhandle.read())
fhandle.close()
except IOError:
self.log.warning("404: %s" % self.path)
self.send_error(404, 'File Not Found: %s' % self.path)