2121
2222from nova .compute import utils as compute_utils
2323from nova import exception
24+ from nova .image import format_inspector
2425from nova import test
2526from nova .virt import images
2627
@@ -99,11 +100,17 @@ def test_qemu_img_info_with_disk_not_found(self, exists, mocked_execute):
99100 exists .assert_called_once_with (path )
100101 mocked_execute .assert_called_once ()
101102
103+ @mock .patch .object (images , 'IMAGE_API' )
104+ @mock .patch ('nova.image.format_inspector.get_inspector' )
102105 @mock .patch .object (images , 'convert_image' ,
103106 side_effect = exception .ImageUnacceptable )
104107 @mock .patch .object (images , 'qemu_img_info' )
105108 @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' }
107114 qemu_img_info .backing_file = None
108115 qemu_img_info .file_format = 'qcow2'
109116 qemu_img_info .virtual_size = 20
@@ -112,12 +119,17 @@ def test_fetch_to_raw_errors(self, convert_image, qemu_img_info, fetch):
112119 images .fetch_to_raw ,
113120 None , 'href123' , '/no/path' )
114121
122+ @mock .patch .object (images , 'IMAGE_API' )
123+ @mock .patch ('nova.image.format_inspector.get_inspector' )
115124 @mock .patch .object (images , 'convert_image' ,
116125 side_effect = exception .ImageUnacceptable )
117126 @mock .patch .object (images , 'qemu_img_info' )
118127 @mock .patch .object (images , 'fetch' )
119128 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
121133 # NOTE(danms): the above test needs the following line as well, as it
122134 # is broken without it.
123135 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,
130142 images .fetch_to_raw ,
131143 None , 'href123' , '/no/path' )
132144
145+ @mock .patch ('nova.image.format_inspector.get_inspector' )
146+ @mock .patch .object (images , 'IMAGE_API' )
133147 @mock .patch ('os.rename' )
134148 @mock .patch .object (images , 'qemu_img_info' )
135149 @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 ):
137152 # Make sure we support a case where we fetch an already-raw image and
138153 # qemu-img returns None for "format_specific".
154+ mock_glance .get .return_value = {'disk_format' : 'raw' }
139155 qemu_img_info = qemu_img_info_fn .return_value
140156 qemu_img_info .file_format = 'raw'
141157 qemu_img_info .backing_file = None
@@ -198,9 +214,15 @@ def test_convert_image_vmdk_allowed_list_checking(self):
198214 imageutils .QemuImgInfo (jsonutils .dumps (info ),
199215 format = 'json' ))
200216
217+ @mock .patch .object (images , 'IMAGE_API' )
218+ @mock .patch ('nova.image.format_inspector.get_inspector' )
201219 @mock .patch .object (images , 'fetch' )
202220 @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
204226 info = {'format' : 'vmdk' ,
205227 'format-specific' : {
206228 'type' : 'vmdk' ,
@@ -212,3 +234,109 @@ def test_fetch_checks_vmdk_rules(self, mock_info, mock_fetch):
212234 e = self .assertRaises (exception .ImageUnacceptable ,
213235 images .fetch_to_raw , None , 'foo' , 'anypath' )
214236 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