blob: 77d44963cbc09187fb19acec1da3e7d6cb2d004d [file] [log] [blame]
Matthew Treinisha051c222016-05-23 15:48:22 -04001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13"""
14Runs tempest tests
15
16This command is used for running the tempest tests
17
18Test Selection
19==============
20Tempest run has several options:
21
Chandan Kumar8a4396e2017-09-15 12:18:10 +053022 * **--regex/-r**: This is a selection regex like what stestr uses. It will run
23 any tests that match on re.match() with the regex
24 * **--smoke/-s**: Run all the tests tagged as smoke
Matthew Treinish13869202018-02-22 13:46:02 -050025 * **--black-regex**: It allows to do simple test exclusion via passing a
26 rejection/black regexp
Matthew Treinisha051c222016-05-23 15:48:22 -040027
Masayuki Igawabbbaad62017-11-21 16:04:03 +090028There are also the ``--blacklist-file`` and ``--whitelist-file`` options that
Matthew Treinisha6b4da92016-05-23 17:24:12 -040029let you pass a filepath to tempest run with the file format being a line
zhangyanxian68d31b82016-07-13 01:48:33 +000030separated regex, with '#' used to signify the start of a comment on a line.
Matthew Treinisha6b4da92016-05-23 17:24:12 -040031For example::
32
33 # Regex file
34 ^regex1 # Match these tests
35 .*regex2 # Match those tests
36
Matthew Treinish3e97aae2018-02-22 13:39:59 -050037These arguments are just passed into stestr, you can refer to the stestr
38selection docs for more details on how these operate:
39http://stestr.readthedocs.io/en/latest/MANUAL.html#test-selection
Matthew Treinisha6b4da92016-05-23 17:24:12 -040040
Masayuki Igawabbbaad62017-11-21 16:04:03 +090041You can also use the ``--list-tests`` option in conjunction with selection
Matthew Treinisha051c222016-05-23 15:48:22 -040042arguments to list which tests will be run.
43
Masayuki Igawabbbaad62017-11-21 16:04:03 +090044You can also use the ``--load-list`` option that lets you pass a filepath to
ubuntu0dba54c2017-07-25 15:25:22 -050045tempest run with the file format being in a non-regex format, similar to the
Masayuki Igawabbbaad62017-11-21 16:04:03 +090046tests generated by the ``--list-tests`` option. You can specify target tests
ubuntu0dba54c2017-07-25 15:25:22 -050047by removing unnecessary tests from a list file which is generated from
Masayuki Igawabbbaad62017-11-21 16:04:03 +090048``--list-tests`` option.
ubuntu0dba54c2017-07-25 15:25:22 -050049
Matthew Treinisha051c222016-05-23 15:48:22 -040050Test Execution
51==============
52There are several options to control how the tests are executed. By default
53tempest will run in parallel with a worker for each CPU present on the machine.
Masayuki Igawabbbaad62017-11-21 16:04:03 +090054If you want to adjust the number of workers use the ``--concurrency`` option
55and if you want to run tests serially use ``--serial/-t``
Matthew Treinisha051c222016-05-23 15:48:22 -040056
Matthew Treinishc89a9512016-06-09 17:43:35 -040057Running with Workspaces
58-----------------------
59Tempest run enables you to run your tempest tests from any setup tempest
60workspace it relies on you having setup a tempest workspace with either the
61``tempest init`` or ``tempest workspace`` commands. Then using the
62``--workspace`` CLI option you can specify which one of your workspaces you
63want to run tempest from. Using this option you don't have to run Tempest
64directly with you current working directory being the workspace, Tempest will
65take care of managing everything to be executed from there.
66
Matthew Treinish30c9ee52016-06-09 17:58:47 -040067Running from Anywhere
68---------------------
69Tempest run provides you with an option to execute tempest from anywhere on
70your system. You are required to provide a config file in this case with the
Chandan Kumar8a4396e2017-09-15 12:18:10 +053071``--config-file`` option. When run tempest will create a .stestr
72directory and a .stestr.conf file in your current working directory. This way
73you can use stestr commands directly to inspect the state of the previous run.
Matthew Treinish30c9ee52016-06-09 17:58:47 -040074
Matthew Treinisha051c222016-05-23 15:48:22 -040075Test Output
76===========
77By default tempest run's output to STDOUT will be generated using the
78subunit-trace output filter. But, if you would prefer a subunit v2 stream be
Masayuki Igawabbbaad62017-11-21 16:04:03 +090079output to STDOUT use the ``--subunit`` flag
Matthew Treinisha051c222016-05-23 15:48:22 -040080
Matthew Treinish7d6e48c2017-03-03 12:44:50 -050081Combining Runs
82==============
83
84There are certain situations in which you want to split a single run of tempest
85across 2 executions of tempest run. (for example to run part of the tests
86serially and others in parallel) To accomplish this but still treat the results
Masayuki Igawabbbaad62017-11-21 16:04:03 +090087as a single run you can leverage the ``--combine`` option which will append
Matthew Treinish7d6e48c2017-03-03 12:44:50 -050088the current run's results with the previous runs.
Matthew Treinisha051c222016-05-23 15:48:22 -040089"""
90
Matthew Treinisha051c222016-05-23 15:48:22 -040091import os
92import sys
Matthew Treinisha051c222016-05-23 15:48:22 -040093
94from cliff import command
Prateek Aroraa028de12017-03-14 09:01:03 -040095from oslo_serialization import jsonutils as json
Davanum Srinivas00e3f452017-01-05 12:40:45 -050096import six
Chandan Kumar8a4396e2017-09-15 12:18:10 +053097from stestr import commands
Matthew Treinisha051c222016-05-23 15:48:22 -040098
ghanshyam009a1f62017-08-08 10:22:57 +030099from tempest import clients
Prateek Aroraa028de12017-03-14 09:01:03 -0400100from tempest.cmd import cleanup_service
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400101from tempest.cmd import init
Matthew Treinishc89a9512016-06-09 17:43:35 -0400102from tempest.cmd import workspace
Prateek Aroraa028de12017-03-14 09:01:03 -0400103from tempest.common import credentials_factory as credentials
Matthew Treinisha051c222016-05-23 15:48:22 -0400104from tempest import config
105
Manik Bindlish21491df2018-12-14 06:58:42 +0000106if six.PY2:
107 # Python 2 has not FileNotFoundError exception
108 FileNotFoundError = IOError
Matthew Treinisha051c222016-05-23 15:48:22 -0400109
Matthew Treinisha051c222016-05-23 15:48:22 -0400110CONF = config.CONF
Prateek Aroraa028de12017-03-14 09:01:03 -0400111SAVED_STATE_JSON = "saved_state.json"
Matthew Treinisha051c222016-05-23 15:48:22 -0400112
113
114class TempestRun(command.Command):
115
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400116 def _set_env(self, config_file=None):
117 if config_file:
Manik Bindlish21491df2018-12-14 06:58:42 +0000118 if os.path.exists(os.path.abspath(config_file)):
119 CONF.set_config_path(os.path.abspath(config_file))
120 else:
121 raise FileNotFoundError(
122 "Config file: %s doesn't exist" % config_file)
123
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530124 # NOTE(mtreinish): This is needed so that stestr doesn't gobble up any
Matthew Treinisha051c222016-05-23 15:48:22 -0400125 # stacktraces on failure.
126 if 'TESTR_PDB' in os.environ:
127 return
128 else:
129 os.environ["TESTR_PDB"] = ""
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530130 # NOTE(dims): most of our .stestr.conf try to test for PYTHON
Davanum Srinivas00e3f452017-01-05 12:40:45 -0500131 # environment variable and fall back to "python", under python3
132 # if it does not exist. we should set it to the python3 executable
133 # to deal with this situation better for now.
134 if six.PY3 and 'PYTHON' not in os.environ:
135 os.environ['PYTHON'] = sys.executable
Matthew Treinisha051c222016-05-23 15:48:22 -0400136
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530137 def _create_stestr_conf(self):
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400138 top_level_path = os.path.dirname(os.path.dirname(__file__))
139 discover_path = os.path.join(top_level_path, 'test_discover')
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530140 file_contents = init.STESTR_CONF % (discover_path, top_level_path)
141 with open('.stestr.conf', 'w+') as stestr_conf_file:
142 stestr_conf_file.write(file_contents)
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400143
Matthew Treinisha051c222016-05-23 15:48:22 -0400144 def take_action(self, parsed_args):
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400145 if parsed_args.config_file:
146 self._set_env(parsed_args.config_file)
147 else:
148 self._set_env()
Matthew Treinishc89a9512016-06-09 17:43:35 -0400149 # Workspace execution mode
150 if parsed_args.workspace:
151 workspace_mgr = workspace.WorkspaceManager(
152 parsed_args.workspace_path)
153 path = workspace_mgr.get_workspace(parsed_args.workspace)
Brant Knudson6a090f42016-10-13 12:51:49 -0500154 if not path:
155 sys.exit(
156 "The %r workspace isn't registered in "
157 "%r. Use 'tempest init' to "
158 "register the workspace." %
159 (parsed_args.workspace, workspace_mgr.path))
Matthew Treinishc89a9512016-06-09 17:43:35 -0400160 os.chdir(path)
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530161 if not os.path.isfile('.stestr.conf'):
162 self._create_stestr_conf()
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400163 # local execution with config file mode
Masayuki Igawaff07eac2018-02-22 16:53:09 +0900164 elif parsed_args.config_file and not os.path.isfile('.stestr.conf'):
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530165 self._create_stestr_conf()
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530166 elif not os.path.isfile('.stestr.conf'):
167 print("No .stestr.conf file was found for local execution")
Matthew Treinisha051c222016-05-23 15:48:22 -0400168 sys.exit(2)
Prateek Aroraa028de12017-03-14 09:01:03 -0400169 if parsed_args.state:
170 self._init_state()
Prateek Aroraa028de12017-03-14 09:01:03 -0400171
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500172 regex = self._build_regex(parsed_args)
Masayuki Igawaff07eac2018-02-22 16:53:09 +0900173 return_code = 0
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500174 if parsed_args.list_tests:
175 return_code = commands.list_command(
176 filters=regex, whitelist_file=parsed_args.whitelist_file,
Matthew Treinish13869202018-02-22 13:46:02 -0500177 blacklist_file=parsed_args.blacklist_file,
178 black_regex=parsed_args.black_regex)
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500179
ghanshyam29981172018-02-23 05:25:28 +0000180 else:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530181 serial = not parsed_args.parallel
182 return_code = commands.run_command(
183 filters=regex, subunit_out=parsed_args.subunit,
184 serial=serial, concurrency=parsed_args.concurrency,
185 blacklist_file=parsed_args.blacklist_file,
186 whitelist_file=parsed_args.whitelist_file,
Matthew Treinish13869202018-02-22 13:46:02 -0500187 black_regex=parsed_args.black_regex,
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530188 load_list=parsed_args.load_list, combine=parsed_args.combine)
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500189 if return_code > 0:
190 sys.exit(return_code)
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530191 return return_code
Matthew Treinisha051c222016-05-23 15:48:22 -0400192
193 def get_description(self):
194 return 'Run tempest'
195
Prateek Aroraa028de12017-03-14 09:01:03 -0400196 def _init_state(self):
197 print("Initializing saved state.")
198 data = {}
199 self.global_services = cleanup_service.get_global_cleanup_services()
ghanshyam009a1f62017-08-08 10:22:57 +0300200 self.admin_mgr = clients.Manager(
201 credentials.get_configured_admin_credentials())
Prateek Aroraa028de12017-03-14 09:01:03 -0400202 admin_mgr = self.admin_mgr
203 kwargs = {'data': data,
204 'is_dry_run': False,
205 'saved_state_json': data,
206 'is_preserve': False,
207 'is_save_state': True}
208 for service in self.global_services:
209 svc = service(admin_mgr, **kwargs)
210 svc.run()
211
212 with open(SAVED_STATE_JSON, 'w+') as f:
afazekas40fcb9b2019-03-08 11:25:11 +0100213 f.write(json.dumps(data, sort_keys=True,
214 indent=2, separators=(',', ': ')))
Prateek Aroraa028de12017-03-14 09:01:03 -0400215
Matthew Treinisha051c222016-05-23 15:48:22 -0400216 def get_parser(self, prog_name):
217 parser = super(TempestRun, self).get_parser(prog_name)
218 parser = self._add_args(parser)
219 return parser
220
221 def _add_args(self, parser):
Matthew Treinishc89a9512016-06-09 17:43:35 -0400222 # workspace args
223 parser.add_argument('--workspace', default=None,
224 help='Name of tempest workspace to use for running'
225 ' tests. You can see a list of workspaces '
226 'with tempest workspace list')
227 parser.add_argument('--workspace-path', default=None,
228 dest='workspace_path',
229 help="The path to the workspace file, the default "
230 "is ~/.tempest/workspace.yaml")
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400231 # Configuration flags
232 parser.add_argument('--config-file', default=None, dest='config_file',
233 help='Configuration file to run tempest with')
Matthew Treinisha051c222016-05-23 15:48:22 -0400234 # test selection args
235 regex = parser.add_mutually_exclusive_group()
Nicolas Bockff27d3b2017-01-11 13:30:32 -0700236 regex.add_argument('--smoke', '-s', action='store_true',
Matthew Treinisha051c222016-05-23 15:48:22 -0400237 help="Run the smoke tests only")
238 regex.add_argument('--regex', '-r', default='',
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530239 help='A normal stestr selection regex used to '
Matthew Treinisha051c222016-05-23 15:48:22 -0400240 'specify a subset of tests to run')
Matthew Treinish13869202018-02-22 13:46:02 -0500241 parser.add_argument('--black-regex', dest='black_regex',
242 help='A regex to exclude tests that match it')
Matthew Treinish3e97aae2018-02-22 13:39:59 -0500243 parser.add_argument('--whitelist-file', '--whitelist_file',
244 help="Path to a whitelist file, this file "
245 "contains a separate regex on each "
246 "newline.")
247 parser.add_argument('--blacklist-file', '--blacklist_file',
248 help='Path to a blacklist file, this file '
249 'contains a separate regex exclude on '
250 'each newline')
251 parser.add_argument('--load-list', '--load_list',
252 help='Path to a non-regex whitelist file, '
baiwenteng781fe072018-06-11 17:39:20 +0800253 'this file contains a separate test '
zhuflde676372018-11-16 15:34:56 +0800254 'on each newline. This command '
255 'supports files created by the tempest '
Matthew Treinish3e97aae2018-02-22 13:39:59 -0500256 'run ``--list-tests`` command')
Matthew Treinisha051c222016-05-23 15:48:22 -0400257 # list only args
258 parser.add_argument('--list-tests', '-l', action='store_true',
259 help='List tests',
260 default=False)
Puneet Arora9ed41042016-07-05 19:46:06 +0000261 # execution args
Matthew Treinisha051c222016-05-23 15:48:22 -0400262 parser.add_argument('--concurrency', '-w',
Masayuki Igawa13c58832019-03-06 22:01:37 +0900263 type=int, default=0,
Matthew Treinisha051c222016-05-23 15:48:22 -0400264 help="The number of workers to use, defaults to "
265 "the number of cpus")
266 parallel = parser.add_mutually_exclusive_group()
267 parallel.add_argument('--parallel', dest='parallel',
268 action='store_true',
269 help='Run tests in parallel (this is the'
270 ' default)')
Nicolas Bockff27d3b2017-01-11 13:30:32 -0700271 parallel.add_argument('--serial', '-t', dest='parallel',
Matthew Treinisha051c222016-05-23 15:48:22 -0400272 action='store_false',
273 help='Run tests serially')
Prateek Aroraa028de12017-03-14 09:01:03 -0400274 parser.add_argument('--save-state', dest='state',
275 action='store_true',
276 help="To save the state of the cloud before "
277 "running tempest.")
Matthew Treinisha051c222016-05-23 15:48:22 -0400278 # output args
279 parser.add_argument("--subunit", action='store_true',
280 help='Enable subunit v2 output')
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500281 parser.add_argument("--combine", action='store_true',
282 help='Combine the output of this run with the '
283 "previous run's as a combined stream in the "
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530284 "stestr repository after it finish")
Matthew Treinisha051c222016-05-23 15:48:22 -0400285
286 parser.set_defaults(parallel=True)
287 return parser
288
289 def _build_regex(self, parsed_args):
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530290 regex = None
Matthew Treinisha051c222016-05-23 15:48:22 -0400291 if parsed_args.smoke:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530292 regex = ['smoke']
Matthew Treinisha051c222016-05-23 15:48:22 -0400293 elif parsed_args.regex:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530294 regex = parsed_args.regex.split()
Matthew Treinisha051c222016-05-23 15:48:22 -0400295 return regex