]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/ragweed.py
bump version to 18.2.4-pve3
[ceph.git] / ceph / qa / tasks / ragweed.py
1 """
2 Run a set of s3 tests on rgw.
3 """
4 from io import BytesIO
5 from configobj import ConfigObj
6 import base64
7 import contextlib
8 import logging
9 import os
10 import random
11 import string
12
13 from teuthology import misc as teuthology
14 from teuthology import contextutil
15 from teuthology.config import config as teuth_config
16 from teuthology.orchestra import run
17
18 log = logging.getLogger(__name__)
19
20
21 def get_ragweed_branches(config, client_conf):
22 """
23 figure out the ragweed branch according to the per-client settings
24
25 use force-branch is specified, and fall back to the ones deduced using ceph
26 branch under testing
27 """
28 force_branch = client_conf.get('force-branch', None)
29 if force_branch:
30 return [force_branch]
31 else:
32 S3_BRANCHES = ['master', 'nautilus', 'mimic',
33 'luminous', 'kraken', 'jewel']
34 ceph_branch = config.get('branch')
35 suite_branch = config.get('suite_branch', ceph_branch)
36 if suite_branch in S3_BRANCHES:
37 branch = client_conf.get('branch', 'ceph-' + suite_branch)
38 else:
39 branch = client_conf.get('branch', suite_branch)
40 default_branch = client_conf.get('default-branch', None)
41 if default_branch:
42 return [branch, default_branch]
43 else:
44 return [branch]
45
46 def get_ragweed_dir(testdir, client):
47 return '{}/ragweed.{}'.format(testdir, client)
48
49 @contextlib.contextmanager
50 def download(ctx, config):
51 """
52 Download the s3 tests from the git builder.
53 Remove downloaded s3 file upon exit.
54
55 The context passed in should be identical to the context
56 passed in to the main task.
57 """
58 assert isinstance(config, dict)
59 log.info('Downloading ragweed...')
60 testdir = teuthology.get_testdir(ctx)
61 for (client, cconf) in config.items():
62 ragweed_dir = get_ragweed_dir(testdir, client)
63 ragweed_repo = ctx.config.get('ragweed_repo',
64 teuth_config.ceph_git_base_url + 'ragweed.git')
65 for branch in get_ragweed_branches(ctx.config, cconf):
66 log.info("Using branch '%s' for ragweed", branch)
67 try:
68 ctx.cluster.only(client).sh(
69 script=f'git clone -b {branch} {ragweed_repo} {ragweed_dir}')
70 break
71 except Exception as e:
72 exc = e
73 else:
74 raise exc
75
76 sha1 = cconf.get('sha1')
77 if sha1 is not None:
78 ctx.cluster.only(client).run(
79 args=[
80 'cd', ragweed_dir,
81 run.Raw('&&'),
82 'git', 'reset', '--hard', sha1,
83 ],
84 )
85 try:
86 yield
87 finally:
88 log.info('Removing ragweed...')
89 for client in config:
90 ragweed_dir = get_ragweed_dir(testdir, client)
91 ctx.cluster.only(client).run(
92 args=['rm', '-rf', ragweed_dir]
93 )
94
95
96 def _config_user(ragweed_conf, section, user):
97 """
98 Configure users for this section by stashing away keys, ids, and
99 email addresses.
100 """
101 ragweed_conf[section].setdefault('user_id', user)
102 ragweed_conf[section].setdefault('email', '{user}+test@test.test'.format(user=user))
103 ragweed_conf[section].setdefault('display_name', 'Mr. {user}'.format(user=user))
104 ragweed_conf[section].setdefault('access_key', ''.join(random.choice(string.ascii_uppercase) for i in range(20)))
105 ragweed_conf[section].setdefault('secret_key', base64.b64encode(os.urandom(40)).decode('ascii'))
106
107
108 @contextlib.contextmanager
109 def create_users(ctx, config, run_stages):
110 """
111 Create a main and an alternate s3 user.
112 """
113 assert isinstance(config, dict)
114
115 for client, properties in config['config'].items():
116 run_stages[client] = properties.get('stages', 'prepare,check').split(',')
117
118 log.info('Creating rgw users...')
119 testdir = teuthology.get_testdir(ctx)
120 users = {'user regular': 'ragweed', 'user system': 'sysuser'}
121 for client in config['clients']:
122 if not 'prepare' in run_stages[client]:
123 # should have been prepared in a previous run
124 continue
125
126 ragweed_conf = config['ragweed_conf'][client]
127 ragweed_conf.setdefault('fixtures', {})
128 ragweed_conf['rgw'].setdefault('bucket_prefix', 'test-' + client)
129 for section, user in users.items():
130 _config_user(ragweed_conf, section, '{user}.{client}'.format(user=user, client=client))
131 log.debug('Creating user {user} on {host}'.format(user=ragweed_conf[section]['user_id'], host=client))
132 if user == 'sysuser':
133 sys_str = 'true'
134 else:
135 sys_str = 'false'
136 ctx.cluster.only(client).run(
137 args=[
138 'adjust-ulimits',
139 'ceph-coverage',
140 '{tdir}/archive/coverage'.format(tdir=testdir),
141 'radosgw-admin',
142 '-n', client,
143 'user', 'create',
144 '--uid', ragweed_conf[section]['user_id'],
145 '--display-name', ragweed_conf[section]['display_name'],
146 '--access-key', ragweed_conf[section]['access_key'],
147 '--secret', ragweed_conf[section]['secret_key'],
148 '--email', ragweed_conf[section]['email'],
149 '--system', sys_str,
150 ],
151 )
152 try:
153 yield
154 finally:
155 for client in config['clients']:
156 if not 'check' in run_stages[client]:
157 # only remove user if went through the check stage
158 continue
159 for user in users.values():
160 uid = '{user}.{client}'.format(user=user, client=client)
161 ctx.cluster.only(client).run(
162 args=[
163 'adjust-ulimits',
164 'ceph-coverage',
165 '{tdir}/archive/coverage'.format(tdir=testdir),
166 'radosgw-admin',
167 '-n', client,
168 'user', 'rm',
169 '--uid', uid,
170 '--purge-data',
171 ],
172 )
173
174
175 @contextlib.contextmanager
176 def configure(ctx, config, run_stages):
177 """
178 Configure the local config files.
179 """
180 assert isinstance(config, dict)
181 log.info('Configuring ragweed...')
182 testdir = teuthology.get_testdir(ctx)
183 for client, properties in config['clients'].items():
184 (remote,) = ctx.cluster.only(client).remotes.keys()
185 preparing = 'prepare' in run_stages[client]
186 if not preparing:
187 # should have been prepared in a previous run
188 continue
189
190 ragweed_conf = config['ragweed_conf'][client]
191 if properties is not None and 'slow_backend' in properties:
192 ragweed_conf['fixtures']['slow backend'] = properties['slow_backend']
193
194 conf_fp = BytesIO()
195 ragweed_conf.write(conf_fp)
196 remote.write_file(
197 path='{tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
198 data=conf_fp.getvalue(),
199 )
200
201 log.info('Configuring boto...')
202 boto_src = os.path.join(os.path.dirname(__file__), 'boto.cfg.template')
203 for client, properties in config['clients'].items():
204 with open(boto_src, 'r') as f:
205 (remote,) = ctx.cluster.only(client).remotes.keys()
206 conf = f.read().format(
207 idle_timeout=config.get('idle_timeout', 30)
208 )
209 remote.write_file('{tdir}/boto.cfg'.format(tdir=testdir), conf)
210
211 try:
212 yield
213
214 finally:
215 log.info('Cleaning up boto...')
216 for client, properties in config['clients'].items():
217 (remote,) = ctx.cluster.only(client).remotes.keys()
218 remote.run(
219 args=[
220 'rm', '-f',
221 '{tdir}/boto.cfg'.format(tdir=testdir),
222 ],
223 )
224
225 def get_toxvenv_dir(ctx):
226 return ctx.tox.venv_path
227
228 def toxvenv_sh(ctx, remote, args, **kwargs):
229 activate = get_toxvenv_dir(ctx) + '/bin/activate'
230 return remote.sh(['source', activate, run.Raw('&&')] + args, **kwargs)
231
232 @contextlib.contextmanager
233 def run_tests(ctx, config, run_stages):
234 """
235 Run the ragweed after everything is set up.
236
237 :param ctx: Context passed to task
238 :param config: specific configuration information
239 """
240 assert isinstance(config, dict)
241 testdir = teuthology.get_testdir(ctx)
242 attrs = ["not fails_on_rgw"]
243 for client, client_config in config.items():
244 ragweed_dir = get_ragweed_dir(testdir, client)
245 stages = ','.join(run_stages[client])
246 args = [
247 'cd', ragweed_dir, run.Raw('&&'),
248 'RAGWEED_CONF={tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
249 'RAGWEED_STAGES={stages}'.format(stages=stages),
250 'BOTO_CONFIG={tdir}/boto.cfg'.format(tdir=testdir),
251 'tox',
252 '--sitepackages',
253 '--',
254 '-v',
255 '-m', ' and '.join(attrs),
256 ]
257 if client_config is not None and 'extra_args' in client_config:
258 args.extend(client_config['extra_args'])
259
260 (remote,) = ctx.cluster.only(client).remotes.keys()
261 toxvenv_sh(ctx, remote, args, label="ragweed tests against rgw")
262 yield
263
264 @contextlib.contextmanager
265 def task(ctx, config):
266 """
267 Run the ragweed suite against rgw.
268
269 To run all tests on all clients::
270
271 tasks:
272 - ceph:
273 - rgw:
274 - ragweed:
275
276 To restrict testing to particular clients::
277
278 tasks:
279 - ceph:
280 - rgw: [client.0]
281 - ragweed: [client.0]
282
283 To run against a server on client.1 and increase the boto timeout to 10m::
284
285 tasks:
286 - ceph:
287 - rgw: [client.1]
288 - ragweed:
289 client.0:
290 rgw_server: client.1
291 idle_timeout: 600
292 stages: prepare,check
293
294 To pass extra arguments to nose (e.g. to run a certain test)::
295
296 tasks:
297 - ceph:
298 - rgw: [client.0]
299 - ragweed:
300 client.0:
301 extra_args: ['test_s3:test_object_acl_grand_public_read']
302 client.1:
303 extra_args: ['--exclude', 'test_100_continue']
304 """
305 assert hasattr(ctx, 'rgw'), 'ragweed must run after the rgw task'
306 assert hasattr(ctx, 'tox'), 'ragweed must run after the tox task'
307 assert config is None or isinstance(config, list) \
308 or isinstance(config, dict), \
309 "task ragweed only supports a list or dictionary for configuration"
310 all_clients = ['client.{id}'.format(id=id_)
311 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
312 if config is None:
313 config = all_clients
314 if isinstance(config, list):
315 config = dict.fromkeys(config)
316 clients = config.keys()
317
318 overrides = ctx.config.get('overrides', {})
319 # merge each client section, not the top level.
320 for client in config.keys():
321 if not config[client]:
322 config[client] = {}
323 teuthology.deep_merge(config[client], overrides.get('ragweed', {}))
324
325 log.debug('ragweed config is %s', config)
326
327 ragweed_conf = {}
328 for client in clients:
329 # use rgw_server endpoint if given, or default to same client
330 target = config[client].get('rgw_server', client)
331
332 endpoint = ctx.rgw.role_endpoints.get(target)
333 assert endpoint, 'ragweed: no rgw endpoint for {}'.format(target)
334
335 ragweed_conf[client] = ConfigObj(
336 indent_type='',
337 infile={
338 'rgw':
339 {
340 'host' : endpoint.dns_name,
341 'port' : endpoint.port,
342 'is_secure' : endpoint.cert is not None,
343 },
344 'fixtures' : {},
345 'user system' : {},
346 'user regular' : {},
347 'rados':
348 {
349 'ceph_conf' : '/etc/ceph/ceph.conf',
350 },
351 }
352 )
353
354 run_stages = {}
355
356 with contextutil.nested(
357 lambda: download(ctx=ctx, config=config),
358 lambda: create_users(ctx=ctx, config=dict(
359 clients=clients,
360 ragweed_conf=ragweed_conf,
361 config=config,
362 ),
363 run_stages=run_stages),
364 lambda: configure(ctx=ctx, config=dict(
365 clients=config,
366 ragweed_conf=ragweed_conf,
367 ),
368 run_stages=run_stages),
369 lambda: run_tests(ctx=ctx, config=config, run_stages=run_stages),
370 ):
371 pass
372 yield