1#!/usr/bin/env python3
2# group: rw
3#
4# Test case for media change monitor commands
5#
6# Copyright (C) 2015 Red Hat, Inc.
7#
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.
12#
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.
17#
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/>.
20#
21
22import os
23import stat
24import time
25import iotests
26from iotests import qemu_img
27
28old_img = os.path.join(iotests.test_dir, 'test0.img')
29new_img = os.path.join(iotests.test_dir, 'test1.img')
30
31def interface_to_device_name(interface):
32    if interface == 'ide':
33        return 'ide-cd'
34    elif interface == 'floppy':
35        return 'floppy'
36    elif interface == 'scsi':
37        return 'scsi-cd'
38    else:
39        return None
40
41class ChangeBaseClass(iotests.QMPTestCase):
42    has_opened = False
43    has_closed = False
44
45    device_name = 'qdev0'
46    use_drive = False
47
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
55                else:
56                    self.has_opened = True
57
58    def wait_for_open(self):
59        if not self.has_real_tray:
60            return
61
62        with iotests.Timeout(3, 'Timeout while waiting for the tray to open'):
63            while not self.has_opened:
64                self.process_events()
65
66    def wait_for_close(self):
67        if not self.has_real_tray:
68            return
69
70        with iotests.Timeout(3, 'Timeout while waiting for the tray to close'):
71            while not self.has_closed:
72                self.process_events()
73
74class GeneralChangeTestsBaseClass(ChangeBaseClass):
75
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)
80
81        self.assert_qmp(result, 'return', {})
82
83        self.wait_for_open()
84        self.wait_for_close()
85
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)
90
91    def test_eject(self):
92        result = self.vm.qmp('eject', id=self.device_name, force=True)
93        self.assert_qmp(result, 'return', {})
94
95        self.wait_for_open()
96
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')
101
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', {})
105
106        self.wait_for_open()
107
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')
112
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', {})
116
117        self.wait_for_close()
118
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)
123
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', {})
128
129        self.wait_for_open()
130
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')
136        else:
137            self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
138
139        result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
140        self.assert_qmp(result, 'return', {})
141
142        if self.has_real_tray or not self.was_empty:
143            self.wait_for_close()
144
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')
150        else:
151            self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
152
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', {})
156
157        self.wait_for_open()
158
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')
163
164        result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
165        self.assert_qmp(result, 'return', {})
166
167        self.wait_for_close()
168
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')
173
174    def test_tray_open_change(self):
175        result = self.vm.qmp('blockdev-open-tray', id=self.device_name,
176                                                   force=True)
177        self.assert_qmp(result, 'return', {})
178
179        self.wait_for_open()
180
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')
186        else:
187            self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
188
189        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
190                                                       filename=new_img,
191                                                       format=iotests.imgfmt)
192        self.assert_qmp(result, 'return', {})
193
194        self.wait_for_close()
195
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)
200
201    def test_cycle(self, read_only_node=False):
202        result = self.vm.qmp('blockdev-add',
203                             node_name='new',
204                             driver=iotests.imgfmt,
205                             read_only=read_only_node,
206                             file={'filename': new_img,
207                                    'driver': 'file'})
208        self.assert_qmp(result, 'return', {})
209
210        result = self.vm.qmp('blockdev-open-tray',
211                             id=self.device_name, force=True)
212        self.assert_qmp(result, 'return', {})
213
214        self.wait_for_open()
215
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')
221        else:
222            self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
223
224        result = self.vm.qmp('blockdev-remove-medium',
225                             id=self.device_name)
226        self.assert_qmp(result, 'return', {})
227
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')
232
233        result = self.vm.qmp('blockdev-insert-medium',
234                             id=self.device_name, node_name='new')
235        self.assert_qmp(result, 'return', {})
236
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)
241
242        result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
243        self.assert_qmp(result, 'return', {})
244
245        self.wait_for_close()
246
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)
251
252    def test_cycle_read_only_media(self):
253        self.test_cycle(True)
254
255    def test_close_on_closed(self):
256        result = self.vm.qmp('blockdev-close-tray', id=self.device_name)
257        # Should be a no-op
258        self.assert_qmp(result, 'return', {})
259        self.assertEqual(self.vm.get_qmp_events(wait=False), [])
260
261    def test_remove_on_closed(self):
262        if not self.has_real_tray:
263            return
264
265        result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
266        self.assert_qmp(result, 'error/class', 'GenericError')
267
268    def test_insert_on_closed(self):
269        if not self.has_real_tray:
270            return
271
272        result = self.vm.qmp('blockdev-add',
273                             node_name='new',
274                             driver=iotests.imgfmt,
275                             file={'filename': new_img,
276                                   'driver': 'file'})
277        self.assert_qmp(result, 'return', {})
278
279        result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
280                                                       node_name='new')
281        self.assert_qmp(result, 'error/class', 'GenericError')
282
283class TestInitiallyFilled(GeneralChangeTestsBaseClass):
284    was_empty = False
285
286    def setUp(self):
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()
290        if self.use_drive:
291            self.vm.add_drive(old_img, 'media=%s' % self.media, 'none')
292        else:
293            self.vm.add_blockdev([ 'node-name=drive0',
294                                   'driver=%s' % iotests.imgfmt,
295                                   'file.driver=file',
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),
301                            self.device_name))
302        self.vm.launch()
303
304    def tearDown(self):
305        self.vm.shutdown()
306        os.remove(old_img)
307        os.remove(new_img)
308
309    def test_insert_on_filled(self):
310        result = self.vm.qmp('blockdev-add',
311                             node_name='new',
312                             driver=iotests.imgfmt,
313                             file={'filename': new_img,
314                                   'driver': 'file'})
315        self.assert_qmp(result, 'return', {})
316
317        result = self.vm.qmp('blockdev-open-tray', id=self.device_name)
318        self.assert_qmp(result, 'return', {})
319
320        self.wait_for_open()
321
322        result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
323                                                       node_name='new')
324        self.assert_qmp(result, 'error/class', 'GenericError')
325
326class TestInitiallyEmpty(GeneralChangeTestsBaseClass):
327    was_empty = True
328
329    def setUp(self):
330        qemu_img('create', '-f', iotests.imgfmt, new_img, '1440k')
331        self.vm = iotests.VM()
332        if self.use_drive:
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 '',
339                            self.device_name))
340        self.vm.launch()
341
342    def tearDown(self):
343        self.vm.shutdown()
344        os.remove(new_img)
345
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', {})
349
350        self.wait_for_open()
351
352        result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
353        # Should be a no-op
354        self.assert_qmp(result, 'return', {})
355
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)
358def create_basic_test_classes():
359    for (media, interface, has_real_tray) in [ ('cdrom', 'ide', True),
360                                               ('cdrom', 'scsi', True),
361                                               ('disk', 'floppy', False) ]:
362
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 }
369
370                name = '%s_%s_%s_%s' % (case.__name__, media, interface,
371                                        'drive' if use_drive else 'blockdev')
372                globals()[name] = type(name, (case, ), attr)
373
374create_basic_test_classes()
375
376class TestChangeReadOnly(ChangeBaseClass):
377    device_name = 'qdev0'
378
379    def setUp(self):
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()
383
384    def tearDown(self):
385        self.vm.shutdown()
386        os.chmod(old_img, 0o666)
387        os.chmod(new_img, 0o666)
388        os.remove(old_img)
389        os.remove(new_img)
390
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)
396        self.vm.launch()
397
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)
401
402        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
403                                                       filename=new_img,
404                                                       format=iotests.imgfmt,
405                                                       read_only_mode='retain')
406        self.assert_qmp(result, 'return', {})
407
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)
411
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)
416        self.vm.launch()
417
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)
421
422        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
423                                                       filename=new_img,
424                                                       format=iotests.imgfmt,
425                                                       read_only_mode='retain')
426        self.assert_qmp(result, 'return', {})
427
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)
431
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)
437        self.vm.launch()
438
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)
442
443        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
444                                                       filename=new_img,
445                                                       format=iotests.imgfmt,
446                                                       read_only_mode='retain')
447        self.assert_qmp(result, 'error/class', 'GenericError')
448
449        self.assertEqual(self.vm.get_qmp_events(wait=False), [])
450
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)
454
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)
459        self.vm.launch()
460
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)
464
465        result = self.vm.qmp('blockdev-change-medium',
466                             id=self.device_name,
467                             filename=new_img,
468                             format=iotests.imgfmt,
469                             read_only_mode='read-write')
470        self.assert_qmp(result, 'return', {})
471
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)
475
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)
480        self.vm.launch()
481
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)
485
486        result = self.vm.qmp('blockdev-change-medium',
487                             id=self.device_name,
488                             filename=new_img,
489                             format=iotests.imgfmt,
490                             read_only_mode='read-only')
491        self.assert_qmp(result, 'return', {})
492
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)
496
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)
500        self.vm.launch()
501
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)
505
506        result = self.vm.qmp('blockdev-change-medium',
507                             id=self.device_name,
508                             filename=new_img,
509                             format=iotests.imgfmt,
510                             read_only_mode='read-only')
511        self.assert_qmp(result, 'return', {})
512
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)
516
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)
522        self.vm.launch()
523
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)
527
528        result = self.vm.qmp('blockdev-change-medium',
529                             id=self.device_name,
530                             filename=new_img,
531                             format=iotests.imgfmt,
532                             read_only_mode='read-write')
533        self.assert_qmp(result, 'error/class', 'GenericError')
534
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)
538
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)
543        self.vm.launch()
544
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)
548
549        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
550                                                       filename=new_img,
551                                                       format=iotests.imgfmt,
552                                                       read_only_mode='retain')
553        self.assert_qmp(result, 'return', {})
554
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)
558
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)
564        self.vm.launch()
565
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)
569
570        result = self.vm.qmp('blockdev-change-medium', id=self.device_name,
571                                                       filename=new_img,
572                                                       format=iotests.imgfmt,
573                                                       read_only_mode='retain')
574        self.assert_qmp(result, 'error/class', 'GenericError')
575
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)
579
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)
584        self.vm.launch()
585
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)
589
590        result = self.vm.qmp('blockdev-add',
591                             node_name='new',
592                             driver=iotests.imgfmt,
593                             read_only=True,
594                             file={'filename': new_img,
595                                    'driver': 'file'})
596        self.assert_qmp(result, 'return', {})
597
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)
601
602        result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
603        self.assert_qmp(result, 'return', {})
604
605        result = self.vm.qmp('query-block')
606        self.assert_qmp_absent(result, 'return[0]/inserted')
607
608        result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
609                                                       node_name='new')
610        self.assert_qmp(result, 'return', {})
611
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)
615
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)
619
620GeneralChangeTestsBaseClass = None
621TestInitiallyFilled = None
622TestInitiallyEmpty = None
623
624
625class TestBlockJobsAfterCycle(ChangeBaseClass):
626    device_name = 'qdev0'
627
628    def setUp(self):
629        qemu_img('create', '-f', iotests.imgfmt, old_img, '1440K')
630
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)
634        self.vm.launch()
635
636        result = self.vm.qmp('query-block')
637        self.assert_qmp(result, 'return[0]/inserted/image/format', 'null-co')
638
639        # For device-less BBs, calling blockdev-open-tray or blockdev-close-tray
640        # is not necessary
641        result = self.vm.qmp('blockdev-remove-medium', id=self.device_name)
642        self.assert_qmp(result, 'return', {})
643
644        result = self.vm.qmp('query-block')
645        self.assert_qmp_absent(result, 'return[0]/inserted')
646
647        result = self.vm.qmp('blockdev-add',
648                             node_name='node0',
649                             driver=iotests.imgfmt,
650                             file={'filename': old_img,
651                                   'driver': 'file'})
652        self.assert_qmp(result, 'return', {})
653
654        result = self.vm.qmp('blockdev-insert-medium', id=self.device_name,
655                                                       node_name='node0')
656        self.assert_qmp(result, 'return', {})
657
658        result = self.vm.qmp('query-block')
659        self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img)
660
661    def tearDown(self):
662        self.vm.shutdown()
663        os.remove(old_img)
664        try:
665            os.remove(new_img)
666        except OSError:
667            pass
668
669    # We need backing file support
670    @iotests.skip_for_formats(('vpc', 'parallels', 'qcow', 'vdi', 'vmdk', 'raw',
671                               'vhdx'))
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', {})
677
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',
682                        old_img)
683
684        result = self.vm.qmp('block-commit', device='drive0')
685        self.assert_qmp(result, 'return', {})
686
687        self.vm.event_wait(name='BLOCK_JOB_READY')
688
689        result = self.vm.qmp('query-block-jobs')
690        self.assert_qmp(result, 'return[0]/device', 'drive0')
691
692        result = self.vm.qmp('block-job-complete', device='drive0')
693        self.assert_qmp(result, 'return', {})
694
695        self.vm.event_wait(name='BLOCK_JOB_COMPLETED')
696
697
698if __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'])
707