]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | """ |
2 | Rbd testing task | |
3 | """ | |
4 | import contextlib | |
5 | import logging | |
6 | import os | |
181888fb | 7 | import tempfile |
11fdf7f2 | 8 | import sys |
7c673cae | 9 | |
f67539c2 | 10 | from io import StringIO |
7c673cae FG |
11 | from teuthology.orchestra import run |
12 | from teuthology import misc as teuthology | |
13 | from teuthology import contextutil | |
14 | from teuthology.parallel import parallel | |
15 | from teuthology.task.common_fs_utils import generic_mkfs | |
16 | from teuthology.task.common_fs_utils import generic_mount | |
17 | from teuthology.task.common_fs_utils import default_image_name | |
18 | ||
9f95a23c | 19 | |
11fdf7f2 TL |
20 | #V1 image unsupported but required for testing purposes |
21 | os.environ["RBD_FORCE_ALLOW_V1"] = "1" | |
22 | ||
7c673cae FG |
23 | log = logging.getLogger(__name__) |
24 | ||
f67539c2 TL |
25 | ENCRYPTION_PASSPHRASE = "password" |
26 | ||
7c673cae FG |
27 | @contextlib.contextmanager |
28 | def create_image(ctx, config): | |
29 | """ | |
30 | Create an rbd image. | |
31 | ||
32 | For example:: | |
33 | ||
34 | tasks: | |
35 | - ceph: | |
36 | - rbd.create_image: | |
37 | client.0: | |
38 | image_name: testimage | |
39 | image_size: 100 | |
40 | image_format: 1 | |
f67539c2 | 41 | encryption_format: luks2 |
7c673cae FG |
42 | client.1: |
43 | ||
44 | Image size is expressed as a number of megabytes; default value | |
45 | is 10240. | |
46 | ||
47 | Image format value must be either 1 or 2; default value is 1. | |
48 | ||
49 | """ | |
50 | assert isinstance(config, dict) or isinstance(config, list), \ | |
51 | "task create_image only supports a list or dictionary for configuration" | |
52 | ||
53 | if isinstance(config, dict): | |
54 | images = config.items() | |
55 | else: | |
56 | images = [(role, None) for role in config] | |
57 | ||
58 | testdir = teuthology.get_testdir(ctx) | |
f67539c2 | 59 | passphrase_file = '{tdir}/passphrase'.format(tdir=testdir) |
7c673cae FG |
60 | for role, properties in images: |
61 | if properties is None: | |
62 | properties = {} | |
63 | name = properties.get('image_name', default_image_name(role)) | |
64 | size = properties.get('image_size', 10240) | |
65 | fmt = properties.get('image_format', 1) | |
f67539c2 | 66 | encryption_format = properties.get('encryption_format', 'none') |
7c673cae FG |
67 | (remote,) = ctx.cluster.only(role).remotes.keys() |
68 | log.info('Creating image {name} with size {size}'.format(name=name, | |
69 | size=size)) | |
70 | args = [ | |
71 | 'adjust-ulimits', | |
9f95a23c | 72 | 'ceph-coverage', |
7c673cae FG |
73 | '{tdir}/archive/coverage'.format(tdir=testdir), |
74 | 'rbd', | |
75 | '-p', 'rbd', | |
76 | 'create', | |
77 | '--size', str(size), | |
78 | name, | |
79 | ] | |
80 | # omit format option if using the default (format 1) | |
81 | # since old versions of don't support it | |
82 | if int(fmt) != 1: | |
83 | args += ['--image-format', str(fmt)] | |
84 | remote.run(args=args) | |
f67539c2 TL |
85 | |
86 | if encryption_format != 'none': | |
87 | remote.run( | |
88 | args=[ | |
89 | 'echo', | |
90 | ENCRYPTION_PASSPHRASE, | |
91 | run.Raw('>'), | |
92 | passphrase_file | |
93 | ] | |
94 | ) | |
95 | remote.run( | |
96 | args=[ | |
97 | 'adjust-ulimits', | |
98 | 'ceph-coverage', | |
99 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
100 | 'rbd', | |
101 | 'encryption', | |
102 | 'format', | |
103 | name, | |
104 | encryption_format, | |
105 | passphrase_file, | |
106 | '-p', | |
107 | 'rbd' | |
108 | ] | |
109 | ) | |
7c673cae FG |
110 | try: |
111 | yield | |
112 | finally: | |
113 | log.info('Deleting rbd images...') | |
f67539c2 | 114 | remote.run(args=['rm', '-f', passphrase_file]) |
7c673cae FG |
115 | for role, properties in images: |
116 | if properties is None: | |
117 | properties = {} | |
118 | name = properties.get('image_name', default_image_name(role)) | |
119 | (remote,) = ctx.cluster.only(role).remotes.keys() | |
120 | remote.run( | |
121 | args=[ | |
122 | 'adjust-ulimits', | |
123 | 'ceph-coverage', | |
124 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
125 | 'rbd', | |
126 | '-p', 'rbd', | |
127 | 'rm', | |
128 | name, | |
129 | ], | |
130 | ) | |
131 | ||
132 | @contextlib.contextmanager | |
133 | def clone_image(ctx, config): | |
134 | """ | |
135 | Clones a parent imag | |
136 | ||
137 | For example:: | |
138 | ||
139 | tasks: | |
140 | - ceph: | |
141 | - rbd.clone_image: | |
142 | client.0: | |
143 | parent_name: testimage | |
144 | image_name: cloneimage | |
145 | """ | |
146 | assert isinstance(config, dict) or isinstance(config, list), \ | |
147 | "task clone_image only supports a list or dictionary for configuration" | |
148 | ||
149 | if isinstance(config, dict): | |
150 | images = config.items() | |
151 | else: | |
152 | images = [(role, None) for role in config] | |
153 | ||
154 | testdir = teuthology.get_testdir(ctx) | |
155 | for role, properties in images: | |
156 | if properties is None: | |
157 | properties = {} | |
158 | ||
159 | name = properties.get('image_name', default_image_name(role)) | |
160 | parent_name = properties.get('parent_name') | |
161 | assert parent_name is not None, \ | |
162 | "parent_name is required" | |
163 | parent_spec = '{name}@{snap}'.format(name=parent_name, snap=name) | |
164 | ||
165 | (remote,) = ctx.cluster.only(role).remotes.keys() | |
166 | log.info('Clone image {parent} to {child}'.format(parent=parent_name, | |
167 | child=name)) | |
168 | for cmd in [('snap', 'create', parent_spec), | |
169 | ('snap', 'protect', parent_spec), | |
170 | ('clone', parent_spec, name)]: | |
171 | args = [ | |
172 | 'adjust-ulimits', | |
9f95a23c | 173 | 'ceph-coverage', |
7c673cae FG |
174 | '{tdir}/archive/coverage'.format(tdir=testdir), |
175 | 'rbd', '-p', 'rbd' | |
176 | ] | |
177 | args.extend(cmd) | |
178 | remote.run(args=args) | |
179 | ||
180 | try: | |
181 | yield | |
182 | finally: | |
183 | log.info('Deleting rbd clones...') | |
184 | for role, properties in images: | |
185 | if properties is None: | |
186 | properties = {} | |
187 | name = properties.get('image_name', default_image_name(role)) | |
188 | parent_name = properties.get('parent_name') | |
189 | parent_spec = '{name}@{snap}'.format(name=parent_name, snap=name) | |
190 | ||
191 | (remote,) = ctx.cluster.only(role).remotes.keys() | |
192 | ||
193 | for cmd in [('rm', name), | |
194 | ('snap', 'unprotect', parent_spec), | |
195 | ('snap', 'rm', parent_spec)]: | |
196 | args = [ | |
197 | 'adjust-ulimits', | |
9f95a23c | 198 | 'ceph-coverage', |
7c673cae FG |
199 | '{tdir}/archive/coverage'.format(tdir=testdir), |
200 | 'rbd', '-p', 'rbd' | |
201 | ] | |
202 | args.extend(cmd) | |
203 | remote.run(args=args) | |
204 | ||
205 | @contextlib.contextmanager | |
206 | def modprobe(ctx, config): | |
207 | """ | |
208 | Load the rbd kernel module.. | |
209 | ||
210 | For example:: | |
211 | ||
212 | tasks: | |
213 | - ceph: | |
214 | - rbd.create_image: [client.0] | |
215 | - rbd.modprobe: [client.0] | |
216 | """ | |
217 | log.info('Loading rbd kernel module...') | |
218 | for role in config: | |
219 | (remote,) = ctx.cluster.only(role).remotes.keys() | |
220 | remote.run( | |
221 | args=[ | |
222 | 'sudo', | |
223 | 'modprobe', | |
224 | 'rbd', | |
225 | ], | |
226 | ) | |
227 | try: | |
228 | yield | |
229 | finally: | |
230 | log.info('Unloading rbd kernel module...') | |
231 | for role in config: | |
232 | (remote,) = ctx.cluster.only(role).remotes.keys() | |
233 | remote.run( | |
234 | args=[ | |
235 | 'sudo', | |
236 | 'modprobe', | |
237 | '-r', | |
238 | 'rbd', | |
239 | # force errors to be ignored; necessary if more | |
240 | # than one device was created, which may mean | |
241 | # the module isn't quite ready to go the first | |
242 | # time through. | |
243 | run.Raw('||'), | |
244 | 'true', | |
245 | ], | |
246 | ) | |
247 | ||
248 | @contextlib.contextmanager | |
249 | def dev_create(ctx, config): | |
250 | """ | |
251 | Map block devices to rbd images. | |
252 | ||
253 | For example:: | |
254 | ||
255 | tasks: | |
256 | - ceph: | |
257 | - rbd.create_image: [client.0] | |
258 | - rbd.modprobe: [client.0] | |
259 | - rbd.dev_create: | |
f67539c2 TL |
260 | client.0: |
261 | image_name: testimage.client.0 | |
262 | encryption_format: luks2 | |
7c673cae FG |
263 | """ |
264 | assert isinstance(config, dict) or isinstance(config, list), \ | |
265 | "task dev_create only supports a list or dictionary for configuration" | |
266 | ||
267 | if isinstance(config, dict): | |
f67539c2 | 268 | images = config.items() |
7c673cae | 269 | else: |
f67539c2 | 270 | images = [(role, None) for role in config] |
7c673cae FG |
271 | |
272 | log.info('Creating rbd block devices...') | |
273 | ||
274 | testdir = teuthology.get_testdir(ctx) | |
f67539c2 TL |
275 | passphrase_file = '{tdir}/passphrase'.format(tdir=testdir) |
276 | device_path = {} | |
7c673cae | 277 | |
f67539c2 TL |
278 | for role, properties in images: |
279 | if properties is None: | |
280 | properties = {} | |
281 | name = properties.get('image_name', default_image_name(role)) | |
282 | encryption_format = properties.get('encryption_format', 'none') | |
7c673cae FG |
283 | (remote,) = ctx.cluster.only(role).remotes.keys() |
284 | ||
f67539c2 TL |
285 | if encryption_format == 'none': |
286 | device_path[role] = '/dev/rbd/rbd/{image}'.format(image=name) | |
287 | device_specific_args = [] | |
288 | else: | |
289 | remote.run( | |
290 | args=[ | |
291 | 'echo', | |
292 | ENCRYPTION_PASSPHRASE, | |
293 | run.Raw('>'), | |
294 | passphrase_file | |
295 | ] | |
296 | ) | |
297 | device_specific_args = [ | |
298 | '-t', 'nbd', '-o', | |
299 | 'encryption-format=%s,encryption-passphrase-file=%s' % ( | |
300 | encryption_format, passphrase_file)] | |
301 | ||
302 | map_fp = StringIO() | |
7c673cae FG |
303 | remote.run( |
304 | args=[ | |
305 | 'sudo', | |
306 | 'adjust-ulimits', | |
307 | 'ceph-coverage', | |
308 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
309 | 'rbd', | |
f67539c2 | 310 | '--id', role.rsplit('.')[-1], |
7c673cae FG |
311 | '-p', 'rbd', |
312 | 'map', | |
f67539c2 TL |
313 | name] + device_specific_args, |
314 | stdout=map_fp, | |
7c673cae | 315 | ) |
f67539c2 TL |
316 | |
317 | if encryption_format != 'none': | |
318 | device_path[role] = map_fp.getvalue().rstrip() | |
319 | properties['device_path'] = device_path[role] | |
320 | remote.run(args=['sudo', 'chmod', '666', device_path[role]]) | |
7c673cae FG |
321 | try: |
322 | yield | |
323 | finally: | |
324 | log.info('Unmapping rbd devices...') | |
f67539c2 TL |
325 | remote.run(args=['rm', '-f', passphrase_file]) |
326 | for role, properties in images: | |
327 | if not device_path.get(role): | |
328 | continue | |
329 | ||
330 | if properties is None: | |
331 | properties = {} | |
332 | encryption_format = properties.get('encryption_format', 'none') | |
7c673cae | 333 | (remote,) = ctx.cluster.only(role).remotes.keys() |
f67539c2 TL |
334 | |
335 | if encryption_format == 'none': | |
336 | device_specific_args = [] | |
337 | else: | |
338 | device_specific_args = ['-t', 'nbd'] | |
339 | ||
7c673cae FG |
340 | remote.run( |
341 | args=[ | |
342 | 'LD_LIBRARY_PATH={tdir}/binary/usr/local/lib'.format(tdir=testdir), | |
343 | 'sudo', | |
344 | 'adjust-ulimits', | |
345 | 'ceph-coverage', | |
346 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
347 | 'rbd', | |
348 | '-p', 'rbd', | |
349 | 'unmap', | |
f67539c2 TL |
350 | device_path[role], |
351 | ] + device_specific_args, | |
7c673cae FG |
352 | ) |
353 | ||
354 | ||
355 | def rbd_devname_rtn(ctx, image): | |
356 | return '/dev/rbd/rbd/{image}'.format(image=image) | |
357 | ||
358 | def canonical_path(ctx, role, path): | |
359 | """ | |
360 | Determine the canonical path for a given path on the host | |
361 | representing the given role. A canonical path contains no | |
362 | . or .. components, and includes no symbolic links. | |
363 | """ | |
f67539c2 | 364 | version_fp = StringIO() |
7c673cae FG |
365 | ctx.cluster.only(role).run( |
366 | args=[ 'readlink', '-f', path ], | |
367 | stdout=version_fp, | |
368 | ) | |
f67539c2 | 369 | canonical_path = version_fp.getvalue().rstrip('\n') |
7c673cae FG |
370 | version_fp.close() |
371 | return canonical_path | |
372 | ||
373 | @contextlib.contextmanager | |
374 | def run_xfstests(ctx, config): | |
375 | """ | |
376 | Run xfstests over specified devices. | |
377 | ||
378 | Warning: both the test and scratch devices specified will be | |
379 | overwritten. Normally xfstests modifies (but does not destroy) | |
380 | the test device, but for now the run script used here re-makes | |
381 | both filesystems. | |
382 | ||
383 | Note: Only one instance of xfstests can run on a single host at | |
384 | a time, although this is not enforced. | |
385 | ||
386 | This task in its current form needs some improvement. For | |
387 | example, it assumes all roles provided in the config are | |
388 | clients, and that the config provided is a list of key/value | |
389 | pairs. For now please use the xfstests() interface, below. | |
390 | ||
391 | For example:: | |
392 | ||
393 | tasks: | |
394 | - ceph: | |
395 | - rbd.run_xfstests: | |
396 | client.0: | |
397 | count: 2 | |
398 | test_dev: 'test_dev' | |
399 | scratch_dev: 'scratch_dev' | |
400 | fs_type: 'xfs' | |
401 | tests: 'generic/100 xfs/003 xfs/005 xfs/006 generic/015' | |
181888fb FG |
402 | exclude: |
403 | - generic/42 | |
7c673cae FG |
404 | randomize: true |
405 | """ | |
406 | with parallel() as p: | |
407 | for role, properties in config.items(): | |
408 | p.spawn(run_xfstests_one_client, ctx, role, properties) | |
f67539c2 | 409 | exc = None |
11fdf7f2 TL |
410 | while True: |
411 | try: | |
412 | p.next() | |
413 | except StopIteration: | |
414 | break | |
415 | except: | |
f67539c2 TL |
416 | exc = sys.exc_info()[1] |
417 | if exc is not None: | |
418 | raise exc | |
7c673cae FG |
419 | yield |
420 | ||
421 | def run_xfstests_one_client(ctx, role, properties): | |
422 | """ | |
423 | Spawned routine to handle xfs tests for a single client | |
424 | """ | |
425 | testdir = teuthology.get_testdir(ctx) | |
426 | try: | |
427 | count = properties.get('count') | |
428 | test_dev = properties.get('test_dev') | |
429 | assert test_dev is not None, \ | |
430 | "task run_xfstests requires test_dev to be defined" | |
431 | test_dev = canonical_path(ctx, role, test_dev) | |
432 | ||
433 | scratch_dev = properties.get('scratch_dev') | |
434 | assert scratch_dev is not None, \ | |
435 | "task run_xfstests requires scratch_dev to be defined" | |
436 | scratch_dev = canonical_path(ctx, role, scratch_dev) | |
437 | ||
438 | fs_type = properties.get('fs_type') | |
439 | tests = properties.get('tests') | |
181888fb | 440 | exclude_list = properties.get('exclude') |
7c673cae FG |
441 | randomize = properties.get('randomize') |
442 | ||
7c673cae FG |
443 | (remote,) = ctx.cluster.only(role).remotes.keys() |
444 | ||
445 | # Fetch the test script | |
446 | test_root = teuthology.get_testdir(ctx) | |
181888fb | 447 | test_script = 'run_xfstests.sh' |
7c673cae FG |
448 | test_path = os.path.join(test_root, test_script) |
449 | ||
450 | xfstests_url = properties.get('xfstests_url') | |
451 | assert xfstests_url is not None, \ | |
452 | "task run_xfstests requires xfstests_url to be defined" | |
453 | ||
454 | xfstests_krbd_url = xfstests_url + '/' + test_script | |
455 | ||
456 | log.info('Fetching {script} for {role} from {url}'.format( | |
457 | script=test_script, | |
458 | role=role, | |
459 | url=xfstests_krbd_url)) | |
460 | ||
461 | args = [ 'wget', '-O', test_path, '--', xfstests_krbd_url ] | |
462 | remote.run(args=args) | |
463 | ||
464 | log.info('Running xfstests on {role}:'.format(role=role)) | |
465 | log.info(' iteration count: {count}:'.format(count=count)) | |
466 | log.info(' test device: {dev}'.format(dev=test_dev)) | |
467 | log.info(' scratch device: {dev}'.format(dev=scratch_dev)) | |
468 | log.info(' using fs_type: {fs_type}'.format(fs_type=fs_type)) | |
469 | log.info(' tests to run: {tests}'.format(tests=tests)) | |
181888fb | 470 | log.info(' exclude list: {}'.format(' '.join(exclude_list))) |
7c673cae FG |
471 | log.info(' randomize: {randomize}'.format(randomize=randomize)) |
472 | ||
181888fb | 473 | if exclude_list: |
e306af50 | 474 | with tempfile.NamedTemporaryFile(mode='w', prefix='exclude') as exclude_file: |
181888fb FG |
475 | for test in exclude_list: |
476 | exclude_file.write("{}\n".format(test)) | |
e306af50 | 477 | exclude_file.flush() |
181888fb FG |
478 | remote.put_file(exclude_file.name, exclude_file.name) |
479 | ||
7c673cae FG |
480 | # Note that the device paths are interpreted using |
481 | # readlink -f <path> in order to get their canonical | |
482 | # pathname (so it matches what the kernel remembers). | |
483 | args = [ | |
484 | '/usr/bin/sudo', | |
485 | 'TESTDIR={tdir}'.format(tdir=testdir), | |
7c673cae FG |
486 | 'adjust-ulimits', |
487 | 'ceph-coverage', | |
488 | '{tdir}/archive/coverage'.format(tdir=testdir), | |
489 | '/bin/bash', | |
490 | test_path, | |
491 | '-c', str(count), | |
492 | '-f', fs_type, | |
493 | '-t', test_dev, | |
494 | '-s', scratch_dev, | |
495 | ] | |
181888fb FG |
496 | if exclude_list: |
497 | args.extend(['-x', exclude_file.name]) | |
7c673cae FG |
498 | if randomize: |
499 | args.append('-r') | |
500 | if tests: | |
501 | args.extend(['--', tests]) | |
502 | remote.run(args=args, logger=log.getChild(role)) | |
503 | finally: | |
504 | log.info('Removing {script} on {role}'.format(script=test_script, | |
505 | role=role)) | |
506 | remote.run(args=['rm', '-f', test_path]) | |
507 | ||
508 | @contextlib.contextmanager | |
509 | def xfstests(ctx, config): | |
510 | """ | |
511 | Run xfstests over rbd devices. This interface sets up all | |
512 | required configuration automatically if not otherwise specified. | |
513 | Note that only one instance of xfstests can run on a single host | |
514 | at a time. By default, the set of tests specified is run once. | |
515 | If a (non-zero) count value is supplied, the complete set of | |
516 | tests will be run that number of times. | |
517 | ||
518 | For example:: | |
519 | ||
520 | tasks: | |
521 | - ceph: | |
522 | # Image sizes are in MB | |
523 | - rbd.xfstests: | |
524 | client.0: | |
525 | count: 3 | |
526 | test_image: 'test_image' | |
527 | test_size: 250 | |
528 | test_format: 2 | |
529 | scratch_image: 'scratch_image' | |
530 | scratch_size: 250 | |
531 | scratch_format: 1 | |
532 | fs_type: 'xfs' | |
533 | tests: 'generic/100 xfs/003 xfs/005 xfs/006 generic/015' | |
181888fb FG |
534 | exclude: |
535 | - generic/42 | |
7c673cae | 536 | randomize: true |
f67539c2 | 537 | xfstests_url: 'https://raw.github.com/ceph/ceph-ci/wip-55555/qa' |
7c673cae FG |
538 | """ |
539 | if config is None: | |
540 | config = { 'all': None } | |
541 | assert isinstance(config, dict) or isinstance(config, list), \ | |
542 | "task xfstests only supports a list or dictionary for configuration" | |
543 | if isinstance(config, dict): | |
544 | config = teuthology.replace_all_with_clients(ctx.cluster, config) | |
545 | runs = config.items() | |
546 | else: | |
547 | runs = [(role, None) for role in config] | |
548 | ||
549 | running_xfstests = {} | |
550 | for role, properties in runs: | |
551 | assert role.startswith('client.'), \ | |
552 | "task xfstests can only run on client nodes" | |
553 | for host, roles_for_host in ctx.cluster.remotes.items(): | |
554 | if role in roles_for_host: | |
555 | assert host not in running_xfstests, \ | |
556 | "task xfstests allows only one instance at a time per host" | |
557 | running_xfstests[host] = True | |
558 | ||
559 | images_config = {} | |
560 | scratch_config = {} | |
561 | modprobe_config = {} | |
562 | image_map_config = {} | |
563 | scratch_map_config = {} | |
564 | xfstests_config = {} | |
565 | for role, properties in runs: | |
566 | if properties is None: | |
567 | properties = {} | |
568 | ||
569 | test_image = properties.get('test_image', 'test_image.{role}'.format(role=role)) | |
570 | test_size = properties.get('test_size', 10000) # 10G | |
571 | test_fmt = properties.get('test_format', 1) | |
572 | scratch_image = properties.get('scratch_image', 'scratch_image.{role}'.format(role=role)) | |
573 | scratch_size = properties.get('scratch_size', 10000) # 10G | |
574 | scratch_fmt = properties.get('scratch_format', 1) | |
575 | ||
576 | images_config[role] = dict( | |
577 | image_name=test_image, | |
578 | image_size=test_size, | |
579 | image_format=test_fmt, | |
580 | ) | |
581 | ||
582 | scratch_config[role] = dict( | |
583 | image_name=scratch_image, | |
584 | image_size=scratch_size, | |
585 | image_format=scratch_fmt, | |
586 | ) | |
587 | ||
588 | xfstests_branch = properties.get('xfstests_branch', 'master') | |
589 | xfstests_url = properties.get('xfstests_url', 'https://raw.github.com/ceph/ceph/{branch}/qa'.format(branch=xfstests_branch)) | |
590 | ||
591 | xfstests_config[role] = dict( | |
592 | count=properties.get('count', 1), | |
593 | test_dev='/dev/rbd/rbd/{image}'.format(image=test_image), | |
594 | scratch_dev='/dev/rbd/rbd/{image}'.format(image=scratch_image), | |
595 | fs_type=properties.get('fs_type', 'xfs'), | |
596 | randomize=properties.get('randomize', False), | |
597 | tests=properties.get('tests'), | |
181888fb | 598 | exclude=properties.get('exclude', []), |
7c673cae FG |
599 | xfstests_url=xfstests_url, |
600 | ) | |
601 | ||
602 | log.info('Setting up xfstests using RBD images:') | |
603 | log.info(' test ({size} MB): {image}'.format(size=test_size, | |
604 | image=test_image)) | |
605 | log.info(' scratch ({size} MB): {image}'.format(size=scratch_size, | |
606 | image=scratch_image)) | |
607 | modprobe_config[role] = None | |
f67539c2 TL |
608 | image_map_config[role] = {'image_name': test_image} |
609 | scratch_map_config[role] = {'image_name': scratch_image} | |
7c673cae FG |
610 | |
611 | with contextutil.nested( | |
612 | lambda: create_image(ctx=ctx, config=images_config), | |
613 | lambda: create_image(ctx=ctx, config=scratch_config), | |
614 | lambda: modprobe(ctx=ctx, config=modprobe_config), | |
615 | lambda: dev_create(ctx=ctx, config=image_map_config), | |
616 | lambda: dev_create(ctx=ctx, config=scratch_map_config), | |
617 | lambda: run_xfstests(ctx=ctx, config=xfstests_config), | |
618 | ): | |
619 | yield | |
620 | ||
621 | ||
622 | @contextlib.contextmanager | |
623 | def task(ctx, config): | |
624 | """ | |
625 | Create and mount an rbd image. | |
626 | ||
627 | For example, you can specify which clients to run on:: | |
628 | ||
629 | tasks: | |
630 | - ceph: | |
631 | - rbd: [client.0, client.1] | |
632 | ||
633 | There are a few image options:: | |
634 | ||
635 | tasks: | |
636 | - ceph: | |
637 | - rbd: | |
638 | client.0: # uses defaults | |
639 | client.1: | |
640 | image_name: foo | |
641 | image_size: 2048 | |
642 | image_format: 2 | |
643 | fs_type: xfs | |
644 | ||
645 | To use default options on all clients:: | |
646 | ||
647 | tasks: | |
648 | - ceph: | |
649 | - rbd: | |
650 | all: | |
651 | ||
652 | To create 20GiB images and format them with xfs on all clients:: | |
653 | ||
654 | tasks: | |
655 | - ceph: | |
656 | - rbd: | |
657 | all: | |
658 | image_size: 20480 | |
659 | fs_type: xfs | |
660 | """ | |
661 | if config is None: | |
662 | config = { 'all': None } | |
663 | norm_config = config | |
664 | if isinstance(config, dict): | |
665 | norm_config = teuthology.replace_all_with_clients(ctx.cluster, config) | |
666 | if isinstance(norm_config, dict): | |
667 | role_images = {} | |
9f95a23c | 668 | for role, properties in norm_config.items(): |
7c673cae FG |
669 | if properties is None: |
670 | properties = {} | |
671 | role_images[role] = properties.get('image_name') | |
672 | else: | |
673 | role_images = norm_config | |
674 | ||
675 | log.debug('rbd config is: %s', norm_config) | |
676 | ||
677 | with contextutil.nested( | |
678 | lambda: create_image(ctx=ctx, config=norm_config), | |
679 | lambda: modprobe(ctx=ctx, config=norm_config), | |
f67539c2 | 680 | lambda: dev_create(ctx=ctx, config=norm_config), |
7c673cae FG |
681 | lambda: generic_mkfs(ctx=ctx, config=norm_config, |
682 | devname_rtn=rbd_devname_rtn), | |
683 | lambda: generic_mount(ctx=ctx, config=role_images, | |
684 | devname_rtn=rbd_devname_rtn), | |
685 | ): | |
686 | yield |