3 # Tests for internal snapshot.
5 # Copyright (C) 2013 IBM, Inc.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from iotests
import qemu_img
, qemu_io
28 test_drv_base_name
= 'drive'
30 class ImageSnapshotTestCase(iotests
.QMPTestCase
):
31 image_len
= 120 * 1024 * 1024 # MB
33 def __init__(self
, *args
):
35 super(ImageSnapshotTestCase
, self
).__init
__(*args
)
37 def _setUp(self
, test_img_base_name
, image_num
):
38 self
.vm
= iotests
.VM()
39 for i
in range(0, image_num
):
40 filename
= '%s%d' % (test_img_base_name
, i
)
41 img
= os
.path
.join(iotests
.test_dir
, filename
)
42 device
= '%s%d' % (test_drv_base_name
, i
)
43 qemu_img('create', '-f', iotests
.imgfmt
, img
, str(self
.image_len
))
44 self
.vm
.add_drive(img
)
45 self
.expect
.append({'image': img
, 'device': device
,
47 'snapshots_name_counter': 0})
52 for dev_expect
in self
.expect
:
53 os
.remove(dev_expect
['image'])
55 def createSnapshotInTransaction(self
, snapshot_num
, abort
= False):
57 for dev_expect
in self
.expect
:
58 num
= dev_expect
['snapshots_name_counter']
59 for j
in range(0, snapshot_num
):
60 name
= '%s_sn%d' % (dev_expect
['device'], num
)
63 dev_expect
['snapshots'].append({'name': name
})
64 dev_expect
['snapshots_name_counter'] = num
66 'type': 'blockdev-snapshot-internal-sync',
67 'data': { 'device': dev_expect
['device'],
77 result
= self
.vm
.qmp('transaction', actions
= actions
)
80 self
.assert_qmp(result
, 'error/class', 'GenericError')
82 self
.assert_qmp(result
, 'return', {})
84 def verifySnapshotInfo(self
):
85 result
= self
.vm
.qmp('query-block')
87 # Verify each expected result
88 for dev_expect
in self
.expect
:
89 # 1. Find the returned image value and snapshot info
91 for device
in result
['return']:
92 if device
['device'] == dev_expect
['device']:
93 image_result
= device
['inserted']['image']
95 self
.assertTrue(image_result
!= None)
96 # Do not consider zero snapshot case now
97 sn_list_result
= image_result
['snapshots']
98 sn_list_expect
= dev_expect
['snapshots']
100 # 2. Verify it with expect
101 self
.assertTrue(len(sn_list_result
) == len(sn_list_expect
))
103 for sn_expect
in sn_list_expect
:
105 for sn
in sn_list_result
:
106 if sn_expect
['name'] == sn
['name']:
109 self
.assertTrue(sn_result
!= None)
110 # Fill in the detail info
111 sn_expect
.update(sn_result
)
113 def deleteSnapshot(self
, device
, id = None, name
= None):
114 sn_list_expect
= None
117 self
.assertTrue(id != None or name
!= None)
119 # Fill in the detail info include ID
120 self
.verifySnapshotInfo()
122 #find the expected snapshot list
123 for dev_expect
in self
.expect
:
124 if dev_expect
['device'] == device
:
125 sn_list_expect
= dev_expect
['snapshots']
127 self
.assertTrue(sn_list_expect
!= None)
129 if id != None and name
!= None:
130 for sn
in sn_list_expect
:
131 if sn
['id'] == id and sn
['name'] == name
:
134 self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
140 for sn
in sn_list_expect
:
144 self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
149 for sn
in sn_list_expect
:
150 if sn
['name'] == name
:
153 self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
158 self
.assertTrue(sn_expect
!= None)
160 self
.assert_qmp(result
, 'return', sn_expect
)
161 sn_list_expect
.remove(sn_expect
)
163 class TestSingleTransaction(ImageSnapshotTestCase
):
165 self
._setUp
('test_a.img', 1)
167 def test_create(self
):
168 self
.createSnapshotInTransaction(1)
169 self
.verifySnapshotInfo()
171 def test_error_name_empty(self
):
172 actions
= [{'type': 'blockdev-snapshot-internal-sync',
173 'data': { 'device': self
.expect
[0]['device'],
176 result
= self
.vm
.qmp('transaction', actions
= actions
)
177 self
.assert_qmp(result
, 'error/class', 'GenericError')
179 def test_error_device(self
):
180 actions
= [{'type': 'blockdev-snapshot-internal-sync',
181 'data': { 'device': 'drive_error',
184 result
= self
.vm
.qmp('transaction', actions
= actions
)
185 self
.assert_qmp(result
, 'error/class', 'GenericError')
187 def test_error_exist(self
):
188 self
.createSnapshotInTransaction(1)
189 self
.verifySnapshotInfo()
190 actions
= [{'type': 'blockdev-snapshot-internal-sync',
191 'data': { 'device': self
.expect
[0]['device'],
192 'name': self
.expect
[0]['snapshots'][0] },
194 result
= self
.vm
.qmp('transaction', actions
= actions
)
195 self
.assert_qmp(result
, 'error/class', 'GenericError')
197 class TestMultipleTransaction(ImageSnapshotTestCase
):
199 self
._setUp
('test_b.img', 2)
201 def test_create(self
):
202 self
.createSnapshotInTransaction(3)
203 self
.verifySnapshotInfo()
205 def test_abort(self
):
206 self
.createSnapshotInTransaction(2)
207 self
.verifySnapshotInfo()
208 self
.createSnapshotInTransaction(3, abort
= True)
209 self
.verifySnapshotInfo()
211 class TestSnapshotDelete(ImageSnapshotTestCase
):
213 self
._setUp
('test_c.img', 1)
215 def test_delete_with_id(self
):
216 self
.createSnapshotInTransaction(2)
217 self
.verifySnapshotInfo()
218 self
.deleteSnapshot(self
.expect
[0]['device'],
219 id = self
.expect
[0]['snapshots'][0]['id'])
220 self
.verifySnapshotInfo()
222 def test_delete_with_name(self
):
223 self
.createSnapshotInTransaction(3)
224 self
.verifySnapshotInfo()
225 self
.deleteSnapshot(self
.expect
[0]['device'],
226 name
= self
.expect
[0]['snapshots'][1]['name'])
227 self
.verifySnapshotInfo()
229 def test_delete_with_id_and_name(self
):
230 self
.createSnapshotInTransaction(4)
231 self
.verifySnapshotInfo()
232 self
.deleteSnapshot(self
.expect
[0]['device'],
233 id = self
.expect
[0]['snapshots'][2]['id'],
234 name
= self
.expect
[0]['snapshots'][2]['name'])
235 self
.verifySnapshotInfo()
238 def test_error_device(self
):
239 result
= self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
240 device
= 'drive_error',
242 self
.assert_qmp(result
, 'error/class', 'GenericError')
244 def test_error_no_id_and_name(self
):
245 result
= self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
246 device
= self
.expect
[0]['device'])
247 self
.assert_qmp(result
, 'error/class', 'GenericError')
249 def test_error_snapshot_not_exist(self
):
250 self
.createSnapshotInTransaction(2)
251 self
.verifySnapshotInfo()
252 result
= self
.vm
.qmp('blockdev-snapshot-delete-internal-sync',
253 device
= self
.expect
[0]['device'],
254 id = self
.expect
[0]['snapshots'][0]['id'],
255 name
= self
.expect
[0]['snapshots'][1]['name'])
256 self
.assert_qmp(result
, 'error/class', 'GenericError')
258 if __name__
== '__main__':
259 iotests
.main(supported_fmts
=['qcow2'])