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