Drop deprecated Glance V1 API support

Glance V1 API was deprecated in Stein release in Horizon and
deprecated in Glance since Newton release.

Change-Id: I18f6fd03dd3e07fa4f2ccfdccfdc3ca766c91c16
This commit is contained in:
Ivan Kolodyazhny 2019-10-03 13:37:40 +03:00
parent cf904d1194
commit 6037a58c6e
7 changed files with 69 additions and 540 deletions

View File

@ -30,7 +30,7 @@ from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files.uploadedfile import TemporaryUploadedFile from django.core.files.uploadedfile import TemporaryUploadedFile
import glanceclient as glance_client from glanceclient.v2 import client
import six import six
from six.moves import _thread as thread from six.moves import _thread as thread
@ -52,20 +52,8 @@ else:
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
VERSIONS = base.APIVersionManager("image", preferred_version=2) VERSIONS = base.APIVersionManager("image", preferred_version=2)
try: VERSIONS.load_supported_version(2, {"client": client,
# pylint: disable=ungrouped-imports "version": 2})
from glanceclient.v2 import client as glance_client_v2
VERSIONS.load_supported_version(2, {"client": glance_client_v2,
"version": 2})
except ImportError:
pass
try:
# pylint: disable=ungrouped-imports
from glanceclient.v1 import client as glance_client_v1
VERSIONS.load_supported_version(1, {"client": glance_client_v1,
"version": 1})
except ImportError:
pass
# TODO(e0ne): remove this workaround once glanceclient will raise # TODO(e0ne): remove this workaround once glanceclient will raise
# RequestURITooLong exception # RequestURITooLong exception
@ -91,9 +79,8 @@ class Image(base.APIResourceWrapper):
def __getattribute__(self, attr): def __getattribute__(self, attr):
# Because Glance v2 treats custom properties as normal # Because Glance v2 treats custom properties as normal
# attributes, we need to be more flexible than the resource # attributes, we need to be more flexible than the resource
# wrappers usually allow. In v1 they were defined under a # wrappers usually allow.
# "properties" attribute. if attr == "properties":
if VERSIONS.active >= 2 and attr == "properties":
return {k: v for (k, v) in self._apiresource.items() return {k: v for (k, v) in self._apiresource.items()
if self.property_visible(k)} if self.property_visible(k)}
try: try:
@ -130,10 +117,6 @@ class Image(base.APIResourceWrapper):
return prop_name not in (self._attrs | self._ext_attrs) return prop_name not in (self._attrs | self._ext_attrs)
def to_dict(self, show_ext_attrs=False): def to_dict(self, show_ext_attrs=False):
# When using v1 Image objects (including when running unit tests
# for v2), self._apiresource is not iterable. In that case,
# the properties are included in the apiresource dict, so
# just return that dict.
if not isinstance(self._apiresource, Iterable): if not isinstance(self._apiresource, Iterable):
return self._apiresource.to_dict() return self._apiresource.to_dict()
image_dict = super(Image, self).to_dict() image_dict = super(Image, self).to_dict()
@ -151,22 +134,15 @@ class Image(base.APIResourceWrapper):
@memoized @memoized
def glanceclient(request, version=None): def glanceclient(request):
api_version = VERSIONS.get_active_version() api_version = VERSIONS.get_active_version()
url = base.url_for(request, 'image') url = base.url_for(request, 'image')
insecure = settings.OPENSTACK_SSL_NO_VERIFY insecure = settings.OPENSTACK_SSL_NO_VERIFY
cacert = settings.OPENSTACK_SSL_CACERT cacert = settings.OPENSTACK_SSL_CACERT
# TODO(jpichon): Temporarily keep both till we update the API calls return api_version['client'].Client(url, token=request.user.token.id,
# to stop hardcoding a version in this file. Once that's done we insecure=insecure, cacert=cacert)
# can get rid of the deprecated 'version' parameter.
if version is None:
return api_version['client'].Client(url, token=request.user.token.id,
insecure=insecure, cacert=cacert)
else:
return glance_client.Client(version, url, token=request.user.token.id,
insecure=insecure, cacert=cacert)
# Note: Glance is adding more than just public and private in Newton or later # Note: Glance is adding more than just public and private in Newton or later
@ -193,75 +169,30 @@ def _normalize_is_public_filter(filters):
if not filters: if not filters:
return return
# Glance v1 uses filter 'is_public' (True, False).
# Glance v2 uses filter 'visibility' ('public', 'private', ...). # Glance v2 uses filter 'visibility' ('public', 'private', ...).
if VERSIONS.active >= 2: if 'is_public' in filters:
if 'is_public' in filters: # Glance v2: Replace 'is_public' with 'visibility'.
# Glance v2: Replace 'is_public' with 'visibility'. visibility = PUBLIC_TO_VISIBILITY_MAP[filters['is_public']]
visibility = PUBLIC_TO_VISIBILITY_MAP[filters['is_public']] del filters['is_public']
del filters['is_public'] if visibility is not None:
if visibility is not None: filters['visibility'] = visibility
filters['visibility'] = visibility
elif 'visibility' in filters:
# Glance v1: Replace 'visibility' with 'is_public'.
filters['is_public'] = filters['visibility'] == "public"
del filters['visibility']
def _normalize_owner_id_filter(filters): def _normalize_owner_id_filter(filters):
if not filters: if not filters:
return return
# Glance v1 uses filter 'property-owner_id' (Project ID).
# Glance v2 uses filter 'owner' (Project ID). # Glance v2 uses filter 'owner' (Project ID).
if VERSIONS.active >= 2: if 'property-owner_id' in filters:
if 'property-owner_id' in filters: # Glance v2: Replace 'property-owner_id' with 'owner'.
# Glance v2: Replace 'property-owner_id' with 'owner'. filters['owner'] = filters['property-owner_id']
filters['owner'] = filters['property-owner_id'] del filters['property-owner_id']
del filters['property-owner_id']
elif 'owner' in filters:
# Glance v1: Replace 'owner' with 'property-owner_id'.
filters['property-owner_id'] = filters['owner']
del filters['owner']
def _normalize_list_input(filters, **kwargs): def _normalize_list_input(filters, **kwargs):
_normalize_is_public_filter(filters) _normalize_is_public_filter(filters)
_normalize_owner_id_filter(filters) _normalize_owner_id_filter(filters)
if VERSIONS.active < 2:
# Glance v1 client processes some keywords specifically.
# Others, it just takes as a nested dict called filters.
# This results in the following being passed into the glance client:
# {
# 'is_public': u'true',
# 'sort_key': u'name',
# 'sort_dir': u'asc',
# 'filters': {
# u'min_disk': u'0',
# u'name': u'mysql',
# 'properties': {
# u'os_shutdown_timeout': u'1'
# }
# }
# }
v1_keywords = ['page_size', 'limit', 'sort_dir', 'sort_key', 'marker',
'is_public', 'return_req_id', 'paginate']
filters = {}
properties = {}
for key, value in iter(kwargs.items()):
if key in v1_keywords:
continue
else:
filters[key] = value
del kwargs[key]
if properties:
filters['properties'] = properties
if filters:
kwargs['filters'] = filters
@profiler.trace @profiler.trace
def image_delete(request, image_id): def image_delete(request, image_id):
@ -399,12 +330,6 @@ def image_list_detailed(request, marker=None, sort_dir='desc',
def image_update(request, image_id, **kwargs): def image_update(request, image_id, **kwargs):
image_data = kwargs.get('data', None) image_data = kwargs.get('data', None)
try: try:
# Horizon doesn't support purging image properties. Make sure we don't
# unintentionally remove properties when using v1. We don't need a
# similar setting for v2 because you have to specify which properties
# to remove, and the default is nothing gets removed.
if VERSIONS.active < 2:
kwargs['purge_props'] = False
return Image(glanceclient(request).images.update( return Image(glanceclient(request).images.update(
image_id, **kwargs)) image_id, **kwargs))
finally: finally:
@ -433,10 +358,7 @@ class ExternallyUploadedImage(Image):
def __init__(self, apiresource, request): def __init__(self, apiresource, request):
super(ExternallyUploadedImage, self).__init__(apiresource) super(ExternallyUploadedImage, self).__init__(apiresource)
image_endpoint = base.url_for(request, 'image', 'publicURL') image_endpoint = base.url_for(request, 'image', 'publicURL')
if VERSIONS.active >= 2: upload_template = "%s/v2/images/%s/file"
upload_template = "%s/v2/images/%s/file"
else:
upload_template = "%s/v1/images/%s"
self._url = upload_template % (image_endpoint, self.id) self._url = upload_template % (image_endpoint, self.id)
self._token_id = request.user.token.id self._token_id = request.user.token.id
@ -509,12 +431,8 @@ def create_image_metadata(data):
raise KeyError('invalid visibility option: %s' % data['visibility']) raise KeyError('invalid visibility option: %s' % data['visibility'])
_normalize_is_public_filter(data) _normalize_is_public_filter(data)
if VERSIONS.active < 2: meta['visibility'] = data.get('visibility', 'private')
meta['properties'] = properties meta.update(properties)
meta['is_public'] = data.get('is_public', False)
else:
meta['visibility'] = data.get('visibility', 'private')
meta.update(properties)
return meta return meta
@ -548,9 +466,7 @@ def image_create(request, **kwargs):
some time and is handed off to a separate thread. some time and is handed off to a separate thread.
""" """
data = kwargs.pop('data', None) data = kwargs.pop('data', None)
location = None location = kwargs.pop('location', None)
if VERSIONS.active >= 2:
location = kwargs.pop('location', None)
image = glanceclient(request).images.create(**kwargs) image = glanceclient(request).images.create(**kwargs)
if location is not None: if location is not None:
@ -573,23 +489,19 @@ def image_create(request, **kwargs):
data = SimpleUploadedFile(data.name, data = SimpleUploadedFile(data.name,
data.read(), data.read(),
data.content_type) data.content_type)
if VERSIONS.active < 2:
thread.start_new_thread(image_update, def upload():
(request, image.id), try:
{'data': data}) return glanceclient(request).images.upload(image.id, data)
else: finally:
def upload(): filename = str(data.file.name)
try: try:
return glanceclient(request).images.upload(image.id, data) os.remove(filename)
finally: except OSError as e:
filename = str(data.file.name) LOG.warning('Failed to remove temporary image file '
try: '%(file)s (%(e)s)',
os.remove(filename) {'file': filename, 'e': e})
except OSError as e: thread.start_new_thread(upload, ())
LOG.warning('Failed to remove temporary image file '
'%(file)s (%(e)s)',
{'file': filename, 'e': e})
thread.start_new_thread(upload, ())
return Image(image) return Image(image)
@ -597,9 +509,9 @@ def image_create(request, **kwargs):
@profiler.trace @profiler.trace
def image_update_properties(request, image_id, remove_props=None, **kwargs): def image_update_properties(request, image_id, remove_props=None, **kwargs):
"""Add or update a custom property of an image.""" """Add or update a custom property of an image."""
return glanceclient(request, '2').images.update(image_id, return glanceclient(request).images.update(image_id,
remove_props, remove_props,
**kwargs) **kwargs)
@profiler.trace @profiler.trace
@ -666,7 +578,7 @@ def filter_properties_target(namespaces_iter,
@memoized @memoized
def metadefs_namespace_get(request, namespace, resource_type=None, wrap=False): def metadefs_namespace_get(request, namespace, resource_type=None, wrap=False):
namespace = glanceclient(request, '2').\ namespace = glanceclient(request).\
metadefs_namespace.get(namespace, resource_type=resource_type) metadefs_namespace.get(namespace, resource_type=resource_type)
# There were problems with using the wrapper class in # There were problems with using the wrapper class in
# nested json serialization. So sometimes, it is not desirable # nested json serialization. So sometimes, it is not desirable
@ -709,9 +621,6 @@ def metadefs_namespace_list(request,
""" """
# Listing namespaces requires the v2 API. If not supported we return an # Listing namespaces requires the v2 API. If not supported we return an
# empty array so callers don't need to worry about version checking. # empty array so callers don't need to worry about version checking.
if get_version() < 2:
return [], False, False
if filters is None: if filters is None:
filters = {} filters = {}
limit = settings.API_RESULT_LIMIT limit = settings.API_RESULT_LIMIT
@ -728,7 +637,7 @@ def metadefs_namespace_list(request,
kwargs['sort_dir'] = sort_dir kwargs['sort_dir'] = sort_dir
kwargs['sort_key'] = sort_key kwargs['sort_key'] = sort_key
namespaces_iter = glanceclient(request, '2').metadefs_namespace.list( namespaces_iter = glanceclient(request).metadefs_namespace.list(
page_size=request_size, limit=limit, **kwargs) page_size=request_size, limit=limit, **kwargs)
# Filter the namespaces based on the provided properties_target since this # Filter the namespaces based on the provided properties_target since this
@ -780,34 +689,31 @@ def metadefs_namespace_full_list(request, resource_type, filters=None,
@profiler.trace @profiler.trace
def metadefs_namespace_create(request, namespace): def metadefs_namespace_create(request, namespace):
return glanceclient(request, '2').metadefs_namespace.create(**namespace) return glanceclient(request).metadefs_namespace.create(**namespace)
@profiler.trace @profiler.trace
def metadefs_namespace_update(request, namespace_name, **properties): def metadefs_namespace_update(request, namespace_name, **properties):
return glanceclient(request, '2').metadefs_namespace.update( return glanceclient(request).metadefs_namespace.update(
namespace_name, namespace_name,
**properties) **properties)
@profiler.trace @profiler.trace
def metadefs_namespace_delete(request, namespace_name): def metadefs_namespace_delete(request, namespace_name):
return glanceclient(request, '2').metadefs_namespace.delete(namespace_name) return glanceclient(request).metadefs_namespace.delete(namespace_name)
@profiler.trace @profiler.trace
def metadefs_resource_types_list(request): def metadefs_resource_types_list(request):
# Listing Resource Types requires the v2 API. If not supported we return # Listing Resource Types requires the v2 API. If not supported we return
# an empty array so callers don't need to worry about version checking. # an empty array so callers don't need to worry about version checking.
if get_version() < 2: return glanceclient(request).metadefs_resource_type.list()
return []
else:
return glanceclient(request, '2').metadefs_resource_type.list()
@profiler.trace @profiler.trace
def metadefs_namespace_resource_types(request, namespace_name): def metadefs_namespace_resource_types(request, namespace_name):
resource_types = glanceclient(request, '2').metadefs_resource_type.get( resource_types = glanceclient(request).metadefs_resource_type.get(
namespace_name) namespace_name)
# metadefs_resource_type.get() returns generator, converting it to list # metadefs_resource_type.get() returns generator, converting it to list
@ -818,7 +724,7 @@ def metadefs_namespace_resource_types(request, namespace_name):
def metadefs_namespace_add_resource_type(request, def metadefs_namespace_add_resource_type(request,
namespace_name, namespace_name,
resource_type): resource_type):
return glanceclient(request, '2').metadefs_resource_type.associate( return glanceclient(request).metadefs_resource_type.associate(
namespace_name, **resource_type) namespace_name, **resource_type)
@ -826,7 +732,7 @@ def metadefs_namespace_add_resource_type(request,
def metadefs_namespace_remove_resource_type(request, def metadefs_namespace_remove_resource_type(request,
namespace_name, namespace_name,
resource_type_name): resource_type_name):
glanceclient(request, '2').metadefs_resource_type.deassociate( glanceclient(request).metadefs_resource_type.deassociate(
namespace_name, resource_type_name) namespace_name, resource_type_name)

View File

@ -87,52 +87,6 @@ class UpdateImageFormTests(test.ResetImageAPIVersionMixin, test.TestCase):
mock_image_get.assert_called_once_with(test.IsHttpRequest(), mock_image_get.assert_called_once_with(test.IsHttpRequest(),
image.id) image.id)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
@mock.patch.object(api.glance, 'image_get')
@mock.patch.object(api.glance, 'image_update')
def test_image_update_post_v1(self, mock_image_update, mock_image_get):
image = self.images.first()
data = {
'name': u'Ubuntu 11.10',
'image_id': str(image.id),
'description': u'Login with admin/admin',
'source_type': u'url',
'image_url': u'http://cloud-images.ubuntu.com/releases/'
u'oneiric/release/ubuntu-11.10-server-cloudimg'
u'-amd64-disk1.img',
'disk_format': u'qcow2',
'architecture': u'x86-64',
'min_disk': 15,
'min_ram': 512,
'is_public': False,
'protected': False,
'method': 'UpdateImageForm'}
mock_image_get.return_value = image
mock_image_update.return_value = image
url = reverse('horizon:project:images:images:update',
args=[image.id])
res = self.client.post(url, data)
self.assertNoFormErrors(res)
self.assertEqual(res.status_code, 302)
mock_image_get.assert_called_once_with(test.IsHttpRequest(),
str(image.id))
mock_image_update.assert_called_once_with(
test.IsHttpRequest(),
image.id,
is_public=data['is_public'],
protected=data['protected'],
disk_format=data['disk_format'],
container_format="bare",
name=data['name'],
min_ram=data['min_ram'],
min_disk=data['min_disk'],
properties={
'description': data['description'],
'architecture': data['architecture']})
@mock.patch.object(api.glance, 'image_get') @mock.patch.object(api.glance, 'image_get')
@mock.patch.object(api.glance, 'image_update') @mock.patch.object(api.glance, 'image_update')
def test_image_update_post_v2(self, mock_image_update, mock_image_get): def test_image_update_post_v2(self, mock_image_update, mock_image_get):
@ -196,30 +150,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertTemplateUsed(res, 'project/images/images/create.html') self.assertTemplateUsed(res, 'project/images/images/create.html')
mock_image_list.assert_has_calls(image_calls) mock_image_list.assert_has_calls(image_calls)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_create_post_copy_from_v1(self):
data = {
'source_type': u'url',
'image_url': u'http://cloud-images.ubuntu.com/releases/'
u'oneiric/release/ubuntu-11.10-server-cloudimg'
u'-amd64-disk1.img',
'is_copying': True}
api_data = {'copy_from': data['image_url']}
self._test_image_create(data, api_data)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_create_post_location_v1(self):
data = {
'source_type': u'url',
'image_url': u'http://cloud-images.ubuntu.com/releases/'
u'oneiric/release/ubuntu-11.10-server-cloudimg'
u'-amd64-disk1.img',
'is_copying': False}
api_data = {'location': data['image_url']}
self._test_image_create(data, api_data)
@override_settings(IMAGES_ALLOW_LOCATION=True) @override_settings(IMAGES_ALLOW_LOCATION=True)
def test_image_create_post_location_v2(self): def test_image_create_post_location_v2(self):
data = { data = {
@ -231,19 +161,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
api_data = {'location': data['image_url']} api_data = {'location': data['image_url']}
self._test_image_create(data, api_data) self._test_image_create(data, api_data)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_create_post_upload_v1(self):
temp_file = tempfile.NamedTemporaryFile()
temp_file.write(b'123')
temp_file.flush()
temp_file.seek(0)
data = {'source_type': u'file',
'image_file': temp_file}
api_data = {'data': test.IsA(InMemoryUploadedFile)}
self._test_image_create(data, api_data)
def test_image_create_post_upload_v2(self): def test_image_create_post_upload_v2(self):
temp_file = tempfile.NamedTemporaryFile() temp_file = tempfile.NamedTemporaryFile()
temp_file.write(b'123') temp_file.write(b'123')
@ -256,23 +173,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
api_data = {'data': test.IsA(InMemoryUploadedFile)} api_data = {'data': test.IsA(InMemoryUploadedFile)}
self._test_image_create(data, api_data) self._test_image_create(data, api_data)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_create_post_with_kernel_ramdisk_v1(self):
temp_file = tempfile.NamedTemporaryFile()
temp_file.write(b'123')
temp_file.flush()
temp_file.seek(0)
data = {
'source_type': u'file',
'image_file': temp_file,
'kernel_id': '007e7d55-fe1e-4c5c-bf08-44b4a496482e',
'ramdisk_id': '007e7d55-fe1e-4c5c-bf08-44b4a496482a'
}
api_data = {'data': test.IsA(InMemoryUploadedFile)}
self._test_image_create(data, api_data)
def test_image_create_post_with_kernel_ramdisk_v2(self): def test_image_create_post_with_kernel_ramdisk_v2(self):
temp_file = tempfile.NamedTemporaryFile() temp_file = tempfile.NamedTemporaryFile()
temp_file.write(b'123') temp_file.write(b'123')
@ -360,12 +260,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
mock_image_get.assert_called_once_with(test.IsHttpRequest(), mock_image_get.assert_called_once_with(test.IsHttpRequest(),
six.text_type(image.id)) six.text_type(image.id))
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_detail_get_v1(self):
image = self.images.first()
self._test_image_detail_get(image)
def test_image_detail_get_v2(self): def test_image_detail_get_v2(self):
image = self.imagesV2.first() image = self.imagesV2.first()
@ -397,12 +291,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
mock_image_get.assert_called_once_with(test.IsHttpRequest(), mock_image_get.assert_called_once_with(test.IsHttpRequest(),
six.text_type(image.id)) six.text_type(image.id))
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_image_detail_custom_props_get_v1(self):
image = self.images.list()[9]
self._test_image_detail_custom_props_get(image)
def test_image_detail_custom_props_get_v2(self): def test_image_detail_custom_props_get_v2(self):
image = self.imagesV2.list()[2] image = self.imagesV2.list()[2]
@ -422,12 +310,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
mock_image_get.assert_called_once_with(test.IsHttpRequest(), mock_image_get.assert_called_once_with(test.IsHttpRequest(),
six.text_type(image.id)) six.text_type(image.id))
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_protected_image_detail_get_v1(self):
image = self.images.list()[2]
self._test_protected_image_detail_get(image)
def test_protected_image_detail_get_v2(self): def test_protected_image_detail_get_v2(self):
image = self.imagesV2.list()[1] image = self.imagesV2.list()[1]

View File

@ -885,13 +885,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase):
image.min_disk = 30 image.min_disk = 30
self._test_create_volume_from_image_under_image_min_disk_size(image) self._test_create_volume_from_image_under_image_min_disk_size(image)
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_create_volume_from_image_under_image_prop_min_disk_size_v1(self):
image = self.images.get(name="protected_images")
image.min_disk = 0
image.properties['min_disk'] = 30
self._test_create_volume_from_image_under_image_min_disk_size(image)
def test_create_volume_from_image_under_image_prop_min_disk_size_v2(self): def test_create_volume_from_image_under_image_prop_min_disk_size_v2(self):
image = self.imagesV2.get(name="protected_images") image = self.imagesV2.get(name="protected_images")
self._test_create_volume_from_image_under_image_min_disk_size(image) self._test_create_volume_from_image_under_image_min_disk_size(image)

View File

@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from glanceclient.v1 import images
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.test.test_data import utils from openstack_dashboard.test.test_data import utils
@ -47,6 +45,9 @@ class APIResourceV2(dict):
def data(TEST): def data(TEST):
# TODO(e0ne): merge images with imagesV2 and snapshots with snapshotsV2
# test data.
TEST.images = utils.TestDataContainer() TEST.images = utils.TestDataContainer()
TEST.images_api = utils.TestDataContainer() TEST.images_api = utils.TestDataContainer()
TEST.snapshots = utils.TestDataContainer() TEST.snapshots = utils.TestDataContainer()
@ -90,14 +91,13 @@ def data(TEST):
'is_public': False, 'is_public': False,
'protected': False} 'protected': False}
snapshot = images.Image(images.ImageManager(None), snapshot_dict) snapshot = APIResourceV2(snapshot_dict)
TEST.snapshots.add(api.glance.Image(snapshot)) TEST.snapshots.add(api.glance.Image(snapshot))
snapshot = images.Image(images.ImageManager(None), snapshot_dict_no_owner) snapshot = APIResourceV2(snapshot_dict_no_owner)
TEST.snapshots.add(api.glance.Image(snapshot)) TEST.snapshots.add(api.glance.Image(snapshot))
snapshot = images.Image(images.ImageManager(None), snapshot_dict_queued) snapshot = APIResourceV2(snapshot_dict_queued)
TEST.snapshots.add(api.glance.Image(snapshot)) TEST.snapshots.add(api.glance.Image(snapshot))
snapshot = images.Image(images.ImageManager(None), snapshot = APIResourceV2(snapshot_dict_with_volume)
snapshot_dict_with_volume)
TEST.snapshots.add(api.glance.Image(snapshot)) TEST.snapshots.add(api.glance.Image(snapshot))
# Images # Images
@ -115,7 +115,7 @@ def data(TEST):
'protected': False, 'protected': False,
'min_ram': 0, 'min_ram': 0,
'created_at': '2014-02-14T20:56:53'} 'created_at': '2014-02-14T20:56:53'}
public_image = images.Image(images.ImageManager(None), image_dict) public_image = APIResourceV2(image_dict)
image_dict = {'id': 'a001c047-22f8-47d0-80a1-8ec94a9524fe', image_dict = {'id': 'a001c047-22f8-47d0-80a1-8ec94a9524fe',
'name': 'private_image', 'name': 'private_image',
@ -129,7 +129,7 @@ def data(TEST):
'protected': False, 'protected': False,
'min_ram': 0, 'min_ram': 0,
'created_at': '2014-03-14T12:56:53'} 'created_at': '2014-03-14T12:56:53'}
private_image = images.Image(images.ImageManager(None), image_dict) private_image = APIResourceV2(image_dict)
image_dict = {'id': 'd6936c86-7fec-474a-85c5-5e467b371c3c', image_dict = {'id': 'd6936c86-7fec-474a-85c5-5e467b371c3c',
'name': 'protected_images', 'name': 'protected_images',
@ -144,7 +144,7 @@ def data(TEST):
'protected': True, 'protected': True,
'min_ram': 0, 'min_ram': 0,
'created_at': '2014-03-16T06:22:14'} 'created_at': '2014-03-16T06:22:14'}
protected_image = images.Image(images.ImageManager(None), image_dict) protected_image = APIResourceV2(image_dict)
image_dict = {'id': '278905a6-4b52-4d1e-98f9-8c57bb25ba32', image_dict = {'id': '278905a6-4b52-4d1e-98f9-8c57bb25ba32',
'name': None, 'name': None,
@ -158,7 +158,7 @@ def data(TEST):
'is_public': True, 'is_public': True,
'protected': False, 'protected': False,
'min_ram': 0} 'min_ram': 0}
public_image2 = images.Image(images.ImageManager(None), image_dict) public_image2 = APIResourceV2(image_dict)
image_dict = {'id': '710a1acf-a3e3-41dd-a32d-5d6b6c86ea10', image_dict = {'id': '710a1acf-a3e3-41dd-a32d-5d6b6c86ea10',
'name': 'private_image 2', 'name': 'private_image 2',
@ -171,7 +171,7 @@ def data(TEST):
'is_public': False, 'is_public': False,
'protected': False, 'protected': False,
'min_ram': 0} 'min_ram': 0}
private_image2 = images.Image(images.ImageManager(None), image_dict) private_image2 = APIResourceV2(image_dict)
image_dict = {'id': '7cd892fd-5652-40f3-a450-547615680132', image_dict = {'id': '7cd892fd-5652-40f3-a450-547615680132',
'name': 'private_image 3', 'name': 'private_image 3',
@ -184,7 +184,7 @@ def data(TEST):
'is_public': False, 'is_public': False,
'protected': False, 'protected': False,
'min_ram': 0} 'min_ram': 0}
private_image3 = images.Image(images.ImageManager(None), image_dict) private_image3 = APIResourceV2(image_dict)
# A community image. Not public and not local tenant, but visibility # A community image. Not public and not local tenant, but visibility
# is set as 'community' # is set as 'community'
@ -200,7 +200,7 @@ def data(TEST):
'protected': False, 'protected': False,
'min_ram': 0, 'min_ram': 0,
'visibility': 'community'} 'visibility': 'community'}
community_image = images.Image(images.ImageManager(None), image_dict) community_image = APIResourceV2(image_dict)
# A shared image. Not public and not local tenant. # A shared image. Not public and not local tenant.
image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849',
@ -214,7 +214,7 @@ def data(TEST):
'is_public': False, 'is_public': False,
'protected': False, 'protected': False,
'min_ram': 0} 'min_ram': 0}
shared_image1 = images.Image(images.ImageManager(None), image_dict) shared_image1 = APIResourceV2(image_dict)
# "Official" image. Public and tenant matches an entry # "Official" image. Public and tenant matches an entry
# in IMAGES_LIST_FILTER_TENANTS. # in IMAGES_LIST_FILTER_TENANTS.
@ -229,7 +229,7 @@ def data(TEST):
'is_public': True, 'is_public': True,
'protected': False, 'protected': False,
'min_ram': 0} 'min_ram': 0}
official_image1 = images.Image(images.ImageManager(None), image_dict) official_image1 = APIResourceV2(image_dict)
image_dict = {'id': 'a67e7d45-fe1e-4c5c-bf08-44b4a4964822', image_dict = {'id': 'a67e7d45-fe1e-4c5c-bf08-44b4a4964822',
'name': 'multi_prop_image', 'name': 'multi_prop_image',
@ -244,7 +244,7 @@ def data(TEST):
'bar': u'bar val'}, 'bar': u'bar val'},
'is_public': True, 'is_public': True,
'protected': False} 'protected': False}
multi_prop_image = images.Image(images.ImageManager(None), image_dict) multi_prop_image = APIResourceV2(image_dict)
# An image without name being returned based on current api # An image without name being returned based on current api
image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849',
@ -256,7 +256,7 @@ def data(TEST):
'container_format': 'aki', 'container_format': 'aki',
'is_public': False, 'is_public': False,
'protected': False} 'protected': False}
no_name_image = images.Image(images.ImageManager(None), image_dict) no_name_image = APIResourceV2(image_dict)
TEST.images_api.add(public_image, private_image, protected_image, TEST.images_api.add(public_image, private_image, protected_image,
public_image2, private_image2, private_image3, public_image2, private_image2, private_image3,

View File

@ -77,38 +77,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
glance.Image().delete(request, "1") glance.Image().delete(request, "1")
self.mock_image_delete.assert_called_once_with(request, "1") self.mock_image_delete.assert_called_once_with(request, "1")
@test.create_mocks({api.glance: ['image_update', 'VERSIONS']})
def test_image_edit_v1(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "aki", "container_format": "aki",
"visibility": "public", "protected": false,
"image_url": "test.com",
"source_type": "url", "architecture": "testArch",
"description": "description", "kernel": "kernel",
"min_disk": 10, "min_ram": 5, "ramdisk": 10 }
''')
self.mock_VERSIONS.active = 1
self.mock_image_update.return_value = self.images.first()
metadata = {'name': 'Test',
'disk_format': 'aki',
'container_format': 'aki',
'is_public': True,
'protected': False,
'min_disk': 10,
'min_ram': 5,
'properties': {'description': 'description',
'architecture': 'testArch',
'ramdisk_id': 10,
'kernel_id': 'kernel'}
}
response = glance.Image().patch(request, "1")
self.assertStatusCode(response, 204)
self.assertEqual(response.content.decode('utf-8'), '')
self.mock_image_update.assert_called_once_with(request, '1',
**metadata)
@test.create_mocks({api.glance: ['image_update', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_update', 'VERSIONS']})
def test_image_edit_v2(self): def test_image_edit_v2(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -166,43 +134,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
filters=filters, filters=filters,
**kwargs) **kwargs)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v1_basic(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "aki", "import_data": false,
"visibility": "public", "container_format": "aki",
"protected": false, "image_url": "test.com",
"source_type": "url", "architecture": "testArch",
"description": "description", "kernel": "kernel",
"min_disk": 10, "min_ram": 5, "ramdisk": 10 }
''')
new = self.mock_image_create.return_value
self.mock_VERSIONS.active = 1
new.to_dict.return_value = {'name': 'testimage'}
new.name = 'testimage'
metadata = {'name': 'Test',
'disk_format': 'aki',
'container_format': 'aki',
'is_public': True,
'protected': False,
'min_disk': 10,
'min_ram': 5,
'location': 'test.com',
'properties': {
'description': 'description',
'architecture': 'testArch',
'ramdisk_id': 10,
'kernel_id': 'kernel',
}}
response = glance.Images().put(request)
self.assertStatusCode(response, 201)
self.assertEqual(response.content.decode('utf-8'),
'{"name": "testimage"}')
self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v2_basic(self): def test_image_create_v2_basic(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -239,43 +170,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(response['location'], '/api/glance/images/testimage') self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata) self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v1_shared(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "aki", "import_data": false,
"visibility": "shared", "container_format": "aki",
"protected": false, "image_url": "test.com",
"source_type": "url", "architecture": "testArch",
"description": "description", "kernel": "kernel",
"min_disk": 10, "min_ram": 5, "ramdisk": 10 }
''')
self.mock_VERSIONS.active = 1
new = self.mock_image_create.return_value
new.to_dict.return_value = {'name': 'testimage'}
new.name = 'testimage'
metadata = {'name': 'Test',
'disk_format': 'aki',
'container_format': 'aki',
'is_public': False,
'protected': False,
'min_disk': 10,
'min_ram': 5,
'location': 'test.com',
'properties': {
'description': 'description',
'architecture': 'testArch',
'ramdisk_id': 10,
'kernel_id': 'kernel',
}}
response = glance.Images().put(request)
self.assertStatusCode(response, 201)
self.assertEqual(response.content.decode('utf-8'),
'{"name": "testimage"}')
self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v2_shared(self): def test_image_create_v2_shared(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -312,43 +206,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(response['location'], '/api/glance/images/testimage') self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata) self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v1_private(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "aki", "import_data": false,
"visibility": "private", "container_format": "aki",
"protected": false, "image_url": "test.com",
"source_type": "url", "architecture": "testArch",
"description": "description", "kernel": "kernel",
"min_disk": 10, "min_ram": 5, "ramdisk": 10 }
''')
self.mock_VERSIONS.active = 1
new = self.mock_image_create.return_value
new.to_dict.return_value = {'name': 'testimage'}
new.name = 'testimage'
metadata = {'name': 'Test',
'disk_format': 'aki',
'container_format': 'aki',
'is_public': False,
'protected': False,
'min_disk': 10,
'min_ram': 5,
'location': 'test.com',
'properties': {
'description': 'description',
'architecture': 'testArch',
'ramdisk_id': 10,
'kernel_id': 'kernel',
}}
response = glance.Images().put(request)
self.assertStatusCode(response, 201)
self.assertEqual(response.content.decode('utf-8'),
'{"name": "testimage"}')
self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v2_private(self): def test_image_create_v2_private(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -385,23 +242,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(response['location'], '/api/glance/images/testimage') self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata) self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['VERSIONS']})
def test_image_create_v1_bad_visibility(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "aki", "import_data": false,
"visibility": "verybad", "container_format": "aki",
"protected": false, "image_url": "test.com",
"source_type": "url", "architecture": "testArch",
"description": "description", "kernel": "kernel",
"min_disk": 10, "min_ram": 5, "ramdisk": 10 }
''')
self.mock_VERSIONS.active = 1
response = glance.Images().put(request)
self.assertStatusCode(response, 400)
self.assertEqual(response.content.decode('utf-8'),
'"invalid visibility option: verybad"')
@test.create_mocks({api.glance: ['VERSIONS']}) @test.create_mocks({api.glance: ['VERSIONS']})
def test_image_create_v2_bad_visibility(self): def test_image_create_v2_bad_visibility(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -419,33 +259,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(response.content.decode('utf-8'), self.assertEqual(response.content.decode('utf-8'),
'"invalid visibility option: verybad"') '"invalid visibility option: verybad"')
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v1_required(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "raw", "import_data": true,
"container_format": "docker",
"visibility": "public", "protected": false,
"source_type": "url", "image_url": "test.com" }''')
self.mock_VERSIONS.active = 1
new = self.mock_image_create.return_value
new.to_dict.return_value = {'name': 'testimage'}
new.name = 'testimage'
metadata = {'name': 'Test',
'disk_format': 'raw',
'container_format': 'docker',
'copy_from': 'test.com',
'is_public': True,
'protected': False,
'min_disk': 0,
'min_ram': 0,
'properties': {}
}
response = glance.Images().put(request)
self.assertStatusCode(response, 201)
self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v2_required(self): def test_image_create_v2_required(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",
@ -472,34 +285,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase):
self.assertEqual(response['location'], '/api/glance/images/testimage') self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata) self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v1_additional_props(self):
request = self.mock_rest_request(body='''{"name": "Test",
"disk_format": "raw", "import_data": true,
"container_format": "docker",
"visibility": "public", "protected": false,
"arbitrary": "property", "another": "prop",
"source_type": "url", "image_url": "test.com" }''')
self.mock_VERSIONS.active = 1
new = self.mock_image_create.return_value
new.to_dict.return_value = {'name': 'testimage'}
new.name = 'testimage'
metadata = {'name': 'Test',
'disk_format': 'raw',
'container_format': 'docker',
'copy_from': 'test.com',
'is_public': True,
'protected': False,
'min_disk': 0,
'min_ram': 0,
'properties': {'arbitrary': 'property', 'another': 'prop'}
}
response = glance.Images().put(request)
self.assertStatusCode(response, 201)
self.assertEqual(response['location'], '/api/glance/images/testimage')
self.mock_image_create.assert_called_once_with(request, **metadata)
@test.create_mocks({api.glance: ['image_create', 'VERSIONS']}) @test.create_mocks({api.glance: ['image_create', 'VERSIONS']})
def test_image_create_v2_additional_props(self): def test_image_create_v2_additional_props(self):
request = self.mock_rest_request(body='''{"name": "Test", request = self.mock_rest_request(body='''{"name": "Test",

View File

@ -359,18 +359,6 @@ class GlanceApiTests(test.APIMockTestCase):
self.assertEqual(1, len(defs)) self.assertEqual(1, len(defs))
self.assertEqual('namespace_4', defs[0].namespace) self.assertEqual('namespace_4', defs[0].namespace)
@mock.patch.object(api.glance, 'get_version', return_value=1)
def test_metadefs_namespace_list_v1(self, mock_version):
defs, more, prev = api.glance.metadefs_namespace_list(self.request)
self.assertItemsEqual(defs, [])
self.assertFalse(more)
self.assertFalse(prev)
@mock.patch.object(api.glance, 'get_version', return_value=1)
def test_metadefs_resource_types_list_v1(self, mock_version):
res_types = api.glance.metadefs_resource_types_list(self.request)
self.assertItemsEqual(res_types, [])
@mock.patch.object(api.glance, 'glanceclient') @mock.patch.object(api.glance, 'glanceclient')
def _test_image_create_external_upload(self, mock_glanceclient, def _test_image_create_external_upload(self, mock_glanceclient,
api_version=2): api_version=2):
@ -393,39 +381,9 @@ class GlanceApiTests(test.APIMockTestCase):
self.assertEqual(upload_url, actual_image.upload_url) self.assertEqual(upload_url, actual_image.upload_url)
self.assertEqual(self.request.user.token.id, actual_image.token_id) self.assertEqual(self.request.user.token.id, actual_image.token_id)
@override_settings(OPENSTACK_API_VERSIONS={"image": 1})
def test_image_create_v1_external_upload(self):
self._test_image_create_external_upload(api_version=1)
def test_image_create_v2_external_upload(self): def test_image_create_v2_external_upload(self):
self._test_image_create_external_upload() self._test_image_create_external_upload()
@override_settings(OPENSTACK_API_VERSIONS={'image': 1})
def test_create_image_metadata_docker_v1(self):
form_data = {
'name': u'Docker image',
'description': u'Docker image test',
'source_type': u'url',
'image_url': u'/',
'disk_format': u'docker',
'architecture': u'x86-64',
'min_disk': 15,
'min_ram': 512,
'is_public': False,
'protected': False,
'is_copying': False
}
meta = api.glance.create_image_metadata(form_data)
self.assertEqual(meta['disk_format'], 'raw')
self.assertEqual(meta['container_format'], 'docker')
self.assertIn('properties', meta)
self.assertNotIn('description', meta)
self.assertNotIn('architecture', meta)
self.assertEqual(meta['properties']['description'],
form_data['description'])
self.assertEqual(meta['properties']['architecture'],
form_data['architecture'])
def test_create_image_metadata_docker_v2(self): def test_create_image_metadata_docker_v2(self):
form_data = { form_data = {
'name': u'Docker image', 'name': u'Docker image',

View File

@ -0,0 +1,5 @@
---
upgrade:
- |
Glance API V1 support has been dropped in Ussuri release. Glance V1 API
support was deprecated in Stein release.