These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / tests / qemu-iotests / 139
1 #!/usr/bin/env python
2 #
3 # Test cases for the QMP 'x-blockdev-del' command
4 #
5 # Copyright (C) 2015 Igalia, S.L.
6 # Author: Alberto Garcia <berto@igalia.com>
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
22 import os
23 import iotests
24 import time
25
26 base_img = os.path.join(iotests.test_dir, 'base.img')
27 new_img = os.path.join(iotests.test_dir, 'new.img')
28
29 class TestBlockdevDel(iotests.QMPTestCase):
30
31     def setUp(self):
32         iotests.qemu_img('create', '-f', iotests.imgfmt, base_img, '1M')
33         self.vm = iotests.VM()
34         self.vm.launch()
35
36     def tearDown(self):
37         self.vm.shutdown()
38         os.remove(base_img)
39         if os.path.isfile(new_img):
40             os.remove(new_img)
41
42     # Check whether a BlockBackend exists
43     def checkBlockBackend(self, backend, node, must_exist = True):
44         result = self.vm.qmp('query-block')
45         backends = filter(lambda x: x['device'] == backend, result['return'])
46         self.assertLessEqual(len(backends), 1)
47         self.assertEqual(must_exist, len(backends) == 1)
48         if must_exist:
49             if node:
50                 self.assertEqual(backends[0]['inserted']['node-name'], node)
51             else:
52                 self.assertFalse(backends[0].has_key('inserted'))
53
54     # Check whether a BlockDriverState exists
55     def checkBlockDriverState(self, node, must_exist = True):
56         result = self.vm.qmp('query-named-block-nodes')
57         nodes = filter(lambda x: x['node-name'] == node, result['return'])
58         self.assertLessEqual(len(nodes), 1)
59         self.assertEqual(must_exist, len(nodes) == 1)
60
61     # Add a new BlockBackend (with its attached BlockDriverState)
62     def addBlockBackend(self, backend, node):
63         file_node = '%s_file' % node
64         self.checkBlockBackend(backend, node, False)
65         self.checkBlockDriverState(node, False)
66         self.checkBlockDriverState(file_node, False)
67         opts = {'driver': iotests.imgfmt,
68                 'id': backend,
69                 'node-name': node,
70                 'file': {'driver': 'file',
71                          'node-name': file_node,
72                          'filename': base_img}}
73         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
74         self.assert_qmp(result, 'return', {})
75         self.checkBlockBackend(backend, node)
76         self.checkBlockDriverState(node)
77         self.checkBlockDriverState(file_node)
78
79     # Add a BlockDriverState without a BlockBackend
80     def addBlockDriverState(self, node):
81         file_node = '%s_file' % node
82         self.checkBlockDriverState(node, False)
83         self.checkBlockDriverState(file_node, False)
84         opts = {'driver': iotests.imgfmt,
85                 'node-name': node,
86                 'file': {'driver': 'file',
87                          'node-name': file_node,
88                          'filename': base_img}}
89         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
90         self.assert_qmp(result, 'return', {})
91         self.checkBlockDriverState(node)
92         self.checkBlockDriverState(file_node)
93
94     # Add a BlockDriverState that will be used as overlay for the base_img BDS
95     def addBlockDriverStateOverlay(self, node):
96         self.checkBlockDriverState(node, False)
97         iotests.qemu_img('create', '-f', iotests.imgfmt,
98                          '-b', base_img, new_img, '1M')
99         opts = {'driver': iotests.imgfmt,
100                 'node-name': node,
101                 'backing': '',
102                 'file': {'driver': 'file',
103                          'filename': new_img}}
104         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
105         self.assert_qmp(result, 'return', {})
106         self.checkBlockDriverState(node)
107
108     # Delete a BlockBackend
109     def delBlockBackend(self, backend, node, expect_error = False,
110                         destroys_media = True):
111         self.checkBlockBackend(backend, node)
112         if node:
113             self.checkBlockDriverState(node)
114         result = self.vm.qmp('x-blockdev-del', id = backend)
115         if expect_error:
116             self.assert_qmp(result, 'error/class', 'GenericError')
117             if node:
118                 self.checkBlockDriverState(node)
119         else:
120             self.assert_qmp(result, 'return', {})
121             if node:
122                 self.checkBlockDriverState(node, not destroys_media)
123         self.checkBlockBackend(backend, node, must_exist = expect_error)
124
125     # Delete a BlockDriverState
126     def delBlockDriverState(self, node, expect_error = False):
127         self.checkBlockDriverState(node)
128         result = self.vm.qmp('x-blockdev-del', node_name = node)
129         if expect_error:
130             self.assert_qmp(result, 'error/class', 'GenericError')
131         else:
132             self.assert_qmp(result, 'return', {})
133         self.checkBlockDriverState(node, expect_error)
134
135     # Add a device model
136     def addDeviceModel(self, device, backend):
137         result = self.vm.qmp('device_add', id = device,
138                              driver = 'virtio-blk-pci', drive = backend)
139         self.assert_qmp(result, 'return', {})
140
141     # Delete a device model
142     def delDeviceModel(self, device):
143         result = self.vm.qmp('device_del', id = device)
144         self.assert_qmp(result, 'return', {})
145
146         result = self.vm.qmp('system_reset')
147         self.assert_qmp(result, 'return', {})
148
149         device_path = '/machine/peripheral/%s/virtio-backend' % device
150         event = self.vm.event_wait(name="DEVICE_DELETED",
151                                    match={'data': {'path': device_path}})
152         self.assertNotEqual(event, None)
153
154         event = self.vm.event_wait(name="DEVICE_DELETED",
155                                    match={'data': {'device': device}})
156         self.assertNotEqual(event, None)
157
158     # Remove a BlockDriverState
159     def ejectDrive(self, backend, node, expect_error = False,
160                    destroys_media = True):
161         self.checkBlockBackend(backend, node)
162         self.checkBlockDriverState(node)
163         result = self.vm.qmp('eject', device = backend)
164         if expect_error:
165             self.assert_qmp(result, 'error/class', 'GenericError')
166             self.checkBlockDriverState(node)
167             self.checkBlockBackend(backend, node)
168         else:
169             self.assert_qmp(result, 'return', {})
170             self.checkBlockDriverState(node, not destroys_media)
171             self.checkBlockBackend(backend, None)
172
173     # Insert a BlockDriverState
174     def insertDrive(self, backend, node):
175         self.checkBlockBackend(backend, None)
176         self.checkBlockDriverState(node)
177         result = self.vm.qmp('x-blockdev-insert-medium',
178                              device = backend, node_name = node)
179         self.assert_qmp(result, 'return', {})
180         self.checkBlockBackend(backend, node)
181         self.checkBlockDriverState(node)
182
183     # Create a snapshot using 'blockdev-snapshot-sync'
184     def createSnapshotSync(self, node, overlay):
185         self.checkBlockDriverState(node)
186         self.checkBlockDriverState(overlay, False)
187         opts = {'node-name': node,
188                 'snapshot-file': new_img,
189                 'snapshot-node-name': overlay,
190                 'format': iotests.imgfmt}
191         result = self.vm.qmp('blockdev-snapshot-sync', conv_keys=False, **opts)
192         self.assert_qmp(result, 'return', {})
193         self.checkBlockDriverState(node)
194         self.checkBlockDriverState(overlay)
195
196     # Create a snapshot using 'blockdev-snapshot'
197     def createSnapshot(self, node, overlay):
198         self.checkBlockDriverState(node)
199         self.checkBlockDriverState(overlay)
200         result = self.vm.qmp('blockdev-snapshot',
201                              node = node, overlay = overlay)
202         self.assert_qmp(result, 'return', {})
203         self.checkBlockDriverState(node)
204         self.checkBlockDriverState(overlay)
205
206     # Create a mirror
207     def createMirror(self, backend, node, new_node):
208         self.checkBlockBackend(backend, node)
209         self.checkBlockDriverState(new_node, False)
210         opts = {'device': backend,
211                 'target': new_img,
212                 'node-name': new_node,
213                 'sync': 'top',
214                 'format': iotests.imgfmt}
215         result = self.vm.qmp('drive-mirror', conv_keys=False, **opts)
216         self.assert_qmp(result, 'return', {})
217         self.checkBlockBackend(backend, node)
218         self.checkBlockDriverState(new_node)
219
220     # Complete an existing block job
221     def completeBlockJob(self, backend, node_before, node_after):
222         self.checkBlockBackend(backend, node_before)
223         result = self.vm.qmp('block-job-complete', device=backend)
224         self.assert_qmp(result, 'return', {})
225         self.wait_until_completed(backend)
226         self.checkBlockBackend(backend, node_after)
227
228     # Add a BlkDebug node
229     # Note that the purpose of this is to test the x-blockdev-del
230     # sanity checks, not to create a usable blkdebug drive
231     def addBlkDebug(self, debug, node):
232         self.checkBlockDriverState(node, False)
233         self.checkBlockDriverState(debug, False)
234         image = {'driver': iotests.imgfmt,
235                  'node-name': node,
236                  'file': {'driver': 'file',
237                           'filename': base_img}}
238         opts = {'driver': 'blkdebug',
239                 'node-name': debug,
240                 'image': image}
241         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
242         self.assert_qmp(result, 'return', {})
243         self.checkBlockDriverState(node)
244         self.checkBlockDriverState(debug)
245
246     # Add a BlkVerify node
247     # Note that the purpose of this is to test the x-blockdev-del
248     # sanity checks, not to create a usable blkverify drive
249     def addBlkVerify(self, blkverify, test, raw):
250         self.checkBlockDriverState(test, False)
251         self.checkBlockDriverState(raw, False)
252         self.checkBlockDriverState(blkverify, False)
253         iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
254         node_0 = {'driver': iotests.imgfmt,
255                   'node-name': test,
256                   'file': {'driver': 'file',
257                            'filename': base_img}}
258         node_1 = {'driver': iotests.imgfmt,
259                   'node-name': raw,
260                   'file': {'driver': 'file',
261                            'filename': new_img}}
262         opts = {'driver': 'blkverify',
263                 'node-name': blkverify,
264                 'test': node_0,
265                 'raw': node_1}
266         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
267         self.assert_qmp(result, 'return', {})
268         self.checkBlockDriverState(test)
269         self.checkBlockDriverState(raw)
270         self.checkBlockDriverState(blkverify)
271
272     # Add a Quorum node
273     def addQuorum(self, quorum, child0, child1):
274         self.checkBlockDriverState(child0, False)
275         self.checkBlockDriverState(child1, False)
276         self.checkBlockDriverState(quorum, False)
277         iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
278         child_0 = {'driver': iotests.imgfmt,
279                    'node-name': child0,
280                    'file': {'driver': 'file',
281                             'filename': base_img}}
282         child_1 = {'driver': iotests.imgfmt,
283                    'node-name': child1,
284                    'file': {'driver': 'file',
285                             'filename': new_img}}
286         opts = {'driver': 'quorum',
287                 'node-name': quorum,
288                 'vote-threshold': 1,
289                 'children': [ child_0, child_1 ]}
290         result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
291         self.assert_qmp(result, 'return', {})
292         self.checkBlockDriverState(child0)
293         self.checkBlockDriverState(child1)
294         self.checkBlockDriverState(quorum)
295
296     ########################
297     # The tests start here #
298     ########################
299
300     def testWrongParameters(self):
301         self.addBlockBackend('drive0', 'node0')
302         result = self.vm.qmp('x-blockdev-del')
303         self.assert_qmp(result, 'error/class', 'GenericError')
304         result = self.vm.qmp('x-blockdev-del', id='drive0', node_name='node0')
305         self.assert_qmp(result, 'error/class', 'GenericError')
306         self.delBlockBackend('drive0', 'node0')
307
308     def testBlockBackend(self):
309         self.addBlockBackend('drive0', 'node0')
310         # You cannot delete a BDS that is attached to a backend
311         self.delBlockDriverState('node0', expect_error = True)
312         self.delBlockBackend('drive0', 'node0')
313
314     def testBlockDriverState(self):
315         self.addBlockDriverState('node0')
316         # You cannot delete a file BDS directly
317         self.delBlockDriverState('node0_file', expect_error = True)
318         self.delBlockDriverState('node0')
319
320     def testEject(self):
321         self.addBlockBackend('drive0', 'node0')
322         self.ejectDrive('drive0', 'node0')
323         self.delBlockBackend('drive0', None)
324
325     def testDeviceModel(self):
326         self.addBlockBackend('drive0', 'node0')
327         self.addDeviceModel('device0', 'drive0')
328         self.ejectDrive('drive0', 'node0', expect_error = True)
329         self.delBlockBackend('drive0', 'node0', expect_error = True)
330         self.delDeviceModel('device0')
331         self.delBlockBackend('drive0', 'node0')
332
333     def testAttachMedia(self):
334         # This creates a BlockBackend and removes its media
335         self.addBlockBackend('drive0', 'node0')
336         self.ejectDrive('drive0', 'node0')
337         # This creates a new BlockDriverState and inserts it into the backend
338         self.addBlockDriverState('node1')
339         self.insertDrive('drive0', 'node1')
340         # The backend can't be removed: the new BDS has an extra reference
341         self.delBlockBackend('drive0', 'node1', expect_error = True)
342         self.delBlockDriverState('node1', expect_error = True)
343         # The BDS still exists after being ejected, but now it can be removed
344         self.ejectDrive('drive0', 'node1', destroys_media = False)
345         self.delBlockDriverState('node1')
346         self.delBlockBackend('drive0', None)
347
348     def testSnapshotSync(self):
349         self.addBlockBackend('drive0', 'node0')
350         self.createSnapshotSync('node0', 'overlay0')
351         # This fails because node0 is now being used as a backing image
352         self.delBlockDriverState('node0', expect_error = True)
353         # This succeeds because overlay0 only has the backend reference
354         self.delBlockBackend('drive0', 'overlay0')
355         self.checkBlockDriverState('node0', False)
356
357     def testSnapshot(self):
358         self.addBlockBackend('drive0', 'node0')
359         self.addBlockDriverStateOverlay('overlay0')
360         self.createSnapshot('node0', 'overlay0')
361         self.delBlockBackend('drive0', 'overlay0', expect_error = True)
362         self.delBlockDriverState('node0', expect_error = True)
363         self.delBlockDriverState('overlay0', expect_error = True)
364         self.ejectDrive('drive0', 'overlay0', destroys_media = False)
365         self.delBlockBackend('drive0', None)
366         self.delBlockDriverState('node0', expect_error = True)
367         self.delBlockDriverState('overlay0')
368         self.checkBlockDriverState('node0', False)
369
370     def testMirror(self):
371         self.addBlockBackend('drive0', 'node0')
372         self.createMirror('drive0', 'node0', 'mirror0')
373         # The block job prevents removing the device
374         self.delBlockBackend('drive0', 'node0', expect_error = True)
375         self.delBlockDriverState('node0', expect_error = True)
376         self.delBlockDriverState('mirror0', expect_error = True)
377         self.wait_ready('drive0')
378         self.completeBlockJob('drive0', 'node0', 'mirror0')
379         self.assert_no_active_block_jobs()
380         self.checkBlockDriverState('node0', False)
381         # This succeeds because the backend now points to mirror0
382         self.delBlockBackend('drive0', 'mirror0')
383
384     def testBlkDebug(self):
385         self.addBlkDebug('debug0', 'node0')
386         # 'node0' is used by the blkdebug node
387         self.delBlockDriverState('node0', expect_error = True)
388         # But we can remove the blkdebug node directly
389         self.delBlockDriverState('debug0')
390         self.checkBlockDriverState('node0', False)
391
392     def testBlkVerify(self):
393         self.addBlkVerify('verify0', 'node0', 'node1')
394         # We cannot remove the children of a blkverify device
395         self.delBlockDriverState('node0', expect_error = True)
396         self.delBlockDriverState('node1', expect_error = True)
397         # But we can remove the blkverify node directly
398         self.delBlockDriverState('verify0')
399         self.checkBlockDriverState('node0', False)
400         self.checkBlockDriverState('node1', False)
401
402     def testQuorum(self):
403         if not 'quorum' in iotests.qemu_img_pipe('--help'):
404             return
405         self.addQuorum('quorum0', 'node0', 'node1')
406         # We cannot remove the children of a Quorum device
407         self.delBlockDriverState('node0', expect_error = True)
408         self.delBlockDriverState('node1', expect_error = True)
409         # But we can remove the Quorum node directly
410         self.delBlockDriverState('quorum0')
411         self.checkBlockDriverState('node0', False)
412         self.checkBlockDriverState('node1', False)
413
414
415 if __name__ == '__main__':
416     iotests.main(supported_fmts=["qcow2"])