]> Git Repo - qemu.git/blame - tests/qemu-iotests/iotests.py
iotests: improve pause_job
[qemu.git] / tests / qemu-iotests / iotests.py
CommitLineData
f345cfd0
SH
1# Common utilities and Python wrappers for qemu-iotests
2#
3# Copyright (C) 2012 IBM Corp.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18
c1c71e49 19import errno
f345cfd0
SH
20import os
21import re
22import subprocess
4f450568 23import string
f345cfd0 24import unittest
ed338bb0 25import sys
2499a096 26import struct
74f69050 27import json
2c93c5cb 28import signal
43851b5b 29import logging
ef6e9228 30import atexit
f345cfd0 31
02f3a911
VSO
32sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
33import qtest
34
f345cfd0 35
934659c4 36# This will not work if arguments contain spaces but is necessary if we
f345cfd0 37# want to support the override options that ./check supports.
934659c4
HR
38qemu_img_args = [os.environ.get('QEMU_IMG_PROG', 'qemu-img')]
39if os.environ.get('QEMU_IMG_OPTIONS'):
40 qemu_img_args += os.environ['QEMU_IMG_OPTIONS'].strip().split(' ')
41
42qemu_io_args = [os.environ.get('QEMU_IO_PROG', 'qemu-io')]
43if os.environ.get('QEMU_IO_OPTIONS'):
44 qemu_io_args += os.environ['QEMU_IO_OPTIONS'].strip().split(' ')
45
bec87774
HR
46qemu_nbd_args = [os.environ.get('QEMU_NBD_PROG', 'qemu-nbd')]
47if os.environ.get('QEMU_NBD_OPTIONS'):
48 qemu_nbd_args += os.environ['QEMU_NBD_OPTIONS'].strip().split(' ')
49
4c44b4a4 50qemu_prog = os.environ.get('QEMU_PROG', 'qemu')
66613974 51qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ')
f345cfd0
SH
52
53imgfmt = os.environ.get('IMGFMT', 'raw')
54imgproto = os.environ.get('IMGPROTO', 'file')
5a8fabf3 55test_dir = os.environ.get('TEST_DIR')
e8f8624d 56output_dir = os.environ.get('OUTPUT_DIR', '.')
58cc2ae1 57cachemode = os.environ.get('CACHEMODE')
e166b414 58qemu_default_machine = os.environ.get('QEMU_DEFAULT_MACHINE')
f345cfd0 59
30b005d9 60socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
c0088d79 61debug = False
30b005d9 62
85a353a0
VSO
63luks_default_secret_object = 'secret,id=keysec0,data=' + \
64 os.environ['IMGKEYSECRET']
65luks_default_key_secret_opt = 'key-secret=keysec0'
66
67
f345cfd0
SH
68def qemu_img(*args):
69 '''Run qemu-img and return the exit code'''
70 devnull = open('/dev/null', 'r+')
2ef6093c
HR
71 exitcode = subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull)
72 if exitcode < 0:
73 sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
74 return exitcode
f345cfd0 75
85a353a0
VSO
76def qemu_img_create(*args):
77 args = list(args)
78
79 # default luks support
80 if '-f' in args and args[args.index('-f') + 1] == 'luks':
81 if '-o' in args:
82 i = args.index('-o')
83 if 'key-secret' not in args[i + 1]:
84 args[i + 1].append(luks_default_key_secret_opt)
85 args.insert(i + 2, '--object')
86 args.insert(i + 3, luks_default_secret_object)
87 else:
88 args = ['-o', luks_default_key_secret_opt,
89 '--object', luks_default_secret_object] + args
90
91 args.insert(0, 'create')
92
93 return qemu_img(*args)
94
d2ef210c 95def qemu_img_verbose(*args):
993d46ce 96 '''Run qemu-img without suppressing its output and return the exit code'''
2ef6093c
HR
97 exitcode = subprocess.call(qemu_img_args + list(args))
98 if exitcode < 0:
99 sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
100 return exitcode
d2ef210c 101
3677e6f6
HR
102def qemu_img_pipe(*args):
103 '''Run qemu-img and return its output'''
491e5e85
DB
104 subp = subprocess.Popen(qemu_img_args + list(args),
105 stdout=subprocess.PIPE,
106 stderr=subprocess.STDOUT)
2ef6093c
HR
107 exitcode = subp.wait()
108 if exitcode < 0:
109 sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
110 return subp.communicate()[0]
3677e6f6 111
5ba141dc
KW
112def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
113 args = [ 'info' ]
114 if imgopts:
115 args.append('--image-opts')
116 else:
117 args += [ '-f', imgfmt ]
118 args += extra_args
119 args.append(filename)
120
121 output = qemu_img_pipe(*args)
6b605ade
KW
122 if not filter_path:
123 filter_path = filename
124 log(filter_img_info(output, filter_path))
125
f345cfd0
SH
126def qemu_io(*args):
127 '''Run qemu-io and return the stdout data'''
128 args = qemu_io_args + list(args)
491e5e85
DB
129 subp = subprocess.Popen(args, stdout=subprocess.PIPE,
130 stderr=subprocess.STDOUT)
2ef6093c
HR
131 exitcode = subp.wait()
132 if exitcode < 0:
133 sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
134 return subp.communicate()[0]
f345cfd0 135
745f2bf4
HR
136def qemu_io_silent(*args):
137 '''Run qemu-io and return the exit code, suppressing stdout'''
138 args = qemu_io_args + list(args)
139 exitcode = subprocess.call(args, stdout=open('/dev/null', 'w'))
140 if exitcode < 0:
141 sys.stderr.write('qemu-io received signal %i: %s\n' %
142 (-exitcode, ' '.join(args)))
143 return exitcode
144
9fa90eec
VSO
145
146class QemuIoInteractive:
147 def __init__(self, *args):
148 self.args = qemu_io_args + list(args)
149 self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE,
150 stdout=subprocess.PIPE,
151 stderr=subprocess.STDOUT)
152 assert self._p.stdout.read(9) == 'qemu-io> '
153
154 def close(self):
155 self._p.communicate('q\n')
156
157 def _read_output(self):
158 pattern = 'qemu-io> '
159 n = len(pattern)
160 pos = 0
161 s = []
162 while pos != n:
163 c = self._p.stdout.read(1)
164 # check unexpected EOF
165 assert c != ''
166 s.append(c)
167 if c == pattern[pos]:
168 pos += 1
169 else:
170 pos = 0
171
172 return ''.join(s[:-n])
173
174 def cmd(self, cmd):
175 # quit command is in close(), '\n' is added automatically
176 assert '\n' not in cmd
177 cmd = cmd.strip()
178 assert cmd != 'q' and cmd != 'quit'
179 self._p.stdin.write(cmd + '\n')
180 return self._read_output()
181
182
bec87774
HR
183def qemu_nbd(*args):
184 '''Run qemu-nbd in daemon mode and return the parent's exit code'''
185 return subprocess.call(qemu_nbd_args + ['--fork'] + list(args))
186
e1b5c51f 187def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt):
3a3918c3 188 '''Return True if two image files are identical'''
e1b5c51f
PB
189 return qemu_img('compare', '-f', fmt1,
190 '-F', fmt2, img1, img2) == 0
3a3918c3 191
2499a096
SH
192def create_image(name, size):
193 '''Create a fully-allocated raw image with sector markers'''
194 file = open(name, 'w')
195 i = 0
196 while i < size:
197 sector = struct.pack('>l504xl', i / 512, i / 512)
198 file.write(sector)
199 i = i + 512
200 file.close()
201
74f69050
FZ
202def image_size(img):
203 '''Return image's virtual size'''
204 r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img)
205 return json.loads(r)['virtual-size']
206
a2d1c8fd
DB
207test_dir_re = re.compile(r"%s" % test_dir)
208def filter_test_dir(msg):
209 return test_dir_re.sub("TEST_DIR", msg)
210
211win32_re = re.compile(r"\r")
212def filter_win32(msg):
213 return win32_re.sub("", msg)
214
215qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)")
216def filter_qemu_io(msg):
217 msg = filter_win32(msg)
218 return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg)
219
220chown_re = re.compile(r"chown [0-9]+:[0-9]+")
221def filter_chown(msg):
222 return chown_re.sub("chown UID:GID", msg)
223
12314f2d
SH
224def filter_qmp_event(event):
225 '''Filter a QMP event dict'''
226 event = dict(event)
227 if 'timestamp' in event:
228 event['timestamp']['seconds'] = 'SECS'
229 event['timestamp']['microseconds'] = 'USECS'
230 return event
231
e234398a
KW
232def filter_testfiles(msg):
233 prefix = os.path.join(test_dir, "%s-" % (os.getpid()))
234 return msg.replace(prefix, 'TEST_DIR/PID-')
235
6b605ade
KW
236def filter_img_info(output, filename):
237 lines = []
238 for line in output.split('\n'):
239 if 'disk size' in line or 'actual-size' in line:
240 continue
241 line = line.replace(filename, 'TEST_IMG') \
242 .replace(imgfmt, 'IMGFMT')
243 line = re.sub('iters: [0-9]+', 'iters: XXX', line)
244 line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
245 lines.append(line)
246 return '\n'.join(lines)
247
a2d1c8fd
DB
248def log(msg, filters=[]):
249 for flt in filters:
250 msg = flt(msg)
251 print msg
252
2c93c5cb
KW
253class Timeout:
254 def __init__(self, seconds, errmsg = "Timeout"):
255 self.seconds = seconds
256 self.errmsg = errmsg
257 def __enter__(self):
258 signal.signal(signal.SIGALRM, self.timeout)
259 signal.setitimer(signal.ITIMER_REAL, self.seconds)
260 return self
261 def __exit__(self, type, value, traceback):
262 signal.setitimer(signal.ITIMER_REAL, 0)
263 return False
264 def timeout(self, signum, frame):
265 raise Exception(self.errmsg)
266
f4844ac0
SH
267
268class FilePath(object):
269 '''An auto-generated filename that cleans itself up.
270
271 Use this context manager to generate filenames and ensure that the file
272 gets deleted::
273
274 with TestFilePath('test.img') as img_path:
275 qemu_img('create', img_path, '1G')
276 # migration_sock_path is automatically deleted
277 '''
278 def __init__(self, name):
279 filename = '{0}-{1}'.format(os.getpid(), name)
280 self.path = os.path.join(test_dir, filename)
281
282 def __enter__(self):
283 return self.path
284
285 def __exit__(self, exc_type, exc_val, exc_tb):
286 try:
287 os.remove(self.path)
288 except OSError:
289 pass
290 return False
291
292
ef6e9228
VSO
293def file_path_remover():
294 for path in reversed(file_path_remover.paths):
295 try:
296 os.remove(path)
297 except OSError:
298 pass
299
300
301def file_path(*names):
302 ''' Another way to get auto-generated filename that cleans itself up.
303
304 Use is as simple as:
305
306 img_a, img_b = file_path('a.img', 'b.img')
307 sock = file_path('socket')
308 '''
309
310 if not hasattr(file_path_remover, 'paths'):
311 file_path_remover.paths = []
312 atexit.register(file_path_remover)
313
314 paths = []
315 for name in names:
316 filename = '{0}-{1}'.format(os.getpid(), name)
317 path = os.path.join(test_dir, filename)
318 file_path_remover.paths.append(path)
319 paths.append(path)
320
321 return paths[0] if len(paths) == 1 else paths
322
5a259e86
KW
323def remote_filename(path):
324 if imgproto == 'file':
325 return path
326 elif imgproto == 'ssh':
327 return "ssh://127.0.0.1%s" % (path)
328 else:
329 raise Exception("Protocol %s not supported" % (imgproto))
ef6e9228 330
4c44b4a4 331class VM(qtest.QEMUQtestMachine):
f345cfd0
SH
332 '''A QEMU VM'''
333
5fcbdf50
HR
334 def __init__(self, path_suffix=''):
335 name = "qemu%s-%d" % (path_suffix, os.getpid())
336 super(VM, self).__init__(qemu_prog, qemu_opts, name=name,
337 test_dir=test_dir,
4c44b4a4 338 socket_scm_helper=socket_scm_helper)
f345cfd0 339 self._num_drives = 0
30b005d9 340
ccc15f7d
SH
341 def add_object(self, opts):
342 self._args.append('-object')
343 self._args.append(opts)
344 return self
345
486b88bd
KW
346 def add_device(self, opts):
347 self._args.append('-device')
348 self._args.append(opts)
349 return self
350
78b666f4
FZ
351 def add_drive_raw(self, opts):
352 self._args.append('-drive')
353 self._args.append(opts)
354 return self
355
e1b5c51f 356 def add_drive(self, path, opts='', interface='virtio', format=imgfmt):
f345cfd0 357 '''Add a virtio-blk drive to the VM'''
8e492253 358 options = ['if=%s' % interface,
f345cfd0 359 'id=drive%d' % self._num_drives]
8e492253
HR
360
361 if path is not None:
362 options.append('file=%s' % path)
e1b5c51f 363 options.append('format=%s' % format)
fc17c259 364 options.append('cache=%s' % cachemode)
8e492253 365
f345cfd0
SH
366 if opts:
367 options.append(opts)
368
85a353a0
VSO
369 if format == 'luks' and 'key-secret' not in opts:
370 # default luks support
371 if luks_default_secret_object not in self._args:
372 self.add_object(luks_default_secret_object)
373
374 options.append(luks_default_key_secret_opt)
375
f345cfd0
SH
376 self._args.append('-drive')
377 self._args.append(','.join(options))
378 self._num_drives += 1
379 return self
380
5694923a
HR
381 def add_blockdev(self, opts):
382 self._args.append('-blockdev')
383 if isinstance(opts, str):
384 self._args.append(opts)
385 else:
386 self._args.append(','.join(opts))
387 return self
388
12314f2d
SH
389 def add_incoming(self, addr):
390 self._args.append('-incoming')
391 self._args.append(addr)
392 return self
393
3cf53c77
FZ
394 def pause_drive(self, drive, event=None):
395 '''Pause drive r/w operations'''
396 if not event:
397 self.pause_drive(drive, "read_aio")
398 self.pause_drive(drive, "write_aio")
399 return
400 self.qmp('human-monitor-command',
401 command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
402
403 def resume_drive(self, drive):
404 self.qmp('human-monitor-command',
405 command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
406
e3409362
IM
407 def hmp_qemu_io(self, drive, cmd):
408 '''Write to a given drive using an HMP command'''
409 return self.qmp('human-monitor-command',
410 command_line='qemu-io %s "%s"' % (drive, cmd))
411
62a94288
KW
412 def flatten_qmp_object(self, obj, output=None, basestr=''):
413 if output is None:
414 output = dict()
415 if isinstance(obj, list):
416 for i in range(len(obj)):
417 self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
418 elif isinstance(obj, dict):
419 for key in obj:
420 self.flatten_qmp_object(obj[key], output, basestr + key + '.')
421 else:
422 output[basestr[:-1]] = obj # Strip trailing '.'
423 return output
424
425 def qmp_to_opts(self, obj):
426 obj = self.flatten_qmp_object(obj)
427 output_list = list()
428 for key in obj:
429 output_list += [key + '=' + obj[key]]
430 return ','.join(output_list)
431
5ad1dbf7
KW
432 def get_qmp_events_filtered(self, wait=True):
433 result = []
434 for ev in self.get_qmp_events(wait=wait):
435 result.append(filter_qmp_event(ev))
436 return result
62a94288 437
e234398a
KW
438 def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs):
439 logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs)
440 log(logmsg, filters)
441 result = self.qmp(cmd, **kwargs)
442 log(str(result), filters)
443 return result
444
fc47d851
KW
445 def run_job(self, job, auto_finalize=True, auto_dismiss=False):
446 while True:
447 for ev in self.get_qmp_events_filtered(wait=True):
448 if ev['event'] == 'JOB_STATUS_CHANGE':
449 status = ev['data']['status']
450 if status == 'aborting':
451 result = self.qmp('query-jobs')
452 for j in result['return']:
453 if j['id'] == job:
454 log('Job failed: %s' % (j['error']))
455 elif status == 'pending' and not auto_finalize:
456 self.qmp_log('job-finalize', id=job)
457 elif status == 'concluded' and not auto_dismiss:
458 self.qmp_log('job-dismiss', id=job)
459 elif status == 'null':
460 return
461 else:
462 iotests.log(ev)
463
7898f74e 464
f345cfd0
SH
465index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
466
467class QMPTestCase(unittest.TestCase):
468 '''Abstract base class for QMP test cases'''
469
470 def dictpath(self, d, path):
471 '''Traverse a path in a nested dict'''
472 for component in path.split('/'):
473 m = index_re.match(component)
474 if m:
475 component, idx = m.groups()
476 idx = int(idx)
477
478 if not isinstance(d, dict) or component not in d:
479 self.fail('failed path traversal for "%s" in "%s"' % (path, str(d)))
480 d = d[component]
481
482 if m:
483 if not isinstance(d, list):
484 self.fail('path component "%s" in "%s" is not a list in "%s"' % (component, path, str(d)))
485 try:
486 d = d[idx]
487 except IndexError:
488 self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
489 return d
490
90f0b711
PB
491 def assert_qmp_absent(self, d, path):
492 try:
493 result = self.dictpath(d, path)
494 except AssertionError:
495 return
496 self.fail('path "%s" has value "%s"' % (path, str(result)))
497
f345cfd0
SH
498 def assert_qmp(self, d, path, value):
499 '''Assert that the value for a specific path in a QMP dict matches'''
500 result = self.dictpath(d, path)
501 self.assertEqual(result, value, 'values not equal "%s" and "%s"' % (str(result), str(value)))
502
ecc1c88e
SH
503 def assert_no_active_block_jobs(self):
504 result = self.vm.qmp('query-block-jobs')
505 self.assert_qmp(result, 'return', [])
506
e71fc0ba
FZ
507 def assert_has_block_node(self, node_name=None, file_name=None):
508 """Issue a query-named-block-nodes and assert node_name and/or
509 file_name is present in the result"""
510 def check_equal_or_none(a, b):
511 return a == None or b == None or a == b
512 assert node_name or file_name
513 result = self.vm.qmp('query-named-block-nodes')
514 for x in result["return"]:
515 if check_equal_or_none(x.get("node-name"), node_name) and \
516 check_equal_or_none(x.get("file"), file_name):
517 return
518 self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \
519 (node_name, file_name, result))
520
e07375f5
HR
521 def assert_json_filename_equal(self, json_filename, reference):
522 '''Asserts that the given filename is a json: filename and that its
523 content is equal to the given reference object'''
524 self.assertEqual(json_filename[:5], 'json:')
62a94288
KW
525 self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
526 self.vm.flatten_qmp_object(reference))
e07375f5 527
3cf53c77 528 def cancel_and_wait(self, drive='drive0', force=False, resume=False):
2575fe16
SH
529 '''Cancel a block job and wait for it to finish, returning the event'''
530 result = self.vm.qmp('block-job-cancel', device=drive, force=force)
531 self.assert_qmp(result, 'return', {})
532
3cf53c77
FZ
533 if resume:
534 self.vm.resume_drive(drive)
535
2575fe16
SH
536 cancelled = False
537 result = None
538 while not cancelled:
539 for event in self.vm.get_qmp_events(wait=True):
540 if event['event'] == 'BLOCK_JOB_COMPLETED' or \
541 event['event'] == 'BLOCK_JOB_CANCELLED':
542 self.assert_qmp(event, 'data/device', drive)
543 result = event
544 cancelled = True
1dac83f1
KW
545 elif event['event'] == 'JOB_STATUS_CHANGE':
546 self.assert_qmp(event, 'data/id', drive)
547
2575fe16
SH
548
549 self.assert_no_active_block_jobs()
550 return result
551
9974ad40 552 def wait_until_completed(self, drive='drive0', check_offset=True):
0dbe8a1b 553 '''Wait for a block job to finish, returning the event'''
c3988519 554 while True:
0dbe8a1b
SH
555 for event in self.vm.get_qmp_events(wait=True):
556 if event['event'] == 'BLOCK_JOB_COMPLETED':
557 self.assert_qmp(event, 'data/device', drive)
558 self.assert_qmp_absent(event, 'data/error')
9974ad40 559 if check_offset:
1d3ba15a 560 self.assert_qmp(event, 'data/offset', event['data']['len'])
c3988519
PX
561 self.assert_no_active_block_jobs()
562 return event
1dac83f1
KW
563 elif event['event'] == 'JOB_STATUS_CHANGE':
564 self.assert_qmp(event, 'data/id', drive)
0dbe8a1b 565
866323f3
FZ
566 def wait_ready(self, drive='drive0'):
567 '''Wait until a block job BLOCK_JOB_READY event'''
d7b25297
FZ
568 f = {'data': {'type': 'mirror', 'device': drive } }
569 event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
866323f3
FZ
570
571 def wait_ready_and_cancel(self, drive='drive0'):
572 self.wait_ready(drive=drive)
573 event = self.cancel_and_wait(drive=drive)
574 self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
575 self.assert_qmp(event, 'data/type', 'mirror')
576 self.assert_qmp(event, 'data/offset', event['data']['len'])
577
578 def complete_and_wait(self, drive='drive0', wait_ready=True):
579 '''Complete a block job and wait for it to finish'''
580 if wait_ready:
581 self.wait_ready(drive=drive)
582
583 result = self.vm.qmp('block-job-complete', device=drive)
584 self.assert_qmp(result, 'return', {})
585
586 event = self.wait_until_completed(drive=drive)
587 self.assert_qmp(event, 'data/type', 'mirror')
588
f03d9d24 589 def pause_wait(self, job_id='job0'):
2c93c5cb
KW
590 with Timeout(1, "Timeout waiting for job to pause"):
591 while True:
592 result = self.vm.qmp('query-block-jobs')
c1bac161 593 found = False
2c93c5cb 594 for job in result['return']:
c1bac161
VSO
595 if job['device'] == job_id:
596 found = True
597 if job['paused'] == True and job['busy'] == False:
598 return job
599 break
600 assert found
2c93c5cb 601
f03d9d24
JS
602 def pause_job(self, job_id='job0', wait=True):
603 result = self.vm.qmp('block-job-pause', device=job_id)
604 self.assert_qmp(result, 'return', {})
605 if wait:
606 return self.pause_wait(job_id)
607 return result
608
2c93c5cb 609
f345cfd0
SH
610def notrun(reason):
611 '''Skip this test suite'''
612 # Each test in qemu-iotests has a number ("seq")
613 seq = os.path.basename(sys.argv[0])
614
e8f8624d 615 open('%s/%s.notrun' % (output_dir, seq), 'wb').write(reason + '\n')
f345cfd0
SH
616 print '%s not run: %s' % (seq, reason)
617 sys.exit(0)
618
3f5c4076 619def verify_image_format(supported_fmts=[], unsupported_fmts=[]):
f48351d2
VSO
620 assert not (supported_fmts and unsupported_fmts)
621
622 if 'generic' in supported_fmts and \
623 os.environ.get('IMGFMT_GENERIC', 'true') == 'true':
624 # similar to
625 # _supported_fmt generic
626 # for bash tests
627 return
628
629 not_sup = supported_fmts and (imgfmt not in supported_fmts)
630 if not_sup or (imgfmt in unsupported_fmts):
3f5c4076 631 notrun('not suitable for this image format: %s' % imgfmt)
f345cfd0 632
5a259e86
KW
633def verify_protocol(supported=[], unsupported=[]):
634 assert not (supported and unsupported)
635
636 if 'generic' in supported:
637 return
638
639 not_sup = supported and (imgproto not in supported)
640 if not_sup or (imgproto in unsupported):
641 notrun('not suitable for this protocol: %s' % imgproto)
642
c6a92369 643def verify_platform(supported_oses=['linux']):
79e7a019 644 if True not in [sys.platform.startswith(x) for x in supported_oses]:
bc521696
FZ
645 notrun('not suitable for this OS: %s' % sys.platform)
646
ac8bd439
VSO
647def verify_cache_mode(supported_cache_modes=[]):
648 if supported_cache_modes and (cachemode not in supported_cache_modes):
649 notrun('not suitable for this cache mode: %s' % cachemode)
650
b0f90495
AG
651def supports_quorum():
652 return 'quorum' in qemu_img_pipe('--help')
653
3f647b51
SS
654def verify_quorum():
655 '''Skip test suite if quorum support is not available'''
b0f90495 656 if not supports_quorum():
3f647b51
SS
657 notrun('quorum support missing')
658
febc8c86
VSO
659def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[],
660 unsupported_fmts=[]):
c6a92369
DB
661 '''Run tests'''
662
c0088d79
KW
663 global debug
664
5a8fabf3
SS
665 # We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
666 # indicate that we're not being run via "check". There may be
667 # other things set up by "check" that individual test cases rely
668 # on.
669 if test_dir is None or qemu_default_machine is None:
670 sys.stderr.write('Please run this test via the "check" script\n')
671 sys.exit(os.EX_USAGE)
672
c6a92369
DB
673 debug = '-d' in sys.argv
674 verbosity = 1
febc8c86 675 verify_image_format(supported_fmts, unsupported_fmts)
c6a92369 676 verify_platform(supported_oses)
ac8bd439 677 verify_cache_mode(supported_cache_modes)
c6a92369 678
f345cfd0
SH
679 # We need to filter out the time taken from the output so that qemu-iotest
680 # can reliably diff the results against master output.
681 import StringIO
aa4f592a
FZ
682 if debug:
683 output = sys.stdout
684 verbosity = 2
685 sys.argv.remove('-d')
686 else:
687 output = StringIO.StringIO()
f345cfd0 688
43851b5b
EH
689 logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
690
f345cfd0 691 class MyTestRunner(unittest.TextTestRunner):
aa4f592a 692 def __init__(self, stream=output, descriptions=True, verbosity=verbosity):
f345cfd0
SH
693 unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity)
694
695 # unittest.main() will use sys.exit() so expect a SystemExit exception
696 try:
697 unittest.main(testRunner=MyTestRunner)
698 finally:
aa4f592a
FZ
699 if not debug:
700 sys.stderr.write(re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', output.getvalue()))
This page took 0.532484 seconds and 4 git commands to generate.