]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/ragweed.py
import 14.2.4 nautilus point release
[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 import util.rgw as rgw_utils
14
15 from teuthology import misc as teuthology
16 from teuthology import contextutil
17 from teuthology.config import config as teuth_config
18 from teuthology.orchestra import run
19 from teuthology.orchestra.connection import split_user
20
21 log = logging.getLogger(__name__)
22
23 @contextlib.contextmanager
24 def download(ctx, config):
25 """
26 Download the s3 tests from the git builder.
27 Remove downloaded s3 file upon exit.
28
29 The context passed in should be identical to the context
30 passed in to the main task.
31 """
32 assert isinstance(config, dict)
33 log.info('Downloading ragweed...')
34 testdir = teuthology.get_testdir(ctx)
35 s3_branches = [ 'master', 'nautilus', 'mimic', 'luminous', 'kraken', 'jewel' ]
36 for (client, cconf) in config.items():
37 default_branch = ''
38 branch = cconf.get('force-branch', None)
39 if not branch:
40 default_branch = cconf.get('default-branch', None)
41 ceph_branch = ctx.config.get('branch')
42 suite_branch = ctx.config.get('suite_branch', ceph_branch)
43 ragweed_repo = ctx.config.get('ragweed_repo', teuth_config.ceph_git_base_url + 'ragweed.git')
44 if suite_branch in s3_branches:
45 branch = cconf.get('branch', 'ceph-' + suite_branch)
46 else:
47 branch = cconf.get('branch', suite_branch)
48 if not branch:
49 raise ValueError(
50 "Could not determine what branch to use for ragweed!")
51 else:
52 log.info("Using branch '%s' for ragweed", branch)
53 sha1 = cconf.get('sha1')
54 try:
55 ctx.cluster.only(client).run(
56 args=[
57 'git', 'clone',
58 '-b', branch,
59 ragweed_repo,
60 '{tdir}/ragweed'.format(tdir=testdir),
61 ],
62 )
63 except Exception as e:
64 if not default_branch:
65 raise e
66 ctx.cluster.only(client).run(
67 args=[
68 'git', 'clone',
69 '-b', default_branch,
70 ragweed_repo,
71 '{tdir}/ragweed'.format(tdir=testdir),
72 ],
73 )
74
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.uppercase) for i in xrange(20)))
107 ragweed_conf[section].setdefault('secret_key', base64.b64encode(os.urandom(40)))
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'].iteritems():
118 run_stages[client] = string.split(properties.get('stages', 'prepare,check'), ',')
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.iteritems():
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.itervalues():
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'].iteritems():
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 'rgw_server' in properties:
204 host = None
205 for target, roles in zip(ctx.config['targets'].iterkeys(), ctx.config['roles']):
206 log.info('roles: ' + str(roles))
207 log.info('target: ' + str(target))
208 if properties['rgw_server'] in roles:
209 _, host = split_user(target)
210 assert host is not None, "Invalid client specified as the rgw_server"
211 ragweed_conf['rgw']['host'] = host
212 else:
213 ragweed_conf['rgw']['host'] = 'localhost'
214
215 if properties is not None and 'slow_backend' in properties:
216 ragweed_conf['fixtures']['slow backend'] = properties['slow_backend']
217
218 conf_fp = StringIO()
219 ragweed_conf.write(conf_fp)
220 teuthology.write_file(
221 remote=remote,
222 path='{tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
223 data=conf_fp.getvalue(),
224 )
225
226 log.info('Configuring boto...')
227 boto_src = os.path.join(os.path.dirname(__file__), 'boto.cfg.template')
228 for client, properties in config['clients'].iteritems():
229 with file(boto_src, 'rb') as f:
230 (remote,) = ctx.cluster.only(client).remotes.keys()
231 conf = f.read().format(
232 idle_timeout=config.get('idle_timeout', 30)
233 )
234 teuthology.write_file(
235 remote=remote,
236 path='{tdir}/boto.cfg'.format(tdir=testdir),
237 data=conf,
238 )
239
240 try:
241 yield
242
243 finally:
244 log.info('Cleaning up boto...')
245 for client, properties in config['clients'].iteritems():
246 (remote,) = ctx.cluster.only(client).remotes.keys()
247 remote.run(
248 args=[
249 'rm',
250 '{tdir}/boto.cfg'.format(tdir=testdir),
251 ],
252 )
253
254 @contextlib.contextmanager
255 def run_tests(ctx, config, run_stages):
256 """
257 Run the ragweed after everything is set up.
258
259 :param ctx: Context passed to task
260 :param config: specific configuration information
261 """
262 assert isinstance(config, dict)
263 testdir = teuthology.get_testdir(ctx)
264 attrs = ["!fails_on_rgw"]
265 for client, client_config in config.iteritems():
266 stages = string.join(run_stages[client], ',')
267 args = [
268 'RAGWEED_CONF={tdir}/archive/ragweed.{client}.conf'.format(tdir=testdir, client=client),
269 'RAGWEED_STAGES={stages}'.format(stages=stages),
270 'BOTO_CONFIG={tdir}/boto.cfg'.format(tdir=testdir),
271 '{tdir}/ragweed/virtualenv/bin/nosetests'.format(tdir=testdir),
272 '-w',
273 '{tdir}/ragweed'.format(tdir=testdir),
274 '-v',
275 '-a', ','.join(attrs),
276 ]
277 if client_config is not None and 'extra_args' in client_config:
278 args.extend(client_config['extra_args'])
279
280 ctx.cluster.only(client).run(
281 args=args,
282 label="ragweed tests against rgw"
283 )
284 yield
285
286 @contextlib.contextmanager
287 def task(ctx, config):
288 """
289 Run the ragweed suite against rgw.
290
291 To run all tests on all clients::
292
293 tasks:
294 - ceph:
295 - rgw:
296 - ragweed:
297
298 To restrict testing to particular clients::
299
300 tasks:
301 - ceph:
302 - rgw: [client.0]
303 - ragweed: [client.0]
304
305 To run against a server on client.1 and increase the boto timeout to 10m::
306
307 tasks:
308 - ceph:
309 - rgw: [client.1]
310 - ragweed:
311 client.0:
312 rgw_server: client.1
313 idle_timeout: 600
314 stages: prepare,check
315
316 To pass extra arguments to nose (e.g. to run a certain test)::
317
318 tasks:
319 - ceph:
320 - rgw: [client.0]
321 - ragweed:
322 client.0:
323 extra_args: ['test_s3:test_object_acl_grand_public_read']
324 client.1:
325 extra_args: ['--exclude', 'test_100_continue']
326 """
327 assert hasattr(ctx, 'rgw'), 'ragweed must run after the rgw task'
328 assert config is None or isinstance(config, list) \
329 or isinstance(config, dict), \
330 "task ragweed only supports a list or dictionary for configuration"
331 all_clients = ['client.{id}'.format(id=id_)
332 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
333 if config is None:
334 config = all_clients
335 if isinstance(config, list):
336 config = dict.fromkeys(config)
337 clients = config.keys()
338
339 overrides = ctx.config.get('overrides', {})
340 # merge each client section, not the top level.
341 for client in config.iterkeys():
342 if not config[client]:
343 config[client] = {}
344 teuthology.deep_merge(config[client], overrides.get('ragweed', {}))
345
346 log.debug('ragweed config is %s', config)
347
348 ragweed_conf = {}
349 for client in clients:
350 endpoint = ctx.rgw.role_endpoints.get(client)
351 assert endpoint, 'ragweed: no rgw endpoint for {}'.format(client)
352
353 ragweed_conf[client] = ConfigObj(
354 indent_type='',
355 infile={
356 'rgw':
357 {
358 'port' : endpoint.port,
359 'is_secure' : endpoint.cert is not None,
360 },
361 'fixtures' : {},
362 'user system' : {},
363 'user regular' : {},
364 'rados':
365 {
366 'ceph_conf' : '/etc/ceph/ceph.conf',
367 },
368 }
369 )
370
371 run_stages = {}
372
373 with contextutil.nested(
374 lambda: download(ctx=ctx, config=config),
375 lambda: create_users(ctx=ctx, config=dict(
376 clients=clients,
377 ragweed_conf=ragweed_conf,
378 config=config,
379 ),
380 run_stages=run_stages),
381 lambda: configure(ctx=ctx, config=dict(
382 clients=config,
383 ragweed_conf=ragweed_conf,
384 ),
385 run_stages=run_stages),
386 lambda: run_tests(ctx=ctx, config=config, run_stages=run_stages),
387 ):
388 pass
389 yield