]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/tempest.py
8550f9b90f2c06e19a63ff09f06a22f2c3807c39
[ceph.git] / ceph / qa / tasks / tempest.py
1 """
2 Deploy and configure Tempest for Teuthology
3 """
4 import contextlib
5 import logging
6
7 from teuthology import misc as teuthology
8 from teuthology import contextutil
9 from teuthology.exceptions import ConfigError
10 from teuthology.orchestra import run
11
12 log = logging.getLogger(__name__)
13
14
15 def get_tempest_dir(ctx):
16 return '{tdir}/tempest'.format(tdir=teuthology.get_testdir(ctx))
17
18 def run_in_tempest_dir(ctx, client, cmdargs, **kwargs):
19 ctx.cluster.only(client).run(
20 args=[ 'cd', get_tempest_dir(ctx), run.Raw('&&'), ] + cmdargs,
21 **kwargs
22 )
23
24 def run_in_tempest_rgw_dir(ctx, client, cmdargs, **kwargs):
25 ctx.cluster.only(client).run(
26 args=[ 'cd', get_tempest_dir(ctx) + '/rgw', run.Raw('&&'), ] + cmdargs,
27 **kwargs
28 )
29
30 def run_in_tempest_venv(ctx, client, cmdargs, **kwargs):
31 run_in_tempest_dir(ctx, client,
32 [ 'source',
33 '.tox/venv/bin/activate',
34 run.Raw('&&')
35 ] + cmdargs, **kwargs)
36
37 @contextlib.contextmanager
38 def download(ctx, config):
39 """
40 Download the Tempest from github.
41 Remove downloaded file upon exit.
42
43 The context passed in should be identical to the context
44 passed in to the main task.
45 """
46 assert isinstance(config, dict)
47 log.info('Downloading Tempest...')
48 for (client, cconf) in config.items():
49 ctx.cluster.only(client).run(
50 args=[
51 'git', 'clone',
52 '-b', cconf.get('force-branch', 'master'),
53 'https://github.com/openstack/tempest.git',
54 get_tempest_dir(ctx)
55 ],
56 )
57
58 sha1 = cconf.get('sha1')
59 if sha1 is not None:
60 run_in_tempest_dir(ctx, client, [ 'git', 'reset', '--hard', sha1 ])
61
62 # tox.ini contains a dead link, replace it with the new one
63 from_url = 'https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt'
64 to_url = 'https://opendev.org/openstack/requirements/raw/branch/stable/pike/upper-constraints.txt'
65 run_in_tempest_dir(ctx, client, [
66 'sed', '-i',
67 run.Raw('"s|{}|{}|"'.format(from_url, to_url)),
68 'tox.ini'
69 ])
70 try:
71 yield
72 finally:
73 log.info('Removing Tempest...')
74 for client in config:
75 ctx.cluster.only(client).run(
76 args=[ 'rm', '-rf', get_tempest_dir(ctx) ],
77 )
78
79 def get_toxvenv_dir(ctx):
80 return ctx.tox.venv_path
81
82 @contextlib.contextmanager
83 def setup_venv(ctx, config):
84 """
85 Setup the virtualenv for Tempest using tox.
86 """
87 assert isinstance(config, dict)
88 log.info('Setting up virtualenv for Tempest')
89 for (client, _) in config.items():
90 run_in_tempest_dir(ctx, client,
91 [ '{tvdir}/bin/tox'.format(tvdir=get_toxvenv_dir(ctx)),
92 '-e', 'venv', '--notest'
93 ])
94 yield
95
96 def setup_logging(ctx, cpar):
97 cpar.set('DEFAULT', 'log_dir', teuthology.get_archive_dir(ctx))
98 cpar.set('DEFAULT', 'log_file', 'tempest.log')
99
100 def to_config(config, params, section, cpar):
101 for (k, v) in config[section].items():
102 if (isinstance(v, str)):
103 v = v.format(**params)
104 cpar.set(section, k, v)
105
106 @contextlib.contextmanager
107 def configure_instance(ctx, config):
108 assert isinstance(config, dict)
109 log.info('Configuring Tempest')
110
111 import ConfigParser
112 for (client, cconfig) in config.items():
113 run_in_tempest_venv(ctx, client,
114 [
115 'tempest',
116 'init',
117 '--workspace-path',
118 get_tempest_dir(ctx) + '/workspace.yaml',
119 'rgw'
120 ])
121
122 # prepare the config file
123 tetcdir = '{tdir}/rgw/etc'.format(tdir=get_tempest_dir(ctx))
124 (remote,) = ctx.cluster.only(client).remotes.keys()
125 local_conf = remote.get_file(tetcdir + '/tempest.conf.sample')
126
127 # fill the params dictionary which allows to use templatized configs
128 keystone_role = cconfig.get('use-keystone-role', None)
129 if keystone_role is None \
130 or keystone_role not in ctx.keystone.public_endpoints:
131 raise ConfigError('the use-keystone-role is misconfigured')
132 public_host, public_port = ctx.keystone.public_endpoints[keystone_role]
133 params = {
134 'keystone_public_host': public_host,
135 'keystone_public_port': str(public_port),
136 }
137
138 cpar = ConfigParser.ConfigParser()
139 cpar.read(local_conf)
140 setup_logging(ctx, cpar)
141 to_config(cconfig, params, 'auth', cpar)
142 to_config(cconfig, params, 'identity', cpar)
143 to_config(cconfig, params, 'object-storage', cpar)
144 to_config(cconfig, params, 'object-storage-feature-enabled', cpar)
145 cpar.write(open(local_conf, 'w+'))
146
147 remote.put_file(local_conf, tetcdir + '/tempest.conf')
148 yield
149
150 @contextlib.contextmanager
151 def run_tempest(ctx, config):
152 assert isinstance(config, dict)
153 log.info('Configuring Tempest')
154
155 for (client, cconf) in config.items():
156 blacklist = cconf.get('blacklist', [])
157 assert isinstance(blacklist, list)
158 run_in_tempest_venv(ctx, client,
159 [
160 'tempest',
161 'run',
162 '--workspace-path',
163 get_tempest_dir(ctx) + '/workspace.yaml',
164 '--workspace',
165 'rgw',
166 '--regex',
167 '(tempest.api.object_storage)' +
168 ''.join([ '(?!{blackitem})'.format(blackitem=blackitem)
169 for blackitem in blacklist])
170 ])
171 try:
172 yield
173 finally:
174 pass
175
176
177 @contextlib.contextmanager
178 def task(ctx, config):
179 """
180 Deploy and run Tempest's object storage campaign
181
182 Example of configuration:
183
184 overrides:
185 ceph:
186 conf:
187 client:
188 rgw keystone admin token: ADMIN
189 rgw keystone accepted roles: admin,Member
190 rgw keystone implicit tenants: true
191 rgw keystone accepted admin roles: admin
192 rgw swift enforce content length: true
193 rgw swift account in url: true
194 rgw swift versioning enabled: true
195 tasks:
196 # typically, the task should be preceded with install, ceph, tox,
197 # keystone and rgw. Tox and Keystone are specific requirements
198 # of tempest.py.
199 - rgw:
200 # it's important to match the prefix with the endpoint's URL
201 # in Keystone. Additionally, if we want to test /info and its
202 # accompanying stuff, the whole Swift API must be put in root
203 # of the whole URL hierarchy (read: frontend_prefix == /swift).
204 frontend_prefix: /swift
205 client.0:
206 use-keystone-role: client.0
207 - tempest:
208 client.0:
209 force-branch: master
210 use-keystone-role: client.0
211 auth:
212 admin_username: admin
213 admin_project_name: admin
214 admin_password: ADMIN
215 admin_domain_name: Default
216 identity:
217 uri: http://{keystone_public_host}:{keystone_public_port}/v2.0/
218 uri_v3: http://{keystone_public_host}:{keystone_public_port}/v3/
219 admin_role: admin
220 object-storage:
221 reseller_admin_role: admin
222 object-storage-feature-enabled:
223 container_sync: false
224 discoverability: false
225 blacklist:
226 # please strip half of these items after merging PRs #15369
227 # and #12704
228 - .*test_list_containers_reverse_order.*
229 - .*test_list_container_contents_with_end_marker.*
230 - .*test_delete_non_empty_container.*
231 - .*test_container_synchronization.*
232 - .*test_get_object_after_expiration_time.*
233 - .*test_create_object_with_transfer_encoding.*
234 """
235 assert config is None or isinstance(config, list) \
236 or isinstance(config, dict), \
237 'task tempest only supports a list or dictionary for configuration'
238
239 if not ctx.tox:
240 raise ConfigError('tempest must run after the tox task')
241 if not ctx.keystone:
242 raise ConfigError('tempest must run after the keystone task')
243
244 all_clients = ['client.{id}'.format(id=id_)
245 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
246 if config is None:
247 config = all_clients
248 if isinstance(config, list):
249 config = dict.fromkeys(config)
250
251 overrides = ctx.config.get('overrides', {})
252 # merge each client section, not the top level.
253 for client in config.keys():
254 if not config[client]:
255 config[client] = {}
256 teuthology.deep_merge(config[client], overrides.get('keystone', {}))
257
258 log.debug('Tempest config is %s', config)
259
260 with contextutil.nested(
261 lambda: download(ctx=ctx, config=config),
262 lambda: setup_venv(ctx=ctx, config=config),
263 lambda: configure_instance(ctx=ctx, config=config),
264 lambda: run_tempest(ctx=ctx, config=config),
265 ):
266 yield