3 # Test case for the QMP 'change' command and all other associated
6 # Copyright (C) 2015 Red Hat, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from iotests import qemu_img
28 old_img = os.path.join(iotests.test_dir, 'test0.img')
29 new_img = os.path.join(iotests.test_dir, 'test1.img')
31 def interface_to_device_name(interface):
32 if interface == 'ide':
34 elif interface == 'floppy':
36 elif interface == 'scsi':
41 class ChangeBaseClass(iotests.QMPTestCase):
48 def process_events(self):
49 for event in self.vm.get_qmp_events(wait=False):
50 if (event['event'] == 'DEVICE_TRAY_MOVED' and
51 (event['data']['device'] == 'drive0' or
52 event['data']['id'] == self.device_name)):
53 if event['data']['tray-open'] == False:
54 self.has_closed = True
56 self.has_opened = True
58 def wait_for_open(self):
59 if not self.has_real_tray:
62 with iotests.Timeout(3, 'Timeout while waiting for the tray to open'):
63 while not self.has_opened:
66 def wait_for_close(self):
67 if not self.has_real_tray:
70 with iotests.Timeout(3, 'Timeout while waiting for the tray to close'):
71 while not self.has_closed:
74 class GeneralChangeTestsBaseClass(ChangeBaseClass):
76 def test_change(self):
77 # 'change' requires a drive name, so skip the test for blockdev
78 if not self.use_drive:
81 result = self.vm.qmp('change', device='drive0', target=new_img,
83 self.assert_qmp(result, 'return', {})
88 result = self.vm.qmp('query-block')
89 if self.has_real_tray:
90 self.assert_qmp(result, 'return[0]/tray_open', False)
91 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
93 def test_blockdev_change_medium(self):
94 result = self.vm.qmp('blockdev-change-medium',
95 id=self.device_name, filename=new_img,
96 format=iotests.imgfmt)
98 self.assert_qmp(result, 'return', {})
101 self.wait_for_close()
103 result = self.vm.qmp('query-block')
104 if self.has_real_tray:
105 self.assert_qmp(result, 'return[0]/tray_open', False)
106 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
108 def test_eject(self):
109 result = self.vm.qmp('eject', id=self.device_name, force=True)
110 self.assert_qmp(result, 'return', {})
114 result = self.vm.qmp('query-block')
115 if self.has_real_tray:
116 self.assert_qmp(result, 'return[0]/tray_open', True)
117 self.assert_qmp_absent(result, 'return[0]/inserted')
119 def test_tray_eject_change(self):
120 result = self.vm.qmp('eject', id=self.device_name, force=True)
121 self.assert_qmp(result, 'return', {})
125 result = self.vm.qmp('query-block')
126 if self.has_real_tray:
127 self.assert_qmp(result, 'return[0]/tray_open', True)
128 self.assert_qmp_absent(result, 'return[0]/inserted')
130 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
131 filename=new_img, format=iotests.imgfmt)
132 self.assert_qmp(result, 'return', {})
134 self.wait_for_close()
136 result = self.vm.qmp('query-block')
137 if self.has_real_tray:
138 self.assert_qmp(result, 'return[0]/tray_open', False)
139 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
141 def test_tray_open_close(self):
142 result = self.vm.qmp('blockdev-open-tray',
143 id=self.device_name, force=True)
144 self.assert_qmp(result, 'return', {})
148 result = self.vm.qmp('query-block')
149 if self.has_real_tray:
150 self.assert_qmp(result, 'return[0]/tray_open', True)
151 if self.was_empty == True:
152 self.assert_qmp_absent(result, 'return[0]/inserted')
154 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
156 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
157 self.assert_qmp(result, 'return', {})
159 if self.has_real_tray or not self.was_empty:
160 self.wait_for_close()
162 result = self.vm.qmp('query-block')
163 if self.has_real_tray:
164 self.assert_qmp(result, 'return[0]/tray_open', False)
165 if self.was_empty == True:
166 self.assert_qmp_absent(result, 'return[0]/inserted')
168 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
170 def test_tray_eject_close(self):
171 result = self.vm.qmp('eject', id=self.device_name, force=True)
172 self.assert_qmp(result, 'return', {})
176 result = self.vm.qmp('query-block')
177 if self.has_real_tray:
178 self.assert_qmp(result, 'return[0]/tray_open', True)
179 self.assert_qmp_absent(result, 'return[0]/inserted')
181 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
182 self.assert_qmp(result, 'return', {})
184 self.wait_for_close()
186 result = self.vm.qmp('query-block')
187 if self.has_real_tray:
188 self.assert_qmp(result, 'return[0]/tray_open', False)
189 self.assert_qmp_absent(result, 'return[0]/inserted')
191 def test_tray_open_change(self):
192 result = self.vm.qmp('blockdev-open-tray', id=self.device_name,
194 self.assert_qmp(result, 'return', {})
198 result = self.vm.qmp('query-block')
199 if self.has_real_tray:
200 self.assert_qmp(result, 'return[0]/tray_open', True)
201 if self.was_empty == True:
202 self.assert_qmp_absent(result, 'return[0]/inserted')
204 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
206 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
208 format=iotests.imgfmt)
209 self.assert_qmp(result, 'return', {})
211 self.wait_for_close()
213 result = self.vm.qmp('query-block')
214 if self.has_real_tray:
215 self.assert_qmp(result, 'return[0]/tray_open', False)
216 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
218 def test_cycle(self, read_only_node=False):
219 result = self.vm.qmp('blockdev-add',
221 driver=iotests.imgfmt,
222 read_only=read_only_node,
223 file={'filename': new_img,
225 self.assert_qmp(result, 'return', {})
227 result = self.vm.qmp('blockdev-open-tray',
228 id=self.device_name, force=True)
229 self.assert_qmp(result, 'return', {})
233 result = self.vm.qmp('query-block')
234 if self.has_real_tray:
235 self.assert_qmp(result, 'return[0]/tray_open', True)
236 if self.was_empty == True:
237 self.assert_qmp_absent(result, 'return[0]/inserted')
239 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
241 result = self.vm.qmp('blockdev-remove-medium',
243 self.assert_qmp(result, 'return', {})
245 result = self.vm.qmp('query-block')
246 if self.has_real_tray:
247 self.assert_qmp(result, 'return[0]/tray_open', True)
248 self.assert_qmp_absent(result, 'return[0]/inserted')
250 result = self.vm.qmp('blockdev-insert-medium',
251 id=self.device_name, node_name='new')
252 self.assert_qmp(result, 'return', {})
254 result = self.vm.qmp('query-block')
255 if self.has_real_tray:
256 self.assert_qmp(result, 'return[0]/tray_open', True)
257 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
259 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
260 self.assert_qmp(result, 'return', {})
262 self.wait_for_close()
264 result = self.vm.qmp('query-block')
265 if self.has_real_tray:
266 self.assert_qmp(result, 'return[0]/tray_open', False)
267 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
269 def test_cycle_read_only_media(self):
270 self.test_cycle(True)
272 def test_close_on_closed(self):
273 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
275 self.assert_qmp(result, 'return', {})
276 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
278 def test_remove_on_closed(self):
279 if not self.has_real_tray:
282 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
283 self.assert_qmp(result, 'error/class', 'GenericError')
285 def test_insert_on_closed(self):
286 if not self.has_real_tray:
289 result = self.vm.qmp('blockdev-add',
291 driver=iotests.imgfmt,
292 file={'filename': new_img,
294 self.assert_qmp(result, 'return', {})
296 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
298 self.assert_qmp(result, 'error/class', 'GenericError')
300 class TestInitiallyFilled(GeneralChangeTestsBaseClass):
304 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
305 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
306 self.vm = iotests.VM()
308 self.vm.add_drive(old_img, 'media=%s' % self.media, 'none')
310 self.vm.add_blockdev([ 'node-name=drive0',
311 'driver=%s' % iotests.imgfmt,
313 'file.filename=%s' % old_img ])
314 if self.interface == 'scsi':
315 self.vm.add_device('virtio-scsi-pci')
316 self.vm.add_device('%s,drive=drive0,id=%s' %
317 (interface_to_device_name(self.interface),
326 def test_insert_on_filled(self):
327 result = self.vm.qmp('blockdev-add',
329 driver=iotests.imgfmt,
330 file={'filename': new_img,
332 self.assert_qmp(result, 'return', {})
334 result = self.vm.qmp('blockdev-open-tray', id=self.device_name)
335 self.assert_qmp(result, 'return', {})
339 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
341 self.assert_qmp(result, 'error/class', 'GenericError')
343 class TestInitiallyEmpty(GeneralChangeTestsBaseClass):
347 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
348 self.vm = iotests.VM()
350 self.vm.add_drive(None, 'media=%s' % self.media, 'none')
351 if self.interface == 'scsi':
352 self.vm.add_device('virtio-scsi-pci')
353 self.vm.add_device('%s,%sid=%s' %
354 (interface_to_device_name(self.interface),
355 'drive=drive0,' if self.use_drive else '',
363 def test_remove_on_empty(self):
364 result = self.vm.qmp('blockdev-open-tray', id=self.device_name)
365 self.assert_qmp(result, 'return', {})
369 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
371 self.assert_qmp(result, 'return', {})
373 # Do this in a function to avoid leaking variables like case into the global
374 # name space (otherwise tests would be run for the abstract base classes)
375 def create_basic_test_classes():
376 for (media, interface, has_real_tray) in [ ('cdrom', 'ide', True),
377 ('cdrom', 'scsi', True),
378 ('disk', 'floppy', False) ]:
380 for case in [ TestInitiallyFilled, TestInitiallyEmpty ]:
381 for use_drive in [ True, False ]:
382 attr = { 'media': media,
383 'interface': interface,
384 'has_real_tray': has_real_tray,
385 'use_drive': use_drive }
387 name = '%s_%s_%s_%s' % (case.__name__, media, interface,
388 'drive' if use_drive else 'blockdev')
389 globals()[name] = type(name, (case, ), attr)
391 create_basic_test_classes()
393 class TestChangeReadOnly(ChangeBaseClass):
394 device_name = 'qdev0'
397 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
398 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
399 self.vm = iotests.VM()
403 os.chmod(old_img, 0o666)
404 os.chmod(new_img, 0o666)
408 def test_ro_ro_retain(self):
409 os.chmod(old_img, 0o444)
410 os.chmod(new_img, 0o444)
411 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
412 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
415 result = self.vm.qmp('query-block')
416 self.assert_qmp(result, 'return[0]/inserted/ro', True)
417 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
419 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
421 format=iotests.imgfmt,
422 read_only_mode='retain')
423 self.assert_qmp(result, 'return', {})
425 result = self.vm.qmp('query-block')
426 self.assert_qmp(result, 'return[0]/inserted/ro', True)
427 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
429 def test_ro_rw_retain(self):
430 os.chmod(old_img, 0o444)
431 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
432 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
435 result = self.vm.qmp('query-block')
436 self.assert_qmp(result, 'return[0]/inserted/ro', True)
437 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
439 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
441 format=iotests.imgfmt,
442 read_only_mode='retain')
443 self.assert_qmp(result, 'return', {})
445 result = self.vm.qmp('query-block')
446 self.assert_qmp(result, 'return[0]/inserted/ro', True)
447 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
449 @iotests.skip_if_user_is_root
450 def test_rw_ro_retain(self):
451 os.chmod(new_img, 0o444)
452 self.vm.add_drive(old_img, 'media=disk', 'none')
453 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
456 result = self.vm.qmp('query-block')
457 self.assert_qmp(result, 'return[0]/inserted/ro', False)
458 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
460 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
462 format=iotests.imgfmt,
463 read_only_mode='retain')
464 self.assert_qmp(result, 'error/class', 'GenericError')
466 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
468 result = self.vm.qmp('query-block')
469 self.assert_qmp(result, 'return[0]/inserted/ro', False)
470 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
472 def test_ro_rw(self):
473 os.chmod(old_img, 0o444)
474 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
475 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
478 result = self.vm.qmp('query-block')
479 self.assert_qmp(result, 'return[0]/inserted/ro', True)
480 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
482 result = self.vm.qmp('blockdev-change-medium',
485 format=iotests.imgfmt,
486 read_only_mode='read-write')
487 self.assert_qmp(result, 'return', {})
489 result = self.vm.qmp('query-block')
490 self.assert_qmp(result, 'return[0]/inserted/ro', False)
491 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
493 def test_rw_ro(self):
494 os.chmod(new_img, 0o444)
495 self.vm.add_drive(old_img, 'media=disk', 'none')
496 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
499 result = self.vm.qmp('query-block')
500 self.assert_qmp(result, 'return[0]/inserted/ro', False)
501 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
503 result = self.vm.qmp('blockdev-change-medium',
506 format=iotests.imgfmt,
507 read_only_mode='read-only')
508 self.assert_qmp(result, 'return', {})
510 result = self.vm.qmp('query-block')
511 self.assert_qmp(result, 'return[0]/inserted/ro', True)
512 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
514 def test_make_rw_ro(self):
515 self.vm.add_drive(old_img, 'media=disk', 'none')
516 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
519 result = self.vm.qmp('query-block')
520 self.assert_qmp(result, 'return[0]/inserted/ro', False)
521 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
523 result = self.vm.qmp('blockdev-change-medium',
526 format=iotests.imgfmt,
527 read_only_mode='read-only')
528 self.assert_qmp(result, 'return', {})
530 result = self.vm.qmp('query-block')
531 self.assert_qmp(result, 'return[0]/inserted/ro', True)
532 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
534 @iotests.skip_if_user_is_root
535 def test_make_ro_rw(self):
536 os.chmod(new_img, 0o444)
537 self.vm.add_drive(old_img, 'media=disk', 'none')
538 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
541 result = self.vm.qmp('query-block')
542 self.assert_qmp(result, 'return[0]/inserted/ro', False)
543 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
545 result = self.vm.qmp('blockdev-change-medium',
548 format=iotests.imgfmt,
549 read_only_mode='read-write')
550 self.assert_qmp(result, 'error/class', 'GenericError')
552 result = self.vm.qmp('query-block')
553 self.assert_qmp(result, 'return[0]/inserted/ro', False)
554 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
556 def test_make_rw_ro_by_retain(self):
557 os.chmod(old_img, 0o444)
558 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
559 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
562 result = self.vm.qmp('query-block')
563 self.assert_qmp(result, 'return[0]/inserted/ro', True)
564 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
566 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
568 format=iotests.imgfmt,
569 read_only_mode='retain')
570 self.assert_qmp(result, 'return', {})
572 result = self.vm.qmp('query-block')
573 self.assert_qmp(result, 'return[0]/inserted/ro', True)
574 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
576 @iotests.skip_if_user_is_root
577 def test_make_ro_rw_by_retain(self):
578 os.chmod(new_img, 0o444)
579 self.vm.add_drive(old_img, 'media=disk', 'none')
580 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
583 result = self.vm.qmp('query-block')
584 self.assert_qmp(result, 'return[0]/inserted/ro', False)
585 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
587 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
589 format=iotests.imgfmt,
590 read_only_mode='retain')
591 self.assert_qmp(result, 'error/class', 'GenericError')
593 result = self.vm.qmp('query-block')
594 self.assert_qmp(result, 'return[0]/inserted/ro', False)
595 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
597 def test_rw_ro_cycle(self):
598 os.chmod(new_img, 0o444)
599 self.vm.add_drive(old_img, 'media=disk', 'none')
600 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
603 result = self.vm.qmp('query-block')
604 self.assert_qmp(result, 'return[0]/inserted/ro', False)
605 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
607 result = self.vm.qmp('blockdev-add',
609 driver=iotests.imgfmt,
611 file={'filename': new_img,
613 self.assert_qmp(result, 'return', {})
615 result = self.vm.qmp('query-block')
616 self.assert_qmp(result, 'return[0]/inserted/ro', False)
617 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
619 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
620 self.assert_qmp(result, 'return', {})
622 result = self.vm.qmp('query-block')
623 self.assert_qmp_absent(result, 'return[0]/inserted')
625 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
627 self.assert_qmp(result, 'return', {})
629 result = self.vm.qmp('query-block')
630 self.assert_qmp(result, 'return[0]/inserted/ro', True)
631 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
633 result = self.vm.qmp('query-block')
634 self.assert_qmp(result, 'return[0]/inserted/ro', True)
635 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
637 GeneralChangeTestsBaseClass = None
638 TestInitiallyFilled = None
639 TestInitiallyEmpty = None
642 class TestBlockJobsAfterCycle(ChangeBaseClass):
643 device_name = 'qdev0'
646 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440K')
648 self.vm = iotests.VM()
649 self.vm.add_drive_raw("id=drive0,driver=null-co,if=none")
650 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
653 result = self.vm.qmp('query-block')
654 self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
656 # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
658 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
659 self.assert_qmp(result, 'return', {})
661 result = self.vm.qmp('query-block')
662 self.assert_qmp_absent(result, 'return[0]/inserted')
664 result = self.vm.qmp('blockdev-add',
666 driver=iotests.imgfmt,
667 file={'filename': old_img,
669 self.assert_qmp(result, 'return', {})
671 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
673 self.assert_qmp(result, 'return', {})
675 result = self.vm.qmp('query-block')
676 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
686 def test_snapshot_and_commit(self):
687 # We need backing file support
688 if iotests.imgfmt != 'qcow2' and iotests.imgfmt != 'qed':
691 result = self.vm.qmp('blockdev-snapshot-sync', device='drive0',
692 snapshot_file=new_img,
693 format=iotests.imgfmt)
694 self.assert_qmp(result, 'return', {})
696 result = self.vm.qmp('query-block')
697 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
698 self.assert_qmp(result,
699 'return[0]/inserted/image/backing-image/filename',
702 result = self.vm.qmp('block-commit', device='drive0')
703 self.assert_qmp(result, 'return', {})
705 self.vm.event_wait(name='BLOCK_JOB_READY')
707 result = self.vm.qmp('query-block-jobs')
708 self.assert_qmp(result, 'return[0]/device', 'drive0')
710 result = self.vm.qmp('block-job-complete', device='drive0')
711 self.assert_qmp(result, 'return', {})
713 self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
716 if __name__ == '__main__':
717 if iotests.qemu_default_machine != 'pc':
718 # We need floppy and IDE CD-ROM
719 iotests.notrun('not suitable for this machine type: %s' %
720 iotests.qemu_default_machine)
721 # Need to support image creation
722 iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2',
723 'vmdk', 'raw', 'vhdx', 'qed'],
724 supported_protocols=['file'])