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 self.vm.cmd('blockdev-change-medium',
78 id=self.device_name, filename=new_img,
79 format=iotests.imgfmt)
84 result = self.vm.qmp('query-block')
85 if self.has_real_tray:
86 self.assert_qmp(result, 'return[0]/tray_open', False)
87 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
90 self.vm.cmd('eject', id=self.device_name, force=True)
94 result = self.vm.qmp('query-block')
95 if self.has_real_tray:
96 self.assert_qmp(result, 'return[0]/tray_open', True)
97 self.assert_qmp_absent(result, 'return[0]/inserted')
99 def test_tray_eject_change(self):
100 self.vm.cmd('eject', id=self.device_name, force=True)
104 result = self.vm.qmp('query-block')
105 if self.has_real_tray:
106 self.assert_qmp(result, 'return[0]/tray_open', True)
107 self.assert_qmp_absent(result, 'return[0]/inserted')
109 self.vm.cmd('blockdev-change-medium', id=self.device_name,
110 filename=new_img, format=iotests.imgfmt)
112 self.wait_for_close()
114 result = self.vm.qmp('query-block')
115 if self.has_real_tray:
116 self.assert_qmp(result, 'return[0]/tray_open', False)
117 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
119 def test_tray_open_close(self):
120 self.vm.cmd('blockdev-open-tray',
121 id=self.device_name, force=True)
125 result = self.vm.qmp('query-block')
126 if self.has_real_tray:
127 self.assert_qmp(result, 'return[0]/tray_open', True)
128 if self.was_empty == True:
129 self.assert_qmp_absent(result, 'return[0]/inserted')
131 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
133 self.vm.cmd('blockdev-close-tray', id=self.device_name)
135 if self.has_real_tray or not self.was_empty:
136 self.wait_for_close()
138 result = self.vm.qmp('query-block')
139 if self.has_real_tray:
140 self.assert_qmp(result, 'return[0]/tray_open', False)
141 if self.was_empty == True:
142 self.assert_qmp_absent(result, 'return[0]/inserted')
144 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
146 def test_tray_eject_close(self):
147 self.vm.cmd('eject', id=self.device_name, force=True)
151 result = self.vm.qmp('query-block')
152 if self.has_real_tray:
153 self.assert_qmp(result, 'return[0]/tray_open', True)
154 self.assert_qmp_absent(result, 'return[0]/inserted')
156 self.vm.cmd('blockdev-close-tray', id=self.device_name)
158 self.wait_for_close()
160 result = self.vm.qmp('query-block')
161 if self.has_real_tray:
162 self.assert_qmp(result, 'return[0]/tray_open', False)
163 self.assert_qmp_absent(result, 'return[0]/inserted')
165 def test_tray_open_change(self):
166 self.vm.cmd('blockdev-open-tray', id=self.device_name,
171 result = self.vm.qmp('query-block')
172 if self.has_real_tray:
173 self.assert_qmp(result, 'return[0]/tray_open', True)
174 if self.was_empty == True:
175 self.assert_qmp_absent(result, 'return[0]/inserted')
177 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
179 self.vm.cmd('blockdev-change-medium', id=self.device_name,
181 format=iotests.imgfmt)
183 self.wait_for_close()
185 result = self.vm.qmp('query-block')
186 if self.has_real_tray:
187 self.assert_qmp(result, 'return[0]/tray_open', False)
188 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
190 def test_cycle(self, read_only_node=False):
191 self.vm.cmd('blockdev-add',
193 driver=iotests.imgfmt,
194 read_only=read_only_node,
195 file={'filename': new_img,
198 self.vm.cmd('blockdev-open-tray',
199 id=self.device_name, force=True)
203 result = self.vm.qmp('query-block')
204 if self.has_real_tray:
205 self.assert_qmp(result, 'return[0]/tray_open', True)
206 if self.was_empty == True:
207 self.assert_qmp_absent(result, 'return[0]/inserted')
209 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
211 self.vm.cmd('blockdev-remove-medium',
214 result = self.vm.qmp('query-block')
215 if self.has_real_tray:
216 self.assert_qmp(result, 'return[0]/tray_open', True)
217 self.assert_qmp_absent(result, 'return[0]/inserted')
219 self.vm.cmd('blockdev-insert-medium',
220 id=self.device_name, node_name='new')
222 result = self.vm.qmp('query-block')
223 if self.has_real_tray:
224 self.assert_qmp(result, 'return[0]/tray_open', True)
225 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
227 self.vm.cmd('blockdev-close-tray', id=self.device_name)
229 self.wait_for_close()
231 result = self.vm.qmp('query-block')
232 if self.has_real_tray:
233 self.assert_qmp(result, 'return[0]/tray_open', False)
234 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
236 def test_cycle_read_only_media(self):
237 self.test_cycle(True)
239 def test_close_on_closed(self):
241 self.vm.cmd('blockdev-close-tray', id=self.device_name)
242 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
244 def test_remove_on_closed(self):
245 if not self.has_real_tray:
248 result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
249 self.assert_qmp(result, 'error/class', 'GenericError')
251 def test_insert_on_closed(self):
252 if not self.has_real_tray:
255 self.vm.cmd('blockdev-add',
257 driver=iotests.imgfmt,
258 file={'filename': new_img,
261 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
263 self.assert_qmp(result, 'error/class', 'GenericError')
265 class TestInitiallyFilled(GeneralChangeTestsBaseClass):
269 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
270 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
271 self.vm = iotests.VM()
273 self.vm.add_drive(old_img, 'media=%s' % self.media, 'none')
275 self.vm.add_blockdev([ 'node-name=drive0',
276 'driver=%s' % iotests.imgfmt,
278 'file.filename=%s' % old_img ])
279 if self.interface == 'scsi':
280 self.vm.add_object('iothread,id=iothread0')
281 self.vm.add_device('virtio-scsi-pci,iothread=iothread0')
282 self.vm.add_device('%s,drive=drive0,id=%s' %
283 (interface_to_device_name(self.interface),
292 def test_insert_on_filled(self):
293 self.vm.cmd('blockdev-add',
295 driver=iotests.imgfmt,
296 file={'filename': new_img,
299 self.vm.cmd('blockdev-open-tray', id=self.device_name)
303 result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
305 self.assert_qmp(result, 'error/class', 'GenericError')
307 class TestInitiallyEmpty(GeneralChangeTestsBaseClass):
311 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
312 self.vm = iotests.VM()
314 self.vm.add_drive(None, 'media=%s' % self.media, 'none')
315 if self.interface == 'scsi':
316 self.vm.add_object('iothread,id=iothread0')
317 self.vm.add_device('virtio-scsi-pci,iothread=iothread0')
318 self.vm.add_device('%s,%sid=%s' %
319 (interface_to_device_name(self.interface),
320 'drive=drive0,' if self.use_drive else '',
328 def test_remove_on_empty(self):
329 self.vm.cmd('blockdev-open-tray', id=self.device_name)
334 self.vm.cmd('blockdev-remove-medium', id=self.device_name)
336 # Do this in a function to avoid leaking variables like case into the global
337 # name space (otherwise tests would be run for the abstract base classes)
338 def create_basic_test_classes():
339 for (media, interface, has_real_tray) in [ ('cdrom', 'ide', True),
340 ('cdrom', 'scsi', True),
341 ('disk', 'floppy', False) ]:
343 for case in [ TestInitiallyFilled, TestInitiallyEmpty ]:
344 for use_drive in [ True, False ]:
345 attr = { 'media': media,
346 'interface': interface,
347 'has_real_tray': has_real_tray,
348 'use_drive': use_drive }
350 name = '%s_%s_%s_%s' % (case.__name__, media, interface,
351 'drive' if use_drive else 'blockdev')
352 globals()[name] = type(name, (case, ), attr)
354 create_basic_test_classes()
356 class TestChangeReadOnly(ChangeBaseClass):
357 device_name = 'qdev0'
360 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440k')
361 qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
362 self.vm = iotests.VM()
366 os.chmod(old_img, 0o666)
367 os.chmod(new_img, 0o666)
371 def test_ro_ro_retain(self):
372 os.chmod(old_img, 0o444)
373 os.chmod(new_img, 0o444)
374 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
375 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
378 result = self.vm.qmp('query-block')
379 self.assert_qmp(result, 'return[0]/inserted/ro', True)
380 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
382 self.vm.cmd('blockdev-change-medium', id=self.device_name,
384 format=iotests.imgfmt,
385 read_only_mode='retain')
387 result = self.vm.qmp('query-block')
388 self.assert_qmp(result, 'return[0]/inserted/ro', True)
389 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
391 def test_ro_rw_retain(self):
392 os.chmod(old_img, 0o444)
393 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
394 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
397 result = self.vm.qmp('query-block')
398 self.assert_qmp(result, 'return[0]/inserted/ro', True)
399 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
401 self.vm.cmd('blockdev-change-medium', id=self.device_name,
403 format=iotests.imgfmt,
404 read_only_mode='retain')
406 result = self.vm.qmp('query-block')
407 self.assert_qmp(result, 'return[0]/inserted/ro', True)
408 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
410 @iotests.skip_if_user_is_root
411 def test_rw_ro_retain(self):
412 os.chmod(new_img, 0o444)
413 self.vm.add_drive(old_img, 'media=disk', 'none')
414 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
417 result = self.vm.qmp('query-block')
418 self.assert_qmp(result, 'return[0]/inserted/ro', False)
419 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
421 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
423 format=iotests.imgfmt,
424 read_only_mode='retain')
425 self.assert_qmp(result, 'error/class', 'GenericError')
427 self.assertEqual(self.vm.get_qmp_events(wait=False), [])
429 result = self.vm.qmp('query-block')
430 self.assert_qmp(result, 'return[0]/inserted/ro', False)
431 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
433 def test_ro_rw(self):
434 os.chmod(old_img, 0o444)
435 self.vm.add_drive(old_img, 'media=disk,read-only=on', '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', True)
441 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
443 self.vm.cmd('blockdev-change-medium',
446 format=iotests.imgfmt,
447 read_only_mode='read-write')
449 result = self.vm.qmp('query-block')
450 self.assert_qmp(result, 'return[0]/inserted/ro', False)
451 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
453 def test_rw_ro(self):
454 os.chmod(new_img, 0o444)
455 self.vm.add_drive(old_img, 'media=disk', 'none')
456 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
459 result = self.vm.qmp('query-block')
460 self.assert_qmp(result, 'return[0]/inserted/ro', False)
461 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
463 self.vm.cmd('blockdev-change-medium',
466 format=iotests.imgfmt,
467 read_only_mode='read-only')
469 result = self.vm.qmp('query-block')
470 self.assert_qmp(result, 'return[0]/inserted/ro', True)
471 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
473 def test_make_rw_ro(self):
474 self.vm.add_drive(old_img, 'media=disk', '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', False)
480 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
482 self.vm.cmd('blockdev-change-medium',
485 format=iotests.imgfmt,
486 read_only_mode='read-only')
488 result = self.vm.qmp('query-block')
489 self.assert_qmp(result, 'return[0]/inserted/ro', True)
490 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
492 @iotests.skip_if_user_is_root
493 def test_make_ro_rw(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-write')
508 self.assert_qmp(result, 'error/class', 'GenericError')
510 result = self.vm.qmp('query-block')
511 self.assert_qmp(result, 'return[0]/inserted/ro', False)
512 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
514 def test_make_rw_ro_by_retain(self):
515 os.chmod(old_img, 0o444)
516 self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none')
517 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
520 result = self.vm.qmp('query-block')
521 self.assert_qmp(result, 'return[0]/inserted/ro', True)
522 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
524 self.vm.cmd('blockdev-change-medium', id=self.device_name,
526 format=iotests.imgfmt,
527 read_only_mode='retain')
529 result = self.vm.qmp('query-block')
530 self.assert_qmp(result, 'return[0]/inserted/ro', True)
531 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
533 @iotests.skip_if_user_is_root
534 def test_make_ro_rw_by_retain(self):
535 os.chmod(new_img, 0o444)
536 self.vm.add_drive(old_img, 'media=disk', 'none')
537 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
540 result = self.vm.qmp('query-block')
541 self.assert_qmp(result, 'return[0]/inserted/ro', False)
542 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
544 result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
546 format=iotests.imgfmt,
547 read_only_mode='retain')
548 self.assert_qmp(result, 'error/class', 'GenericError')
550 result = self.vm.qmp('query-block')
551 self.assert_qmp(result, 'return[0]/inserted/ro', False)
552 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
554 def test_rw_ro_cycle(self):
555 os.chmod(new_img, 0o444)
556 self.vm.add_drive(old_img, 'media=disk', 'none')
557 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
560 result = self.vm.qmp('query-block')
561 self.assert_qmp(result, 'return[0]/inserted/ro', False)
562 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
564 self.vm.cmd('blockdev-add',
566 driver=iotests.imgfmt,
568 file={'filename': new_img,
571 result = self.vm.qmp('query-block')
572 self.assert_qmp(result, 'return[0]/inserted/ro', False)
573 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
575 self.vm.cmd('blockdev-remove-medium', id=self.device_name)
577 result = self.vm.qmp('query-block')
578 self.assert_qmp_absent(result, 'return[0]/inserted')
580 self.vm.cmd('blockdev-insert-medium', id=self.device_name,
583 result = self.vm.qmp('query-block')
584 self.assert_qmp(result, 'return[0]/inserted/ro', True)
585 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
587 result = self.vm.qmp('query-block')
588 self.assert_qmp(result, 'return[0]/inserted/ro', True)
589 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
591 GeneralChangeTestsBaseClass = None
592 TestInitiallyFilled = None
593 TestInitiallyEmpty = None
596 class TestBlockJobsAfterCycle(ChangeBaseClass):
597 device_name = 'qdev0'
600 qemu_img('create', '-f', iotests.imgfmt, old_img, '1440K')
602 self.vm = iotests.VM()
603 self.vm.add_drive_raw("id=drive0,driver=null-co,if=none")
604 self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name)
607 result = self.vm.qmp('query-block')
608 self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
610 # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
612 self.vm.cmd('blockdev-remove-medium', id=self.device_name)
614 result = self.vm.qmp('query-block')
615 self.assert_qmp_absent(result, 'return[0]/inserted')
617 self.vm.cmd('blockdev-add',
619 driver=iotests.imgfmt,
620 file={'filename': old_img,
623 self.vm.cmd('blockdev-insert-medium', id=self.device_name,
626 result = self.vm.qmp('query-block')
627 self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
637 # We need backing file support
638 @iotests.skip_for_formats(('vpc', 'parallels', 'qcow', 'vdi', 'vmdk', 'raw',
640 def test_snapshot_and_commit(self):
641 self.vm.cmd('blockdev-snapshot-sync', device='drive0',
642 snapshot_file=new_img,
643 format=iotests.imgfmt)
645 result = self.vm.qmp('query-block')
646 self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img)
647 self.assert_qmp(result,
648 'return[0]/inserted/image/backing-image/filename',
651 self.vm.cmd('block-commit', device='drive0')
653 self.vm.event_wait(name='BLOCK_JOB_READY')
655 result = self.vm.qmp('query-block-jobs')
656 self.assert_qmp(result, 'return[0]/device', 'drive0')
658 self.vm.cmd('block-job-complete', device='drive0')
660 self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
663 if __name__ == '__main__':
664 if iotests.qemu_default_machine != 'pc':
665 # We need floppy and IDE CD-ROM
666 iotests.notrun('not suitable for this machine type: %s' %
667 iotests.qemu_default_machine)
668 # Need to support image creation
669 iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2',
670 'vmdk', 'raw', 'vhdx', 'qed'],
671 supported_protocols=['file'])