blob: 8bebce2c9d21dbddb7fccec4107d650df6d0418b [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
Masayuki Igawa9c25b1d2019-07-05 16:32:49 +090022* ``--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
25* ``--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
Arx Cruzc06c3712020-02-20 11:03:52 +010050You can also use ``--worker-file`` option that let you pass a filepath to a
51worker yaml file, allowing you to manually schedule the tests run.
52For example, you can setup a tempest run with
53different concurrences to be used with different regexps.
54An example of worker file is showed below::
55
56 # YAML Worker file
57 - worker:
58 # you can have more than one regex per worker
59 - tempest.api.*
60 - neutron_tempest_tests
61 - worker:
62 - tempest.scenario.*
63
64This will run test matching with 'tempest.api.*' and 'neutron_tempest_tests'
65against worker 1. Run tests matching with 'tempest.scenario.*' under worker 2.
66
67You can mix manual scheduling with the standard scheduling mechanisms by
68concurrency field on a worker. For example::
69
70 # YAML Worker file
71 - worker:
72 # you can have more than one regex per worker
73 - tempest.api.*
74 - neutron_tempest_tests
75 concurrency: 3
76 - worker:
77 - tempest.scenario.*
78 concurrency: 2
79
80This will run tests matching with 'tempest.scenario.*' against 2 workers.
81
82This worker file is passed into stestr. For some more details on how it
83operates please refer to the stestr scheduling docs:
84https://stestr.readthedocs.io/en/stable/MANUAL.html#test-scheduling
85
Matthew Treinisha051c222016-05-23 15:48:22 -040086Test Execution
87==============
88There are several options to control how the tests are executed. By default
89tempest will run in parallel with a worker for each CPU present on the machine.
Masayuki Igawabbbaad62017-11-21 16:04:03 +090090If you want to adjust the number of workers use the ``--concurrency`` option
91and if you want to run tests serially use ``--serial/-t``
Matthew Treinisha051c222016-05-23 15:48:22 -040092
Matthew Treinishc89a9512016-06-09 17:43:35 -040093Running with Workspaces
94-----------------------
95Tempest run enables you to run your tempest tests from any setup tempest
96workspace it relies on you having setup a tempest workspace with either the
97``tempest init`` or ``tempest workspace`` commands. Then using the
98``--workspace`` CLI option you can specify which one of your workspaces you
99want to run tempest from. Using this option you don't have to run Tempest
100directly with you current working directory being the workspace, Tempest will
101take care of managing everything to be executed from there.
102
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400103Running from Anywhere
104---------------------
105Tempest run provides you with an option to execute tempest from anywhere on
106your system. You are required to provide a config file in this case with the
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530107``--config-file`` option. When run tempest will create a .stestr
108directory and a .stestr.conf file in your current working directory. This way
109you can use stestr commands directly to inspect the state of the previous run.
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400110
Matthew Treinisha051c222016-05-23 15:48:22 -0400111Test Output
112===========
113By default tempest run's output to STDOUT will be generated using the
114subunit-trace output filter. But, if you would prefer a subunit v2 stream be
Masayuki Igawabbbaad62017-11-21 16:04:03 +0900115output to STDOUT use the ``--subunit`` flag
Matthew Treinisha051c222016-05-23 15:48:22 -0400116
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500117Combining Runs
118==============
119
120There are certain situations in which you want to split a single run of tempest
121across 2 executions of tempest run. (for example to run part of the tests
122serially and others in parallel) To accomplish this but still treat the results
Masayuki Igawabbbaad62017-11-21 16:04:03 +0900123as a single run you can leverage the ``--combine`` option which will append
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500124the current run's results with the previous runs.
Matthew Treinisha051c222016-05-23 15:48:22 -0400125"""
126
Matthew Treinisha051c222016-05-23 15:48:22 -0400127import os
128import sys
Matthew Treinisha051c222016-05-23 15:48:22 -0400129
130from cliff import command
Prateek Aroraa028de12017-03-14 09:01:03 -0400131from oslo_serialization import jsonutils as json
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530132from stestr import commands
Matthew Treinisha051c222016-05-23 15:48:22 -0400133
ghanshyam009a1f62017-08-08 10:22:57 +0300134from tempest import clients
Prateek Aroraa028de12017-03-14 09:01:03 -0400135from tempest.cmd import cleanup_service
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400136from tempest.cmd import init
Matthew Treinishc89a9512016-06-09 17:43:35 -0400137from tempest.cmd import workspace
Prateek Aroraa028de12017-03-14 09:01:03 -0400138from tempest.common import credentials_factory as credentials
Matthew Treinisha051c222016-05-23 15:48:22 -0400139from tempest import config
140
Matthew Treinisha051c222016-05-23 15:48:22 -0400141CONF = config.CONF
Prateek Aroraa028de12017-03-14 09:01:03 -0400142SAVED_STATE_JSON = "saved_state.json"
Matthew Treinisha051c222016-05-23 15:48:22 -0400143
144
145class TempestRun(command.Command):
146
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400147 def _set_env(self, config_file=None):
148 if config_file:
Manik Bindlish21491df2018-12-14 06:58:42 +0000149 if os.path.exists(os.path.abspath(config_file)):
150 CONF.set_config_path(os.path.abspath(config_file))
151 else:
152 raise FileNotFoundError(
153 "Config file: %s doesn't exist" % config_file)
154
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530155 # NOTE(mtreinish): This is needed so that stestr doesn't gobble up any
Matthew Treinisha051c222016-05-23 15:48:22 -0400156 # stacktraces on failure.
157 if 'TESTR_PDB' in os.environ:
158 return
159 else:
160 os.environ["TESTR_PDB"] = ""
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530161 # NOTE(dims): most of our .stestr.conf try to test for PYTHON
Davanum Srinivas00e3f452017-01-05 12:40:45 -0500162 # environment variable and fall back to "python", under python3
163 # if it does not exist. we should set it to the python3 executable
164 # to deal with this situation better for now.
likui7d91c872020-09-22 12:29:16 +0800165 if 'PYTHON' not in os.environ:
Davanum Srinivas00e3f452017-01-05 12:40:45 -0500166 os.environ['PYTHON'] = sys.executable
Matthew Treinisha051c222016-05-23 15:48:22 -0400167
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530168 def _create_stestr_conf(self):
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400169 top_level_path = os.path.dirname(os.path.dirname(__file__))
170 discover_path = os.path.join(top_level_path, 'test_discover')
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530171 file_contents = init.STESTR_CONF % (discover_path, top_level_path)
172 with open('.stestr.conf', 'w+') as stestr_conf_file:
173 stestr_conf_file.write(file_contents)
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400174
Matthew Treinisha051c222016-05-23 15:48:22 -0400175 def take_action(self, parsed_args):
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400176 if parsed_args.config_file:
177 self._set_env(parsed_args.config_file)
178 else:
179 self._set_env()
Matthew Treinishc89a9512016-06-09 17:43:35 -0400180 # Workspace execution mode
181 if parsed_args.workspace:
182 workspace_mgr = workspace.WorkspaceManager(
183 parsed_args.workspace_path)
184 path = workspace_mgr.get_workspace(parsed_args.workspace)
Brant Knudson6a090f42016-10-13 12:51:49 -0500185 if not path:
186 sys.exit(
187 "The %r workspace isn't registered in "
188 "%r. Use 'tempest init' to "
189 "register the workspace." %
190 (parsed_args.workspace, workspace_mgr.path))
Matthew Treinishc89a9512016-06-09 17:43:35 -0400191 os.chdir(path)
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530192 if not os.path.isfile('.stestr.conf'):
193 self._create_stestr_conf()
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400194 # local execution with config file mode
Masayuki Igawaff07eac2018-02-22 16:53:09 +0900195 elif parsed_args.config_file and not os.path.isfile('.stestr.conf'):
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530196 self._create_stestr_conf()
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530197 elif not os.path.isfile('.stestr.conf'):
198 print("No .stestr.conf file was found for local execution")
Matthew Treinisha051c222016-05-23 15:48:22 -0400199 sys.exit(2)
Prateek Aroraa028de12017-03-14 09:01:03 -0400200 if parsed_args.state:
201 self._init_state()
Prateek Aroraa028de12017-03-14 09:01:03 -0400202
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500203 regex = self._build_regex(parsed_args)
Masayuki Igawaff07eac2018-02-22 16:53:09 +0900204 return_code = 0
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500205 if parsed_args.list_tests:
206 return_code = commands.list_command(
207 filters=regex, whitelist_file=parsed_args.whitelist_file,
Matthew Treinish13869202018-02-22 13:46:02 -0500208 blacklist_file=parsed_args.blacklist_file,
209 black_regex=parsed_args.black_regex)
Matthew Treinishf9902ec2018-02-22 12:11:46 -0500210
ghanshyam29981172018-02-23 05:25:28 +0000211 else:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530212 serial = not parsed_args.parallel
213 return_code = commands.run_command(
214 filters=regex, subunit_out=parsed_args.subunit,
215 serial=serial, concurrency=parsed_args.concurrency,
216 blacklist_file=parsed_args.blacklist_file,
217 whitelist_file=parsed_args.whitelist_file,
Matthew Treinish13869202018-02-22 13:46:02 -0500218 black_regex=parsed_args.black_regex,
Arx Cruzc06c3712020-02-20 11:03:52 +0100219 worker_path=parsed_args.worker_file,
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530220 load_list=parsed_args.load_list, combine=parsed_args.combine)
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500221 if return_code > 0:
222 sys.exit(return_code)
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530223 return return_code
Matthew Treinisha051c222016-05-23 15:48:22 -0400224
225 def get_description(self):
226 return 'Run tempest'
227
Prateek Aroraa028de12017-03-14 09:01:03 -0400228 def _init_state(self):
229 print("Initializing saved state.")
230 data = {}
231 self.global_services = cleanup_service.get_global_cleanup_services()
ghanshyam009a1f62017-08-08 10:22:57 +0300232 self.admin_mgr = clients.Manager(
233 credentials.get_configured_admin_credentials())
Prateek Aroraa028de12017-03-14 09:01:03 -0400234 admin_mgr = self.admin_mgr
235 kwargs = {'data': data,
236 'is_dry_run': False,
237 'saved_state_json': data,
238 'is_preserve': False,
239 'is_save_state': True}
240 for service in self.global_services:
241 svc = service(admin_mgr, **kwargs)
242 svc.run()
243
244 with open(SAVED_STATE_JSON, 'w+') as f:
afazekas40fcb9b2019-03-08 11:25:11 +0100245 f.write(json.dumps(data, sort_keys=True,
246 indent=2, separators=(',', ': ')))
Prateek Aroraa028de12017-03-14 09:01:03 -0400247
Matthew Treinisha051c222016-05-23 15:48:22 -0400248 def get_parser(self, prog_name):
249 parser = super(TempestRun, self).get_parser(prog_name)
250 parser = self._add_args(parser)
251 return parser
252
253 def _add_args(self, parser):
Matthew Treinishc89a9512016-06-09 17:43:35 -0400254 # workspace args
255 parser.add_argument('--workspace', default=None,
256 help='Name of tempest workspace to use for running'
257 ' tests. You can see a list of workspaces '
258 'with tempest workspace list')
259 parser.add_argument('--workspace-path', default=None,
260 dest='workspace_path',
261 help="The path to the workspace file, the default "
262 "is ~/.tempest/workspace.yaml")
Matthew Treinish30c9ee52016-06-09 17:58:47 -0400263 # Configuration flags
264 parser.add_argument('--config-file', default=None, dest='config_file',
265 help='Configuration file to run tempest with')
Matthew Treinisha051c222016-05-23 15:48:22 -0400266 # test selection args
267 regex = parser.add_mutually_exclusive_group()
Nicolas Bockff27d3b2017-01-11 13:30:32 -0700268 regex.add_argument('--smoke', '-s', action='store_true',
Matthew Treinisha051c222016-05-23 15:48:22 -0400269 help="Run the smoke tests only")
270 regex.add_argument('--regex', '-r', default='',
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530271 help='A normal stestr selection regex used to '
Matthew Treinisha051c222016-05-23 15:48:22 -0400272 'specify a subset of tests to run')
Matthew Treinish13869202018-02-22 13:46:02 -0500273 parser.add_argument('--black-regex', dest='black_regex',
274 help='A regex to exclude tests that match it')
Matthew Treinish3e97aae2018-02-22 13:39:59 -0500275 parser.add_argument('--whitelist-file', '--whitelist_file',
276 help="Path to a whitelist file, this file "
277 "contains a separate regex on each "
278 "newline.")
279 parser.add_argument('--blacklist-file', '--blacklist_file',
280 help='Path to a blacklist file, this file '
281 'contains a separate regex exclude on '
282 'each newline')
283 parser.add_argument('--load-list', '--load_list',
284 help='Path to a non-regex whitelist file, '
baiwenteng781fe072018-06-11 17:39:20 +0800285 'this file contains a separate test '
zhuflde676372018-11-16 15:34:56 +0800286 'on each newline. This command '
287 'supports files created by the tempest '
Matthew Treinish3e97aae2018-02-22 13:39:59 -0500288 'run ``--list-tests`` command')
Arx Cruzc06c3712020-02-20 11:03:52 +0100289 parser.add_argument('--worker-file', '--worker_file',
290 help='Optional path to a worker file. This file '
291 'contains each worker configuration to be '
292 'used to schedule the tests run')
Matthew Treinisha051c222016-05-23 15:48:22 -0400293 # list only args
294 parser.add_argument('--list-tests', '-l', action='store_true',
295 help='List tests',
296 default=False)
Puneet Arora9ed41042016-07-05 19:46:06 +0000297 # execution args
Matthew Treinisha051c222016-05-23 15:48:22 -0400298 parser.add_argument('--concurrency', '-w',
Masayuki Igawa13c58832019-03-06 22:01:37 +0900299 type=int, default=0,
Matthew Treinisha051c222016-05-23 15:48:22 -0400300 help="The number of workers to use, defaults to "
301 "the number of cpus")
302 parallel = parser.add_mutually_exclusive_group()
303 parallel.add_argument('--parallel', dest='parallel',
304 action='store_true',
305 help='Run tests in parallel (this is the'
306 ' default)')
Nicolas Bockff27d3b2017-01-11 13:30:32 -0700307 parallel.add_argument('--serial', '-t', dest='parallel',
Matthew Treinisha051c222016-05-23 15:48:22 -0400308 action='store_false',
309 help='Run tests serially')
Prateek Aroraa028de12017-03-14 09:01:03 -0400310 parser.add_argument('--save-state', dest='state',
311 action='store_true',
312 help="To save the state of the cloud before "
313 "running tempest.")
Matthew Treinisha051c222016-05-23 15:48:22 -0400314 # output args
315 parser.add_argument("--subunit", action='store_true',
316 help='Enable subunit v2 output')
Matthew Treinish7d6e48c2017-03-03 12:44:50 -0500317 parser.add_argument("--combine", action='store_true',
318 help='Combine the output of this run with the '
319 "previous run's as a combined stream in the "
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530320 "stestr repository after it finish")
Matthew Treinisha051c222016-05-23 15:48:22 -0400321
322 parser.set_defaults(parallel=True)
323 return parser
324
325 def _build_regex(self, parsed_args):
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530326 regex = None
Matthew Treinisha051c222016-05-23 15:48:22 -0400327 if parsed_args.smoke:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530328 regex = ['smoke']
Matthew Treinisha051c222016-05-23 15:48:22 -0400329 elif parsed_args.regex:
Chandan Kumar8a4396e2017-09-15 12:18:10 +0530330 regex = parsed_args.regex.split()
Matthew Treinisha051c222016-05-23 15:48:22 -0400331 return regex