2012-09-23 11:53:38 +00:00
|
|
|
'''
|
|
|
|
Provides class to run TankCore from console environment
|
|
|
|
'''
|
2012-10-03 16:49:13 +00:00
|
|
|
from Tank.Plugins.ConsoleOnline import RealConsoleMarkup
|
|
|
|
from tankcore import TankCore
|
2012-09-12 13:18:58 +00:00
|
|
|
import ConfigParser
|
2012-10-03 16:49:13 +00:00
|
|
|
import datetime
|
2012-09-12 13:18:58 +00:00
|
|
|
import fnmatch
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
import time
|
|
|
|
import traceback
|
2012-11-29 13:37:13 +00:00
|
|
|
import signal
|
|
|
|
|
|
|
|
# required for non-tty python runs
|
|
|
|
def signal_handler(signal, frame):
|
|
|
|
raise KeyboardInterrupt()
|
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
2012-12-05 09:54:27 +00:00
|
|
|
signal.signal(signal.SIGTERM, signal_handler)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
2012-09-18 13:09:09 +00:00
|
|
|
# TODO: 2 add system resources busy check
|
2012-09-12 13:18:58 +00:00
|
|
|
class ConsoleTank:
|
|
|
|
"""
|
|
|
|
Worker class that runs tank core accepting cmdline params
|
|
|
|
"""
|
|
|
|
|
2012-11-29 13:37:13 +00:00
|
|
|
IGNORE_LOCKS = "ignore_locks"
|
|
|
|
|
2012-09-12 13:18:58 +00:00
|
|
|
MIGRATE_SECTION = 'migrate_old'
|
|
|
|
|
|
|
|
old_options_mapping = {
|
|
|
|
'instances': ('phantom', ''),
|
|
|
|
'tank_type': ('phantom', ''),
|
|
|
|
'gatling_ip': ('phantom', ''),
|
|
|
|
'ssl': ('phantom', ''),
|
|
|
|
'address': ('phantom', ''),
|
|
|
|
'port': ('phantom', ''),
|
|
|
|
'writelog': ('phantom', ''),
|
|
|
|
'phantom_http_line': ('phantom', ''),
|
|
|
|
'phantom_http_field_num': ('phantom', ''),
|
|
|
|
'phantom_http_field': ('phantom', ''),
|
|
|
|
'phantom_http_entity': ('phantom', ''),
|
|
|
|
|
|
|
|
'load': ('phantom', 'rps_schedule'),
|
|
|
|
'instances_schedule': ('phantom', ''),
|
|
|
|
'ammofile': ('phantom', ''),
|
|
|
|
'loop': ('phantom', ''),
|
|
|
|
'autocases': ('phantom', ''),
|
|
|
|
'chosen_cases': ('phantom', ''),
|
|
|
|
'uri': ('phantom', 'uris'),
|
|
|
|
'header': ('phantom', 'headers'),
|
|
|
|
|
|
|
|
'time_periods': ('aggregator', ''),
|
|
|
|
'detailed_field': ('aggregator', ''),
|
|
|
|
|
|
|
|
'task': ('meta', ''),
|
|
|
|
'job_name': ('meta', ''),
|
|
|
|
'job_dsc': ('meta', ''),
|
|
|
|
'component': ('meta', ''),
|
|
|
|
'regress': ('meta', ''),
|
|
|
|
'ver': ('meta', ''),
|
2012-09-24 13:46:59 +00:00
|
|
|
'inform': ('meta', 'notify'),
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
'autostop': ('autostop', ''),
|
|
|
|
'monitoring_config': ('monitoring', 'config'),
|
|
|
|
'manual_start': ('tank', ''),
|
|
|
|
'http_base': ('meta', 'api_address')
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, options, ammofile):
|
|
|
|
self.core = TankCore()
|
|
|
|
|
|
|
|
self.options = options
|
|
|
|
self.ammofile = ammofile
|
|
|
|
|
2012-09-26 14:18:17 +00:00
|
|
|
self.baseconfigs_location = '/etc/yandex-tank'
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
self.log_filename = self.options.log
|
|
|
|
self.core.add_artifact_file(self.log_filename)
|
|
|
|
self.log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
self.signal_count = 0
|
2012-10-01 11:28:53 +00:00
|
|
|
self.scheduled_start = None
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
def set_baseconfigs_dir(self, directory):
|
2012-09-23 11:53:38 +00:00
|
|
|
'''
|
|
|
|
Set directory where to read configs set
|
|
|
|
'''
|
2012-09-12 13:18:58 +00:00
|
|
|
self.baseconfigs_location = directory
|
|
|
|
|
|
|
|
def init_logging(self):
|
2012-09-23 11:53:38 +00:00
|
|
|
'''
|
|
|
|
Set up logging, as it is very important for console tool
|
|
|
|
'''
|
2012-09-12 13:18:58 +00:00
|
|
|
logger = logging.getLogger()
|
|
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
|
|
|
|
# create file handler which logs even debug messages
|
|
|
|
if (self.log_filename):
|
2012-09-19 07:49:06 +00:00
|
|
|
file_handler = logging.FileHandler(self.log_filename)
|
|
|
|
file_handler.setLevel(logging.DEBUG)
|
|
|
|
file_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(message)s"))
|
|
|
|
logger.addHandler(file_handler)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
# create console handler with a higher log level
|
2012-09-19 07:49:06 +00:00
|
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
if self.options.verbose:
|
2012-09-19 07:49:06 +00:00
|
|
|
console_handler.setLevel(logging.DEBUG)
|
|
|
|
console_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(message)s"))
|
2012-09-12 13:18:58 +00:00
|
|
|
elif self.options.quiet:
|
2012-09-19 07:49:06 +00:00
|
|
|
console_handler.setLevel(logging.WARNING)
|
|
|
|
console_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s", "%H:%M:%S"))
|
2012-09-12 13:18:58 +00:00
|
|
|
else:
|
2012-09-19 07:49:06 +00:00
|
|
|
console_handler.setLevel(logging.INFO)
|
|
|
|
console_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s", "%H:%M:%S"))
|
|
|
|
logger.addHandler(console_handler)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __convert_old_multiline_options(self, old_lines):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' supports old-school 'lunapark' tool configs '''
|
2012-09-23 11:53:38 +00:00
|
|
|
opts = {}
|
2012-09-24 15:04:24 +00:00
|
|
|
option = None
|
2012-09-23 11:53:38 +00:00
|
|
|
res = ''
|
|
|
|
for line in old_lines:
|
|
|
|
try:
|
|
|
|
if line.strip() and line.strip()[0] == '#':
|
2012-09-24 15:04:24 +00:00
|
|
|
res += line
|
2012-09-23 11:53:38 +00:00
|
|
|
continue
|
|
|
|
option = line[:line.index('=')]
|
2012-09-24 15:04:24 +00:00
|
|
|
value = line[line.index('=') + 1:]
|
2012-09-23 11:53:38 +00:00
|
|
|
if option not in opts.keys():
|
|
|
|
opts[option] = []
|
|
|
|
opts[option].append(value.strip())
|
|
|
|
except Exception:
|
2012-09-23 12:29:20 +00:00
|
|
|
if option:
|
|
|
|
opts[option].append(line.strip())
|
|
|
|
else:
|
2012-09-24 15:04:24 +00:00
|
|
|
res += line.strip() + "\n"
|
2012-09-23 11:53:38 +00:00
|
|
|
|
|
|
|
for option, values in opts.iteritems():
|
|
|
|
res += option + '=' + "\n\t".join(values) + "\n"
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
2012-09-23 12:29:20 +00:00
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __adapt_old_config(self, config):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' supports old-school 'lunapark' tool configs '''
|
2012-09-12 13:18:58 +00:00
|
|
|
test_parser = ConfigParser.ConfigParser()
|
|
|
|
try:
|
|
|
|
test_parser.read(config)
|
|
|
|
self.log.debug("Config passed ini format test: %s", config)
|
|
|
|
return config
|
2012-09-20 16:18:04 +00:00
|
|
|
except Exception:
|
2012-09-12 13:18:58 +00:00
|
|
|
self.log.warning("Config failed INI format test, consider upgrading it: %s", config)
|
2012-09-23 11:53:38 +00:00
|
|
|
file_handle, corrected_file = tempfile.mkstemp(".ini", "corrected_")
|
2012-09-12 13:18:58 +00:00
|
|
|
self.log.debug("Creating corrected INI config for it: %s", corrected_file)
|
2012-09-19 07:49:06 +00:00
|
|
|
os.write(file_handle, "[" + self.MIGRATE_SECTION + "]\n")
|
2012-09-23 11:53:38 +00:00
|
|
|
os.write(file_handle, self.__convert_old_multiline_options(open(config, 'r').readlines()))
|
2012-10-22 11:11:19 +00:00
|
|
|
os.close(file_handle)
|
2012-09-12 13:18:58 +00:00
|
|
|
return corrected_file
|
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __add_adapted_config(self, configs, conf_file):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' supports old-school 'lunapark' tool configs '''
|
2012-09-23 11:53:38 +00:00
|
|
|
conf_file = self.__adapt_old_config(conf_file)
|
2012-09-12 13:18:58 +00:00
|
|
|
configs += [conf_file]
|
|
|
|
self.core.add_artifact_file(conf_file, True)
|
|
|
|
|
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __override_config_from_cmdline(self):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' override config options from command line'''
|
2012-09-12 13:18:58 +00:00
|
|
|
if self.options.option:
|
2012-10-01 12:24:53 +00:00
|
|
|
self.core.apply_shorthand_options(self.options.option, self.MIGRATE_SECTION)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __translate_old_options(self):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' supports old-school 'lunapark' tool configs '''
|
2012-09-12 13:18:58 +00:00
|
|
|
for old_option, value in self.core.config.get_options(self.MIGRATE_SECTION):
|
|
|
|
if old_option in self.old_options_mapping.keys():
|
|
|
|
new_sect = self.old_options_mapping[old_option][0]
|
|
|
|
new_opt = self.old_options_mapping[old_option][1] if self.old_options_mapping[old_option][1] else old_option
|
|
|
|
self.log.debug("Translating old option %s=%s into new: %s.%s", old_option, value, new_sect, new_opt)
|
|
|
|
self.core.set_option(new_sect, new_opt, value)
|
|
|
|
else:
|
|
|
|
self.log.warn("Unknown old option, please add it to translation mapping: %s=%s", old_option, value)
|
|
|
|
|
|
|
|
if self.core.config.config.has_section(self.MIGRATE_SECTION):
|
|
|
|
self.core.config.config.remove_section(self.MIGRATE_SECTION)
|
|
|
|
|
|
|
|
|
|
|
|
def configure(self):
|
2012-09-23 11:53:38 +00:00
|
|
|
'''
|
|
|
|
Make all console-specific preparations before running Tank
|
|
|
|
'''
|
2012-10-08 14:54:31 +00:00
|
|
|
if self.options.ignore_lock:
|
2012-11-29 13:37:13 +00:00
|
|
|
self.log.warn("Lock files ignored. This is highly unrecommended practice!")
|
2012-10-08 14:54:31 +00:00
|
|
|
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
self.core.get_lock(self.options.ignore_lock)
|
2012-10-23 17:24:58 +00:00
|
|
|
break
|
2012-10-08 14:54:31 +00:00
|
|
|
except Exception:
|
2012-09-17 13:48:02 +00:00
|
|
|
if self.options.lock_fail:
|
|
|
|
raise RuntimeError("Lock file present, cannot continue")
|
2012-09-24 13:46:59 +00:00
|
|
|
self.log.info("Waiting 5s for retry...")
|
2012-09-12 13:18:58 +00:00
|
|
|
time.sleep(5)
|
|
|
|
|
2012-10-08 14:54:31 +00:00
|
|
|
try:
|
|
|
|
configs = []
|
|
|
|
|
|
|
|
if not self.options.no_rc:
|
|
|
|
try:
|
2012-10-23 17:24:58 +00:00
|
|
|
conf_files = os.listdir(self.baseconfigs_location)
|
2012-10-17 15:28:24 +00:00
|
|
|
conf_files.sort()
|
|
|
|
for filename in conf_files:
|
2012-10-08 14:54:31 +00:00
|
|
|
if fnmatch.fnmatch(filename, '*.ini'):
|
|
|
|
configs += [os.path.realpath(self.baseconfigs_location + os.sep + filename)]
|
|
|
|
except OSError:
|
|
|
|
self.log.warn(self.baseconfigs_location + ' is not acessible to get configs list')
|
2012-09-12 13:18:58 +00:00
|
|
|
|
2012-10-08 14:54:31 +00:00
|
|
|
configs += [os.path.expanduser('~/.yandex-tank')]
|
|
|
|
|
|
|
|
if not self.options.config:
|
2012-11-12 12:20:06 +00:00
|
|
|
if os.path.exists(os.path.realpath('load.ini')):
|
|
|
|
self.log.info("No config passed via cmdline, using ./load.ini")
|
|
|
|
configs += [os.path.realpath('load.ini')]
|
|
|
|
self.core.add_artifact_file(os.path.realpath('load.ini'), True)
|
|
|
|
elif os.path.exists(os.path.realpath('load.conf')):
|
|
|
|
# just for old 'lunapark' compatibility
|
|
|
|
self.log.warn("Using 'load.conf' is unrecommended, please use 'load.ini' instead")
|
|
|
|
conf_file = self.__adapt_old_config(os.path.realpath('load.conf'))
|
|
|
|
configs += [conf_file]
|
|
|
|
self.core.add_artifact_file(conf_file, True)
|
2012-10-08 14:54:31 +00:00
|
|
|
else:
|
|
|
|
for config_file in self.options.config:
|
|
|
|
self.__add_adapted_config(configs, config_file)
|
2012-09-25 13:59:58 +00:00
|
|
|
|
2012-10-08 14:54:31 +00:00
|
|
|
self.core.load_configs(configs)
|
|
|
|
|
|
|
|
if self.ammofile:
|
|
|
|
self.log.debug("Ammofile: %s", self.ammofile)
|
|
|
|
self.core.set_option(self.MIGRATE_SECTION, 'ammofile', self.ammofile[0])
|
|
|
|
|
|
|
|
self.__translate_old_options()
|
|
|
|
self.__override_config_from_cmdline()
|
|
|
|
|
|
|
|
self.core.load_plugins()
|
2012-10-01 11:23:59 +00:00
|
|
|
|
2012-10-08 14:54:31 +00:00
|
|
|
if self.options.scheduled_start:
|
|
|
|
try:
|
|
|
|
self.scheduled_start = datetime.datetime.strptime(self.options.scheduled_start, '%Y-%m-%d %H:%M:%S')
|
|
|
|
except ValueError:
|
|
|
|
self.scheduled_start = datetime.datetime.strptime(datetime.datetime.now().strftime('%Y-%m-%d ') + self.options.scheduled_start, '%Y-%m-%d %H:%M:%S')
|
2012-11-29 13:37:13 +00:00
|
|
|
|
|
|
|
if self.options.ignore_lock:
|
|
|
|
self.core.set_option(self.core.SECTION, self.IGNORE_LOCKS, "1")
|
|
|
|
|
2012-10-08 14:54:31 +00:00
|
|
|
except Exception, ex:
|
|
|
|
self.log.info("Exception: %s", traceback.format_exc(ex))
|
|
|
|
sys.stdout.write(RealConsoleMarkup.RED)
|
|
|
|
self.log.error("%s", ex)
|
|
|
|
sys.stdout.write(RealConsoleMarkup.RESET)
|
|
|
|
sys.stdout.write(RealConsoleMarkup.TOTAL_RESET)
|
|
|
|
self.core.release_lock()
|
|
|
|
raise ex
|
2012-09-12 13:18:58 +00:00
|
|
|
|
2012-09-23 11:53:38 +00:00
|
|
|
def __graceful_shutdown(self):
|
2012-10-23 17:24:58 +00:00
|
|
|
''' call shutdown routines '''
|
2012-09-19 07:49:06 +00:00
|
|
|
retcode = 1
|
2012-09-12 13:18:58 +00:00
|
|
|
self.log.info("Trying to shutdown gracefully...")
|
2012-09-19 07:49:06 +00:00
|
|
|
retcode = self.core.plugins_end_test(retcode)
|
|
|
|
retcode = self.core.plugins_post_process(retcode)
|
2012-10-02 13:22:22 +00:00
|
|
|
self.log.info("Done graceful shutdown")
|
2012-09-19 07:49:06 +00:00
|
|
|
return retcode
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def perform_test(self):
|
2012-09-23 11:53:38 +00:00
|
|
|
'''
|
|
|
|
Run the test sequence via Tank Core
|
|
|
|
'''
|
2012-09-12 13:18:58 +00:00
|
|
|
self.log.info("Performing test")
|
2012-09-19 07:49:06 +00:00
|
|
|
retcode = 1
|
2012-09-12 13:18:58 +00:00
|
|
|
try:
|
2012-09-14 11:16:51 +00:00
|
|
|
self.core.plugins_configure()
|
2012-09-12 13:18:58 +00:00
|
|
|
self.core.plugins_prepare_test()
|
2012-10-01 11:28:53 +00:00
|
|
|
if self.scheduled_start:
|
|
|
|
self.log.info("Waiting scheduled time: %s...", self.scheduled_start)
|
|
|
|
while datetime.datetime.now() < self.scheduled_start:
|
|
|
|
self.log.debug("Not yet: %s < %s", datetime.datetime.now(), self.scheduled_start)
|
|
|
|
time.sleep(1)
|
|
|
|
self.log.info("Time has come: %s", datetime.datetime.now())
|
|
|
|
|
|
|
|
if self.options.manual_start:
|
|
|
|
raw_input("Press Enter key to start test:")
|
|
|
|
|
2012-09-12 13:18:58 +00:00
|
|
|
self.core.plugins_start_test()
|
2012-09-19 07:49:06 +00:00
|
|
|
retcode = self.core.wait_for_finish()
|
|
|
|
retcode = self.core.plugins_end_test(retcode)
|
|
|
|
retcode = self.core.plugins_post_process(retcode)
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
except KeyboardInterrupt as ex:
|
2012-11-01 17:47:13 +00:00
|
|
|
sys.stdout.write(RealConsoleMarkup.YELLOW)
|
|
|
|
self.log.info("Do not press Ctrl+C again, the test will be broken otherwise")
|
|
|
|
sys.stdout.write(RealConsoleMarkup.RESET)
|
|
|
|
sys.stdout.write(RealConsoleMarkup.TOTAL_RESET)
|
2012-09-12 13:18:58 +00:00
|
|
|
self.signal_count += 1
|
|
|
|
self.log.debug("Caught KeyboardInterrupt: %s", traceback.format_exc(ex))
|
|
|
|
try:
|
2012-09-23 11:53:38 +00:00
|
|
|
retcode = self.__graceful_shutdown()
|
2012-09-12 13:18:58 +00:00
|
|
|
except KeyboardInterrupt as ex:
|
|
|
|
self.log.debug("Caught KeyboardInterrupt again: %s", traceback.format_exc(ex))
|
2012-09-14 11:16:51 +00:00
|
|
|
self.log.info("User insists on exiting, aborting graceful shutdown...")
|
2012-09-19 07:49:06 +00:00
|
|
|
retcode = 1
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
except Exception as ex:
|
2012-09-20 10:46:55 +00:00
|
|
|
self.log.info("Exception: %s", traceback.format_exc(ex))
|
2012-09-14 10:34:39 +00:00
|
|
|
sys.stdout.write(RealConsoleMarkup.RED)
|
2012-09-12 13:18:58 +00:00
|
|
|
self.log.error("%s", ex)
|
2012-09-14 10:34:39 +00:00
|
|
|
sys.stdout.write(RealConsoleMarkup.RESET)
|
2012-10-04 08:29:50 +00:00
|
|
|
sys.stdout.write(RealConsoleMarkup.TOTAL_RESET)
|
2012-09-23 11:53:38 +00:00
|
|
|
retcode = self.__graceful_shutdown()
|
2012-10-08 14:54:31 +00:00
|
|
|
self.core.release_lock()
|
2012-09-12 13:18:58 +00:00
|
|
|
|
2012-09-19 07:49:06 +00:00
|
|
|
self.log.info("Done performing test with code %s", retcode)
|
|
|
|
return retcode
|
2012-09-12 13:18:58 +00:00
|
|
|
|
|
|
|
|