blob: 360506e6dc380893c4b327c272540ed438811186 [file] [log] [blame]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +03001import os
2import sys
3import time
4import signal
5import logging
6import argparse
7import functools
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02008from typing import List, Tuple, Any, Callable, IO, cast, Optional
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +03009from yaml import load as _yaml_load
10
koder aka kdanilov22d134e2016-11-08 11:33:19 +020011
12YLoader = Callable[[IO], Any]
13yaml_load = None # type: YLoader
14
15
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030016try:
17 from yaml import CLoader
koder aka kdanilov22d134e2016-11-08 11:33:19 +020018 yaml_load = cast(YLoader, functools.partial(_yaml_load, Loader=CLoader))
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030019except ImportError:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020020 yaml_load = cast(YLoader, _yaml_load)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030021
22
23import texttable
24
25try:
26 import faulthandler
27except ImportError:
28 faulthandler = None
29
30
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030031from . import utils, run_test, pretty_yaml
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020032from .storage import make_storage, Storage
koder aka kdanilov22d134e2016-11-08 11:33:19 +020033from .config import Config
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030034from .logger import setup_loggers
koder aka kdanilov22d134e2016-11-08 11:33:19 +020035from .stage import log_stage, StageType
36from .test_run_class import TestRun
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030037
38
39logger = logging.getLogger("wally")
40
41
koder aka kdanilov22d134e2016-11-08 11:33:19 +020042def list_results(path: str) -> List[Tuple[str, str, str, str]]:
koder aka kdanilov73084622016-11-16 21:51:08 +020043 results = [] # type: List[Tuple[float, str, str, str, str]]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030044
koder aka kdanilov22d134e2016-11-08 11:33:19 +020045 for dir_name in os.listdir(path):
46 full_path = os.path.join(path, dir_name)
47
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030048 try:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020049 stor = make_storage(full_path, existing=True)
50 except Exception as exc:
51 logger.warning("Can't load folder {}. Error {}".format(full_path, exc))
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030052
koder aka kdanilov73084622016-11-16 21:51:08 +020053 comment = cast(str, stor['info/comment'])
54 run_uuid = cast(str, stor['info/run_uuid'])
55 run_time = cast(float, stor['info/run_time'])
koder aka kdanilov22d134e2016-11-08 11:33:19 +020056 test_types = ""
koder aka kdanilov73084622016-11-16 21:51:08 +020057 results.append((run_time,
koder aka kdanilov22d134e2016-11-08 11:33:19 +020058 run_uuid,
59 test_types,
koder aka kdanilov73084622016-11-16 21:51:08 +020060 time.ctime(run_time),
koder aka kdanilov22d134e2016-11-08 11:33:19 +020061 '-' if comment is None else comment))
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030062
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030063 results.sort()
koder aka kdanilov22d134e2016-11-08 11:33:19 +020064 return [i[1:] for i in results]
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030065
66
koder aka kdanilov22d134e2016-11-08 11:33:19 +020067def log_nodes_statistic_stage(ctx: TestRun) -> None:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030068 utils.log_nodes_statistic(ctx.nodes)
69
70
71def parse_args(argv):
72 descr = "Disk io performance test suite"
73 parser = argparse.ArgumentParser(prog='wally', description=descr)
koder aka kdanilov22d134e2016-11-08 11:33:19 +020074 parser.add_argument("-l", '--log-level', help="print some extra log info")
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +020075 parser.add_argument("-s", '--settings-dir', default=None,
76 help="Folder to store key/settings/history files")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030077
78 subparsers = parser.add_subparsers(dest='subparser_name')
79
80 # ---------------------------------------------------------------------
koder aka kdanilov22d134e2016-11-08 11:33:19 +020081 report_parser = subparsers.add_parser('ls', help='list all results')
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030082 report_parser.add_argument("result_storage", help="Folder with test results")
83
84 # ---------------------------------------------------------------------
85 compare_help = 'compare two results'
86 report_parser = subparsers.add_parser('compare', help=compare_help)
87 report_parser.add_argument("data_path1", help="First folder with test results")
88 report_parser.add_argument("data_path2", help="Second folder with test results")
89
90 # ---------------------------------------------------------------------
91 report_help = 'run report on previously obtained results'
92 report_parser = subparsers.add_parser('report', help=report_help)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030093 report_parser.add_argument("data_dir", help="folder with rest results")
94
95 # ---------------------------------------------------------------------
96 test_parser = subparsers.add_parser('test', help='run tests')
koder aka kdanilov22d134e2016-11-08 11:33:19 +020097 test_parser.add_argument('--build-description', type=str, default="Build info")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +030098 test_parser.add_argument('--build-id', type=str, default="id")
99 test_parser.add_argument('--build-type', type=str, default="GA")
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200100 test_parser.add_argument('-n', '--no-tests', action='store_true', help="Don't run tests")
101 test_parser.add_argument('--load-report', action='store_true')
102 test_parser.add_argument("-k", '--keep-vm', action='store_true', help="Don't remove test vm's")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300103 test_parser.add_argument("-d", '--dont-discover-nodes', action='store_true',
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200104 help="Don't connect/discover fuel nodes")
105 test_parser.add_argument('--no-report', action='store_true', help="Skip report stages")
106 test_parser.add_argument('-r', '--resume', default=None, help="Resume previously stopped test, stored in DIR",
107 metavar="DIR")
108 test_parser.add_argument('--result-dir', default=None, help="Save results to DIR", metavart="DIR")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300109 test_parser.add_argument("comment", help="Test information")
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200110 test_parser.add_argument("config_file", help="Yaml config file", nargs='?', default=None)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300111
112 # ---------------------------------------------------------------------
113
114 return parser.parse_args(argv[1:])
115
116
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200117def get_config_path(config: Config, opts_value: Optional[str]) -> str:
118 if opts_value is None and 'settings_dir' not in config:
119 val = "~/.wally"
120 elif opts_value is not None:
121 val = opts_value
122 else:
123 val = config.settings_dir
124
125 return os.path.abspath(os.path.expanduser(val))
126
127
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200128def main(argv: List[str]) -> int:
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300129 if faulthandler is not None:
130 faulthandler.register(signal.SIGUSR1, all_threads=True)
131
132 opts = parse_args(argv)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300133
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200134 stages = [] # type: List[StageType]
135 report_stages = [] # type: List[StageType]
136
137 # stop mypy from telling that config & storage might be undeclared
138 config = None # type: Config
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200139 storage = None # type: Storage
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300140
141 if opts.subparser_name == 'test':
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200142 if opts.resume:
143 storage = make_storage(opts.resume, existing=True)
144 config = storage.load('config', Config)
145 else:
146 file_name = os.path.abspath(opts.config_file)
147 with open(file_name) as fd:
148 config = Config(yaml_load(fd.read())) # type: ignore
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300149
koder aka kdanilov73084622016-11-16 21:51:08 +0200150 config.storage_url, config.run_uuid = utils.get_uniq_path_uuid(config.results_dir)
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200151 config.comment = opts.comment
152 config.keep_vm = opts.keep_vm
153 config.no_tests = opts.no_tests
154 config.dont_discover_nodes = opts.dont_discover_nodes
155 config.build_id = opts.build_id
156 config.build_description = opts.build_description
157 config.build_type = opts.build_type
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200158 config.settings_dir = get_config_path(config, opts.settings_dir)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300159
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200160 storage = make_storage(config.storage_url)
161
koder aka kdanilov73084622016-11-16 21:51:08 +0200162 storage['config'] = config # type: ignore
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300163
164 stages.extend([
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200165 run_test.discover_stage,
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300166 run_test.reuse_vms_stage,
167 log_nodes_statistic_stage,
168 run_test.save_nodes_stage,
169 run_test.connect_stage])
170
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200171 if config.get("collect_info", True):
koder aka kdanilov73084622016-11-16 21:51:08 +0200172 stages.append(run_test.collect_info_stage)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300173
174 stages.extend([
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300175 run_test.run_tests_stage,
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300176 ])
177
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300178 elif opts.subparser_name == 'ls':
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200179 tab = texttable.Texttable(max_width=200)
180 tab.set_deco(tab.HEADER | tab.VLINES | tab.BORDER)
181 tab.set_cols_align(["l", "l", "l", "l"])
182 tab.header(["Name", "Tests", "Run at", "Comment"])
183 tab.add_rows(list_results(opts.result_storage))
184 print(tab.draw())
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300185 return 0
186
187 elif opts.subparser_name == 'report':
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200188 storage = make_storage(opts.data_dir, existing=True)
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200189 config.settings_dir = get_config_path(config, opts.settings_dir)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300190
191 elif opts.subparser_name == 'compare':
koder aka kdanilov73084622016-11-16 21:51:08 +0200192 # x = run_test.load_data_from_path(opts.data_path1)
193 # y = run_test.load_data_from_path(opts.data_path2)
194 # print(run_test.IOPerfTest.format_diff_for_console(
195 # [x['io'][0], y['io'][0]]))
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300196 return 0
197
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200198 if not getattr(opts, "no_report", False):
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300199 report_stages.append(run_test.console_report_stage)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300200 report_stages.append(run_test.html_report_stage)
201
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200202 # log level is not a part of config
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300203 if opts.log_level is not None:
204 str_level = opts.log_level
205 else:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200206 str_level = config.get('logging/log_level', 'INFO')
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300207
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200208 setup_loggers(getattr(logging, str_level), log_fd=storage.get_stream('log'))
209 logger.info("All info would be stored into %r", config.storage_url)
210
211 ctx = TestRun(config, storage)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300212
213 for stage in stages:
214 ok = False
215 with log_stage(stage):
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200216 stage(ctx)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300217 ok = True
218 if not ok:
219 break
220
221 exc, cls, tb = sys.exc_info()
222 for stage in ctx.clear_calls_stack[::-1]:
223 with log_stage(stage):
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200224 stage(ctx)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300225
226 logger.debug("Start utils.cleanup")
227 for clean_func, args, kwargs in utils.iter_clean_func():
228 with log_stage(clean_func):
229 clean_func(*args, **kwargs)
230
231 if exc is None:
232 for report_stage in report_stages:
233 with log_stage(report_stage):
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200234 report_stage(ctx)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300235
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200236 logger.info("All info is stored into %r", config.storage_url)
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300237
238 if exc is None:
239 logger.info("Tests finished successfully")
240 return 0
241 else:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200242 logger.error("Tests are failed. See error details in log above")
koder aka kdanilov0fdaaee2015-06-30 11:10:48 +0300243 return 1