13
13
# License for the specific language governing permissions and limitations
14
14
# under the License.
15
15
16
+ import fixtures
16
17
import json
17
18
import os
18
19
from unittest import mock
24
25
import glance .async_ .flows .api_image_import as import_flow
25
26
import glance .async_ .flows .plugins .image_conversion as image_conversion
26
27
from glance .async_ import utils as async_utils
28
+ from glance .common import format_inspector
27
29
from glance .common import utils
28
30
from glance import domain
29
31
from glance import gateway
@@ -90,6 +92,11 @@ def setUp(self):
90
92
self .image_id ,
91
93
self .task .task_id )
92
94
95
+ self .inspector_mock = mock .MagicMock ()
96
+ self .useFixture (fixtures .MockPatch ('glance.common.format_inspector.'
97
+ 'get_inspector' ,
98
+ self .inspector_mock ))
99
+
93
100
@mock .patch .object (os , 'stat' )
94
101
@mock .patch .object (os , 'remove' )
95
102
def test_image_convert_success (self , mock_os_remove , mock_os_stat ):
@@ -104,7 +111,7 @@ def test_image_convert_success(self, mock_os_remove, mock_os_stat):
104
111
image = mock .MagicMock (image_id = self .image_id , virtual_size = None ,
105
112
extra_properties = {
106
113
'os_glance_import_task' : self .task .task_id },
107
- disk_format = 'qcow2 ' )
114
+ disk_format = 'raw ' )
108
115
self .img_repo .get .return_value = image
109
116
110
117
with mock .patch .object (processutils , 'execute' ) as exc_mock :
@@ -126,7 +133,7 @@ def test_image_convert_success(self, mock_os_remove, mock_os_stat):
126
133
self .assertEqual (456 , image .virtual_size )
127
134
self .assertEqual (123 , image .size )
128
135
129
- def _setup_image_convert_info_fail (self ):
136
+ def _setup_image_convert_info_fail (self , disk_format = 'qcow2' ):
130
137
image_convert = image_conversion ._ConvertImage (self .context ,
131
138
self .task .task_id ,
132
139
self .task_type ,
@@ -136,7 +143,7 @@ def _setup_image_convert_info_fail(self):
136
143
image = mock .MagicMock (image_id = self .image_id , virtual_size = None ,
137
144
extra_properties = {
138
145
'os_glance_import_task' : self .task .task_id },
139
- disk_format = 'qcow2' )
146
+ disk_format = disk_format )
140
147
self .img_repo .get .return_value = image
141
148
return image_convert
142
149
@@ -148,6 +155,7 @@ def test_image_convert_fails_inspection(self):
148
155
convert .execute , 'file:///test/path.raw' )
149
156
exc_mock .assert_called_once_with (
150
157
'qemu-img' , 'info' ,
158
+ '-f' , 'qcow2' ,
151
159
'--output=json' ,
152
160
'/test/path.raw' ,
153
161
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
@@ -164,6 +172,7 @@ def test_image_convert_inspection_reports_error(self):
164
172
convert .execute , 'file:///test/path.raw' )
165
173
exc_mock .assert_called_once_with (
166
174
'qemu-img' , 'info' ,
175
+ '-f' , 'qcow2' ,
167
176
'--output=json' ,
168
177
'/test/path.raw' ,
169
178
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
@@ -200,14 +209,44 @@ def test_image_convert_invalid_qcow_data_file(self):
200
209
self .assertEqual ('QCOW images with data-file set are not allowed' ,
201
210
str (e ))
202
211
212
+ def test_image_convert_no_inspector_match (self ):
213
+ convert = self ._setup_image_convert_info_fail ()
214
+ self .inspector_mock .return_value = None
215
+ self .assertRaisesRegex (RuntimeError ,
216
+ 'Unable to convert from format' ,
217
+ convert .execute , 'file:///test/path.hpfs' )
218
+
219
+ def test_image_convert_fails_inspection_safety_check (self ):
220
+ convert = self ._setup_image_convert_info_fail ()
221
+ inspector = self .inspector_mock .return_value .from_file .return_value
222
+ inspector .safety_check .return_value = False
223
+ self .assertRaisesRegex (RuntimeError ,
224
+ 'Image has disallowed configuration' ,
225
+ convert .execute , 'file:///test/path.qcow' )
226
+
227
+ def test_image_convert_fails_inspection_format_check (self ):
228
+ convert = self ._setup_image_convert_info_fail ()
229
+ self .inspector_mock .return_value .from_file .side_effect = (
230
+ format_inspector .ImageFormatError ())
231
+ self .assertRaisesRegex (RuntimeError ,
232
+ 'Image format detection failed' ,
233
+ convert .execute , 'file:///test/path.qcow' )
234
+
235
+ def test_image_convert_fails_inspection_error (self ):
236
+ convert = self ._setup_image_convert_info_fail ()
237
+ self .inspector_mock .return_value .from_file .side_effect = ValueError
238
+ self .assertRaisesRegex (RuntimeError ,
239
+ 'Unable to inspect image' ,
240
+ convert .execute , 'file:///test/path.qcow' )
241
+
203
242
def _test_image_convert_invalid_vmdk (self ):
204
243
data = {'format' : 'vmdk' ,
205
244
'format-specific' : {
206
245
'data' : {
207
246
'create-type' : 'monolithicFlat' ,
208
247
}}}
209
248
210
- convert = self ._setup_image_convert_info_fail ()
249
+ convert = self ._setup_image_convert_info_fail (disk_format = 'vmdk' )
211
250
with mock .patch .object (processutils , 'execute' ) as exc_mock :
212
251
exc_mock .return_value = json .dumps (data ), ''
213
252
convert .execute ('file:///test/path.vmdk' )
@@ -236,14 +275,15 @@ def test_image_convert_valid_vmdk(self):
236
275
self ._test_image_convert_invalid_vmdk )
237
276
238
277
def test_image_convert_fails (self ):
239
- convert = self ._setup_image_convert_info_fail ()
278
+ convert = self ._setup_image_convert_info_fail (disk_format = 'raw' )
240
279
with mock .patch .object (processutils , 'execute' ) as exc_mock :
241
280
exc_mock .side_effect = [('{"format":"raw"}' , '' ),
242
281
OSError ('convert_fail' )]
243
282
self .assertRaises (OSError ,
244
283
convert .execute , 'file:///test/path.raw' )
245
284
exc_mock .assert_has_calls (
246
285
[mock .call ('qemu-img' , 'info' ,
286
+ '-f' , 'raw' ,
247
287
'--output=json' ,
248
288
'/test/path.raw' ,
249
289
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
@@ -256,14 +296,15 @@ def test_image_convert_fails(self):
256
296
self .img_repo .save .assert_not_called ()
257
297
258
298
def test_image_convert_reports_fail (self ):
259
- convert = self ._setup_image_convert_info_fail ()
299
+ convert = self ._setup_image_convert_info_fail (disk_format = 'raw' )
260
300
with mock .patch .object (processutils , 'execute' ) as exc_mock :
261
301
exc_mock .side_effect = [('{"format":"raw"}' , '' ),
262
302
('' , 'some error' )]
263
303
self .assertRaises (RuntimeError ,
264
304
convert .execute , 'file:///test/path.raw' )
265
305
exc_mock .assert_has_calls (
266
306
[mock .call ('qemu-img' , 'info' ,
307
+ '-f' , 'raw' ,
267
308
'--output=json' ,
268
309
'/test/path.raw' ,
269
310
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
@@ -281,9 +322,10 @@ def test_image_convert_fails_source_format(self):
281
322
exc_mock .return_value = ('{}' , '' )
282
323
exc = self .assertRaises (RuntimeError ,
283
324
convert .execute , 'file:///test/path.raw' )
284
- self .assertIn ('Source format not reported ' , str (exc ))
325
+ self .assertIn ('Image metadata disagrees about format ' , str (exc ))
285
326
exc_mock .assert_called_once_with (
286
327
'qemu-img' , 'info' ,
328
+ '-f' , 'qcow2' ,
287
329
'--output=json' ,
288
330
'/test/path.raw' ,
289
331
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
@@ -301,6 +343,7 @@ def test_image_convert_same_format_does_nothing(self):
301
343
# Make sure we only called qemu-img for inspection, not conversion
302
344
exc_mock .assert_called_once_with (
303
345
'qemu-img' , 'info' ,
346
+ '-f' , 'qcow2' ,
304
347
'--output=json' ,
305
348
'/test/path.qcow' ,
306
349
prlimit = async_utils .QEMU_IMG_PROC_LIMITS ,
0 commit comments