]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/qemu.py
aac51d5f54027ea284d2ccd83fbe62b2cd389bfb
4 from cStringIO
import StringIO
11 from teuthology
import misc
as teuthology
12 from teuthology
import contextutil
14 from teuthology
.orchestra
import run
15 from teuthology
.config
import config
as teuth_config
17 log
= logging
.getLogger(__name__
)
20 DEFAULT_IMAGE_URL
= 'http://download.ceph.com/qa/ubuntu-12.04.qcow2'
21 DEFAULT_IMAGE_SIZE
= 10240 # in megabytes
23 DEFAULT_MEM
= 4096 # in megabytes
25 def create_images(ctx
, config
, managers
):
26 for client
, client_config
in config
.iteritems():
27 disks
= client_config
.get('disks', DEFAULT_NUM_DISKS
)
28 if not isinstance(disks
, list):
29 disks
= [{} for n
in range(int(disks
))]
30 clone
= client_config
.get('clone', False)
31 assert disks
, 'at least one rbd device must be used'
32 for i
, disk
in enumerate(disks
[1:]):
35 'image_name': '{client}.{num}'.format(client
=client
,
37 'image_format': 2 if clone
else 1,
38 'image_size': (disk
or {}).get('image_size',
43 lambda create_config
=create_config
:
44 rbd
.create_image(ctx
=ctx
, config
=create_config
)
47 def create_clones(ctx
, config
, managers
):
48 for client
, client_config
in config
.iteritems():
49 clone
= client_config
.get('clone', False)
51 num_disks
= client_config
.get('disks', DEFAULT_NUM_DISKS
)
52 if isinstance(num_disks
, list):
53 num_disks
= len(num_disks
)
54 for i
in xrange(num_disks
):
58 '{client}.{num}-clone'.format(client
=client
, num
=i
),
60 '{client}.{num}'.format(client
=client
, num
=i
),
64 lambda create_config
=create_config
:
65 rbd
.clone_image(ctx
=ctx
, config
=create_config
)
68 @contextlib.contextmanager
69 def create_dirs(ctx
, config
):
71 Handle directory creation and cleanup
73 testdir
= teuthology
.get_testdir(ctx
)
74 for client
, client_config
in config
.iteritems():
75 assert 'test' in client_config
, 'You must specify a test to run'
76 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
79 'install', '-d', '-m0755', '--',
80 '{tdir}/qemu'.format(tdir
=testdir
),
81 '{tdir}/archive/qemu'.format(tdir
=testdir
),
87 for client
, client_config
in config
.iteritems():
88 assert 'test' in client_config
, 'You must specify a test to run'
89 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
92 'rmdir', '{tdir}/qemu'.format(tdir
=testdir
), run
.Raw('||'), 'true',
96 @contextlib.contextmanager
97 def generate_iso(ctx
, config
):
98 """Execute system commands to generate iso"""
99 log
.info('generating iso...')
100 testdir
= teuthology
.get_testdir(ctx
)
102 # use ctx.config instead of config, because config has been
103 # through teuthology.replace_all_with_clients()
104 refspec
= ctx
.config
.get('branch')
106 refspec
= ctx
.config
.get('tag')
108 refspec
= ctx
.config
.get('sha1')
112 # hack: the git_url is always ceph-ci or ceph
113 git_url
= teuth_config
.get_ceph_git_url()
114 repo_name
= 'ceph.git'
115 if git_url
.count('ceph-ci'):
116 repo_name
= 'ceph-ci.git'
118 for client
, client_config
in config
.iteritems():
119 assert 'test' in client_config
, 'You must specify a test to run'
120 test_url
= client_config
['test'].format(repo
=repo_name
, branch
=refspec
)
121 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
122 src_dir
= os
.path
.dirname(__file__
)
123 userdata_path
= os
.path
.join(testdir
, 'qemu', 'userdata.' + client
)
124 metadata_path
= os
.path
.join(testdir
, 'qemu', 'metadata.' + client
)
126 with
file(os
.path
.join(src_dir
, 'userdata_setup.yaml'), 'rb') as f
:
127 test_setup
= ''.join(f
.readlines())
128 # configuring the commands to setup the nfs mount
129 mnt_dir
= "/export/{client}".format(client
=client
)
130 test_setup
= test_setup
.format(
134 with
file(os
.path
.join(src_dir
, 'userdata_teardown.yaml'), 'rb') as f
:
135 test_teardown
= ''.join(f
.readlines())
137 user_data
= test_setup
138 if client_config
.get('type', 'filesystem') == 'filesystem':
139 num_disks
= client_config
.get('disks', DEFAULT_NUM_DISKS
)
140 if isinstance(num_disks
, list):
141 num_disks
= len(num_disks
)
142 for i
in xrange(1, num_disks
):
143 dev_letter
= chr(ord('a') + i
)
147 mkdir /mnt/test_{dev_letter}
148 mkfs -t xfs /dev/vd{dev_letter}
149 mount -t xfs /dev/vd{dev_letter} /mnt/test_{dev_letter}
150 """.format(dev_letter
=dev_letter
)
155 test -d /etc/ceph || mkdir /etc/ceph
156 cp /mnt/cdrom/ceph.* /etc/ceph/
159 cloud_config_archive
= client_config
.get('cloud_config_archive', [])
160 if cloud_config_archive
:
161 user_data
+= yaml
.safe_dump(cloud_config_archive
, default_style
='|',
162 default_flow_style
=False)
164 # this may change later to pass the directories as args to the
165 # script or something. xfstests needs that.
169 test -d /mnt/test_b && cd /mnt/test_b
170 /mnt/cdrom/test.sh > /mnt/log/test.log 2>&1 && touch /mnt/log/success
173 teuthology
.write_file(remote
, userdata_path
, StringIO(user_data
))
175 with
file(os
.path
.join(src_dir
, 'metadata.yaml'), 'rb') as f
:
176 teuthology
.write_file(remote
, metadata_path
, f
)
178 test_file
= '{tdir}/qemu/{client}.test.sh'.format(tdir
=testdir
, client
=client
)
180 log
.info('fetching test %s for %s', test_url
, client
)
183 'wget', '-nv', '-O', test_file
,
186 'chmod', '755', test_file
,
191 'genisoimage', '-quiet', '-input-charset', 'utf-8',
192 '-volid', 'cidata', '-joliet', '-rock',
193 '-o', '{tdir}/qemu/{client}.iso'.format(tdir
=testdir
, client
=client
),
195 'user-data={userdata}'.format(userdata
=userdata_path
),
196 'meta-data={metadata}'.format(metadata
=metadata_path
),
197 'ceph.conf=/etc/ceph/ceph.conf',
198 'ceph.keyring=/etc/ceph/ceph.keyring',
199 'test.sh={file}'.format(file=test_file
),
205 for client
in config
.iterkeys():
206 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
210 '{tdir}/qemu/{client}.iso'.format(tdir
=testdir
, client
=client
),
211 os
.path
.join(testdir
, 'qemu', 'userdata.' + client
),
212 os
.path
.join(testdir
, 'qemu', 'metadata.' + client
),
213 '{tdir}/qemu/{client}.test.sh'.format(tdir
=testdir
, client
=client
),
217 @contextlib.contextmanager
218 def download_image(ctx
, config
):
219 """Downland base image, remove image file when done"""
220 log
.info('downloading base image')
221 testdir
= teuthology
.get_testdir(ctx
)
222 for client
, client_config
in config
.iteritems():
223 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
224 base_file
= '{tdir}/qemu/base.{client}.qcow2'.format(tdir
=testdir
, client
=client
)
225 image_url
= client_config
.get('image_url', DEFAULT_IMAGE_URL
)
228 'wget', '-nv', '-O', base_file
, image_url
,
232 disks
= client_config
.get('disks', None)
233 if not isinstance(disks
, list):
235 image_name
= '{client}.0'.format(client
=client
)
236 image_size
= (disks
[0] or {}).get('image_size', DEFAULT_IMAGE_SIZE
)
239 'qemu-img', 'convert', '-f', 'qcow2', '-O', 'raw',
240 base_file
, 'rbd:rbd/{image_name}'.format(image_name
=image_name
)
246 '--size={image_size}M'.format(image_size
=image_size
),
253 log
.debug('cleaning up base image files')
254 for client
in config
.iterkeys():
255 base_file
= '{tdir}/qemu/base.{client}.qcow2'.format(
259 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
262 'rm', '-f', base_file
,
267 def _setup_nfs_mount(remote
, client
, mount_dir
):
269 Sets up an nfs mount on the remote that the guest can use to
270 store logs. This nfs mount is also used to touch a file
271 at the end of the test to indiciate if the test was successful
274 export_dir
= "/export/{client}".format(client
=client
)
275 log
.info("Creating the nfs export directory...")
277 'sudo', 'mkdir', '-p', export_dir
,
279 log
.info("Mounting the test directory...")
281 'sudo', 'mount', '--bind', mount_dir
, export_dir
,
283 log
.info("Adding mount to /etc/exports...")
284 export
= "{dir} *(rw,no_root_squash,no_subtree_check,insecure)".format(
288 'sudo', 'sed', '-i', '/^\/export\//d', "/etc/exports",
291 'echo', export
, run
.Raw("|"),
292 'sudo', 'tee', '-a', "/etc/exports",
294 log
.info("Restarting NFS...")
295 if remote
.os
.package_type
== "deb":
296 remote
.run(args
=['sudo', 'service', 'nfs-kernel-server', 'restart'])
298 remote
.run(args
=['sudo', 'systemctl', 'restart', 'nfs'])
301 def _teardown_nfs_mount(remote
, client
):
303 Tears down the nfs mount on the remote used for logging and reporting the
304 status of the tests being ran in the guest.
306 log
.info("Tearing down the nfs mount for {remote}".format(remote
=remote
))
307 export_dir
= "/export/{client}".format(client
=client
)
308 log
.info("Stopping NFS...")
309 if remote
.os
.package_type
== "deb":
311 'sudo', 'service', 'nfs-kernel-server', 'stop'
315 'sudo', 'systemctl', 'stop', 'nfs'
317 log
.info("Unmounting exported directory...")
319 'sudo', 'umount', export_dir
321 log
.info("Deleting exported directory...")
323 'sudo', 'rm', '-r', '/export'
325 log
.info("Deleting export from /etc/exports...")
327 'sudo', 'sed', '-i', '$ d', '/etc/exports'
329 log
.info("Starting NFS...")
330 if remote
.os
.package_type
== "deb":
332 'sudo', 'service', 'nfs-kernel-server', 'start'
336 'sudo', 'systemctl', 'start', 'nfs'
340 @contextlib.contextmanager
341 def run_qemu(ctx
, config
):
342 """Setup kvm environment and start qemu"""
344 testdir
= teuthology
.get_testdir(ctx
)
345 for client
, client_config
in config
.iteritems():
346 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
347 log_dir
= '{tdir}/archive/qemu/{client}'.format(tdir
=testdir
, client
=client
)
350 'mkdir', log_dir
, run
.Raw('&&'),
351 'sudo', 'modprobe', 'kvm',
355 # make an nfs mount to use for logging and to
356 # allow to test to tell teuthology the tests outcome
357 _setup_nfs_mount(remote
, client
, log_dir
)
359 # Hack to make sure /dev/kvm permissions are set correctly
360 # See http://tracker.ceph.com/issues/17977 and
361 # https://bugzilla.redhat.com/show_bug.cgi?id=1333159
362 remote
.run(args
='sudo udevadm control --reload')
363 remote
.run(args
='sudo udevadm trigger /dev/kvm')
364 remote
.run(args
='ls -l /dev/kvm')
366 qemu_cmd
= 'qemu-system-x86_64'
367 if remote
.os
.package_type
== "rpm":
368 qemu_cmd
= "/usr/libexec/qemu-kvm"
372 '{tdir}/archive/coverage'.format(tdir
=testdir
),
375 qemu_cmd
, '-enable-kvm', '-nographic', '-cpu', 'host',
376 '-smp', str(client_config
.get('cpus', DEFAULT_CPUS
)),
377 '-m', str(client_config
.get('memory', DEFAULT_MEM
)),
378 # cd holding metadata for cloud-init
379 '-cdrom', '{tdir}/qemu/{client}.iso'.format(tdir
=testdir
, client
=client
),
383 ceph_config
= ctx
.ceph
['ceph'].conf
.get('global', {})
384 ceph_config
.update(ctx
.ceph
['ceph'].conf
.get('client', {}))
385 ceph_config
.update(ctx
.ceph
['ceph'].conf
.get(client
, {}))
386 if ceph_config
.get('rbd cache'):
387 if ceph_config
.get('rbd cache max dirty', 1) > 0:
388 cachemode
= 'writeback'
390 cachemode
= 'writethrough'
392 clone
= client_config
.get('clone', False)
393 num_disks
= client_config
.get('disks', DEFAULT_NUM_DISKS
)
394 if isinstance(num_disks
, list):
395 num_disks
= len(num_disks
)
396 for i
in xrange(num_disks
):
397 suffix
= '-clone' if clone
else ''
400 'file=rbd:rbd/{img}:id={id},format=raw,if=virtio,cache={cachemode}'.format(
401 img
='{client}.{num}{suffix}'.format(client
=client
, num
=i
,
403 id=client
[len('client.'):],
408 log
.info('starting qemu...')
412 logger
=log
.getChild(client
),
421 log
.info('waiting for qemu tests to finish...')
424 log
.debug('checking that qemu tests succeeded...')
425 for client
in config
.iterkeys():
426 (remote
,) = ctx
.cluster
.only(client
).remotes
.keys()
428 _teardown_nfs_mount(remote
, client
)
429 # check for test status
433 '{tdir}/archive/qemu/{client}/success'.format(
441 @contextlib.contextmanager
442 def task(ctx
, config
):
444 Run a test inside of QEMU on top of rbd. Only one test
445 is supported per client.
447 For example, you can specify which clients to run on::
453 test: http://download.ceph.com/qa/test.sh
455 test: http://download.ceph.com/qa/test2.sh
457 Or use the same settings on all clients:
463 test: http://download.ceph.com/qa/test.sh
465 For tests that don't need a filesystem, set type to block::
471 test: http://download.ceph.com/qa/test.sh
474 The test should be configured to run on /dev/vdb and later
477 If you want to run a test that uses more than one rbd image,
478 specify how many images to use::
484 test: http://download.ceph.com/qa/test.sh
494 test: http://ceph.com/qa/test.sh
500 You can set the amount of CPUs and memory the VM has (default is 1 CPU and
507 test: http://download.ceph.com/qa/test.sh
509 memory: 512 # megabytes
511 If you want to run a test against a cloned rbd image, set clone to true::
517 test: http://download.ceph.com/qa/test.sh
520 If you need to configure additional cloud-config options, set cloud_config
521 to the required data set::
527 test: http://ceph.com/qa/test.sh
528 cloud_config_archive:
537 If you need to override the default cloud image, set image_url:
543 test: http://ceph.com/qa/test.sh
544 image_url: https://cloud-images.ubuntu.com/releases/16.04/release/ubuntu-16.04-server-cloudimg-amd64-disk1.img
546 assert isinstance(config
, dict), \
547 "task qemu only supports a dictionary for configuration"
549 config
= teuthology
.replace_all_with_clients(ctx
.cluster
, config
)
552 create_images(ctx
=ctx
, config
=config
, managers
=managers
)
554 lambda: create_dirs(ctx
=ctx
, config
=config
),
555 lambda: generate_iso(ctx
=ctx
, config
=config
),
556 lambda: download_image(ctx
=ctx
, config
=config
),
558 create_clones(ctx
=ctx
, config
=config
, managers
=managers
)
560 lambda: run_qemu(ctx
=ctx
, config
=config
),
563 with contextutil
.nested(*managers
):