Browse Source

Add image name support when create a baymodel

Currently, if we create a baymodel and pass an image name as parameter,
an image not found exception would be raised.

This patch add image name support when create a baymodel

Closes-Bug: #1448952

Change-Id: If8b41eacccf0880908dafebb3e86e374399c0bef
Lan Qi song 4 years ago
parent
commit
1c3027fc52

+ 6
- 6
magnum/api/controllers/v1/baymodel.py View File

@@ -206,19 +206,19 @@ class BayModelsController(rest.RestController):
206 206
                                                 sort_key=sort_key,
207 207
                                                 sort_dir=sort_dir)
208 208
 
209
-    def _get_image_data(self, context, image_id):
209
+    def _get_image_data(self, context, image_ident):
210 210
         """Retrieves os_distro and other metadata from the Glance image.
211 211
 
212
-        :param image_id: image_id of baymodel.
212
+        :param image_ident: image id or name of baymodel.
213 213
         """
214 214
         try:
215 215
             cli = clients.OpenStackClients(context)
216
-            image_data = cli.glance().images.get(image_id)
217
-            return image_data
216
+            return api_utils.get_openstack_resource(cli.glance().images,
217
+                                                    image_ident, 'images')
218 218
         except glanceclient.exc.NotFound:
219
-            raise exception.ImageNotFound(image_id=image_id)
219
+            raise exception.ImageNotFound(image_id=image_ident)
220 220
         except glanceclient.exc.HTTPForbidden:
221
-            raise exception.ImageNotAuthorized(image_id=image_id)
221
+            raise exception.ImageNotAuthorized(image_id=image_ident)
222 222
 
223 223
     @wsme_pecan.wsexpose(BayModelCollection, types.uuid,
224 224
                          types.uuid, int, wtypes.text, wtypes.text)

+ 28
- 0
magnum/api/controllers/v1/utils.py View File

@@ -74,3 +74,31 @@ def get_rpc_resource(resource, resource_ident):
74 74
         return resource.get_by_name(pecan.request.context, resource_ident)
75 75
 
76 76
     raise exception.InvalidUuidOrName(name=resource_ident)
77
+
78
+
79
+def get_openstack_resource(manager, resource_ident, resource_type):
80
+    """Get the openstack resource from the uuid or logical name.
81
+
82
+    :param manager: the resource manager class.
83
+    :param resource_ident: the UUID or logical name of the resource.
84
+    :param resource_type: the type of the resource
85
+
86
+    :returns: The openstack resource.
87
+    :raises: ResourceNotFound if the openstack resource is not exist.
88
+             Conflict if multi openstack resources have same name.
89
+    """
90
+    if utils.is_uuid_like(resource_ident):
91
+        resource_data = manager.get(resource_ident)
92
+    else:
93
+        filters = {'name': resource_ident}
94
+        matches = list(manager.list(filters=filters))
95
+        if len(matches) == 0:
96
+            raise exception.ResourceNotFound(name=resource_type,
97
+                                             id=resource_ident)
98
+        if len(matches) > 1:
99
+            msg = ("Multiple '%s' exist with same name '%s'. "
100
+                   "Please use the resource id instead." %
101
+                   (resource_type, resource_ident))
102
+            raise exception.Conflict(msg)
103
+        resource_data = matches[0]
104
+    return resource_data

+ 59
- 20
magnum/tests/unit/api/controllers/v1/test_baymodel.py View File

@@ -20,6 +20,7 @@ from webtest.app import AppError
20 20
 from wsme import types as wtypes
21 21
 
22 22
 from magnum.api.controllers.v1 import baymodel as api_baymodel
23
+from magnum.common.clients import OpenStackClients as openstack_client
23 24
 from magnum.common import utils
24 25
 from magnum.tests import base
25 26
 from magnum.tests.unit.api import base as api_base
@@ -334,14 +335,14 @@ class TestPost(api_base.FunctionalTest):
334 335
     def setUp(self):
335 336
         super(TestPost, self).setUp()
336 337
 
337
-    @mock.patch('magnum.common.clients.OpenStackClients')
338
+    @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
338 339
     @mock.patch('oslo_utils.timeutils.utcnow')
339
-    def test_create_baymodel(self, mock_utcnow, mock_openstack_client):
340
+    def test_create_baymodel(self, mock_utcnow, mock_image_data):
340 341
         cdict = apiutils.baymodel_post_data()
341 342
         test_time = datetime.datetime(2000, 1, 1, 0, 0)
342 343
         mock_utcnow.return_value = test_time
343
-        test_auth_url = 'http://127.0.0.1:5000/v2.0'
344
-        mock_openstack_client.glance.return_value = test_auth_url
344
+        mock_image_data.return_value = {'name': 'mock_name',
345
+                                        'os_distro': 'fedora-atomic'}
345 346
 
346 347
         response = self.post_json('/baymodels', cdict)
347 348
         self.assertEqual(201, response.status_int)
@@ -356,12 +357,12 @@ class TestPost(api_base.FunctionalTest):
356 357
                             response.json['created_at']).replace(tzinfo=None)
357 358
         self.assertEqual(test_time, return_created_at)
358 359
 
359
-    @mock.patch('magnum.common.clients.OpenStackClients')
360
-    def test_create_baymodel_doesnt_contain_id(self, mock_openstack_client):
360
+    @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
361
+    def test_create_baymodel_doesnt_contain_id(self, mock_image_data):
361 362
         with mock.patch.object(self.dbapi, 'create_baymodel',
362 363
                                wraps=self.dbapi.create_baymodel) as cc_mock:
363
-            test_auth_url = 'http://127.0.0.1:5000/v2.0'
364
-            mock_openstack_client.glance.return_value = test_auth_url
364
+            mock_image_data.return_value = {'name': 'mock_name',
365
+                                            'os_distro': 'fedora-atomic'}
365 366
             cdict = apiutils.baymodel_post_data(image_id='my-image')
366 367
             response = self.post_json('/baymodels', cdict)
367 368
             self.assertEqual(cdict['image_id'], response.json['image_id'])
@@ -383,24 +384,24 @@ class TestPost(api_base.FunctionalTest):
383 384
             self.assertRaises(AppError, self.post_json, '/baymodels', cdict)
384 385
             self.assertFalse(cc_mock.called)
385 386
 
386
-    @mock.patch('magnum.common.clients.OpenStackClients')
387
+    @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
387 388
     def test_create_baymodel_with_invalid_docker_volume_size(self,
388
-                               mock_openstack_client):
389
+                               mock_image_data):
389 390
         with mock.patch.object(self.dbapi, 'create_baymodel',
390 391
                                wraps=self.dbapi.create_baymodel) as cc_mock:
391
-            test_auth_url = 'http://127.0.0.1:5000/v2.0'
392
-            mock_openstack_client.glance.return_value = test_auth_url
392
+            mock_image_data.return_value = {'name': 'mock_name',
393
+                                            'os_distro': 'fedora-atomic'}
393 394
             cdict = apiutils.baymodel_post_data(docker_volume_size='docker')
394 395
             self.assertRaises(AppError, self.post_json, '/baymodels', cdict)
395 396
             self.assertFalse(cc_mock.called)
396 397
 
397
-    @mock.patch('magnum.common.clients.OpenStackClients')
398
+    @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
398 399
     def test_create_baymodel_with_docker_volume_size(self,
399
-                               mock_openstack_client):
400
+                               mock_image_data):
400 401
         with mock.patch.object(self.dbapi, 'create_baymodel',
401 402
                                wraps=self.dbapi.create_baymodel) as cc_mock:
402
-            test_auth_url = 'http://127.0.0.1:5000/v2.0'
403
-            mock_openstack_client.glance.return_value = test_auth_url
403
+            mock_image_data.return_value = {'name': 'mock_name',
404
+                                            'os_distro': 'fedora-atomic'}
404 405
             cdict = apiutils.baymodel_post_data(docker_volume_size=99)
405 406
             response = self.post_json('/baymodels', cdict)
406 407
             self.assertEqual(cdict['docker_volume_size'],
@@ -408,10 +409,10 @@ class TestPost(api_base.FunctionalTest):
408 409
             cc_mock.assert_called_once_with(mock.ANY)
409 410
             self.assertNotIn('id', cc_mock.call_args[0][0])
410 411
 
411
-    @mock.patch('magnum.common.clients.OpenStackClients')
412
-    def test_create_baymodel_generate_uuid(self, mock_openstack_client):
413
-        test_auth_url = 'http://127.0.0.1:5000/v2.0'
414
-        mock_openstack_client.glance.return_value = test_auth_url
412
+    @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
413
+    def test_create_baymodel_generate_uuid(self, mock_image_data):
414
+        mock_image_data.return_value = {'name': 'mock_name',
415
+                                        'os_distro': 'fedora-atomic'}
415 416
         cdict = apiutils.baymodel_post_data()
416 417
         del cdict['uuid']
417 418
         response = self.post_json('/baymodels', cdict)
@@ -436,6 +437,44 @@ class TestPost(api_base.FunctionalTest):
436 437
         response = self.post_json('/baymodels', cdict, expect_errors=True)
437 438
         self.assertEqual(201, response.status_int)
438 439
 
440
+    @mock.patch.object(openstack_client, 'glance')
441
+    def test_create_baymodel_with_image_name(self, mock_glance_client):
442
+        mock_images = [{'name': 'mock_name',
443
+                       'os_distro': 'fedora-atomic'}]
444
+        mock_glance = mock.MagicMock()
445
+        mock_glance.images.list.return_value = mock_images
446
+        mock_glance_client.return_value = mock_glance
447
+        cdict = apiutils.baymodel_post_data()
448
+        del cdict['uuid']
449
+        response = self.post_json('/baymodels', cdict, expect_errors=True)
450
+        self.assertEqual(201, response.status_int)
451
+
452
+    @mock.patch.object(openstack_client, 'glance')
453
+    def test_create_baymodel_with_no_exist_image_name(self,
454
+                                                      mock_glance_client):
455
+        mock_images = []
456
+        mock_glance = mock.MagicMock()
457
+        mock_glance.images.list.return_value = mock_images
458
+        mock_glance_client.return_value = mock_glance
459
+        cdict = apiutils.baymodel_post_data()
460
+        del cdict['uuid']
461
+        response = self.post_json('/baymodels', cdict, expect_errors=True)
462
+        self.assertEqual(404, response.status_int)
463
+
464
+    @mock.patch.object(openstack_client, 'glance')
465
+    def test_create_baymodel_with_multi_image_name(self, mock_glance_client):
466
+        mock_images = [{'name': 'mock_name',
467
+                       'os_distro': 'fedora-atomic'},
468
+                       {'name': 'mock_name',
469
+                       'os_distro': 'fedora-atomic'}]
470
+        mock_glance = mock.MagicMock()
471
+        mock_glance.images.list.return_value = mock_images
472
+        mock_glance_client.return_value = mock_glance
473
+        cdict = apiutils.baymodel_post_data()
474
+        del cdict['uuid']
475
+        response = self.post_json('/baymodels', cdict, expect_errors=True)
476
+        self.assertEqual(409, response.status_int)
477
+
439 478
 
440 479
 class TestDelete(api_base.FunctionalTest):
441 480
 

+ 38
- 0
magnum/tests/unit/api/controllers/v1/test_utils.py View File

@@ -13,9 +13,12 @@
13 13
 #    License for the specific language governing permissions and limitations
14 14
 #    under the License.
15 15
 
16
+import mock
16 17
 import wsme
17 18
 
18 19
 from magnum.api.controllers.v1 import utils
20
+from magnum.common import exception
21
+from magnum.common import utils as common_utils
19 22
 from magnum.tests.unit.api import base
20 23
 
21 24
 from oslo_config import cfg
@@ -47,3 +50,38 @@ class TestApiUtils(base.FunctionalTest):
47 50
         self.assertRaises(wsme.exc.ClientSideError,
48 51
                           utils.validate_sort_dir,
49 52
                           'fake-sort')
53
+
54
+    @mock.patch.object(common_utils, 'is_uuid_like', return_value=True)
55
+    def test_get_openstack_resource_by_uuid(self, fake_is_uuid_like):
56
+        fake_manager = mock.MagicMock()
57
+        fake_manager.get.return_value = 'fake_resource_data'
58
+        resource_data = utils.get_openstack_resource(fake_manager,
59
+                                                     'fake_resource',
60
+                                                     'fake_resource_type')
61
+        self.assertEqual('fake_resource_data', resource_data)
62
+
63
+    @mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
64
+    def test_get_openstack_resource_by_name(self, fake_is_uuid_like):
65
+        fake_manager = mock.MagicMock()
66
+        fake_manager.list.return_value = ['fake_resource_data']
67
+        resource_data = utils.get_openstack_resource(fake_manager,
68
+                                                     'fake_resource',
69
+                                                     'fake_resource_type')
70
+        self.assertEqual('fake_resource_data', resource_data)
71
+
72
+    @mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
73
+    def test_get_openstack_resource_non_exist(self, fake_is_uuid_like):
74
+        fake_manager = mock.MagicMock()
75
+        fake_manager.list.return_value = []
76
+        self.assertRaises(exception.ResourceNotFound,
77
+                          utils.get_openstack_resource,
78
+                          fake_manager, 'fake_resource', 'fake_resource_type')
79
+
80
+    @mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
81
+    def test_get_openstack_resource_multi_exist(self, fake_is_uuid_like):
82
+        fake_manager = mock.MagicMock()
83
+        fake_manager.list.return_value = ['fake_resource_data1',
84
+                                          'fake_resource_data2']
85
+        self.assertRaises(exception.Conflict,
86
+                          utils.get_openstack_resource,
87
+                          fake_manager, 'fake_resource', 'fake_resource_type')

Loading…
Cancel
Save