5669fde10ada30a783a809c1467fd8e8406010f5
[yardstick.git] / yardstick / main.py
1 #!/usr/bin/env python
2
3 ##############################################################################
4 # Copyright (c) 2015 Ericsson AB and others.
5 #
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 ##############################################################################
11
12 """ yardstick - command line tool for managing benchmarks
13
14     Example invocation:
15     $ yardstick samples/ping-task.yaml
16 """
17
18 import sys
19 import yaml
20 import atexit
21 import pkg_resources
22
23 from yardstick.benchmark.context.model import Context
24 from yardstick.benchmark.runners import base as base_runner
25 from yardstick.cmdparser import CmdParser
26
27
28 class TaskParser(object):
29     '''Parser for task config files in yaml format'''
30     def __init__(self, path):
31         self.path = path
32
33     def parse(self):
34         '''parses the task file and return an context and scenario instances'''
35         print "Parsing task config:", self.path
36         try:
37             with open(self.path) as stream:
38                 cfg = yaml.load(stream)
39         except IOError as ioerror:
40             sys.exit(ioerror)
41
42         if cfg["schema"] != "yardstick:task:0.1":
43             sys.exit("error: file %s has unknown schema %s" % (self.path,
44                                                                cfg["schema"]))
45
46         # TODO: support one or many contexts? Many would simpler and precise
47         if "context" in cfg:
48             context_cfgs = [cfg["context"]]
49         else:
50             context_cfgs = cfg["contexts"]
51
52         for cfg_attrs in context_cfgs:
53             context = Context()
54             context.init(cfg_attrs)
55
56         run_in_parallel = cfg.get("run_in_parallel", False)
57
58         # TODO we need something better here, a class that represent the file
59         return cfg["scenarios"], run_in_parallel
60
61
62 def atexit_handler():
63     '''handler for process termination'''
64     base_runner.Runner.terminate_all()
65
66     if len(Context.list) > 0:
67         print "Undeploying all contexts"
68         for context in Context.list:
69             context.undeploy()
70
71
72 def run_one_scenario(scenario_cfg, output_file):
73     '''run one scenario using context'''
74     key_filename = pkg_resources.resource_filename(
75         'yardstick.resources', 'files/yardstick_key')
76
77     host = Context.get_server(scenario_cfg["host"])
78
79     runner_cfg = scenario_cfg["runner"]
80     runner_cfg['host'] = host.floating_ip["ipaddr"]
81     runner_cfg['user'] = host.context.user
82     runner_cfg['key_filename'] = key_filename
83     runner_cfg['output_filename'] = output_file
84
85     # TODO target should be optional to support single VM scenarios
86     target = Context.get_server(scenario_cfg["target"])
87     if target.floating_ip:
88         runner_cfg['target'] = target.floating_ip["ipaddr"]
89
90     # TODO scenario_cfg["ipaddr"] is bad, "dest_ip" is better
91     if host.context != target.context:
92         # target is in another context, get its public IP
93         scenario_cfg["ipaddr"] = target.floating_ip["ipaddr"]
94     else:
95         # TODO hardcoded name below, a server can be attached to several nets
96         scenario_cfg["ipaddr"] = target.ports["test"]["ipaddr"]
97
98     runner = base_runner.Runner.get(runner_cfg)
99
100     print "Starting runner of type '%s'" % runner_cfg["type"]
101     runner.run(scenario_cfg["type"], scenario_cfg)
102
103     return runner
104
105
106 def runner_join(runner):
107     '''join (wait for) a runner, exit process at runner failure'''
108     status = runner.join()
109     base_runner.Runner.release(runner)
110     if status != 0:
111         sys.exit("Runner failed")
112
113
114 def main():
115     '''yardstick main'''
116
117     atexit.register(atexit_handler)
118
119     prog_args = CmdParser().parse_args()
120
121     parser = TaskParser(prog_args.taskfile[0])
122     scenarios, run_in_parallel = parser.parse()
123
124     if prog_args.parse_only:
125         sys.exit(0)
126
127     for context in Context.list:
128         context.deploy()
129
130     runners = []
131     if run_in_parallel:
132         for scenario in scenarios:
133             runner = run_one_scenario(scenario, prog_args.output_file)
134             runners.append(runner)
135
136         # Wait for runners to finish
137         for runner in runners:
138             runner_join(runner)
139             print "Runner ended, output in", prog_args.output_file
140     else:
141         # run serially
142         for scenario in scenarios:
143             runner = run_one_scenario(scenario, prog_args.output_file)
144             runner_join(runner)
145             print "Runner ended, output in", prog_args.output_file
146
147     if prog_args.keep_deploy:
148         # keep deployment, forget about stack (hide it for exit handler)
149         Context.list = []
150     else:
151         for context in Context.list:
152             context.undeploy()
153
154     print "Done, exiting"
155
156 if __name__ == '__main__':
157     main()