21
21
22
22
from nova .compute import utils as compute_utils
23
23
from nova import exception
24
+ from nova .image import format_inspector
24
25
from nova import test
25
26
from nova .virt import images
26
27
@@ -99,11 +100,17 @@ def test_qemu_img_info_with_disk_not_found(self, exists, mocked_execute):
99
100
exists .assert_called_once_with (path )
100
101
mocked_execute .assert_called_once ()
101
102
103
+ @mock .patch .object (images , 'IMAGE_API' )
104
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
102
105
@mock .patch .object (images , 'convert_image' ,
103
106
side_effect = exception .ImageUnacceptable )
104
107
@mock .patch .object (images , 'qemu_img_info' )
105
108
@mock .patch .object (images , 'fetch' )
106
- def test_fetch_to_raw_errors (self , convert_image , qemu_img_info , fetch ):
109
+ def test_fetch_to_raw_errors (self , convert_image , qemu_img_info , fetch ,
110
+ get_inspector , glance ):
111
+ inspector = get_inspector .return_value .from_file .return_value
112
+ inspector .safety_check .return_value = True
113
+ glance .get .return_value = {'disk_format' : 'qcow2' }
107
114
qemu_img_info .backing_file = None
108
115
qemu_img_info .file_format = 'qcow2'
109
116
qemu_img_info .virtual_size = 20
@@ -112,12 +119,17 @@ def test_fetch_to_raw_errors(self, convert_image, qemu_img_info, fetch):
112
119
images .fetch_to_raw ,
113
120
None , 'href123' , '/no/path' )
114
121
122
+ @mock .patch .object (images , 'IMAGE_API' )
123
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
115
124
@mock .patch .object (images , 'convert_image' ,
116
125
side_effect = exception .ImageUnacceptable )
117
126
@mock .patch .object (images , 'qemu_img_info' )
118
127
@mock .patch .object (images , 'fetch' )
119
128
def test_fetch_to_raw_data_file (self , convert_image , qemu_img_info_fn ,
120
- fetch ):
129
+ fetch , mock_gi , mock_glance ):
130
+ mock_glance .get .return_value = {'disk_format' : 'qcow2' }
131
+ inspector = mock_gi .return_value .from_file .return_value
132
+ inspector .safety_check .return_value = True
121
133
# NOTE(danms): the above test needs the following line as well, as it
122
134
# is broken without it.
123
135
qemu_img_info = qemu_img_info_fn .return_value
@@ -130,12 +142,16 @@ def test_fetch_to_raw_data_file(self, convert_image, qemu_img_info_fn,
130
142
images .fetch_to_raw ,
131
143
None , 'href123' , '/no/path' )
132
144
145
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
146
+ @mock .patch .object (images , 'IMAGE_API' )
133
147
@mock .patch ('os.rename' )
134
148
@mock .patch .object (images , 'qemu_img_info' )
135
149
@mock .patch .object (images , 'fetch' )
136
- def test_fetch_to_raw_from_raw (self , fetch , qemu_img_info_fn , mock_rename ):
150
+ def test_fetch_to_raw_from_raw (self , fetch , qemu_img_info_fn , mock_rename ,
151
+ mock_glance , mock_gi ):
137
152
# Make sure we support a case where we fetch an already-raw image and
138
153
# qemu-img returns None for "format_specific".
154
+ mock_glance .get .return_value = {'disk_format' : 'raw' }
139
155
qemu_img_info = qemu_img_info_fn .return_value
140
156
qemu_img_info .file_format = 'raw'
141
157
qemu_img_info .backing_file = None
@@ -198,9 +214,15 @@ def test_convert_image_vmdk_allowed_list_checking(self):
198
214
imageutils .QemuImgInfo (jsonutils .dumps (info ),
199
215
format = 'json' ))
200
216
217
+ @mock .patch .object (images , 'IMAGE_API' )
218
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
201
219
@mock .patch .object (images , 'fetch' )
202
220
@mock .patch ('nova.privsep.qemu.unprivileged_qemu_img_info' )
203
- def test_fetch_checks_vmdk_rules (self , mock_info , mock_fetch ):
221
+ def test_fetch_checks_vmdk_rules (self , mock_info , mock_fetch , mock_gi ,
222
+ mock_glance ):
223
+ mock_glance .get .return_value = {'disk_format' : 'vmdk' }
224
+ inspector = mock_gi .return_value .from_file .return_value
225
+ inspector .safety_check .return_value = True
204
226
info = {'format' : 'vmdk' ,
205
227
'format-specific' : {
206
228
'type' : 'vmdk' ,
@@ -212,3 +234,109 @@ def test_fetch_checks_vmdk_rules(self, mock_info, mock_fetch):
212
234
e = self .assertRaises (exception .ImageUnacceptable ,
213
235
images .fetch_to_raw , None , 'foo' , 'anypath' )
214
236
self .assertIn ('Invalid VMDK create-type specified' , str (e ))
237
+
238
+ @mock .patch .object (images , 'IMAGE_API' )
239
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
240
+ @mock .patch .object (images , 'qemu_img_info' )
241
+ @mock .patch .object (images , 'fetch' )
242
+ def test_fetch_to_raw_inspector (self , fetch , qemu_img_info , mock_gi ,
243
+ mock_glance ):
244
+ # Image claims to be qcow2, is qcow2, but fails safety check, so we
245
+ # abort before qemu-img-info
246
+ mock_glance .get .return_value = {'disk_format' : 'qcow2' }
247
+ inspector = mock_gi .return_value .from_file .return_value
248
+ inspector .safety_check .return_value = False
249
+ self .assertRaises (exception .ImageUnacceptable ,
250
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
251
+ qemu_img_info .assert_not_called ()
252
+ mock_gi .assert_called_once_with ('qcow2' )
253
+ mock_gi .return_value .from_file .assert_called_once_with ('/no.path.part' )
254
+ inspector .safety_check .assert_called_once_with ()
255
+ mock_glance .get .assert_called_once_with (None , 'href123' )
256
+
257
+ # Image claims to be qcow2, is qcow2, passes safety check, so we make
258
+ # it all the way to qemu-img-info
259
+ inspector .safety_check .return_value = True
260
+ qemu_img_info .side_effect = test .TestingException
261
+ self .assertRaises (test .TestingException ,
262
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
263
+
264
+ # Image claims to be qcow2 in glance, but the image is something else,
265
+ # so we abort before qemu-img-info
266
+ qemu_img_info .reset_mock ()
267
+ mock_gi .reset_mock ()
268
+ inspector .safety_check .reset_mock ()
269
+ mock_gi .return_value .from_file .side_effect = (
270
+ format_inspector .ImageFormatError )
271
+ self .assertRaises (exception .ImageUnacceptable ,
272
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
273
+ mock_gi .assert_called_once_with ('qcow2' )
274
+ inspector .safety_check .assert_not_called ()
275
+ qemu_img_info .assert_not_called ()
276
+
277
+ @mock .patch .object (images , 'IMAGE_API' )
278
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
279
+ @mock .patch .object (images , 'qemu_img_info' )
280
+ @mock .patch .object (images , 'fetch' )
281
+ def test_fetch_to_raw_inspector_disabled (self , fetch , qemu_img_info ,
282
+ mock_gi , mock_glance ):
283
+ self .flags (disable_deep_image_inspection = True ,
284
+ group = 'workarounds' )
285
+ qemu_img_info .side_effect = test .TestingException
286
+ self .assertRaises (test .TestingException ,
287
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
288
+ # If deep inspection is disabled, we should never call the inspector
289
+ mock_gi .assert_not_called ()
290
+ # ... and we let qemu-img detect the format itself.
291
+ qemu_img_info .assert_called_once_with ('/no.path.part' ,
292
+ format = None )
293
+ mock_glance .get .assert_not_called ()
294
+
295
+ @mock .patch .object (images , 'IMAGE_API' )
296
+ @mock .patch .object (images , 'qemu_img_info' )
297
+ def test_fetch_inspect_ami (self , imginfo , glance ):
298
+ glance .get .return_value = {'disk_format' : 'ami' }
299
+ self .assertRaises (exception .ImageUnacceptable ,
300
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
301
+ # Make sure 'ami was translated into 'raw' before we call qemu-img
302
+ imginfo .assert_called_once_with ('/no.path.part' , format = 'raw' )
303
+
304
+ @mock .patch .object (images , 'IMAGE_API' )
305
+ @mock .patch .object (images , 'qemu_img_info' )
306
+ def test_fetch_inspect_aki (self , imginfo , glance ):
307
+ glance .get .return_value = {'disk_format' : 'aki' }
308
+ self .assertRaises (exception .ImageUnacceptable ,
309
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
310
+ # Make sure 'aki was translated into 'raw' before we call qemu-img
311
+ imginfo .assert_called_once_with ('/no.path.part' , format = 'raw' )
312
+
313
+ @mock .patch .object (images , 'IMAGE_API' )
314
+ @mock .patch .object (images , 'qemu_img_info' )
315
+ def test_fetch_inspect_ari (self , imginfo , glance ):
316
+ glance .get .return_value = {'disk_format' : 'ari' }
317
+ self .assertRaises (exception .ImageUnacceptable ,
318
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
319
+ # Make sure 'aki was translated into 'raw' before we call qemu-img
320
+ imginfo .assert_called_once_with ('/no.path.part' , format = 'raw' )
321
+
322
+ @mock .patch .object (images , 'IMAGE_API' )
323
+ @mock .patch .object (images , 'qemu_img_info' )
324
+ def test_fetch_inspect_unknown_format (self , imginfo , glance ):
325
+ glance .get .return_value = {'disk_format' : 'commodore-64-disk' }
326
+ self .assertRaises (exception .ImageUnacceptable ,
327
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
328
+ # Unsupported formats do not make it past deep inspection
329
+ imginfo .assert_not_called ()
330
+
331
+ @mock .patch .object (images , 'IMAGE_API' )
332
+ @mock .patch .object (images , 'qemu_img_info' )
333
+ @mock .patch ('nova.image.format_inspector.get_inspector' )
334
+ def test_fetch_inspect_disagrees_qemu (self , mock_gi , imginfo , glance ):
335
+ glance .get .return_value = {'disk_format' : 'qcow2' }
336
+ # Glance and inspector think it is a qcow2 file, but qemu-img does not
337
+ # agree. It was forced to interpret as a qcow2, but returned no
338
+ # format information as a result.
339
+ imginfo .return_value .data_file = None
340
+ self .assertRaises (exception .ImageUnacceptable ,
341
+ images .fetch_to_raw , None , 'href123' , '/no.path' )
342
+ imginfo .assert_called_once_with ('/no.path.part' , format = 'qcow2' )
0 commit comments