~zulcss/cinder/cinder-ca-g2

« back to all changes in this revision

Viewing changes to cinder/tests/image/test_glance.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-10-09 08:26:21 UTC
  • mfrom: (3.1.10 quantal)
  • Revision ID: package-import@ubuntu.com-20121009082621-stc86vcuyzyrp7ow
Tags: 2012.2-0ubuntu2
* debian/cinder_tgt.conf: Add missing configuration file. (LP: #1064366) 
* debian/README.Debian: Added note about migration from nova-volume
  to cinder-volume.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright 2011 OpenStack LLC.
 
4
# All Rights Reserved.
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
 
 
18
 
 
19
import datetime
 
20
import random
 
21
import time
 
22
 
 
23
import glanceclient.exc
 
24
 
 
25
from cinder import context
 
26
from cinder import exception
 
27
from cinder.image import glance
 
28
from cinder import test
 
29
from cinder.tests.api.openstack import fakes
 
30
from cinder.tests.glance import stubs as glance_stubs
 
31
 
 
32
 
 
33
class NullWriter(object):
 
34
    """Used to test ImageService.get which takes a writer object"""
 
35
 
 
36
    def write(self, *arg, **kwargs):
 
37
        pass
 
38
 
 
39
 
 
40
class TestGlanceSerializer(test.TestCase):
 
41
    def test_serialize(self):
 
42
        metadata = {'name': 'image1',
 
43
                    'is_public': True,
 
44
                    'foo': 'bar',
 
45
                    'properties': {
 
46
                        'prop1': 'propvalue1',
 
47
                        'mappings': [
 
48
                            {'virtual': 'aaa',
 
49
                             'device': 'bbb'},
 
50
                            {'virtual': 'xxx',
 
51
                             'device': 'yyy'}],
 
52
                        'block_device_mapping': [
 
53
                            {'virtual_device': 'fake',
 
54
                             'device_name': '/dev/fake'},
 
55
                            {'virtual_device': 'ephemeral0',
 
56
                             'device_name': '/dev/fake0'}]}}
 
57
 
 
58
        converted_expected = {
 
59
            'name': 'image1',
 
60
            'is_public': True,
 
61
            'foo': 'bar',
 
62
            'properties': {
 
63
                'prop1': 'propvalue1',
 
64
                'mappings':
 
65
                '[{"device": "bbb", "virtual": "aaa"}, '
 
66
                '{"device": "yyy", "virtual": "xxx"}]',
 
67
                'block_device_mapping':
 
68
                '[{"virtual_device": "fake", "device_name": "/dev/fake"}, '
 
69
                '{"virtual_device": "ephemeral0", '
 
70
                '"device_name": "/dev/fake0"}]'}}
 
71
        converted = glance._convert_to_string(metadata)
 
72
        self.assertEqual(converted, converted_expected)
 
73
        self.assertEqual(glance._convert_from_string(converted), metadata)
 
74
 
 
75
 
 
76
class TestGlanceImageService(test.TestCase):
 
77
    """
 
78
    Tests the Glance image service.
 
79
 
 
80
    At a high level, the translations involved are:
 
81
 
 
82
        1. Glance -> ImageService - This is needed so we can support
 
83
           multple ImageServices (Glance, Local, etc)
 
84
 
 
85
        2. ImageService -> API - This is needed so we can support multple
 
86
           APIs (OpenStack, EC2)
 
87
 
 
88
    """
 
89
    NOW_GLANCE_OLD_FORMAT = "2010-10-11T10:30:22"
 
90
    NOW_GLANCE_FORMAT = "2010-10-11T10:30:22.000000"
 
91
 
 
92
    class tzinfo(datetime.tzinfo):
 
93
        @staticmethod
 
94
        def utcoffset(*args, **kwargs):
 
95
            return datetime.timedelta()
 
96
 
 
97
    NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22, tzinfo=tzinfo())
 
98
 
 
99
    def setUp(self):
 
100
        super(TestGlanceImageService, self).setUp()
 
101
        #fakes.stub_out_compute_api_snapshot(self.stubs)
 
102
 
 
103
        client = glance_stubs.StubGlanceClient()
 
104
        self.service = self._create_image_service(client)
 
105
        self.context = context.RequestContext('fake', 'fake', auth_token=True)
 
106
 
 
107
    def _create_image_service(self, client):
 
108
        def _fake_create_glance_client(context, host, port, version):
 
109
            return client
 
110
 
 
111
        self.stubs.Set(glance, '_create_glance_client',
 
112
                _fake_create_glance_client)
 
113
 
 
114
        client_wrapper = glance.GlanceClientWrapper(
 
115
                'fake', 'fake_host', 9292)
 
116
        return glance.GlanceImageService(client=client_wrapper)
 
117
 
 
118
    @staticmethod
 
119
    def _make_fixture(**kwargs):
 
120
        fixture = {'name': None,
 
121
                   'properties': {},
 
122
                   'status': None,
 
123
                   'is_public': None}
 
124
        fixture.update(kwargs)
 
125
        return fixture
 
126
 
 
127
    def _make_datetime_fixture(self):
 
128
        return self._make_fixture(created_at=self.NOW_GLANCE_FORMAT,
 
129
                                  updated_at=self.NOW_GLANCE_FORMAT,
 
130
                                  deleted_at=self.NOW_GLANCE_FORMAT)
 
131
 
 
132
    def test_create_with_instance_id(self):
 
133
        """Ensure instance_id is persisted as an image-property"""
 
134
        fixture = {'name': 'test image',
 
135
                   'is_public': False,
 
136
                   'properties': {'instance_id': '42', 'user_id': 'fake'}}
 
137
 
 
138
        image_id = self.service.create(self.context, fixture)['id']
 
139
        image_meta = self.service.show(self.context, image_id)
 
140
        expected = {
 
141
            'id': image_id,
 
142
            'name': 'test image',
 
143
            'is_public': False,
 
144
            'size': None,
 
145
            'min_disk': None,
 
146
            'min_ram': None,
 
147
            'disk_format': None,
 
148
            'container_format': None,
 
149
            'checksum': None,
 
150
            'created_at': self.NOW_DATETIME,
 
151
            'updated_at': self.NOW_DATETIME,
 
152
            'deleted_at': None,
 
153
            'deleted': None,
 
154
            'status': None,
 
155
            'properties': {'instance_id': '42', 'user_id': 'fake'},
 
156
            'owner': None,
 
157
        }
 
158
        self.assertDictMatch(image_meta, expected)
 
159
 
 
160
        image_metas = self.service.detail(self.context)
 
161
        self.assertDictMatch(image_metas[0], expected)
 
162
 
 
163
    def test_create_without_instance_id(self):
 
164
        """
 
165
        Ensure we can create an image without having to specify an
 
166
        instance_id. Public images are an example of an image not tied to an
 
167
        instance.
 
168
        """
 
169
        fixture = {'name': 'test image', 'is_public': False}
 
170
        image_id = self.service.create(self.context, fixture)['id']
 
171
 
 
172
        expected = {
 
173
            'id': image_id,
 
174
            'name': 'test image',
 
175
            'is_public': False,
 
176
            'size': None,
 
177
            'min_disk': None,
 
178
            'min_ram': None,
 
179
            'disk_format': None,
 
180
            'container_format': None,
 
181
            'checksum': None,
 
182
            'created_at': self.NOW_DATETIME,
 
183
            'updated_at': self.NOW_DATETIME,
 
184
            'deleted_at': None,
 
185
            'deleted': None,
 
186
            'status': None,
 
187
            'properties': {},
 
188
            'owner': None,
 
189
        }
 
190
        actual = self.service.show(self.context, image_id)
 
191
        self.assertDictMatch(actual, expected)
 
192
 
 
193
    def test_create(self):
 
194
        fixture = self._make_fixture(name='test image')
 
195
        num_images = len(self.service.detail(self.context))
 
196
        image_id = self.service.create(self.context, fixture)['id']
 
197
 
 
198
        self.assertNotEquals(None, image_id)
 
199
        self.assertEquals(num_images + 1,
 
200
                          len(self.service.detail(self.context)))
 
201
 
 
202
    def test_create_and_show_non_existing_image(self):
 
203
        fixture = self._make_fixture(name='test image')
 
204
        image_id = self.service.create(self.context, fixture)['id']
 
205
 
 
206
        self.assertNotEquals(None, image_id)
 
207
        self.assertRaises(exception.ImageNotFound,
 
208
                          self.service.show,
 
209
                          self.context,
 
210
                          'bad image id')
 
211
 
 
212
    def test_detail_private_image(self):
 
213
        fixture = self._make_fixture(name='test image')
 
214
        fixture['is_public'] = False
 
215
        properties = {'owner_id': 'proj1'}
 
216
        fixture['properties'] = properties
 
217
 
 
218
        self.service.create(self.context, fixture)['id']
 
219
 
 
220
        proj = self.context.project_id
 
221
        self.context.project_id = 'proj1'
 
222
 
 
223
        image_metas = self.service.detail(self.context)
 
224
 
 
225
        self.context.project_id = proj
 
226
 
 
227
        self.assertEqual(1, len(image_metas))
 
228
        self.assertEqual(image_metas[0]['name'], 'test image')
 
229
        self.assertEqual(image_metas[0]['is_public'], False)
 
230
 
 
231
    def test_detail_marker(self):
 
232
        fixtures = []
 
233
        ids = []
 
234
        for i in range(10):
 
235
            fixture = self._make_fixture(name='TestImage %d' % (i))
 
236
            fixtures.append(fixture)
 
237
            ids.append(self.service.create(self.context, fixture)['id'])
 
238
 
 
239
        image_metas = self.service.detail(self.context, marker=ids[1])
 
240
        self.assertEquals(len(image_metas), 8)
 
241
        i = 2
 
242
        for meta in image_metas:
 
243
            expected = {
 
244
                'id': ids[i],
 
245
                'status': None,
 
246
                'is_public': None,
 
247
                'name': 'TestImage %d' % (i),
 
248
                'properties': {},
 
249
                'size': None,
 
250
                'min_disk': None,
 
251
                'min_ram': None,
 
252
                'disk_format': None,
 
253
                'container_format': None,
 
254
                'checksum': None,
 
255
                'created_at': self.NOW_DATETIME,
 
256
                'updated_at': self.NOW_DATETIME,
 
257
                'deleted_at': None,
 
258
                'deleted': None,
 
259
                'owner': None,
 
260
            }
 
261
 
 
262
            self.assertDictMatch(meta, expected)
 
263
            i = i + 1
 
264
 
 
265
    def test_detail_limit(self):
 
266
        fixtures = []
 
267
        ids = []
 
268
        for i in range(10):
 
269
            fixture = self._make_fixture(name='TestImage %d' % (i))
 
270
            fixtures.append(fixture)
 
271
            ids.append(self.service.create(self.context, fixture)['id'])
 
272
 
 
273
        image_metas = self.service.detail(self.context, limit=5)
 
274
        self.assertEquals(len(image_metas), 5)
 
275
 
 
276
    def test_detail_default_limit(self):
 
277
        fixtures = []
 
278
        ids = []
 
279
        for i in range(10):
 
280
            fixture = self._make_fixture(name='TestImage %d' % (i))
 
281
            fixtures.append(fixture)
 
282
            ids.append(self.service.create(self.context, fixture)['id'])
 
283
 
 
284
        image_metas = self.service.detail(self.context)
 
285
        for i, meta in enumerate(image_metas):
 
286
            self.assertEqual(meta['name'], 'TestImage %d' % (i))
 
287
 
 
288
    def test_detail_marker_and_limit(self):
 
289
        fixtures = []
 
290
        ids = []
 
291
        for i in range(10):
 
292
            fixture = self._make_fixture(name='TestImage %d' % (i))
 
293
            fixtures.append(fixture)
 
294
            ids.append(self.service.create(self.context, fixture)['id'])
 
295
 
 
296
        image_metas = self.service.detail(self.context, marker=ids[3], limit=5)
 
297
        self.assertEquals(len(image_metas), 5)
 
298
        i = 4
 
299
        for meta in image_metas:
 
300
            expected = {
 
301
                'id': ids[i],
 
302
                'status': None,
 
303
                'is_public': None,
 
304
                'name': 'TestImage %d' % (i),
 
305
                'properties': {},
 
306
                'size': None,
 
307
                'min_disk': None,
 
308
                'min_ram': None,
 
309
                'disk_format': None,
 
310
                'container_format': None,
 
311
                'checksum': None,
 
312
                'created_at': self.NOW_DATETIME,
 
313
                'updated_at': self.NOW_DATETIME,
 
314
                'deleted_at': None,
 
315
                'deleted': None,
 
316
                'owner': None,
 
317
            }
 
318
            self.assertDictMatch(meta, expected)
 
319
            i = i + 1
 
320
 
 
321
    def test_detail_invalid_marker(self):
 
322
        fixtures = []
 
323
        ids = []
 
324
        for i in range(10):
 
325
            fixture = self._make_fixture(name='TestImage %d' % (i))
 
326
            fixtures.append(fixture)
 
327
            ids.append(self.service.create(self.context, fixture)['id'])
 
328
 
 
329
        self.assertRaises(exception.Invalid, self.service.detail,
 
330
                          self.context, marker='invalidmarker')
 
331
 
 
332
    def test_update(self):
 
333
        fixture = self._make_fixture(name='test image')
 
334
        image = self.service.create(self.context, fixture)
 
335
        print image
 
336
        image_id = image['id']
 
337
        fixture['name'] = 'new image name'
 
338
        self.service.update(self.context, image_id, fixture)
 
339
 
 
340
        new_image_data = self.service.show(self.context, image_id)
 
341
        self.assertEquals('new image name', new_image_data['name'])
 
342
 
 
343
    def test_delete(self):
 
344
        fixture1 = self._make_fixture(name='test image 1')
 
345
        fixture2 = self._make_fixture(name='test image 2')
 
346
        fixtures = [fixture1, fixture2]
 
347
 
 
348
        num_images = len(self.service.detail(self.context))
 
349
        self.assertEquals(0, num_images)
 
350
 
 
351
        ids = []
 
352
        for fixture in fixtures:
 
353
            new_id = self.service.create(self.context, fixture)['id']
 
354
            ids.append(new_id)
 
355
 
 
356
        num_images = len(self.service.detail(self.context))
 
357
        self.assertEquals(2, num_images)
 
358
 
 
359
        self.service.delete(self.context, ids[0])
 
360
 
 
361
        num_images = len(self.service.detail(self.context))
 
362
        self.assertEquals(1, num_images)
 
363
 
 
364
    def test_show_passes_through_to_client(self):
 
365
        fixture = self._make_fixture(name='image1', is_public=True)
 
366
        image_id = self.service.create(self.context, fixture)['id']
 
367
 
 
368
        image_meta = self.service.show(self.context, image_id)
 
369
        expected = {
 
370
            'id': image_id,
 
371
            'name': 'image1',
 
372
            'is_public': True,
 
373
            'size': None,
 
374
            'min_disk': None,
 
375
            'min_ram': None,
 
376
            'disk_format': None,
 
377
            'container_format': None,
 
378
            'checksum': None,
 
379
            'created_at': self.NOW_DATETIME,
 
380
            'updated_at': self.NOW_DATETIME,
 
381
            'deleted_at': None,
 
382
            'deleted': None,
 
383
            'status': None,
 
384
            'properties': {},
 
385
            'owner': None,
 
386
        }
 
387
        self.assertEqual(image_meta, expected)
 
388
 
 
389
    def test_show_raises_when_no_authtoken_in_the_context(self):
 
390
        fixture = self._make_fixture(name='image1',
 
391
                                     is_public=False,
 
392
                                     properties={'one': 'two'})
 
393
        image_id = self.service.create(self.context, fixture)['id']
 
394
        self.context.auth_token = False
 
395
        self.assertRaises(exception.ImageNotFound,
 
396
                          self.service.show,
 
397
                          self.context,
 
398
                          image_id)
 
399
 
 
400
    def test_detail_passes_through_to_client(self):
 
401
        fixture = self._make_fixture(name='image10', is_public=True)
 
402
        image_id = self.service.create(self.context, fixture)['id']
 
403
        image_metas = self.service.detail(self.context)
 
404
        expected = [
 
405
            {
 
406
                'id': image_id,
 
407
                'name': 'image10',
 
408
                'is_public': True,
 
409
                'size': None,
 
410
                'min_disk': None,
 
411
                'min_ram': None,
 
412
                'disk_format': None,
 
413
                'container_format': None,
 
414
                'checksum': None,
 
415
                'created_at': self.NOW_DATETIME,
 
416
                'updated_at': self.NOW_DATETIME,
 
417
                'deleted_at': None,
 
418
                'deleted': None,
 
419
                'status': None,
 
420
                'properties': {},
 
421
                'owner': None,
 
422
            },
 
423
        ]
 
424
        self.assertEqual(image_metas, expected)
 
425
 
 
426
    def test_show_makes_datetimes(self):
 
427
        fixture = self._make_datetime_fixture()
 
428
        image_id = self.service.create(self.context, fixture)['id']
 
429
        image_meta = self.service.show(self.context, image_id)
 
430
        self.assertEqual(image_meta['created_at'], self.NOW_DATETIME)
 
431
        self.assertEqual(image_meta['updated_at'], self.NOW_DATETIME)
 
432
 
 
433
    def test_detail_makes_datetimes(self):
 
434
        fixture = self._make_datetime_fixture()
 
435
        self.service.create(self.context, fixture)
 
436
        image_meta = self.service.detail(self.context)[0]
 
437
        self.assertEqual(image_meta['created_at'], self.NOW_DATETIME)
 
438
        self.assertEqual(image_meta['updated_at'], self.NOW_DATETIME)
 
439
 
 
440
    def test_download_with_retries(self):
 
441
        tries = [0]
 
442
 
 
443
        class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
444
            """A client that fails the first time, then succeeds."""
 
445
            def get(self, image_id):
 
446
                if tries[0] == 0:
 
447
                    tries[0] = 1
 
448
                    raise glanceclient.exc.ServiceUnavailable('')
 
449
                else:
 
450
                    return {}
 
451
 
 
452
        client = MyGlanceStubClient()
 
453
        service = self._create_image_service(client)
 
454
        image_id = 1  # doesn't matter
 
455
        writer = NullWriter()
 
456
 
 
457
        # When retries are disabled, we should get an exception
 
458
        self.flags(glance_num_retries=0)
 
459
        self.assertRaises(exception.GlanceConnectionFailed,
 
460
                service.download, self.context, image_id, writer)
 
461
 
 
462
        # Now lets enable retries. No exception should happen now.
 
463
        tries = [0]
 
464
        self.flags(glance_num_retries=1)
 
465
        service.download(self.context, image_id, writer)
 
466
 
 
467
    def test_client_forbidden_converts_to_imagenotauthed(self):
 
468
        class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
469
            """A client that raises a Forbidden exception."""
 
470
            def get(self, image_id):
 
471
                raise glanceclient.exc.Forbidden(image_id)
 
472
 
 
473
        client = MyGlanceStubClient()
 
474
        service = self._create_image_service(client)
 
475
        image_id = 1  # doesn't matter
 
476
        writer = NullWriter()
 
477
        self.assertRaises(exception.ImageNotAuthorized, service.download,
 
478
                          self.context, image_id, writer)
 
479
 
 
480
    def test_client_httpforbidden_converts_to_imagenotauthed(self):
 
481
        class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
482
            """A client that raises a HTTPForbidden exception."""
 
483
            def get(self, image_id):
 
484
                raise glanceclient.exc.HTTPForbidden(image_id)
 
485
 
 
486
        client = MyGlanceStubClient()
 
487
        service = self._create_image_service(client)
 
488
        image_id = 1  # doesn't matter
 
489
        writer = NullWriter()
 
490
        self.assertRaises(exception.ImageNotAuthorized, service.download,
 
491
                          self.context, image_id, writer)
 
492
 
 
493
    def test_client_notfound_converts_to_imagenotfound(self):
 
494
        class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
495
            """A client that raises a NotFound exception."""
 
496
            def get(self, image_id):
 
497
                raise glanceclient.exc.NotFound(image_id)
 
498
 
 
499
        client = MyGlanceStubClient()
 
500
        service = self._create_image_service(client)
 
501
        image_id = 1  # doesn't matter
 
502
        writer = NullWriter()
 
503
        self.assertRaises(exception.ImageNotFound, service.download,
 
504
                          self.context, image_id, writer)
 
505
 
 
506
    def test_client_httpnotfound_converts_to_imagenotfound(self):
 
507
        class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
508
            """A client that raises a HTTPNotFound exception."""
 
509
            def get(self, image_id):
 
510
                raise glanceclient.exc.HTTPNotFound(image_id)
 
511
 
 
512
        client = MyGlanceStubClient()
 
513
        service = self._create_image_service(client)
 
514
        image_id = 1  # doesn't matter
 
515
        writer = NullWriter()
 
516
        self.assertRaises(exception.ImageNotFound, service.download,
 
517
                          self.context, image_id, writer)
 
518
 
 
519
    def test_glance_client_image_id(self):
 
520
        fixture = self._make_fixture(name='test image')
 
521
        image_id = self.service.create(self.context, fixture)['id']
 
522
        (service, same_id) = glance.get_remote_image_service(
 
523
                self.context, image_id)
 
524
        self.assertEquals(same_id, image_id)
 
525
 
 
526
    def test_glance_client_image_ref(self):
 
527
        fixture = self._make_fixture(name='test image')
 
528
        image_id = self.service.create(self.context, fixture)['id']
 
529
        image_url = 'http://something-less-likely/%s' % image_id
 
530
        (service, same_id) = glance.get_remote_image_service(
 
531
                self.context, image_url)
 
532
        self.assertEquals(same_id, image_id)
 
533
        self.assertEquals(service._client.host,
 
534
                'something-less-likely')
 
535
 
 
536
 
 
537
def _create_failing_glance_client(info):
 
538
    class MyGlanceStubClient(glance_stubs.StubGlanceClient):
 
539
        """A client that fails the first time, then succeeds."""
 
540
        def get(self, image_id):
 
541
            info['num_calls'] += 1
 
542
            if info['num_calls'] == 1:
 
543
                raise glanceclient.exc.ServiceUnavailable('')
 
544
            return {}
 
545
 
 
546
    return MyGlanceStubClient()