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
This commit is contained in:
Lan Qi song 2015-04-27 16:03:02 +08:00
parent e2ee7eb6e4
commit 1c3027fc52
4 changed files with 131 additions and 26 deletions

View File

@ -206,19 +206,19 @@ class BayModelsController(rest.RestController):
sort_key=sort_key, sort_key=sort_key,
sort_dir=sort_dir) sort_dir=sort_dir)
def _get_image_data(self, context, image_id): def _get_image_data(self, context, image_ident):
"""Retrieves os_distro and other metadata from the Glance image. """Retrieves os_distro and other metadata from the Glance image.
:param image_id: image_id of baymodel. :param image_ident: image id or name of baymodel.
""" """
try: try:
cli = clients.OpenStackClients(context) cli = clients.OpenStackClients(context)
image_data = cli.glance().images.get(image_id) return api_utils.get_openstack_resource(cli.glance().images,
return image_data image_ident, 'images')
except glanceclient.exc.NotFound: except glanceclient.exc.NotFound:
raise exception.ImageNotFound(image_id=image_id) raise exception.ImageNotFound(image_id=image_ident)
except glanceclient.exc.HTTPForbidden: except glanceclient.exc.HTTPForbidden:
raise exception.ImageNotAuthorized(image_id=image_id) raise exception.ImageNotAuthorized(image_id=image_ident)
@wsme_pecan.wsexpose(BayModelCollection, types.uuid, @wsme_pecan.wsexpose(BayModelCollection, types.uuid,
types.uuid, int, wtypes.text, wtypes.text) types.uuid, int, wtypes.text, wtypes.text)

View File

@ -74,3 +74,31 @@ def get_rpc_resource(resource, resource_ident):
return resource.get_by_name(pecan.request.context, resource_ident) return resource.get_by_name(pecan.request.context, resource_ident)
raise exception.InvalidUuidOrName(name=resource_ident) raise exception.InvalidUuidOrName(name=resource_ident)
def get_openstack_resource(manager, resource_ident, resource_type):
"""Get the openstack resource from the uuid or logical name.
:param manager: the resource manager class.
:param resource_ident: the UUID or logical name of the resource.
:param resource_type: the type of the resource
:returns: The openstack resource.
:raises: ResourceNotFound if the openstack resource is not exist.
Conflict if multi openstack resources have same name.
"""
if utils.is_uuid_like(resource_ident):
resource_data = manager.get(resource_ident)
else:
filters = {'name': resource_ident}
matches = list(manager.list(filters=filters))
if len(matches) == 0:
raise exception.ResourceNotFound(name=resource_type,
id=resource_ident)
if len(matches) > 1:
msg = ("Multiple '%s' exist with same name '%s'. "
"Please use the resource id instead." %
(resource_type, resource_ident))
raise exception.Conflict(msg)
resource_data = matches[0]
return resource_data

View File

@ -20,6 +20,7 @@ from webtest.app import AppError
from wsme import types as wtypes from wsme import types as wtypes
from magnum.api.controllers.v1 import baymodel as api_baymodel from magnum.api.controllers.v1 import baymodel as api_baymodel
from magnum.common.clients import OpenStackClients as openstack_client
from magnum.common import utils from magnum.common import utils
from magnum.tests import base from magnum.tests import base
from magnum.tests.unit.api import base as api_base from magnum.tests.unit.api import base as api_base
@ -334,14 +335,14 @@ class TestPost(api_base.FunctionalTest):
def setUp(self): def setUp(self):
super(TestPost, self).setUp() super(TestPost, self).setUp()
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
@mock.patch('oslo_utils.timeutils.utcnow') @mock.patch('oslo_utils.timeutils.utcnow')
def test_create_baymodel(self, mock_utcnow, mock_openstack_client): def test_create_baymodel(self, mock_utcnow, mock_image_data):
cdict = apiutils.baymodel_post_data() cdict = apiutils.baymodel_post_data()
test_time = datetime.datetime(2000, 1, 1, 0, 0) test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time mock_utcnow.return_value = test_time
test_auth_url = 'http://127.0.0.1:5000/v2.0' mock_image_data.return_value = {'name': 'mock_name',
mock_openstack_client.glance.return_value = test_auth_url 'os_distro': 'fedora-atomic'}
response = self.post_json('/baymodels', cdict) response = self.post_json('/baymodels', cdict)
self.assertEqual(201, response.status_int) self.assertEqual(201, response.status_int)
@ -356,12 +357,12 @@ class TestPost(api_base.FunctionalTest):
response.json['created_at']).replace(tzinfo=None) response.json['created_at']).replace(tzinfo=None)
self.assertEqual(test_time, return_created_at) self.assertEqual(test_time, return_created_at)
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
def test_create_baymodel_doesnt_contain_id(self, mock_openstack_client): def test_create_baymodel_doesnt_contain_id(self, mock_image_data):
with mock.patch.object(self.dbapi, 'create_baymodel', with mock.patch.object(self.dbapi, 'create_baymodel',
wraps=self.dbapi.create_baymodel) as cc_mock: wraps=self.dbapi.create_baymodel) as cc_mock:
test_auth_url = 'http://127.0.0.1:5000/v2.0' mock_image_data.return_value = {'name': 'mock_name',
mock_openstack_client.glance.return_value = test_auth_url 'os_distro': 'fedora-atomic'}
cdict = apiutils.baymodel_post_data(image_id='my-image') cdict = apiutils.baymodel_post_data(image_id='my-image')
response = self.post_json('/baymodels', cdict) response = self.post_json('/baymodels', cdict)
self.assertEqual(cdict['image_id'], response.json['image_id']) self.assertEqual(cdict['image_id'], response.json['image_id'])
@ -383,24 +384,24 @@ class TestPost(api_base.FunctionalTest):
self.assertRaises(AppError, self.post_json, '/baymodels', cdict) self.assertRaises(AppError, self.post_json, '/baymodels', cdict)
self.assertFalse(cc_mock.called) self.assertFalse(cc_mock.called)
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
def test_create_baymodel_with_invalid_docker_volume_size(self, def test_create_baymodel_with_invalid_docker_volume_size(self,
mock_openstack_client): mock_image_data):
with mock.patch.object(self.dbapi, 'create_baymodel', with mock.patch.object(self.dbapi, 'create_baymodel',
wraps=self.dbapi.create_baymodel) as cc_mock: wraps=self.dbapi.create_baymodel) as cc_mock:
test_auth_url = 'http://127.0.0.1:5000/v2.0' mock_image_data.return_value = {'name': 'mock_name',
mock_openstack_client.glance.return_value = test_auth_url 'os_distro': 'fedora-atomic'}
cdict = apiutils.baymodel_post_data(docker_volume_size='docker') cdict = apiutils.baymodel_post_data(docker_volume_size='docker')
self.assertRaises(AppError, self.post_json, '/baymodels', cdict) self.assertRaises(AppError, self.post_json, '/baymodels', cdict)
self.assertFalse(cc_mock.called) self.assertFalse(cc_mock.called)
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
def test_create_baymodel_with_docker_volume_size(self, def test_create_baymodel_with_docker_volume_size(self,
mock_openstack_client): mock_image_data):
with mock.patch.object(self.dbapi, 'create_baymodel', with mock.patch.object(self.dbapi, 'create_baymodel',
wraps=self.dbapi.create_baymodel) as cc_mock: wraps=self.dbapi.create_baymodel) as cc_mock:
test_auth_url = 'http://127.0.0.1:5000/v2.0' mock_image_data.return_value = {'name': 'mock_name',
mock_openstack_client.glance.return_value = test_auth_url 'os_distro': 'fedora-atomic'}
cdict = apiutils.baymodel_post_data(docker_volume_size=99) cdict = apiutils.baymodel_post_data(docker_volume_size=99)
response = self.post_json('/baymodels', cdict) response = self.post_json('/baymodels', cdict)
self.assertEqual(cdict['docker_volume_size'], self.assertEqual(cdict['docker_volume_size'],
@ -408,10 +409,10 @@ class TestPost(api_base.FunctionalTest):
cc_mock.assert_called_once_with(mock.ANY) cc_mock.assert_called_once_with(mock.ANY)
self.assertNotIn('id', cc_mock.call_args[0][0]) self.assertNotIn('id', cc_mock.call_args[0][0])
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch.object(api_baymodel.BayModelsController, '_get_image_data')
def test_create_baymodel_generate_uuid(self, mock_openstack_client): def test_create_baymodel_generate_uuid(self, mock_image_data):
test_auth_url = 'http://127.0.0.1:5000/v2.0' mock_image_data.return_value = {'name': 'mock_name',
mock_openstack_client.glance.return_value = test_auth_url 'os_distro': 'fedora-atomic'}
cdict = apiutils.baymodel_post_data() cdict = apiutils.baymodel_post_data()
del cdict['uuid'] del cdict['uuid']
response = self.post_json('/baymodels', cdict) response = self.post_json('/baymodels', cdict)
@ -436,6 +437,44 @@ class TestPost(api_base.FunctionalTest):
response = self.post_json('/baymodels', cdict, expect_errors=True) response = self.post_json('/baymodels', cdict, expect_errors=True)
self.assertEqual(201, response.status_int) self.assertEqual(201, response.status_int)
@mock.patch.object(openstack_client, 'glance')
def test_create_baymodel_with_image_name(self, mock_glance_client):
mock_images = [{'name': 'mock_name',
'os_distro': 'fedora-atomic'}]
mock_glance = mock.MagicMock()
mock_glance.images.list.return_value = mock_images
mock_glance_client.return_value = mock_glance
cdict = apiutils.baymodel_post_data()
del cdict['uuid']
response = self.post_json('/baymodels', cdict, expect_errors=True)
self.assertEqual(201, response.status_int)
@mock.patch.object(openstack_client, 'glance')
def test_create_baymodel_with_no_exist_image_name(self,
mock_glance_client):
mock_images = []
mock_glance = mock.MagicMock()
mock_glance.images.list.return_value = mock_images
mock_glance_client.return_value = mock_glance
cdict = apiutils.baymodel_post_data()
del cdict['uuid']
response = self.post_json('/baymodels', cdict, expect_errors=True)
self.assertEqual(404, response.status_int)
@mock.patch.object(openstack_client, 'glance')
def test_create_baymodel_with_multi_image_name(self, mock_glance_client):
mock_images = [{'name': 'mock_name',
'os_distro': 'fedora-atomic'},
{'name': 'mock_name',
'os_distro': 'fedora-atomic'}]
mock_glance = mock.MagicMock()
mock_glance.images.list.return_value = mock_images
mock_glance_client.return_value = mock_glance
cdict = apiutils.baymodel_post_data()
del cdict['uuid']
response = self.post_json('/baymodels', cdict, expect_errors=True)
self.assertEqual(409, response.status_int)
class TestDelete(api_base.FunctionalTest): class TestDelete(api_base.FunctionalTest):

View File

@ -13,9 +13,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock
import wsme import wsme
from magnum.api.controllers.v1 import utils from magnum.api.controllers.v1 import utils
from magnum.common import exception
from magnum.common import utils as common_utils
from magnum.tests.unit.api import base from magnum.tests.unit.api import base
from oslo_config import cfg from oslo_config import cfg
@ -47,3 +50,38 @@ class TestApiUtils(base.FunctionalTest):
self.assertRaises(wsme.exc.ClientSideError, self.assertRaises(wsme.exc.ClientSideError,
utils.validate_sort_dir, utils.validate_sort_dir,
'fake-sort') 'fake-sort')
@mock.patch.object(common_utils, 'is_uuid_like', return_value=True)
def test_get_openstack_resource_by_uuid(self, fake_is_uuid_like):
fake_manager = mock.MagicMock()
fake_manager.get.return_value = 'fake_resource_data'
resource_data = utils.get_openstack_resource(fake_manager,
'fake_resource',
'fake_resource_type')
self.assertEqual('fake_resource_data', resource_data)
@mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
def test_get_openstack_resource_by_name(self, fake_is_uuid_like):
fake_manager = mock.MagicMock()
fake_manager.list.return_value = ['fake_resource_data']
resource_data = utils.get_openstack_resource(fake_manager,
'fake_resource',
'fake_resource_type')
self.assertEqual('fake_resource_data', resource_data)
@mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
def test_get_openstack_resource_non_exist(self, fake_is_uuid_like):
fake_manager = mock.MagicMock()
fake_manager.list.return_value = []
self.assertRaises(exception.ResourceNotFound,
utils.get_openstack_resource,
fake_manager, 'fake_resource', 'fake_resource_type')
@mock.patch.object(common_utils, 'is_uuid_like', return_value=False)
def test_get_openstack_resource_multi_exist(self, fake_is_uuid_like):
fake_manager = mock.MagicMock()
fake_manager.list.return_value = ['fake_resource_data1',
'fake_resource_data2']
self.assertRaises(exception.Conflict,
utils.get_openstack_resource,
fake_manager, 'fake_resource', 'fake_resource_type')