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:
parent
e2ee7eb6e4
commit
1c3027fc52
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue