Merge "Bugfix:can not run a test suite if not under yardstick root path"
[yardstick.git] / yardstick / cmd / cli.py
1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 """
11 Command-line interface to yardstick
12 """
13
14 from __future__ import absolute_import
15 import logging
16 import os
17 import sys
18
19 from pkg_resources import get_distribution
20 from argparse import RawDescriptionHelpFormatter
21 from oslo_config import cfg
22
23 from yardstick import _init_logging, LOG
24 from yardstick.cmd.commands import task
25 from yardstick.cmd.commands import runner
26 from yardstick.cmd.commands import scenario
27 from yardstick.cmd.commands import testcase
28 from yardstick.cmd.commands import plugin
29 from yardstick.cmd.commands import env
30
31 CONF = cfg.CONF
32 cli_opts = [
33     cfg.BoolOpt('debug',
34                 short='d',
35                 default=False,
36                 help='increase output verbosity to debug'),
37     cfg.BoolOpt('verbose',
38                 short='v',
39                 default=False,
40                 help='increase output verbosity to info')
41 ]
42 CONF.register_cli_opts(cli_opts)
43
44 CONFIG_SEARCH_PATHS = [sys.prefix + "/etc/yardstick",
45                        "~/.yardstick",
46                        "/etc/yardstick"]
47
48
49 def find_config_files(path_list):
50     for path in path_list:
51         abspath = os.path.abspath(os.path.expanduser(path))
52         confname = abspath + "/yardstick.conf"
53         if os.path.isfile(confname):
54             return [confname]
55
56     return None
57
58
59 class YardstickCLI():
60     """Command-line interface to yardstick"""
61
62     # Command categories
63     categories = {
64         'task': task.TaskCommands,
65         'runner': runner.RunnerCommands,
66         'scenario': scenario.ScenarioCommands,
67         'testcase': testcase.TestcaseCommands,
68         'plugin': plugin.PluginCommands,
69         'env': env.EnvCommand
70     }
71
72     def __init__(self):
73         self.opts = []
74         self._version = 'yardstick version %s ' % \
75             get_distribution('yardstick').version
76
77     def _find_actions(self, subparsers, actions_module):
78         """find action methods"""
79         # Find action methods inside actions_module and
80         # add them to the command parser.
81         # The 'actions_module' argument may be a class
82         # or module. Action methods start with 'do_'
83         for attr in (a for a in dir(actions_module) if a.startswith('do_')):
84             command = attr[3:].replace('_', '-')
85             callback = getattr(actions_module, attr)
86             desc = callback.__doc__ or ''
87             arguments = getattr(callback, 'arguments', [])
88             subparser = subparsers.add_parser(
89                 command,
90                 description=desc
91             )
92             for (args, kwargs) in arguments:
93                 subparser.add_argument(*args, **kwargs)
94             subparser.set_defaults(func=callback)
95
96     def _add_command_parsers(self, categories, subparsers):
97         """add commands to command-line parser"""
98         for category in categories:
99             command_object = categories[category]()
100             desc = command_object.__doc__ or ''
101             subparser = subparsers.add_parser(
102                 category, description=desc,
103                 formatter_class=RawDescriptionHelpFormatter
104             )
105             subparser.set_defaults(command_object=command_object)
106             cmd_subparsers = subparser.add_subparsers(title='subcommands')
107             self._find_actions(cmd_subparsers, command_object)
108
109     def _register_cli_opt(self):
110
111         # register subcommands to parse additional command line arguments
112         def parser(subparsers):
113             self._add_command_parsers(YardstickCLI.categories, subparsers)
114
115         category_opt = cfg.SubCommandOpt("category",
116                                          title="Command categories",
117                                          help="Available categories",
118                                          handler=parser)
119         self._register_opt(category_opt)
120
121     def _register_opt(self, opt):
122
123         CONF.register_cli_opt(opt)
124         self.opts.append(opt)
125
126     def _load_cli_config(self, argv):
127
128         # load CLI args and config files
129         CONF(argv, project="yardstick", version=self._version,
130              default_config_files=find_config_files(CONFIG_SEARCH_PATHS))
131
132     def _handle_global_opts(self):
133
134         _init_logging()
135         if CONF.verbose:
136             LOG.setLevel(logging.INFO)
137
138         if CONF.debug:
139             LOG.setLevel(logging.DEBUG)
140
141     def _dispath_func_notask(self):
142
143         # dispatch to category parser
144         func = CONF.category.func
145         func(CONF.category)
146
147     def _dispath_func_task(self, task_id):
148
149         # dispatch to category parser
150         func = CONF.category.func
151         func(CONF.category, task_id=task_id)
152
153     def _clear_config_opts(self):
154
155         CONF.clear()
156         CONF.unregister_opts(self.opts)
157
158     def main(self, argv):    # pragma: no cover
159         """run the command line interface"""
160         try:
161             self._register_cli_opt()
162
163             self._load_cli_config(argv)
164
165             self._handle_global_opts()
166
167             self._dispath_func_notask()
168         finally:
169             self._clear_config_opts()
170
171     def api(self, argv, task_id):    # pragma: no cover
172         """run the api interface"""
173         try:
174             self._register_cli_opt()
175
176             self._load_cli_config(argv)
177
178             self._handle_global_opts()
179
180             self._dispath_func_task(task_id)
181         finally:
182             self._clear_config_opts()