3 # Test cases for the QMP 'blockdev-del' command
5 # Copyright (C) 2015 Igalia, S.L.
6 # Author: Alberto Garcia <berto@igalia.com>
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 base_img
= os
.path
.join(iotests
.test_dir
, 'base.img')
27 new_img
= os
.path
.join(iotests
.test_dir
, 'new.img')
28 if iotests
.qemu_default_machine
== 's390-ccw-virtio':
29 default_virtio_blk
= 'virtio-blk-ccw'
31 default_virtio_blk
= 'virtio-blk-pci'
33 class TestBlockdevDel(iotests
.QMPTestCase
):
36 iotests
.qemu_img('create', '-f', iotests
.imgfmt
, base_img
, '1M')
37 self
.vm
= iotests
.VM()
38 if iotests
.qemu_default_machine
== 's390-ccw-virtio':
39 self
.vm
.add_device("virtio-scsi-ccw,id=virtio-scsi")
41 self
.vm
.add_device("virtio-scsi-pci,id=virtio-scsi")
48 if os
.path
.isfile(new_img
):
51 # Check whether a BlockDriverState exists
52 def checkBlockDriverState(self
, node
, must_exist
= True):
53 result
= self
.vm
.qmp('query-named-block-nodes')
54 nodes
= [x
for x
in result
['return'] if x
['node-name'] == node
]
55 self
.assertLessEqual(len(nodes
), 1)
56 self
.assertEqual(must_exist
, len(nodes
) == 1)
58 # Add a BlockDriverState without a BlockBackend
59 def addBlockDriverState(self
, node
):
60 file_node
= '%s_file' % node
61 self
.checkBlockDriverState(node
, False)
62 self
.checkBlockDriverState(file_node
, False)
63 opts
= {'driver': iotests
.imgfmt
,
65 'file': {'driver': 'file',
66 'node-name': file_node
,
67 'filename': base_img
}}
68 result
= self
.vm
.qmp('blockdev-add', conv_keys
= False, **opts
)
69 self
.assert_qmp(result
, 'return', {})
70 self
.checkBlockDriverState(node
)
71 self
.checkBlockDriverState(file_node
)
73 # Add a BlockDriverState that will be used as overlay for the base_img BDS
74 def addBlockDriverStateOverlay(self
, node
):
75 self
.checkBlockDriverState(node
, False)
76 iotests
.qemu_img('create', '-u', '-f', iotests
.imgfmt
,
77 '-b', base_img
, new_img
, '1M')
78 opts
= {'driver': iotests
.imgfmt
,
81 'file': {'driver': 'file',
83 result
= self
.vm
.qmp('blockdev-add', conv_keys
= False, **opts
)
84 self
.assert_qmp(result
, 'return', {})
85 self
.checkBlockDriverState(node
)
87 # Delete a BlockDriverState
88 def delBlockDriverState(self
, node
, expect_error
= False):
89 self
.checkBlockDriverState(node
)
90 result
= self
.vm
.qmp('blockdev-del', node_name
= node
)
92 self
.assert_qmp(result
, 'error/class', 'GenericError')
94 self
.assert_qmp(result
, 'return', {})
95 self
.checkBlockDriverState(node
, expect_error
)
98 def addDeviceModel(self
, device
, backend
, driver
= default_virtio_blk
):
99 result
= self
.vm
.qmp('device_add', id = device
,
100 driver
= driver
, drive
= backend
)
101 self
.assert_qmp(result
, 'return', {})
103 # Delete a device model
104 def delDeviceModel(self
, device
, is_virtio_blk
= True):
105 result
= self
.vm
.qmp('device_del', id = device
)
106 self
.assert_qmp(result
, 'return', {})
108 result
= self
.vm
.qmp('system_reset')
109 self
.assert_qmp(result
, 'return', {})
112 device_path
= '/machine/peripheral/%s/virtio-backend' % device
113 event
= self
.vm
.event_wait(name
="DEVICE_DELETED",
114 match
={'data': {'path': device_path
}})
115 self
.assertNotEqual(event
, None)
117 event
= self
.vm
.event_wait(name
="DEVICE_DELETED",
118 match
={'data': {'device': device
}})
119 self
.assertNotEqual(event
, None)
121 # Remove a BlockDriverState
122 def ejectDrive(self
, device
, node
, expect_error
= False,
123 destroys_media
= True):
124 self
.checkBlockDriverState(node
)
125 result
= self
.vm
.qmp('eject', id = device
)
127 self
.assert_qmp(result
, 'error/class', 'GenericError')
128 self
.checkBlockDriverState(node
)
130 self
.assert_qmp(result
, 'return', {})
131 self
.checkBlockDriverState(node
, not destroys_media
)
133 # Insert a BlockDriverState
134 def insertDrive(self
, device
, node
):
135 self
.checkBlockDriverState(node
)
136 result
= self
.vm
.qmp('blockdev-insert-medium',
137 id = device
, node_name
= node
)
138 self
.assert_qmp(result
, 'return', {})
139 self
.checkBlockDriverState(node
)
141 # Create a snapshot using 'blockdev-snapshot-sync'
142 def createSnapshotSync(self
, node
, overlay
):
143 self
.checkBlockDriverState(node
)
144 self
.checkBlockDriverState(overlay
, False)
145 opts
= {'node-name': node
,
146 'snapshot-file': new_img
,
147 'snapshot-node-name': overlay
,
148 'format': iotests
.imgfmt
}
149 result
= self
.vm
.qmp('blockdev-snapshot-sync', conv_keys
=False, **opts
)
150 self
.assert_qmp(result
, 'return', {})
151 self
.checkBlockDriverState(node
)
152 self
.checkBlockDriverState(overlay
)
154 # Create a snapshot using 'blockdev-snapshot'
155 def createSnapshot(self
, node
, overlay
):
156 self
.checkBlockDriverState(node
)
157 self
.checkBlockDriverState(overlay
)
158 result
= self
.vm
.qmp('blockdev-snapshot',
159 node
= node
, overlay
= overlay
)
160 self
.assert_qmp(result
, 'return', {})
161 self
.checkBlockDriverState(node
)
162 self
.checkBlockDriverState(overlay
)
165 def createMirror(self
, node
, new_node
):
166 self
.checkBlockDriverState(new_node
, False)
167 opts
= {'device': node
,
170 'node-name': new_node
,
172 'format': iotests
.imgfmt
}
173 result
= self
.vm
.qmp('drive-mirror', conv_keys
=False, **opts
)
174 self
.assert_qmp(result
, 'return', {})
175 self
.checkBlockDriverState(new_node
)
177 # Complete an existing block job
178 def completeBlockJob(self
, id, node_before
, node_after
):
179 result
= self
.vm
.qmp('block-job-complete', device
=id)
180 self
.assert_qmp(result
, 'return', {})
181 self
.wait_until_completed(id)
183 # Add a BlkDebug node
184 # Note that the purpose of this is to test the blockdev-del
185 # sanity checks, not to create a usable blkdebug drive
186 def addBlkDebug(self
, debug
, node
):
187 self
.checkBlockDriverState(node
, False)
188 self
.checkBlockDriverState(debug
, False)
189 image
= {'driver': iotests
.imgfmt
,
191 'file': {'driver': 'file',
192 'filename': base_img
}}
193 opts
= {'driver': 'blkdebug',
196 result
= self
.vm
.qmp('blockdev-add', conv_keys
= False, **opts
)
197 self
.assert_qmp(result
, 'return', {})
198 self
.checkBlockDriverState(node
)
199 self
.checkBlockDriverState(debug
)
201 # Add a BlkVerify node
202 # Note that the purpose of this is to test the blockdev-del
203 # sanity checks, not to create a usable blkverify drive
204 def addBlkVerify(self
, blkverify
, test
, raw
):
205 self
.checkBlockDriverState(test
, False)
206 self
.checkBlockDriverState(raw
, False)
207 self
.checkBlockDriverState(blkverify
, False)
208 iotests
.qemu_img('create', '-f', iotests
.imgfmt
, new_img
, '1M')
209 node_0
= {'driver': iotests
.imgfmt
,
211 'file': {'driver': 'file',
212 'filename': base_img
}}
213 node_1
= {'driver': iotests
.imgfmt
,
215 'file': {'driver': 'file',
216 'filename': new_img
}}
217 opts
= {'driver': 'blkverify',
218 'node-name': blkverify
,
221 result
= self
.vm
.qmp('blockdev-add', conv_keys
= False, **opts
)
222 self
.assert_qmp(result
, 'return', {})
223 self
.checkBlockDriverState(test
)
224 self
.checkBlockDriverState(raw
)
225 self
.checkBlockDriverState(blkverify
)
228 def addQuorum(self
, quorum
, child0
, child1
):
229 self
.checkBlockDriverState(child0
, False)
230 self
.checkBlockDriverState(child1
, False)
231 self
.checkBlockDriverState(quorum
, False)
232 iotests
.qemu_img('create', '-f', iotests
.imgfmt
, new_img
, '1M')
233 child_0
= {'driver': iotests
.imgfmt
,
235 'file': {'driver': 'file',
236 'filename': base_img
}}
237 child_1
= {'driver': iotests
.imgfmt
,
239 'file': {'driver': 'file',
240 'filename': new_img
}}
241 opts
= {'driver': 'quorum',
244 'children': [ child_0
, child_1
]}
245 result
= self
.vm
.qmp('blockdev-add', conv_keys
= False, **opts
)
246 self
.assert_qmp(result
, 'return', {})
247 self
.checkBlockDriverState(child0
)
248 self
.checkBlockDriverState(child1
)
249 self
.checkBlockDriverState(quorum
)
251 ########################
252 # The tests start here #
253 ########################
255 def testBlockDriverState(self
):
256 self
.addBlockDriverState('node0')
257 # You cannot delete a file BDS directly
258 self
.delBlockDriverState('node0_file', expect_error
= True)
259 self
.delBlockDriverState('node0')
261 def testDeviceModel(self
):
262 self
.addBlockDriverState('node0')
263 self
.addDeviceModel('device0', 'node0')
264 self
.ejectDrive('device0', 'node0', expect_error
= True)
265 self
.delBlockDriverState('node0', expect_error
= True)
266 self
.delDeviceModel('device0')
267 self
.delBlockDriverState('node0')
269 def testAttachMedia(self
):
270 # This creates a BlockBackend and removes its media
271 self
.addBlockDriverState('node0')
272 self
.addDeviceModel('device0', 'node0', 'scsi-cd')
273 self
.ejectDrive('device0', 'node0', destroys_media
= False)
274 self
.delBlockDriverState('node0')
276 # This creates a new BlockDriverState and inserts it into the device
277 self
.addBlockDriverState('node1')
278 self
.insertDrive('device0', 'node1')
279 # The node can't be removed: the new device has an extra reference
280 self
.delBlockDriverState('node1', expect_error
= True)
281 # The BDS still exists after being ejected, but now it can be removed
282 self
.ejectDrive('device0', 'node1', destroys_media
= False)
283 self
.delBlockDriverState('node1')
284 self
.delDeviceModel('device0', False)
286 def testSnapshotSync(self
):
287 self
.addBlockDriverState('node0')
288 self
.addDeviceModel('device0', 'node0')
289 self
.createSnapshotSync('node0', 'overlay0')
290 # This fails because node0 is now being used as a backing image
291 self
.delBlockDriverState('node0', expect_error
= True)
292 self
.delBlockDriverState('overlay0', expect_error
= True)
293 # This succeeds because device0 only has the backend reference
294 self
.delDeviceModel('device0')
295 # FIXME Would still be there if blockdev-snapshot-sync took a ref
296 self
.checkBlockDriverState('overlay0', False)
297 self
.delBlockDriverState('node0')
299 def testSnapshot(self
):
300 self
.addBlockDriverState('node0')
301 self
.addDeviceModel('device0', 'node0', 'scsi-cd')
302 self
.addBlockDriverStateOverlay('overlay0')
303 self
.createSnapshot('node0', 'overlay0')
304 self
.delBlockDriverState('node0', expect_error
= True)
305 self
.delBlockDriverState('overlay0', expect_error
= True)
306 self
.ejectDrive('device0', 'overlay0', destroys_media
= False)
307 self
.delBlockDriverState('node0', expect_error
= True)
308 self
.delBlockDriverState('overlay0')
309 self
.delBlockDriverState('node0')
311 def testMirror(self
):
312 self
.addBlockDriverState('node0')
313 self
.addDeviceModel('device0', 'node0', 'scsi-cd')
314 self
.createMirror('node0', 'mirror0')
315 # The block job prevents removing the device
316 self
.delBlockDriverState('node0', expect_error
= True)
317 self
.delBlockDriverState('mirror0', expect_error
= True)
318 self
.wait_ready('node0')
319 self
.completeBlockJob('node0', 'node0', 'mirror0')
320 self
.assert_no_active_block_jobs()
321 # This succeeds because the device now points to mirror0
322 self
.delBlockDriverState('node0')
323 self
.delBlockDriverState('mirror0', expect_error
= True)
324 self
.delDeviceModel('device0', False)
325 # FIXME mirror0 disappears, drive-mirror doesn't take a reference
326 #self.delBlockDriverState('mirror0')
328 @iotests.skip_if_unsupported(['blkdebug'])
329 def testBlkDebug(self
):
330 self
.addBlkDebug('debug0', 'node0')
331 # 'node0' is used by the blkdebug node
332 self
.delBlockDriverState('node0', expect_error
= True)
333 # But we can remove the blkdebug node directly
334 self
.delBlockDriverState('debug0')
335 self
.checkBlockDriverState('node0', False)
337 @iotests.skip_if_unsupported(['blkverify'])
338 def testBlkVerify(self
):
339 self
.addBlkVerify('verify0', 'node0', 'node1')
340 # We cannot remove the children of a blkverify device
341 self
.delBlockDriverState('node0', expect_error
= True)
342 self
.delBlockDriverState('node1', expect_error
= True)
343 # But we can remove the blkverify node directly
344 self
.delBlockDriverState('verify0')
345 self
.checkBlockDriverState('node0', False)
346 self
.checkBlockDriverState('node1', False)
348 @iotests.skip_if_unsupported(['quorum'])
349 def testQuorum(self
):
350 if not iotests
.supports_quorum():
353 self
.addQuorum('quorum0', 'node0', 'node1')
354 # We cannot remove the children of a Quorum device
355 self
.delBlockDriverState('node0', expect_error
= True)
356 self
.delBlockDriverState('node1', expect_error
= True)
357 # But we can remove the Quorum node directly
358 self
.delBlockDriverState('quorum0')
359 self
.checkBlockDriverState('node0', False)
360 self
.checkBlockDriverState('node1', False)
363 if __name__
== '__main__':
364 iotests
.main(supported_fmts
=["qcow2"])