yandex-tank/tankcore.py

689 lines
23 KiB
Python
Raw Normal View History

2013-11-19 12:47:46 +00:00
""" The central part of the tool: Core """
2012-09-12 13:18:58 +00:00
from ConfigParser import NoSectionError
import ConfigParser
2012-10-03 16:35:52 +00:00
import datetime
import errno
import itertools
2012-09-12 13:18:58 +00:00
import logging
import os
2012-10-03 16:35:52 +00:00
import re
import select
2013-11-25 13:45:34 +00:00
import shlex
2012-09-12 13:18:58 +00:00
import shutil
2012-10-03 16:35:52 +00:00
import subprocess
2012-09-12 13:18:58 +00:00
import sys
import tempfile
import time
import traceback
import fnmatch
2012-11-26 16:03:22 +00:00
import psutil
2012-10-03 16:35:52 +00:00
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
def log_stdout_stderr(log, stdout, stderr, comment=""):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
This function polls stdout and stderr streams and writes their contents to log
2013-11-19 12:47:46 +00:00
"""
2013-06-19 11:25:54 +00:00
readable = select.select([stdout], [], [], 0)[0]
2012-10-03 16:35:52 +00:00
if stderr:
2013-06-19 11:25:54 +00:00
exceptional = select.select([stderr], [], [], 0)[0]
2012-10-03 16:35:52 +00:00
else:
exceptional = []
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
log.debug("Selected: %s, %s", readable, exceptional)
for handle in readable:
line = handle.read()
readable.remove(handle)
if line:
log.debug("%s stdout: %s", comment, line.strip())
for handle in exceptional:
line = handle.read()
exceptional.remove(handle)
if line:
log.warn("%s stderr: %s", comment, line.strip())
def expand_to_milliseconds(str_time):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
converts 1d2s into milliseconds
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
return expand_time(str_time, 'ms', 1000)
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
def expand_to_seconds(str_time):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
converts 1d2s into seconds
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
return expand_time(str_time, 's', 1)
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
def expand_time(str_time, default_unit='s', multiplier=1):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
helper for above functions
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
parser = re.compile('(\d+)([a-zA-Z]*)')
parts = parser.findall(str_time)
result = 0.0
for value, unit in parts:
value = int(value)
unit = unit.lower()
2013-06-19 11:25:54 +00:00
if unit == '':
unit = default_unit
if unit == 'ms':
result += value * 0.001
2012-10-03 16:35:52 +00:00
continue
2013-06-19 11:25:54 +00:00
elif unit == 's':
2012-10-03 16:35:52 +00:00
result += value
continue
2013-06-19 11:25:54 +00:00
elif unit == 'm':
2012-10-03 16:35:52 +00:00
result += value * 60
continue
2013-06-19 11:25:54 +00:00
elif unit == 'h':
2012-10-03 16:35:52 +00:00
result += value * 60 * 60
continue
2013-06-19 11:25:54 +00:00
elif unit == 'd':
2012-10-03 16:35:52 +00:00
result += value * 60 * 60 * 24
continue
2013-06-19 11:25:54 +00:00
elif unit == 'w':
2012-10-03 16:35:52 +00:00
result += value * 60 * 60 * 24 * 7
continue
2013-06-19 11:25:54 +00:00
else:
raise ValueError(
"String contains unsupported unit %s: %s" % (unit, str_time))
2012-10-03 16:35:52 +00:00
return int(result * multiplier)
2012-11-26 16:03:22 +00:00
2012-10-03 16:35:52 +00:00
def pid_exists(pid):
"""Check whether pid exists in the current process table."""
if pid < 0:
return False
try:
os.kill(pid, 0)
except OSError, exc:
2012-11-26 16:03:22 +00:00
logging.debug("No process[%s]: %s", exc.errno, exc)
2012-10-03 16:35:52 +00:00
return exc.errno == errno.EPERM
else:
2012-11-26 16:03:22 +00:00
p = psutil.Process(pid)
return p.status != psutil.STATUS_ZOMBIE
2012-10-03 16:35:52 +00:00
2014-02-11 10:21:34 +00:00
def execute(cmd, shell=False, poll_period=1.0, catch_out=False):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
Wrapper for Popen
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
log = logging.getLogger(__name__)
log.debug("Starting: %s", cmd)
2013-03-22 13:55:13 +00:00
stdout = ""
stderr = ""
2012-12-05 09:54:57 +00:00
2014-02-11 10:13:19 +00:00
if not shell and isinstance(cmd, basestring):
2013-11-25 13:45:34 +00:00
cmd = shlex.split(cmd)
2012-10-03 16:35:52 +00:00
if catch_out:
2013-06-19 11:25:54 +00:00
process = subprocess.Popen(
cmd, shell=shell, stderr=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
2012-10-03 16:35:52 +00:00
else:
process = subprocess.Popen(cmd, shell=shell, close_fds=True)
2013-06-19 11:25:54 +00:00
2013-11-19 12:47:46 +00:00
while process.poll() is None:
2012-10-03 16:35:52 +00:00
log.debug("Waiting for process to finish: %s", process)
time.sleep(poll_period)
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
if catch_out:
for line in process.stderr.readlines():
2013-03-22 13:55:13 +00:00
stderr += line
2012-10-03 16:35:52 +00:00
log.warn(line.strip())
for line in process.stdout.readlines():
2013-03-22 13:55:13 +00:00
stdout += line
2012-10-03 16:35:52 +00:00
log.debug(line.strip())
2013-06-19 11:25:54 +00:00
2012-10-03 16:35:52 +00:00
retcode = process.poll()
log.debug("Process exit code: %s", retcode)
2012-12-05 09:54:57 +00:00
return retcode, stdout, stderr
2012-10-03 16:35:52 +00:00
2012-11-27 11:20:08 +00:00
2012-10-03 16:35:52 +00:00
def splitstring(string):
"""
>>> string = 'apple orange "banana tree" green'
>>> splitstring(string)
['apple', 'orange', 'green', '"banana tree"']
"""
patt = re.compile(r'"[\w ]+"')
if patt.search(string):
quoted_item = patt.search(string).group()
newstring = patt.sub('', string)
return newstring.split() + [quoted_item]
else:
return string.split()
def pairs(lst):
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
Iterate over pairs in the list
2013-11-19 12:47:46 +00:00
"""
2012-10-03 16:35:52 +00:00
return itertools.izip(lst[::2], lst[1::2])
2012-09-12 13:18:58 +00:00
class TankCore:
"""
2012-09-20 15:45:27 +00:00
JMeter + dstat inspired :)
2012-09-12 13:18:58 +00:00
"""
SECTION = 'tank'
PLUGIN_PREFIX = 'plugin_'
PID_OPTION = 'pid'
LOCK_DIR = '/var/lock'
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def __init__(self):
self.log = logging.getLogger(__name__)
self.config = ConfigManager()
self.plugins = {}
self.artifacts_dir = None
self.artifact_files = {}
self.plugins_order = []
2012-09-20 16:18:04 +00:00
self.artifacts_base_dir = '.'
2012-10-01 11:23:59 +00:00
self.manual_start = False
self.scheduled_start = None
2012-10-04 16:33:50 +00:00
self.interrupted = False
self.lock_file = None
2013-06-19 11:25:54 +00:00
self.flush_config_to = None
self.lock_dir = None
2014-06-09 12:31:34 +00:00
self.set_option(self.SECTION, self.PID_OPTION, str(os.getpid()))
2013-03-22 13:55:13 +00:00
def get_available_options(self):
2013-04-15 12:47:33 +00:00
return ["artifacts_base_dir", "artifacts_dir", "flush_config_to"]
2013-03-22 13:55:13 +00:00
2012-09-12 13:18:58 +00:00
def load_configs(self, configs):
2013-11-19 12:47:46 +00:00
""" Tells core to load configs set into options storage """
2012-09-12 13:18:58 +00:00
self.log.info("Loading configs...")
2014-06-09 12:31:34 +00:00
for fname in configs:
if not os.path.isfile(fname):
# can't raise exception, since ~/.yandex-tank may not exist
logging.debug("Config file not found: %s", fname)
2012-09-12 13:18:58 +00:00
self.config.load_files(configs)
dotted_options = []
for option, value in self.config.get_options(self.SECTION):
if '.' in option:
dotted_options += [option + '=' + value]
self.apply_shorthand_options(dotted_options, self.SECTION)
2012-09-12 13:18:58 +00:00
self.config.flush()
self.add_artifact_file(self.config.file)
2014-06-09 12:31:34 +00:00
self.set_option(self.SECTION, self.PID_OPTION, str(os.getpid()))
2013-06-19 11:25:54 +00:00
self.flush_config_to = self.get_option(
self.SECTION, "flush_config_to", "")
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
2012-09-12 13:18:58 +00:00
def load_plugins(self):
2013-11-19 12:47:46 +00:00
""" Tells core to take plugin options and instantiate plugin classes """
2012-09-12 13:18:58 +00:00
self.log.info("Loading plugins...")
2013-06-19 11:25:54 +00:00
self.artifacts_base_dir = os.path.expanduser(
self.get_option(self.SECTION, "artifacts_base_dir", self.artifacts_base_dir))
2014-06-09 12:31:34 +00:00
self.artifacts_dir = self.get_option(self.SECTION, "artifacts_dir", self.artifacts_dir)
2012-09-17 13:48:02 +00:00
if self.artifacts_dir:
self.artifacts_dir = os.path.expanduser(self.artifacts_dir)
2012-09-12 13:18:58 +00:00
options = self.config.get_options(self.SECTION, self.PLUGIN_PREFIX)
for (plugin_name, plugin_path) in options:
2012-09-12 13:18:58 +00:00
if not plugin_path:
2013-06-19 11:25:54 +00:00
self.log.debug(
"Seems the plugin '%s' was disabled", plugin_name)
2012-09-12 13:18:58 +00:00
continue
2012-09-20 16:18:04 +00:00
instance = self.__load_plugin(plugin_name, plugin_path)
key = os.path.realpath(instance.get_key())
2013-06-19 11:25:54 +00:00
self.plugins[key] = instance
self.plugins_order.append(key)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
self.log.debug("Plugin instances: %s", self.plugins)
self.log.debug("Plugins order: %s", self.plugins_order)
2013-06-19 11:25:54 +00:00
2012-09-20 16:18:04 +00:00
def __load_plugin(self, name, path):
2013-11-19 12:47:46 +00:00
""" Load single plugin using 'exec' statement """
2012-09-12 13:18:58 +00:00
self.log.debug("Loading plugin %s from %s", name, path)
for basedir in [''] + sys.path:
if basedir:
new_dir = basedir + '/' + path
else:
new_dir = path
if os.path.exists(new_dir):
new_dir = os.path.dirname(new_dir)
if new_dir not in sys.path:
self.log.debug('Append to path: %s', new_dir)
sys.path.append(new_dir)
2012-09-12 13:18:58 +00:00
res = None
classname = os.path.basename(path)[:-3]
self.log.debug("sys.path: %s", sys.path)
2012-09-12 13:18:58 +00:00
exec ("import " + classname)
script = "res=" + classname + "." + classname + "Plugin(self)"
self.log.debug("Exec: " + script)
2013-11-19 12:47:46 +00:00
exec script
2012-09-20 16:00:27 +00:00
self.log.debug("Instantiated: %s", res)
2012-09-12 13:18:58 +00:00
return res
def plugins_configure(self):
2013-11-19 12:47:46 +00:00
""" Call configure() on all plugins """
2013-06-19 11:25:54 +00:00
if not os.path.exists(self.artifacts_base_dir):
os.makedirs(self.artifacts_base_dir)
2012-10-17 12:29:43 +00:00
os.chmod(self.artifacts_base_dir, 0755)
2012-09-12 13:18:58 +00:00
self.log.info("Configuring plugins...")
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Configuring %s", plugin)
plugin.configure()
self.config.flush()
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def plugins_prepare_test(self):
2013-11-19 12:47:46 +00:00
""" Call prepare_test() on all plugins """
2012-09-12 13:18:58 +00:00
self.log.info("Preparing test...")
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Preparing %s", plugin)
plugin.prepare_test()
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def plugins_start_test(self):
2013-11-19 12:47:46 +00:00
""" Call start_test() on all plugins """
2012-09-12 13:18:58 +00:00
self.log.info("Starting test...")
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Starting %s", plugin)
plugin.start_test()
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def wait_for_finish(self):
2013-11-19 12:47:46 +00:00
""" Call is_test_finished() on all plugins 'till one of them initiates exit """
2012-09-20 16:18:04 +00:00
2012-09-12 13:18:58 +00:00
self.log.info("Waiting for test to finish...")
if not self.plugins:
2012-09-14 10:34:39 +00:00
raise RuntimeError("It's strange: we have no plugins loaded...")
2013-06-19 11:25:54 +00:00
2012-10-04 16:33:50 +00:00
while not self.interrupted:
2012-09-12 13:18:58 +00:00
begin_time = time.time()
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Polling %s", plugin)
2012-09-19 07:49:06 +00:00
retcode = plugin.is_test_finished()
if retcode >= 0:
2012-09-20 16:00:27 +00:00
return retcode
2012-09-12 13:18:58 +00:00
end_time = time.time()
diff = end_time - begin_time
self.log.debug("Polling took %s", diff)
# screen refresh every 0.5 s
2013-11-19 12:47:46 +00:00
if diff < 0.5:
time.sleep(0.5 - diff)
2012-10-04 16:33:50 +00:00
return 1
2012-09-12 13:18:58 +00:00
2012-09-20 16:18:04 +00:00
def plugins_end_test(self, retcode):
2013-11-19 12:47:46 +00:00
""" Call end_test() on all plugins """
2012-09-12 13:18:58 +00:00
self.log.info("Finishing test...")
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Finalize %s", plugin)
try:
2013-03-28 09:14:40 +00:00
self.log.debug("RC before: %s", retcode)
2012-09-20 16:18:04 +00:00
plugin.end_test(retcode)
2013-03-28 09:14:40 +00:00
self.log.debug("RC after: %s", retcode)
2012-09-12 13:18:58 +00:00
except Exception, ex:
self.log.error("Failed finishing plugin %s: %s", plugin, ex)
2013-06-19 11:25:54 +00:00
self.log.debug(
"Failed finishing plugin: %s", traceback.format_exc(ex))
2012-09-20 16:18:04 +00:00
if not retcode:
retcode = 1
2012-09-12 13:18:58 +00:00
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
2012-09-20 16:18:04 +00:00
return retcode
2012-09-20 16:18:04 +00:00
def plugins_post_process(self, retcode):
2013-11-19 12:47:46 +00:00
"""
2012-09-20 16:18:04 +00:00
Call post_process() on all plugins
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
self.log.info("Post-processing test...")
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
for plugin_key in self.plugins_order:
2012-09-20 16:18:04 +00:00
plugin = self.__get_plugin_by_key(plugin_key)
2012-09-12 13:18:58 +00:00
self.log.debug("Post-process %s", plugin)
try:
2013-03-28 09:14:40 +00:00
self.log.debug("RC before: %s", retcode)
2013-06-19 11:25:54 +00:00
retcode = plugin.post_process(retcode)
2013-03-28 09:14:40 +00:00
self.log.debug("RC after: %s", retcode)
2012-09-12 13:18:58 +00:00
except Exception, ex:
2013-06-19 11:25:54 +00:00
self.log.error(
"Failed post-processing plugin %s: %s", plugin, ex)
self.log.debug(
"Failed post-processing plugin: %s", traceback.format_exc(ex))
2012-09-12 13:18:58 +00:00
del self.plugins[plugin_key]
2012-09-20 16:18:04 +00:00
if not retcode:
retcode = 1
2012-09-12 13:18:58 +00:00
2013-04-15 12:47:33 +00:00
if self.flush_config_to:
self.config.flush(self.flush_config_to)
self.__collect_artifacts()
2013-06-19 11:25:54 +00:00
2012-09-20 16:18:04 +00:00
return retcode
2012-10-18 10:46:50 +00:00
def __collect_artifacts(self):
self.log.debug("Collecting artifacts")
if not self.artifacts_dir:
date_str = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S.")
2013-06-19 11:25:54 +00:00
self.artifacts_dir = tempfile.mkdtemp(
"", date_str, self.artifacts_base_dir)
2012-10-18 10:46:50 +00:00
if not os.path.isdir(self.artifacts_dir):
os.makedirs(self.artifacts_dir)
2013-06-19 11:25:54 +00:00
2012-11-12 12:20:36 +00:00
os.chmod(self.artifacts_dir, 0755)
2012-10-18 10:46:50 +00:00
self.log.info("Artifacts dir: %s", self.artifacts_dir)
for filename, keep in self.artifact_files.items():
try:
self.__collect_file(filename, keep)
except Exception, ex:
self.log.warn("Failed to collect file %s: %s", filename, ex)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def get_option(self, section, option, default=None):
2013-11-19 12:47:46 +00:00
"""
2012-09-20 16:18:04 +00:00
Get an option from option storage
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
if not self.config.config.has_section(section):
self.log.debug("No section '%s', adding", section)
self.config.config.add_section(section)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
try:
2012-12-05 09:54:57 +00:00
value = self.config.config.get(section, option).strip()
2012-09-12 13:18:58 +00:00
except ConfigParser.NoOptionError as ex:
2013-06-19 11:25:54 +00:00
if default is not None:
2013-03-22 13:55:13 +00:00
default = str(default)
2012-09-12 13:18:58 +00:00
self.config.config.set(section, option, default)
self.config.flush()
2012-12-05 09:54:57 +00:00
value = default.strip()
2012-09-12 13:18:58 +00:00
else:
2014-02-11 10:21:34 +00:00
self.log.warn("Mandatory option %s was not found in section %s", option, section)
2012-09-12 13:18:58 +00:00
raise ex
2013-06-19 11:25:54 +00:00
if len(value) > 1 and value[0] == '`' and value[-1] == '`':
2012-12-05 09:54:57 +00:00
self.log.debug("Expanding shell option %s", value)
retcode, stdout, stderr = execute(value[1:-1], True, 0.1, True)
if retcode or stderr:
2013-06-19 11:25:54 +00:00
raise ValueError(
"Error expanding option %s, RC: %s" % (value, retcode))
2013-03-22 13:55:13 +00:00
value = stdout.strip()
2013-06-19 11:25:54 +00:00
2012-12-05 09:54:57 +00:00
return value
2012-09-12 13:18:58 +00:00
def set_option(self, section, option, value):
2013-11-19 12:47:46 +00:00
"""
2012-09-20 16:18:04 +00:00
Set an option in storage
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
if not self.config.config.has_section(section):
self.config.config.add_section(section)
self.config.config.set(section, option, value)
self.config.flush()
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def get_plugin_of_type(self, needle):
2013-11-19 12:47:46 +00:00
"""
2012-09-20 16:18:04 +00:00
Retrieve a plugin of desired class, KeyError raised otherwise
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
self.log.debug("Searching for plugin: %s", needle)
key = os.path.realpath(needle.get_key())
2013-06-19 11:25:54 +00:00
2012-09-20 16:18:04 +00:00
return self.__get_plugin_by_key(key)
2013-06-19 11:25:54 +00:00
2012-09-20 16:18:04 +00:00
def __get_plugin_by_key(self, key):
2013-11-19 12:47:46 +00:00
"""
Get plugin from loaded by its key
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
if key in self.plugins.keys():
return self.plugins[key]
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
ext = os.path.splitext(key)[1].lower()
2013-06-19 11:25:54 +00:00
2013-03-22 13:55:13 +00:00
if ext == '.py' and key + 'c' in self.plugins.keys(): # .py => .pyc
2012-09-12 13:18:58 +00:00
return self.plugins[key + 'c']
2013-06-19 11:25:54 +00:00
2013-03-22 13:55:13 +00:00
if ext == '.pyc' and key[:-1] in self.plugins.keys(): # .pyc => .py:
2012-09-12 13:18:58 +00:00
return self.plugins[key[:-1]]
2013-06-19 11:25:54 +00:00
raise KeyError("Requested plugin type not found: %s" % key)
2012-09-20 16:18:04 +00:00
def __collect_file(self, filename, keep_original=False):
2013-11-19 12:47:46 +00:00
"""
Move or copy single file to artifacts dir
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
if not self.artifacts_dir:
self.log.warning("No artifacts dir configured")
2013-06-19 11:25:54 +00:00
return
dest = self.artifacts_dir + '/' + os.path.basename(filename)
2012-09-17 13:48:02 +00:00
self.log.debug("Collecting file: %s to %s", filename, dest)
2012-09-12 13:18:58 +00:00
if not filename or not os.path.exists(filename):
self.log.warning("File not found to collect: %s", filename)
return
2013-06-19 11:25:54 +00:00
2012-09-17 13:48:02 +00:00
if os.path.exists(dest):
# FIXME: 3 find a way to store artifacts anyway
2012-09-17 13:48:02 +00:00
self.log.warning("File already exists: %s", dest)
return
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
if keep_original:
shutil.copy(filename, self.artifacts_dir)
else:
shutil.move(filename, self.artifacts_dir)
os.chmod(dest, 0644)
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def add_artifact_file(self, filename, keep_original=False):
2013-11-19 12:47:46 +00:00
"""
2012-09-20 16:18:04 +00:00
Add file to be stored as result artifact on post-process phase
2013-11-19 12:47:46 +00:00
"""
2012-09-12 13:18:58 +00:00
if filename:
2014-06-09 12:31:34 +00:00
logging.debug("Adding artifact file to collect (keep=%s): %s", keep_original, filename)
2012-09-12 13:18:58 +00:00
self.artifact_files[filename] = keep_original
def apply_shorthand_options(self, options, default_section='DEFAULT'):
for option_str in options:
try:
section = option_str[:option_str.index('.')]
2013-11-19 12:47:46 +00:00
option = option_str[option_str.index('.') + 1:option_str.index('=')]
except ValueError:
section = default_section
option = option_str[:option_str.index('=')]
2013-06-19 11:25:54 +00:00
value = option_str[option_str.index('=') + 1:]
self.log.debug(
"Override option: %s => [%s] %s=%s", option_str, section, option, value)
self.set_option(section, option, value)
def get_lock_dir(self):
if not self.lock_dir:
self.lock_dir = self.get_option(self.SECTION, "lock_dir", self.LOCK_DIR)
return os.path.expanduser(self.lock_dir)
def get_lock(self, force=False):
if not force and self.__there_is_locks():
raise RuntimeError("There is lock files")
2013-06-19 11:25:54 +00:00
fh, self.lock_file = tempfile.mkstemp(
'.lock', 'lunapark_', self.get_lock_dir())
2012-10-22 11:11:19 +00:00
os.close(fh)
2012-10-08 16:59:27 +00:00
os.chmod(self.lock_file, 0644)
self.config.file = self.lock_file
2014-06-09 12:31:34 +00:00
self.config.flush()
2013-06-19 11:25:54 +00:00
def release_lock(self):
self.config.file = None
2013-07-22 13:39:36 +00:00
if self.lock_file and os.path.exists(self.lock_file):
2014-06-09 12:31:34 +00:00
self.log.debug("Releasing lock: %s", self.lock_file)
os.remove(self.lock_file)
2013-06-19 11:25:54 +00:00
def __there_is_locks(self):
retcode = False
lock_dir = self.get_lock_dir()
for filename in os.listdir(lock_dir):
if fnmatch.fnmatch(filename, 'lunapark_*.lock'):
full_name = os.path.join(lock_dir, filename)
self.log.warn("Lock file present: %s", full_name)
2013-06-19 11:25:54 +00:00
try:
info = ConfigParser.ConfigParser()
info.read(full_name)
pid = info.get(TankCore.SECTION, self.PID_OPTION)
if not pid_exists(int(pid)):
2013-06-19 11:25:54 +00:00
self.log.debug(
"Lock PID %s not exists, ignoring and trying to remove", pid)
try:
os.remove(full_name)
except Exception, exc:
2013-06-19 11:25:54 +00:00
self.log.debug(
"Failed to delete lock %s: %s", full_name, exc)
else:
2012-10-08 16:59:27 +00:00
retcode = True
except Exception, exc:
2013-06-19 11:25:54 +00:00
self.log.warn(
"Failed to load info from lock %s: %s", full_name, exc)
retcode = True
return retcode
2013-06-19 11:25:54 +00:00
2014-06-09 12:31:34 +00:00
def mkstemp(self, suffix, prefix, directory=None):
2013-11-19 12:47:46 +00:00
"""
2012-10-22 11:11:19 +00:00
Generate temp file name in artifacts base dir
and close temp file handle
2013-11-19 12:47:46 +00:00
"""
2014-06-09 12:31:34 +00:00
if not directory:
directory = self.artifacts_base_dir
fd, fname = tempfile.mkstemp(suffix, prefix, directory)
2012-10-22 11:11:19 +00:00
os.close(fd)
2014-06-09 12:31:34 +00:00
os.chmod(fname, 0644) # FIXME: chmod to parent dir's mode?
2012-10-22 11:11:19 +00:00
return fname
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
class ConfigManager:
2013-11-19 12:47:46 +00:00
""" Option storage class """
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def __init__(self):
self.file = None
2012-09-12 13:18:58 +00:00
self.log = logging.getLogger(__name__)
self.config = ConfigParser.ConfigParser()
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def load_files(self, configs):
2013-11-19 12:47:46 +00:00
""" Read configs set into storage """
2012-09-12 13:18:58 +00:00
self.log.debug("Reading configs: %s", configs)
try:
self.config.read(configs)
except Exception as ex:
self.log.error("Can't load configs: %s", ex)
raise ex
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def flush(self, filename=None):
2013-11-19 12:47:46 +00:00
""" Flush current stat to file """
2012-09-12 13:18:58 +00:00
if not filename:
filename = self.file
2013-06-19 11:25:54 +00:00
if filename:
self.log.debug("Flushing config to: %s", filename)
handle = open(filename, 'wb')
self.config.write(handle)
handle.close()
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def get_options(self, section, prefix=''):
2013-11-19 12:47:46 +00:00
""" Get options list with requested prefix """
2012-09-12 13:18:58 +00:00
res = []
2013-06-19 11:25:54 +00:00
self.log.debug(
"Looking in section '%s' for options starting with '%s'", section, prefix)
try:
2012-09-12 13:18:58 +00:00
for option in self.config.options(section):
self.log.debug("Option: %s", option)
if not prefix or option.find(prefix) == 0:
self.log.debug("Option: %s matched", option)
2013-06-19 11:25:54 +00:00
res += [
(option[len(prefix):], self.config.get(section, option))]
2012-09-12 13:18:58 +00:00
except NoSectionError, ex:
self.log.debug("No section: %s", ex)
2013-06-19 11:25:54 +00:00
2012-09-20 16:00:27 +00:00
self.log.debug("Found options: %s", res)
2012-09-12 13:18:58 +00:00
return res
2013-01-27 15:56:23 +00:00
def find_sections(self, prefix):
2013-11-19 12:47:46 +00:00
""" return sections with specified prefix """
2013-03-22 13:55:13 +00:00
res = []
2013-01-27 15:56:23 +00:00
for section in self.config.sections():
if section.startswith(prefix):
res.append(section)
return res
2012-09-12 13:18:58 +00:00
class AbstractPlugin:
2013-11-19 12:47:46 +00:00
""" Parent class for all plugins/modules """
2012-09-20 16:00:27 +00:00
SECTION = 'DEFAULT'
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
@staticmethod
def get_key():
2013-11-19 12:47:46 +00:00
""" Get dictionary key for plugin, should point to __file__ magic constant """
2012-09-12 13:18:58 +00:00
raise TypeError("Abstract method needs to be overridden")
2013-06-19 11:25:54 +00:00
2012-09-12 16:48:22 +00:00
def __init__(self, core):
2014-06-09 12:31:34 +00:00
"""
@type core: TankCore
"""
2012-09-12 16:48:22 +00:00
self.log = logging.getLogger(__name__)
self.core = core
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def configure(self):
2013-11-19 12:47:46 +00:00
""" A stage to read config values and instantiate objects """
2012-09-20 16:18:04 +00:00
pass
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def prepare_test(self):
2013-11-19 12:47:46 +00:00
""" Test preparation tasks """
2012-09-20 16:18:04 +00:00
pass
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def start_test(self):
2013-11-19 12:47:46 +00:00
""" Launch test process """
2012-09-20 16:18:04 +00:00
pass
2013-06-19 11:25:54 +00:00
2012-09-12 13:18:58 +00:00
def is_test_finished(self):
2013-11-19 12:47:46 +00:00
""" Polling call, if result differs from -1 then test end will be triggeted """
2012-09-20 16:00:27 +00:00
return -1
2013-06-19 11:25:54 +00:00
2012-09-12 16:48:22 +00:00
def end_test(self, retcode):
2013-11-19 12:47:46 +00:00
""" Stop processes launched at 'start_test', change return code if necessary """
2012-09-20 16:18:04 +00:00
return retcode
2013-06-19 11:25:54 +00:00
2012-09-19 07:49:06 +00:00
def post_process(self, retcode):
2013-11-19 12:47:46 +00:00
""" Post-process test data """
2012-09-20 16:00:27 +00:00
return retcode
2012-09-12 13:18:58 +00:00
2012-09-13 10:19:33 +00:00
def get_option(self, option_name, default_value=None):
2013-11-19 12:47:46 +00:00
""" Wrapper to get option from plugins' section """
return self.core.get_option(self.SECTION, option_name, default_value)
2012-09-18 15:54:48 +00:00
def set_option(self, option_name, value):
2013-11-19 12:47:46 +00:00
""" Wrapper to set option to plugins' section """
2012-09-18 15:54:48 +00:00
return self.core.set_option(self.SECTION, option_name, value)
2013-03-22 13:55:13 +00:00
def get_available_options(self):
2013-11-19 12:47:46 +00:00
""" returns array containing known options for plugin """
2013-03-22 13:55:13 +00:00
return []
2014-06-09 12:31:34 +00:00
def get_multiline_option(self, option_name, default_value=None):
if default_value is not None:
default = ' '.join(default_value)
else:
default = None
value = self.get_option(option_name, default)
if value:
return (' '.join(value.split("\n"))).split(' ')
else:
return ()