]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/ragweed.py
import 15.2.4
[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 @contextlib.contextmanager
22 def download(ctx, config):
23 """
24 Download the s3 tests from the git builder.
25 Remove downloaded s3 file upon exit.
26
27 The context passed in should be identical to the context
28 passed in to the main task.
29 """
30 assert isinstance(config, dict)
31 log.info('Downloading ragweed...')
32 testdir = teuthology.get_testdir(ctx)
33 s3_branches = [ 'master', 'nautilus', 'mimic', 'luminous', 'kraken', 'jewel' ]
34 for (client, cconf) in config.items():
35 default_branch = ''
36 branch = cconf.get('force-branch', None)
37 if not branch:
38 default_branch = cconf.get('default-branch', None)
39 ceph_branch = ctx.config.get('branch')
40 suite_branch = ctx.config.get('suite_branch', ceph_branch)
41 ragweed_repo = ctx.config.get('ragweed_repo', teuth_config.ceph_git_base_url + 'ragweed.git')
42 if suite_branch in s3_branches:
43 branch = cconf.get('branch', 'ceph-' + suite_branch)
44 else:
45 branch = cconf.get('branch', suite_branch)
46 if not branch:
47 raise ValueError(
48 "Could not determine what branch to use for ragweed!")
49 else:
50 log.info("Using branch '%s' for ragweed", branch)
51 sha1 = cconf.get('sha1')
52 try:
53 ctx.cluster.only(client).run(
54 args=[
55 'git', 'clone',
56 '-b', branch,
57 ragweed_repo,
58 '{tdir}/ragweed'.format(tdir=testdir),
59 ],
60 )
61 except Exception as e:
62 if not default_branch:
63 raise e
64 ctx.cluster.only(client).run(
65 args=[
66 'git', 'clone',
67 '-b', default_branch,
68 ragweed_repo,
69 '{tdir}/ragweed'.format(tdir=testdir),
70 ],
71 )
72
73 if sha1 is not None:
74 ctx.cluster.only(client).run(
75 args=[
76 'cd', '{tdir}/ragweed'.format(tdir=testdir),
77 run.Raw('&&'),
78 'git', 'reset', '--hard', sha1,
79 ],
80 )
81 try:
82 yield
83 finally:
84 log.info('Removing ragweed...')
85 testdir = teuthology.get_testdir(ctx)
86 for client in config:
87 ctx.cluster.only(client).run(
88 args=[
89 'rm',
90 '-rf',
91 '{tdir}/ragweed'.format(tdir=testdir),
92 ],
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 ragweed. This includes the running of the
179 bootstrap code and the updating of local conf files.
180 """
181 assert isinstance(config, dict)
182 log.info('Configuring ragweed...')
183 testdir = teuthology.get_testdir(ctx)
184 for client, properties in config['clients'].items():
185 (remote,) = ctx.cluster.only(client).remotes.keys()
186 remote.run(
187 args=[
188 'cd',
189 '{tdir}/ragweed'.format(tdir=testdir),
190 run.Raw('&&'),
191 './bootstrap',
192 ],
193 )
194
195 preparing = 'prepare' in run_stages[client]
196 if not preparing:
197 # should have been prepared in a previous run
198 continue
199
200 ragweed_conf = config['ragweed_conf'][client]
201 if properties is not None and 'slow_backend' in properties:
202 ragweed_conf['fixtures']['slow backend'] = properties['slow_backend']
203
204 conf_fp = BytesIO()
205 ragweed_conf.write(conf_fp)
206 teuthology.write_file(
207 remote=remote,
208 path='{tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
209 data=conf_fp.getvalue(),
210 )
211
212 log.info('Configuring boto...')
213 boto_src = os.path.join(os.path.dirname(__file__), 'boto.cfg.template')
214 for client, properties in config['clients'].items():
215 with open(boto_src, 'r') as f:
216 (remote,) = ctx.cluster.only(client).remotes.keys()
217 conf = f.read().format(
218 idle_timeout=config.get('idle_timeout', 30)
219 )
220 teuthology.write_file(
221 remote=remote,
222 path='{tdir}/boto.cfg'.format(tdir=testdir),
223 data=conf,
224 )
225
226 try:
227 yield
228
229 finally:
230 log.info('Cleaning up boto...')
231 for client, properties in config['clients'].items():
232 (remote,) = ctx.cluster.only(client).remotes.keys()
233 remote.run(
234 args=[
235 'rm',
236 '{tdir}/boto.cfg'.format(tdir=testdir),
237 ],
238 )
239
240 @contextlib.contextmanager
241 def run_tests(ctx, config, run_stages):
242 """
243 Run the ragweed after everything is set up.
244
245 :param ctx: Context passed to task
246 :param config: specific configuration information
247 """
248 assert isinstance(config, dict)
249 testdir = teuthology.get_testdir(ctx)
250 attrs = ["!fails_on_rgw"]
251 for client, client_config in config.items():
252 stages = ','.join(run_stages[client])
253 args = [
254 'RAGWEED_CONF={tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
255 'RAGWEED_STAGES={stages}'.format(stages=stages),
256 'BOTO_CONFIG={tdir}/boto.cfg'.format(tdir=testdir),
257 '{tdir}/ragweed/virtualenv/bin/python'.format(tdir=testdir),
258 '-m', 'nose',
259 '-w',
260 '{tdir}/ragweed'.format(tdir=testdir),
261 '-v',
262 '-a', ','.join(attrs),
263 ]
264 if client_config is not None and 'extra_args' in client_config:
265 args.extend(client_config['extra_args'])
266
267 ctx.cluster.only(client).run(
268 args=args,
269 label="ragweed tests against rgw"
270 )
271 yield
272
273 @contextlib.contextmanager
274 def task(ctx, config):
275 """
276 Run the ragweed suite against rgw.
277
278 To run all tests on all clients::
279
280 tasks:
281 - ceph:
282 - rgw:
283 - ragweed:
284
285 To restrict testing to particular clients::
286
287 tasks:
288 - ceph:
289 - rgw: [client.0]
290 - ragweed: [client.0]
291
292 To run against a server on client.1 and increase the boto timeout to 10m::
293
294 tasks:
295 - ceph:
296 - rgw: [client.1]
297 - ragweed:
298 client.0:
299 rgw_server: client.1
300 idle_timeout: 600
301 stages: prepare,check
302
303 To pass extra arguments to nose (e.g. to run a certain test)::
304
305 tasks:
306 - ceph:
307 - rgw: [client.0]
308 - ragweed:
309 client.0:
310 extra_args: ['test_s3:test_object_acl_grand_public_read']
311 client.1:
312 extra_args: ['--exclude', 'test_100_continue']
313 """
314 assert hasattr(ctx, 'rgw'), 'ragweed must run after the rgw task'
315 assert config is None or isinstance(config, list) \
316 or isinstance(config, dict), \
317 "task ragweed only supports a list or dictionary for configuration"
318 all_clients = ['client.{id}'.format(id=id_)
319 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
320 if config is None:
321 config = all_clients
322 if isinstance(config, list):
323 config = dict.fromkeys(config)
324 clients = config.keys()
325
326 overrides = ctx.config.get('overrides', {})
327 # merge each client section, not the top level.
328 for client in config.keys():
329 if not config[client]:
330 config[client] = {}
331 teuthology.deep_merge(config[client], overrides.get('ragweed', {}))
332
333 log.debug('ragweed config is %s', config)
334
335 ragweed_conf = {}
336 for client in clients:
337 # use rgw_server endpoint if given, or default to same client
338 target = config[client].get('rgw_server', client)
339
340 endpoint = ctx.rgw.role_endpoints.get(target)
341 assert endpoint, 'ragweed: no rgw endpoint for {}'.format(target)
342
343 ragweed_conf[client] = ConfigObj(
344 indent_type='',
345 infile={
346 'rgw':
347 {
348 'host' : endpoint.dns_name,
349 'port' : endpoint.port,
350 'is_secure' : endpoint.cert is not None,
351 },
352 'fixtures' : {},
353 'user system' : {},
354 'user regular' : {},
355 'rados':
356 {
357 'ceph_conf' : '/etc/ceph/ceph.conf',
358 },
359 }
360 )
361
362 run_stages = {}
363
364 with contextutil.nested(
365 lambda: download(ctx=ctx, config=config),
366 lambda: create_users(ctx=ctx, config=dict(
367 clients=clients,
368 ragweed_conf=ragweed_conf,
369 config=config,
370 ),
371 run_stages=run_stages),
372 lambda: configure(ctx=ctx, config=dict(
373 clients=config,
374 ragweed_conf=ragweed_conf,
375 ),
376 run_stages=run_stages),
377 lambda: run_tests(ctx=ctx, config=config, run_stages=run_stages),
378 ):
379 pass
380 yield