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