blob: 1d58eded20aae25fb499cb7a614777f17d06454b [file] [log] [blame]
gstepanov023c1e42015-04-08 15:50:19 +03001import os
2import pickle
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08003import sys
koder aka kdanilovda45e882015-04-06 02:24:42 +03004import json
koder aka kdanilov2c473092015-03-29 17:12:13 +03005import Queue
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08006import pprint
koder aka kdanilove21d7472015-02-14 19:02:04 -08007import logging
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -08008import argparse
koder aka kdanilov2c473092015-03-29 17:12:13 +03009import threading
10import collections
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080011
koder aka kdanilov2c473092015-03-29 17:12:13 +030012from concurrent.futures import ThreadPoolExecutor
koder aka kdanilov6c491062015-04-09 22:33:13 +030013
Yulia Portnova407ca952015-04-10 10:38:15 +030014import report
koder aka kdanilov6c491062015-04-09 22:33:13 +030015# import formatters
koder aka kdanilov2c473092015-03-29 17:12:13 +030016
17import utils
koder aka kdanilove06762a2015-03-22 23:32:09 +020018import ssh_utils
koder aka kdanilovda45e882015-04-06 02:24:42 +030019import start_vms
koder aka kdanilove06762a2015-03-22 23:32:09 +020020from nodes import discover
koder aka kdanilov2c473092015-03-29 17:12:13 +030021from nodes.node import Node
gstepanovcd256d62015-04-07 17:47:32 +030022from config import cfg_dict, parse_config
koder aka kdanilovda45e882015-04-06 02:24:42 +030023from tests.itest import IOPerfTest, PgBenchTest
koder aka kdanilov2c473092015-03-29 17:12:13 +030024from sensors.api import start_monitoring
25
26
koder aka kdanilove21d7472015-02-14 19:02:04 -080027logger = logging.getLogger("io-perf-tool")
koder aka kdanilove21d7472015-02-14 19:02:04 -080028
29
koder aka kdanilov2c473092015-03-29 17:12:13 +030030def setup_logger(logger, level=logging.DEBUG):
31 logger.setLevel(level)
32 ch = logging.StreamHandler()
33 ch.setLevel(level)
34 logger.addHandler(ch)
Yulia Portnova7ddfa732015-02-24 17:32:58 +020035
koder aka kdanilov2c473092015-03-29 17:12:13 +030036 log_format = '%(asctime)s - %(levelname)-6s - %(name)s - %(message)s'
37 formatter = logging.Formatter(log_format,
38 "%H:%M:%S")
39 ch.setFormatter(formatter)
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080040
koder aka kdanilov6c491062015-04-09 22:33:13 +030041 # logger.setLevel(logging.INFO)
42 # logger.addHandler(logging.FileHandler('log.txt'))
43
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -080044
Yulia Portnova7ddfa732015-02-24 17:32:58 +020045def format_result(res, formatter):
koder aka kdanilove21d7472015-02-14 19:02:04 -080046 data = "\n{0}\n".format("=" * 80)
47 data += pprint.pformat(res) + "\n"
48 data += "{0}\n".format("=" * 80)
koder aka kdanilovfe056622015-02-19 08:46:15 -080049 templ = "{0}\n\n====> {1}\n\n{2}\n\n"
Yulia Portnova7ddfa732015-02-24 17:32:58 +020050 return templ.format(data, formatter(res), "=" * 80)
koder aka kdanilove21d7472015-02-14 19:02:04 -080051
52
koder aka kdanilov5d589b42015-03-26 12:25:51 +020053def connect_one(node):
54 try:
koder aka kdanilov2c473092015-03-29 17:12:13 +030055 ssh_pref = "ssh://"
56 if node.conn_url.startswith(ssh_pref):
57 url = node.conn_url[len(ssh_pref):]
58 node.connection = ssh_utils.connect(url)
59 else:
60 raise ValueError("Unknown url type {0}".format(node.conn_url))
koder aka kdanilov3a6633e2015-03-26 18:20:00 +020061 except Exception:
koder aka kdanilov2c473092015-03-29 17:12:13 +030062 logger.exception("During connect to {0}".format(node))
koder aka kdanilov5d589b42015-03-26 12:25:51 +020063
64
65def connect_all(nodes):
koder aka kdanilov2c473092015-03-29 17:12:13 +030066 logger.info("Connecting to nodes")
67 with ThreadPoolExecutor(32) as pool:
68 list(pool.map(connect_one, nodes))
koder aka kdanilovda45e882015-04-06 02:24:42 +030069 logger.info("All nodes connected successfully")
koder aka kdanilov2c473092015-03-29 17:12:13 +030070
71
72def save_sensors_data(q):
73 logger.info("Start receiving sensors data")
koder aka kdanilovda45e882015-04-06 02:24:42 +030074 sensor_data = []
koder aka kdanilov2c473092015-03-29 17:12:13 +030075 while True:
76 val = q.get()
77 if val is None:
koder aka kdanilovda45e882015-04-06 02:24:42 +030078 print sensor_data
79 q.put(sensor_data)
koder aka kdanilov2c473092015-03-29 17:12:13 +030080 break
koder aka kdanilovda45e882015-04-06 02:24:42 +030081 sensor_data.append(val)
koder aka kdanilov2c473092015-03-29 17:12:13 +030082 logger.info("Sensors thread exits")
83
84
85def test_thread(test, node, barrier):
86 try:
87 logger.debug("Run preparation for {0}".format(node.conn_url))
88 test.pre_run(node.connection)
89 logger.debug("Run test for {0}".format(node.conn_url))
90 test.run(node.connection, barrier)
91 except:
92 logger.exception("In test {0} for node {1}".format(test, node))
93
94
95def run_tests(config, nodes):
96 tool_type_mapper = {
97 "io": IOPerfTest,
98 "pgbench": PgBenchTest,
99 }
100
101 test_nodes = [node for node in nodes
102 if 'testnode' in node.roles]
103
104 res_q = Queue.Queue()
105
koder aka kdanilovda45e882015-04-06 02:24:42 +0300106 for test in config['tests']:
gstepanov82489e72015-04-10 16:18:03 +0300107 for test in config['tests'][test]['tests']:
gstepanov023c1e42015-04-08 15:50:19 +0300108 for name, params in test.items():
109 logger.info("Starting {0} tests".format(name))
koder aka kdanilov2c473092015-03-29 17:12:13 +0300110
gstepanov023c1e42015-04-08 15:50:19 +0300111 threads = []
112 barrier = utils.Barrier(len(test_nodes))
113 for node in test_nodes:
114 msg = "Starting {0} test on {1} node"
115 logger.debug(msg.format(name, node.conn_url))
116 test = tool_type_mapper[name](params, res_q.put)
117 th = threading.Thread(None, test_thread, None,
118 (test, node, barrier))
119 threads.append(th)
120 th.daemon = True
121 th.start()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300122
gstepanov023c1e42015-04-08 15:50:19 +0300123 for th in threads:
124 th.join()
koder aka kdanilov2c473092015-03-29 17:12:13 +0300125
gstepanov023c1e42015-04-08 15:50:19 +0300126 results = []
127 while not res_q.empty():
128 results.append(res_q.get())
129 # logger.info("Get test result {0!r}".format(results[-1]))
130 yield name, results
koder aka kdanilov2c473092015-03-29 17:12:13 +0300131
132
133def parse_args(argv):
134 parser = argparse.ArgumentParser(
135 description="Run disk io performance test")
136
137 parser.add_argument("-l", dest='extra_logs',
138 action='store_true', default=False,
139 help="print some extra log info")
140
gstepanov4861d712015-04-09 13:28:02 +0300141 parser.add_argument("-b", '--build_description',
142 type=str, default="Build info")
gstepanovaffcdb12015-04-07 17:18:29 +0300143 parser.add_argument("-i", '--build_id', type=str, default="id")
144 parser.add_argument("-t", '--build_type', type=str, default="GA")
145 parser.add_argument("-u", '--username', type=str, default="admin")
koder aka kdanilovda45e882015-04-06 02:24:42 +0300146 parser.add_argument("-o", '--output-dest', nargs="*")
147 parser.add_argument("config_file", nargs="?", default="config.yaml")
koder aka kdanilov2c473092015-03-29 17:12:13 +0300148
149 return parser.parse_args(argv[1:])
150
151
koder aka kdanilovda45e882015-04-06 02:24:42 +0300152def log_nodes_statistic(_, ctx):
153 nodes = ctx.nodes
koder aka kdanilov2c473092015-03-29 17:12:13 +0300154 logger.info("Found {0} nodes total".format(len(nodes)))
155 per_role = collections.defaultdict(lambda: 0)
156 for node in nodes:
157 for role in node.roles:
158 per_role[role] += 1
159
160 for role, count in sorted(per_role.items()):
161 logger.debug("Found {0} nodes with role {1}".format(count, role))
162
163
164def log_sensors_config(cfg):
koder aka kdanilov5d589b42015-03-26 12:25:51 +0200165 pass
166
167
koder aka kdanilovda45e882015-04-06 02:24:42 +0300168def connect_stage(cfg, ctx):
169 ctx.clear_calls_stack.append(disconnect_stage)
170 connect_all(ctx.nodes)
171
172
173def discover_stage(cfg, ctx):
174 if 'discover' in cfg:
175 discover_objs = [i.strip() for i in cfg['discover'].strip().split(",")]
176 ctx.nodes.extend(discover.discover(discover_objs, cfg['clouds']))
177
178 for url, roles in cfg.get('explicit_nodes', {}).items():
179 ctx.nodes.append(Node(url, roles.split(",")))
180
181
182def deploy_sensors_stage(cfg_dict, ctx):
183 ctx.clear_calls_stack.append(remove_sensors_stage)
184 if 'sensors' not in cfg_dict:
185 return
186
187 cfg = cfg_dict.get('sensors')
188 sens_cfg = []
189
190 for role, sensors_str in cfg["roles_mapping"].items():
191 sensors = [sens.strip() for sens in sensors_str.split(",")]
192
193 collect_cfg = dict((sensor, {}) for sensor in sensors)
194
195 for node in ctx.nodes:
196 if role in node.roles:
197 sens_cfg.append((node.connection, collect_cfg))
198
199 log_sensors_config(sens_cfg)
200
201 ctx.sensor_cm = start_monitoring(cfg["receiver_uri"], None,
202 connected_config=sens_cfg)
203
204 ctx.sensors_control_queue = ctx.sensor_cm.__enter__()
205
206 th = threading.Thread(None, save_sensors_data, None,
207 (ctx.sensors_control_queue,))
208 th.daemon = True
209 th.start()
210 ctx.sensor_listen_thread = th
211
212
213def remove_sensors_stage(cfg, ctx):
214 ctx.sensors_control_queue.put(None)
215 ctx.sensor_listen_thread.join()
216 ctx.sensor_data = ctx.sensors_control_queue.get()
217
218
gstepanov023c1e42015-04-08 15:50:19 +0300219def run_all_test(cfg, ctx, store_nodes):
koder aka kdanilovda45e882015-04-06 02:24:42 +0300220 ctx.results = []
221
gstepanov023c1e42015-04-08 15:50:19 +0300222 if 'start_test_nodes' in cfg['tests']:
223 params = cfg['tests']['start_test_nodes']['openstack']
koder aka kdanilov6c491062015-04-09 22:33:13 +0300224
gstepanov023c1e42015-04-08 15:50:19 +0300225 for new_node in start_vms.launch_vms(params):
226 new_node.roles.append('testnode')
227 ctx.nodes.append(new_node)
228
koder aka kdanilovda45e882015-04-06 02:24:42 +0300229 if 'tests' in cfg:
gstepanov023c1e42015-04-08 15:50:19 +0300230 store_nodes(ctx.nodes)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300231 ctx.results.extend(run_tests(cfg_dict, ctx.nodes))
232
gstepanov023c1e42015-04-08 15:50:19 +0300233
234def shut_down_vms(cfg, ctx):
235 with open('vm_journal.log') as f:
236 data = str(f.read())
237 nodes = pickle.loads(data)
238
239 for node in nodes:
240 logger.info("Node " + str(node) + " has been loaded")
241
242 logger.info("Removing nodes")
243 start_vms.clear_nodes()
244 logger.info("Nodes has been removed")
245
246
247def store_nodes(nodes):
248 with open('vm_journal.log', 'w+') as f:
249 f.write(pickle.dumps([nodes]))
250 for node in nodes:
251 logger.info("Node " + str(node) + " has been stored")
252
253
254def clear_enviroment(cfg, ctx):
255 if os.path.exists('vm_journal.log'):
256 shut_down_vms(cfg, ctx)
257 os.remove('vm_journal.log')
258
259
260def run_tests_stage(cfg, ctx):
261 # clear nodes that possible were created on previous test running
262 clear_enviroment(cfg, ctx)
263 ctx.clear_calls_stack.append(shut_down_vms)
264 run_all_test(cfg, ctx, store_nodes)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300265
266
267def disconnect_stage(cfg, ctx):
268 for node in ctx.nodes:
269 if node.connection is not None:
270 node.connection.close()
271
272
273def report_stage(cfg, ctx):
274 output_dest = cfg.get('output_dest')
275 if output_dest is not None:
Yulia Portnova407ca952015-04-10 10:38:15 +0300276 if output_dest.endswith(".html"):
277 report.render_html_results(ctx, output_dest)
278 logger.info("Results were stored in %s" % output_dest)
279 else:
280 with open(output_dest, "w") as fd:
281 data = {"sensor_data": ctx.sensor_data,
282 "results": ctx.results}
283 fd.write(json.dumps(data))
koder aka kdanilovda45e882015-04-06 02:24:42 +0300284 else:
285 print "=" * 20 + " RESULTS " + "=" * 20
286 pprint.pprint(ctx.results)
287 print "=" * 60
288
289
290def complete_log_nodes_statistic(cfg, ctx):
291 nodes = ctx.nodes
292 for node in nodes:
293 logger.debug(str(node))
294
295
296class Context(object):
297 def __init__(self):
gstepanovaffcdb12015-04-07 17:18:29 +0300298 self.build_meta = {}
koder aka kdanilovda45e882015-04-06 02:24:42 +0300299 self.nodes = []
300 self.clear_calls_stack = []
301
302
gstepanovcd256d62015-04-07 17:47:32 +0300303def load_config(path):
304 global cfg_dict
305 cfg_dict = parse_config(path)
306
307
koder aka kdanilov3f356262015-02-13 08:06:14 -0800308def main(argv):
koder aka kdanilove06762a2015-03-22 23:32:09 +0200309 opts = parse_args(argv)
koder aka kdanilov2c473092015-03-29 17:12:13 +0300310
311 level = logging.DEBUG if opts.extra_logs else logging.WARNING
312 setup_logger(logger, level)
313
koder aka kdanilovda45e882015-04-06 02:24:42 +0300314 stages = [
315 discover_stage,
koder aka kdanilov6c491062015-04-09 22:33:13 +0300316 log_nodes_statistic,
koder aka kdanilovda45e882015-04-06 02:24:42 +0300317 complete_log_nodes_statistic,
koder aka kdanilov6c491062015-04-09 22:33:13 +0300318 connect_stage,
319 # complete_log_nodes_statistic,
320 deploy_sensors_stage,
koder aka kdanilovda45e882015-04-06 02:24:42 +0300321 run_tests_stage,
koder aka kdanilov6c491062015-04-09 22:33:13 +0300322 # report_stage
koder aka kdanilovda45e882015-04-06 02:24:42 +0300323 ]
koder aka kdanilov2c473092015-03-29 17:12:13 +0300324
gstepanovcd256d62015-04-07 17:47:32 +0300325 load_config(opts.config_file)
326
koder aka kdanilovda45e882015-04-06 02:24:42 +0300327 ctx = Context()
gstepanovaffcdb12015-04-07 17:18:29 +0300328 ctx.build_meta['build_id'] = opts.build_id
329 ctx.build_meta['build_descrption'] = opts.build_description
330 ctx.build_meta['build_type'] = opts.build_type
331 ctx.build_meta['username'] = opts.username
koder aka kdanilov6c491062015-04-09 22:33:13 +0300332
koder aka kdanilovda45e882015-04-06 02:24:42 +0300333 try:
334 for stage in stages:
335 logger.info("Start {0.__name__} stage".format(stage))
gstepanov023c1e42015-04-08 15:50:19 +0300336 print "Start {0.__name__} stage".format(stage)
koder aka kdanilovda45e882015-04-06 02:24:42 +0300337 stage(cfg_dict, ctx)
338 finally:
339 exc, cls, tb = sys.exc_info()
340 for stage in ctx.clear_calls_stack[::-1]:
341 try:
342 logger.info("Start {0.__name__} stage".format(stage))
343 stage(cfg_dict, ctx)
344 except:
345 pass
koder aka kdanilov2c473092015-03-29 17:12:13 +0300346
koder aka kdanilovda45e882015-04-06 02:24:42 +0300347 if exc is not None:
348 raise exc, cls, tb
koder aka kdanilov2c473092015-03-29 17:12:13 +0300349
koder aka kdanilove06762a2015-03-22 23:32:09 +0200350 return 0
koder aka kdanilov3f356262015-02-13 08:06:14 -0800351
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800352
koder aka kdanilov7acd6bd2015-02-12 14:28:30 -0800353if __name__ == '__main__':
koder aka kdanilove06762a2015-03-22 23:32:09 +0200354 exit(main(sys.argv))