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