4 # Test case for media change monitor commands
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_blockdev_change_medium(self):
77 result = self.vm.qmp('blockdev-change-medium',
78 id=self.device_name, filename=new_img,
79 format=iotests.imgfmt)
81 self.assert_qmp(result, 'return', {})
86 result = self.vm.qmp('query-block')
87 if self.has_real_tray:
88 self.assert_qmp(result, 'return[0]/tray_open', False)
89 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
92 result = self.vm.qmp('eject', id=self.device_name, force=True)
93 self.assert_qmp(result, 'return', {})
97 result = self.vm.qmp('query-block')
98 if self.has_real_tray:
99 self.assert_qmp(result, 'return[0]/tray_open', True)
100 self.assert_qmp_absent(result, 'return[0]/inserted')
102 def test_tray_eject_change(self):
103 result = self.vm.qmp('eject', id=self.device_name, force=True)
104 self.assert_qmp(result, 'return', {})
108 result = self.vm.qmp('query-block')
109 if self.has_real_tray:
110 self.assert_qmp(result, 'return[0]/tray_open', True)
111 self.assert_qmp_absent(result, 'return[0]/inserted')
113 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
114 filename=new_img, format=iotests.imgfmt)
115 self.assert_qmp(result, 'return', {})
117 self.wait_for_close()
119 result = self.vm.qmp('query-block')
120 if self.has_real_tray:
121 self.assert_qmp(result, 'return[0]/tray_open', False)
122 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
124 def test_tray_open_close(self):
125 result = self.vm.qmp('blockdev-open-tray',
126 id=self.device_name, force=True)
127 self.assert_qmp(result, 'return', {})
131 result = self.vm.qmp('query-block')
132 if self.has_real_tray:
133 self.assert_qmp(result, 'return[0]/tray_open', True)
134 if self.was_empty == True:
135 self.assert_qmp_absent(result, 'return[0]/inserted')
137 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
139 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
140 self.assert_qmp(result, 'return', {})
142 if self.has_real_tray or not self.was_empty:
143 self.wait_for_close()
145 result = self.vm.qmp('query-block')
146 if self.has_real_tray:
147 self.assert_qmp(result, 'return[0]/tray_open', False)
148 if self.was_empty == True:
149 self.assert_qmp_absent(result, 'return[0]/inserted')
151 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
153 def test_tray_eject_close(self):
154 result = self.vm.qmp('eject', id=self.device_name, force=True)
155 self.assert_qmp(result, 'return', {})
159 result = self.vm.qmp('query-block')
160 if self.has_real_tray:
161 self.assert_qmp(result, 'return[0]/tray_open', True)
162 self.assert_qmp_absent(result, 'return[0]/inserted')
164 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
165 self.assert_qmp(result, 'return', {})
167 self.wait_for_close()
169 result = self.vm.qmp('query-block')
170 if self.has_real_tray:
171 self.assert_qmp(result, 'return[0]/tray_open', False)
172 self.assert_qmp_absent(result, 'return[0]/inserted')
174 def test_tray_open_change(self):
175 result = self.vm.qmp('blockdev-open-tray', id=self.device_name,
177 self.assert_qmp(result, 'return', {})
181 result = self.vm.qmp('query-block')
182 if self.has_real_tray:
183 self.assert_qmp(result, 'return[0]/tray_open', True)
184 if self.was_empty == True:
185 self.assert_qmp_absent(result, 'return[0]/inserted')
187 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
189 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
191 format=iotests.imgfmt)
192 self.assert_qmp(result, 'return', {})
194 self.wait_for_close()
196 result = self.vm.qmp('query-block')
197 if self.has_real_tray:
198 self.assert_qmp(result, 'return[0]/tray_open', False)
199 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
201 def test_cycle(self, read_only_node=False):
202 result = self.vm.qmp('blockdev-add',
204 driver=iotests.imgfmt,
205 read_only=read_only_node,
206 file={'filename': new_img,
208 self.assert_qmp(result, 'return', {})
210 result = self.vm.qmp('blockdev-open-tray',
211 id=self.device_name, force=True)
212 self.assert_qmp(result, 'return', {})
216 result = self.vm.qmp('query-block')
217 if self.has_real_tray:
218 self.assert_qmp(result, 'return[0]/tray_open', True)
219 if self.was_empty == True:
220 self.assert_qmp_absent(result, 'return[0]/inserted')
222 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
224 result = self.vm.qmp('blockdev-remove-medium',
226 self.assert_qmp(result, 'return', {})
228 result = self.vm.qmp('query-block')
229 if self.has_real_tray:
230 self.assert_qmp(result, 'return[0]/tray_open', True)
231 self.assert_qmp_absent(result, 'return[0]/inserted')
233 result = self.vm.qmp('blockdev-insert-medium',
234 id=self.device_name, node_name='new')
235 self.assert_qmp(result, 'return', {})
237 result = self.vm.qmp('query-block')
238 if self.has_real_tray:
239 self.assert_qmp(result, 'return[0]/tray_open', True)
240 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
242 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
243 self.assert_qmp(result, 'return', {})
245 self.wait_for_close()
247 result = self.vm.qmp('query-block')
248 if self.has_real_tray:
249 self.assert_qmp(result, 'return[0]/tray_open', False)
250 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
252 def test_cycle_read_only_media(self):
253 self.test_cycle(True)
255 def test_close_on_closed(self):
256 result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
258 self.assert_qmp(result, 'return', {})
259 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
261 def test_remove_on_closed(self):
262 if not self.has_real_tray:
265 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
266 self.assert_qmp(result, 'error/class', 'GenericError')
268 def test_insert_on_closed(self):
269 if not self.has_real_tray:
272 result = self.vm.qmp('blockdev-add',
274 driver=iotests.imgfmt,
275 file={'filename': new_img,
277 self.assert_qmp(result, 'return', {})
279 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
281 self.assert_qmp(result, 'error/class', 'GenericError')
283 class TestInitiallyFilled(GeneralChangeTestsBaseClass):
287 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
288 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
289 self.vm = iotests.VM()
291 self.vm.add_drive(old_img, 'media=%s' % self.media, 'none')
293 self.vm.add_blockdev([ 'node-name=drive0',
294 'driver=%s' % iotests.imgfmt,
296 'file.filename=%s' % old_img ])
297 if self.interface == 'scsi':
298 self.vm.add_device('virtio-scsi-pci')
299 self.vm.add_device('%s,drive=drive0,id=%s' %
300 (interface_to_device_name(self.interface),
309 def test_insert_on_filled(self):
310 result = self.vm.qmp('blockdev-add',
312 driver=iotests.imgfmt,
313 file={'filename': new_img,
315 self.assert_qmp(result, 'return', {})
317 result = self.vm.qmp('blockdev-open-tray', id=self.device_name)
318 self.assert_qmp(result, 'return', {})
322 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
324 self.assert_qmp(result, 'error/class', 'GenericError')
326 class TestInitiallyEmpty(GeneralChangeTestsBaseClass):
330 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
331 self.vm = iotests.VM()
333 self.vm.add_drive(None, 'media=%s' % self.media, 'none')
334 if self.interface == 'scsi':
335 self.vm.add_device('virtio-scsi-pci')
336 self.vm.add_device('%s,%sid=%s' %
337 (interface_to_device_name(self.interface),
338 'drive=drive0,' if self.use_drive else '',
346 def test_remove_on_empty(self):
347 result = self.vm.qmp('blockdev-open-tray', id=self.device_name)
348 self.assert_qmp(result, 'return', {})
352 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
354 self.assert_qmp(result, 'return', {})
356 # Do this in a function to avoid leaking variables like case into the global
357 # name space (otherwise tests would be run for the abstract base classes)
358 def create_basic_test_classes():
359 for (media, interface, has_real_tray) in [ ('cdrom', 'ide', True),
360 ('cdrom', 'scsi', True),
361 ('disk', 'floppy', False) ]:
363 for case in [ TestInitiallyFilled, TestInitiallyEmpty ]:
364 for use_drive in [ True, False ]:
365 attr = { 'media': media,
366 'interface': interface,
367 'has_real_tray': has_real_tray,
368 'use_drive': use_drive }
370 name = '%s_%s_%s_%s' % (case.__name__, media, interface,
371 'drive' if use_drive else 'blockdev')
372 globals()[name] = type(name, (case, ), attr)
374 create_basic_test_classes()
376 class TestChangeReadOnly(ChangeBaseClass):
377 device_name = 'qdev0'
380 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
381 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
382 self.vm = iotests.VM()
386 os.chmod(old_img, 0o666)
387 os.chmod(new_img, 0o666)
391 def test_ro_ro_retain(self):
392 os.chmod(old_img, 0o444)
393 os.chmod(new_img, 0o444)
394 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
395 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
398 result = self.vm.qmp('query-block')
399 self.assert_qmp(result, 'return[0]/inserted/ro', True)
400 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
402 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
404 format=iotests.imgfmt,
405 read_only_mode='retain')
406 self.assert_qmp(result, 'return', {})
408 result = self.vm.qmp('query-block')
409 self.assert_qmp(result, 'return[0]/inserted/ro', True)
410 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
412 def test_ro_rw_retain(self):
413 os.chmod(old_img, 0o444)
414 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
415 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
418 result = self.vm.qmp('query-block')
419 self.assert_qmp(result, 'return[0]/inserted/ro', True)
420 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
422 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
424 format=iotests.imgfmt,
425 read_only_mode='retain')
426 self.assert_qmp(result, 'return', {})
428 result = self.vm.qmp('query-block')
429 self.assert_qmp(result, 'return[0]/inserted/ro', True)
430 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
432 @iotests.skip_if_user_is_root
433 def test_rw_ro_retain(self):
434 os.chmod(new_img, 0o444)
435 self.vm.add_drive(old_img, 'media=disk', 'none')
436 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
439 result = self.vm.qmp('query-block')
440 self.assert_qmp(result, 'return[0]/inserted/ro', False)
441 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
443 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
445 format=iotests.imgfmt,
446 read_only_mode='retain')
447 self.assert_qmp(result, 'error/class', 'GenericError')
449 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
451 result = self.vm.qmp('query-block')
452 self.assert_qmp(result, 'return[0]/inserted/ro', False)
453 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
455 def test_ro_rw(self):
456 os.chmod(old_img, 0o444)
457 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
458 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
461 result = self.vm.qmp('query-block')
462 self.assert_qmp(result, 'return[0]/inserted/ro', True)
463 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
465 result = self.vm.qmp('blockdev-change-medium',
468 format=iotests.imgfmt,
469 read_only_mode='read-write')
470 self.assert_qmp(result, 'return', {})
472 result = self.vm.qmp('query-block')
473 self.assert_qmp(result, 'return[0]/inserted/ro', False)
474 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
476 def test_rw_ro(self):
477 os.chmod(new_img, 0o444)
478 self.vm.add_drive(old_img, 'media=disk', 'none')
479 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
482 result = self.vm.qmp('query-block')
483 self.assert_qmp(result, 'return[0]/inserted/ro', False)
484 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
486 result = self.vm.qmp('blockdev-change-medium',
489 format=iotests.imgfmt,
490 read_only_mode='read-only')
491 self.assert_qmp(result, 'return', {})
493 result = self.vm.qmp('query-block')
494 self.assert_qmp(result, 'return[0]/inserted/ro', True)
495 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
497 def test_make_rw_ro(self):
498 self.vm.add_drive(old_img, 'media=disk', 'none')
499 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
502 result = self.vm.qmp('query-block')
503 self.assert_qmp(result, 'return[0]/inserted/ro', False)
504 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
506 result = self.vm.qmp('blockdev-change-medium',
509 format=iotests.imgfmt,
510 read_only_mode='read-only')
511 self.assert_qmp(result, 'return', {})
513 result = self.vm.qmp('query-block')
514 self.assert_qmp(result, 'return[0]/inserted/ro', True)
515 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
517 @iotests.skip_if_user_is_root
518 def test_make_ro_rw(self):
519 os.chmod(new_img, 0o444)
520 self.vm.add_drive(old_img, 'media=disk', 'none')
521 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
524 result = self.vm.qmp('query-block')
525 self.assert_qmp(result, 'return[0]/inserted/ro', False)
526 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
528 result = self.vm.qmp('blockdev-change-medium',
531 format=iotests.imgfmt,
532 read_only_mode='read-write')
533 self.assert_qmp(result, 'error/class', 'GenericError')
535 result = self.vm.qmp('query-block')
536 self.assert_qmp(result, 'return[0]/inserted/ro', False)
537 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
539 def test_make_rw_ro_by_retain(self):
540 os.chmod(old_img, 0o444)
541 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
542 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
545 result = self.vm.qmp('query-block')
546 self.assert_qmp(result, 'return[0]/inserted/ro', True)
547 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
549 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
551 format=iotests.imgfmt,
552 read_only_mode='retain')
553 self.assert_qmp(result, 'return', {})
555 result = self.vm.qmp('query-block')
556 self.assert_qmp(result, 'return[0]/inserted/ro', True)
557 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
559 @iotests.skip_if_user_is_root
560 def test_make_ro_rw_by_retain(self):
561 os.chmod(new_img, 0o444)
562 self.vm.add_drive(old_img, 'media=disk', 'none')
563 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
566 result = self.vm.qmp('query-block')
567 self.assert_qmp(result, 'return[0]/inserted/ro', False)
568 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
570 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
572 format=iotests.imgfmt,
573 read_only_mode='retain')
574 self.assert_qmp(result, 'error/class', 'GenericError')
576 result = self.vm.qmp('query-block')
577 self.assert_qmp(result, 'return[0]/inserted/ro', False)
578 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
580 def test_rw_ro_cycle(self):
581 os.chmod(new_img, 0o444)
582 self.vm.add_drive(old_img, 'media=disk', 'none')
583 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
586 result = self.vm.qmp('query-block')
587 self.assert_qmp(result, 'return[0]/inserted/ro', False)
588 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
590 result = self.vm.qmp('blockdev-add',
592 driver=iotests.imgfmt,
594 file={'filename': new_img,
596 self.assert_qmp(result, 'return', {})
598 result = self.vm.qmp('query-block')
599 self.assert_qmp(result, 'return[0]/inserted/ro', False)
600 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
602 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
603 self.assert_qmp(result, 'return', {})
605 result = self.vm.qmp('query-block')
606 self.assert_qmp_absent(result, 'return[0]/inserted')
608 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
610 self.assert_qmp(result, 'return', {})
612 result = self.vm.qmp('query-block')
613 self.assert_qmp(result, 'return[0]/inserted/ro', True)
614 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
616 result = self.vm.qmp('query-block')
617 self.assert_qmp(result, 'return[0]/inserted/ro', True)
618 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
620 GeneralChangeTestsBaseClass = None
621 TestInitiallyFilled = None
622 TestInitiallyEmpty = None
625 class TestBlockJobsAfterCycle(ChangeBaseClass):
626 device_name = 'qdev0'
629 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440K')
631 self.vm = iotests.VM()
632 self.vm.add_drive_raw("id=drive0,driver=null-co,if=none")
633 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
636 result = self.vm.qmp('query-block')
637 self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
639 # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
641 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
642 self.assert_qmp(result, 'return', {})
644 result = self.vm.qmp('query-block')
645 self.assert_qmp_absent(result, 'return[0]/inserted')
647 result = self.vm.qmp('blockdev-add',
649 driver=iotests.imgfmt,
650 file={'filename': old_img,
652 self.assert_qmp(result, 'return', {})
654 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
656 self.assert_qmp(result, 'return', {})
658 result = self.vm.qmp('query-block')
659 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
669 # We need backing file support
670 @iotests.skip_for_formats(('vpc', 'parallels', 'qcow', 'vdi', 'vmdk', 'raw',
672 def test_snapshot_and_commit(self):
673 result = self.vm.qmp('blockdev-snapshot-sync', device='drive0',
674 snapshot_file=new_img,
675 format=iotests.imgfmt)
676 self.assert_qmp(result, 'return', {})
678 result = self.vm.qmp('query-block')
679 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
680 self.assert_qmp(result,
681 'return[0]/inserted/image/backing-image/filename',
684 result = self.vm.qmp('block-commit', device='drive0')
685 self.assert_qmp(result, 'return', {})
687 self.vm.event_wait(name='BLOCK_JOB_READY')
689 result = self.vm.qmp('query-block-jobs')
690 self.assert_qmp(result, 'return[0]/device', 'drive0')
692 result = self.vm.qmp('block-job-complete', device='drive0')
693 self.assert_qmp(result, 'return', {})
695 self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
698 if __name__ == '__main__':
699 if iotests.qemu_default_machine != 'pc':
700 # We need floppy and IDE CD-ROM
701 iotests.notrun('not suitable for this machine type: %s' %
702 iotests.qemu_default_machine)
703 # Need to support image creation
704 iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2',
705 'vmdk', 'raw', 'vhdx', 'qed'],
706 supported_protocols=['file'])