From 6037a58c6e2d4e0915c1d4f09bc73a3646d86615 Mon Sep 17 00:00:00 2001 From: Ivan Kolodyazhny Date: Thu, 3 Oct 2019 13:37:40 +0300 Subject: [PATCH] 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 --- openstack_dashboard/api/glance.py | 186 ++++----------- .../dashboards/project/images/images/tests.py | 118 ---------- .../dashboards/project/volumes/tests.py | 7 - .../test/test_data/glance_data.py | 36 +-- .../test/unit/api/rest/test_glance.py | 215 ------------------ .../test/unit/api/test_glance.py | 42 ---- ...op-glance-v1-support-2c161d19b179951e.yaml | 5 + 7 files changed, 69 insertions(+), 540 deletions(-) create mode 100644 releasenotes/notes/drop-glance-v1-support-2c161d19b179951e.yaml diff --git a/openstack_dashboard/api/glance.py b/openstack_dashboard/api/glance.py index e2afe3e5b4..6b95472948 100644 --- a/openstack_dashboard/api/glance.py +++ b/openstack_dashboard/api/glance.py @@ -30,7 +30,7 @@ from django.core.files.uploadedfile import InMemoryUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import TemporaryUploadedFile -import glanceclient as glance_client +from glanceclient.v2 import client import six from six.moves import _thread as thread @@ -52,20 +52,8 @@ else: LOG = logging.getLogger(__name__) VERSIONS = base.APIVersionManager("image", preferred_version=2) -try: - # pylint: disable=ungrouped-imports - 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 +VERSIONS.load_supported_version(2, {"client": client, + "version": 2}) # TODO(e0ne): remove this workaround once glanceclient will raise # RequestURITooLong exception @@ -91,9 +79,8 @@ class Image(base.APIResourceWrapper): def __getattribute__(self, attr): # Because Glance v2 treats custom properties as normal # attributes, we need to be more flexible than the resource - # wrappers usually allow. In v1 they were defined under a - # "properties" attribute. - if VERSIONS.active >= 2 and attr == "properties": + # wrappers usually allow. + if attr == "properties": return {k: v for (k, v) in self._apiresource.items() if self.property_visible(k)} try: @@ -130,10 +117,6 @@ class Image(base.APIResourceWrapper): return prop_name not in (self._attrs | self._ext_attrs) 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): return self._apiresource.to_dict() image_dict = super(Image, self).to_dict() @@ -151,22 +134,15 @@ class Image(base.APIResourceWrapper): @memoized -def glanceclient(request, version=None): +def glanceclient(request): api_version = VERSIONS.get_active_version() url = base.url_for(request, 'image') insecure = settings.OPENSTACK_SSL_NO_VERIFY cacert = settings.OPENSTACK_SSL_CACERT - # TODO(jpichon): Temporarily keep both till we update the API calls - # to stop hardcoding a version in this file. Once that's done we - # 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) + return api_version['client'].Client(url, token=request.user.token.id, + insecure=insecure, cacert=cacert) # 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: return - # Glance v1 uses filter 'is_public' (True, False). # Glance v2 uses filter 'visibility' ('public', 'private', ...). - if VERSIONS.active >= 2: - if 'is_public' in filters: - # Glance v2: Replace 'is_public' with 'visibility'. - visibility = PUBLIC_TO_VISIBILITY_MAP[filters['is_public']] - del filters['is_public'] - if visibility is not None: - filters['visibility'] = visibility - elif 'visibility' in filters: - # Glance v1: Replace 'visibility' with 'is_public'. - filters['is_public'] = filters['visibility'] == "public" - del filters['visibility'] + if 'is_public' in filters: + # Glance v2: Replace 'is_public' with 'visibility'. + visibility = PUBLIC_TO_VISIBILITY_MAP[filters['is_public']] + del filters['is_public'] + if visibility is not None: + filters['visibility'] = visibility def _normalize_owner_id_filter(filters): if not filters: return - # Glance v1 uses filter 'property-owner_id' (Project ID). # Glance v2 uses filter 'owner' (Project ID). - if VERSIONS.active >= 2: - if 'property-owner_id' in filters: - # Glance v2: Replace 'property-owner_id' with 'owner'. - filters['owner'] = 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'] + if 'property-owner_id' in filters: + # Glance v2: Replace 'property-owner_id' with 'owner'. + filters['owner'] = filters['property-owner_id'] + del filters['property-owner_id'] def _normalize_list_input(filters, **kwargs): _normalize_is_public_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 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): image_data = kwargs.get('data', None) 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( image_id, **kwargs)) finally: @@ -433,10 +358,7 @@ class ExternallyUploadedImage(Image): def __init__(self, apiresource, request): super(ExternallyUploadedImage, self).__init__(apiresource) image_endpoint = base.url_for(request, 'image', 'publicURL') - if VERSIONS.active >= 2: - upload_template = "%s/v2/images/%s/file" - else: - upload_template = "%s/v1/images/%s" + upload_template = "%s/v2/images/%s/file" self._url = upload_template % (image_endpoint, self.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']) _normalize_is_public_filter(data) - if VERSIONS.active < 2: - meta['properties'] = properties - meta['is_public'] = data.get('is_public', False) - else: - meta['visibility'] = data.get('visibility', 'private') - meta.update(properties) + meta['visibility'] = data.get('visibility', 'private') + meta.update(properties) return meta @@ -548,9 +466,7 @@ def image_create(request, **kwargs): some time and is handed off to a separate thread. """ data = kwargs.pop('data', None) - location = None - if VERSIONS.active >= 2: - location = kwargs.pop('location', None) + location = kwargs.pop('location', None) image = glanceclient(request).images.create(**kwargs) if location is not None: @@ -573,23 +489,19 @@ def image_create(request, **kwargs): data = SimpleUploadedFile(data.name, data.read(), data.content_type) - if VERSIONS.active < 2: - thread.start_new_thread(image_update, - (request, image.id), - {'data': data}) - else: - def upload(): + + def upload(): + try: + return glanceclient(request).images.upload(image.id, data) + finally: + filename = str(data.file.name) try: - return glanceclient(request).images.upload(image.id, data) - finally: - filename = str(data.file.name) - try: - os.remove(filename) - except OSError as e: - LOG.warning('Failed to remove temporary image file ' - '%(file)s (%(e)s)', - {'file': filename, 'e': e}) - thread.start_new_thread(upload, ()) + os.remove(filename) + except OSError as e: + LOG.warning('Failed to remove temporary image file ' + '%(file)s (%(e)s)', + {'file': filename, 'e': e}) + thread.start_new_thread(upload, ()) return Image(image) @@ -597,9 +509,9 @@ def image_create(request, **kwargs): @profiler.trace def image_update_properties(request, image_id, remove_props=None, **kwargs): """Add or update a custom property of an image.""" - return glanceclient(request, '2').images.update(image_id, - remove_props, - **kwargs) + return glanceclient(request).images.update(image_id, + remove_props, + **kwargs) @profiler.trace @@ -666,7 +578,7 @@ def filter_properties_target(namespaces_iter, @memoized 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) # There were problems with using the wrapper class in # 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 # empty array so callers don't need to worry about version checking. - if get_version() < 2: - return [], False, False - if filters is None: filters = {} limit = settings.API_RESULT_LIMIT @@ -728,7 +637,7 @@ def metadefs_namespace_list(request, kwargs['sort_dir'] = sort_dir 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) # 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 def metadefs_namespace_create(request, namespace): - return glanceclient(request, '2').metadefs_namespace.create(**namespace) + return glanceclient(request).metadefs_namespace.create(**namespace) @profiler.trace def metadefs_namespace_update(request, namespace_name, **properties): - return glanceclient(request, '2').metadefs_namespace.update( + return glanceclient(request).metadefs_namespace.update( namespace_name, **properties) @profiler.trace 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 def metadefs_resource_types_list(request): # 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. - if get_version() < 2: - return [] - else: - return glanceclient(request, '2').metadefs_resource_type.list() + return glanceclient(request).metadefs_resource_type.list() @profiler.trace 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) # 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, namespace_name, resource_type): - return glanceclient(request, '2').metadefs_resource_type.associate( + return glanceclient(request).metadefs_resource_type.associate( namespace_name, **resource_type) @@ -826,7 +732,7 @@ def metadefs_namespace_add_resource_type(request, def metadefs_namespace_remove_resource_type(request, namespace_name, resource_type_name): - glanceclient(request, '2').metadefs_resource_type.deassociate( + glanceclient(request).metadefs_resource_type.deassociate( namespace_name, resource_type_name) diff --git a/openstack_dashboard/dashboards/project/images/images/tests.py b/openstack_dashboard/dashboards/project/images/images/tests.py index e025b8e668..1379058688 100644 --- a/openstack_dashboard/dashboards/project/images/images/tests.py +++ b/openstack_dashboard/dashboards/project/images/images/tests.py @@ -87,52 +87,6 @@ class UpdateImageFormTests(test.ResetImageAPIVersionMixin, test.TestCase): mock_image_get.assert_called_once_with(test.IsHttpRequest(), 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_update') 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') 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) def test_image_create_post_location_v2(self): data = { @@ -231,19 +161,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): api_data = {'location': data['image_url']} 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): temp_file = tempfile.NamedTemporaryFile() temp_file.write(b'123') @@ -256,23 +173,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): api_data = {'data': test.IsA(InMemoryUploadedFile)} 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): temp_file = tempfile.NamedTemporaryFile() temp_file.write(b'123') @@ -360,12 +260,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): mock_image_get.assert_called_once_with(test.IsHttpRequest(), 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): image = self.imagesV2.first() @@ -397,12 +291,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): mock_image_get.assert_called_once_with(test.IsHttpRequest(), 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): image = self.imagesV2.list()[2] @@ -422,12 +310,6 @@ class ImageViewTests(test.ResetImageAPIVersionMixin, test.TestCase): mock_image_get.assert_called_once_with(test.IsHttpRequest(), 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): image = self.imagesV2.list()[1] diff --git a/openstack_dashboard/dashboards/project/volumes/tests.py b/openstack_dashboard/dashboards/project/volumes/tests.py index 49a3492cd5..569f100054 100644 --- a/openstack_dashboard/dashboards/project/volumes/tests.py +++ b/openstack_dashboard/dashboards/project/volumes/tests.py @@ -885,13 +885,6 @@ class VolumeViewTests(test.ResetImageAPIVersionMixin, test.TestCase): image.min_disk = 30 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): image = self.imagesV2.get(name="protected_images") self._test_create_volume_from_image_under_image_min_disk_size(image) diff --git a/openstack_dashboard/test/test_data/glance_data.py b/openstack_dashboard/test/test_data/glance_data.py index 632247b5e3..a3a901c7ae 100644 --- a/openstack_dashboard/test/test_data/glance_data.py +++ b/openstack_dashboard/test/test_data/glance_data.py @@ -12,8 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -from glanceclient.v1 import images - from openstack_dashboard import api from openstack_dashboard.test.test_data import utils @@ -47,6 +45,9 @@ class APIResourceV2(dict): def data(TEST): + # TODO(e0ne): merge images with imagesV2 and snapshots with snapshotsV2 + # test data. + TEST.images = utils.TestDataContainer() TEST.images_api = utils.TestDataContainer() TEST.snapshots = utils.TestDataContainer() @@ -90,14 +91,13 @@ def data(TEST): 'is_public': False, 'protected': False} - snapshot = images.Image(images.ImageManager(None), snapshot_dict) + snapshot = APIResourceV2(snapshot_dict) 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)) - snapshot = images.Image(images.ImageManager(None), snapshot_dict_queued) + snapshot = APIResourceV2(snapshot_dict_queued) TEST.snapshots.add(api.glance.Image(snapshot)) - snapshot = images.Image(images.ImageManager(None), - snapshot_dict_with_volume) + snapshot = APIResourceV2(snapshot_dict_with_volume) TEST.snapshots.add(api.glance.Image(snapshot)) # Images @@ -115,7 +115,7 @@ def data(TEST): 'protected': False, 'min_ram': 0, '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', 'name': 'private_image', @@ -129,7 +129,7 @@ def data(TEST): 'protected': False, 'min_ram': 0, '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', 'name': 'protected_images', @@ -144,7 +144,7 @@ def data(TEST): 'protected': True, 'min_ram': 0, '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', 'name': None, @@ -158,7 +158,7 @@ def data(TEST): 'is_public': True, 'protected': False, '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', 'name': 'private_image 2', @@ -171,7 +171,7 @@ def data(TEST): 'is_public': False, 'protected': False, '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', 'name': 'private_image 3', @@ -184,7 +184,7 @@ def data(TEST): 'is_public': False, 'protected': False, '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 # is set as 'community' @@ -200,7 +200,7 @@ def data(TEST): 'protected': False, 'min_ram': 0, '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. image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', @@ -214,7 +214,7 @@ def data(TEST): 'is_public': False, 'protected': False, '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 # in IMAGES_LIST_FILTER_TENANTS. @@ -229,7 +229,7 @@ def data(TEST): 'is_public': True, 'protected': False, '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', 'name': 'multi_prop_image', @@ -244,7 +244,7 @@ def data(TEST): 'bar': u'bar val'}, 'is_public': True, '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 image_dict = {'id': 'c8756975-7a3b-4e43-b7f7-433576112849', @@ -256,7 +256,7 @@ def data(TEST): 'container_format': 'aki', 'is_public': 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, public_image2, private_image2, private_image3, diff --git a/openstack_dashboard/test/unit/api/rest/test_glance.py b/openstack_dashboard/test/unit/api/rest/test_glance.py index 9b0711e11c..9bfe213978 100644 --- a/openstack_dashboard/test/unit/api/rest/test_glance.py +++ b/openstack_dashboard/test/unit/api/rest/test_glance.py @@ -77,38 +77,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase): glance.Image().delete(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']}) def test_image_edit_v2(self): request = self.mock_rest_request(body='''{"name": "Test", @@ -166,43 +134,6 @@ class ImagesRestTestCase(test.ResetImageAPIVersionMixin, test.TestCase): filters=filters, **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']}) def test_image_create_v2_basic(self): 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.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']}) def test_image_create_v2_shared(self): 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.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']}) def test_image_create_v2_private(self): 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.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']}) def test_image_create_v2_bad_visibility(self): 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'), '"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']}) def test_image_create_v2_required(self): 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.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']}) def test_image_create_v2_additional_props(self): request = self.mock_rest_request(body='''{"name": "Test", diff --git a/openstack_dashboard/test/unit/api/test_glance.py b/openstack_dashboard/test/unit/api/test_glance.py index 5d58ddc2f3..a81442f041 100644 --- a/openstack_dashboard/test/unit/api/test_glance.py +++ b/openstack_dashboard/test/unit/api/test_glance.py @@ -359,18 +359,6 @@ class GlanceApiTests(test.APIMockTestCase): self.assertEqual(1, len(defs)) 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') def _test_image_create_external_upload(self, mock_glanceclient, api_version=2): @@ -393,39 +381,9 @@ class GlanceApiTests(test.APIMockTestCase): self.assertEqual(upload_url, actual_image.upload_url) 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): 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): form_data = { 'name': u'Docker image', diff --git a/releasenotes/notes/drop-glance-v1-support-2c161d19b179951e.yaml b/releasenotes/notes/drop-glance-v1-support-2c161d19b179951e.yaml new file mode 100644 index 0000000000..b9f9dcdd7c --- /dev/null +++ b/releasenotes/notes/drop-glance-v1-support-2c161d19b179951e.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Glance API V1 support has been dropped in Ussuri release. Glance V1 API + support was deprecated in Stein release.