Remove Glance v1 APIs tests
Glance v1 APIs were removed in Rocky and tempest master does not support the Rocky release. If glance v1 APIs needs to be tested for older release then older Tempest can be used. This removes the Glance v1 APIs tests, config option, and its service clients. Change-Id: I6741e2204de251ee8f16b0249a73bee5912c1135
This commit is contained in:
parent
054c06aec9
commit
3562cd0017
@ -0,0 +1,8 @@
|
||||
---
|
||||
prelude: >
|
||||
Glance v1 APIs were removed in Rocky release and last
|
||||
supported release for v1 was Queens. Tempest master does
|
||||
not support the Rocky or Queens release so we removed
|
||||
the Glance v1 tests, config option, and its service clients.
|
||||
If you would like to test the v1 APIs then you can use the old
|
||||
Tempest version.
|
@ -113,14 +113,11 @@ class BaseV2ComputeTest(api_version_utils.BaseMicroversionTest,
|
||||
cls.attachments_client = cls.os_primary.attachments_client_latest
|
||||
cls.snapshots_client = cls.os_primary.snapshots_client_latest
|
||||
if CONF.service_available.glance:
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.images_client = cls.os_primary.image_client
|
||||
elif CONF.image_feature_enabled.api_v2:
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
cls.images_client = cls.os_primary.image_client_v2
|
||||
else:
|
||||
raise lib_exc.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
|
@ -17,7 +17,6 @@ import io
|
||||
import random
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common import utils
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
@ -48,23 +47,15 @@ class FlavorsV2NegativeTest(base.BaseV2ComputeTest):
|
||||
'name': data_utils.rand_name('image'),
|
||||
'container_format': CONF.image.container_formats[0],
|
||||
'disk_format': CONF.image.disk_formats[0],
|
||||
'min_ram': min_img_ram
|
||||
'min_ram': min_img_ram,
|
||||
'visibility': 'private'
|
||||
}
|
||||
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
params.update({'is_public': False})
|
||||
params = {'headers': common_image.image_meta_to_headers(**params)}
|
||||
else:
|
||||
params.update({'visibility': 'private'})
|
||||
|
||||
image = self.images_client.create_image(**params)
|
||||
image = image['image'] if 'image' in image else image
|
||||
self.addCleanup(self.images_client.delete_image, image['id'])
|
||||
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
self.images_client.update_image(image['id'], data=image_file)
|
||||
else:
|
||||
self.images_client.store_image_file(image['id'], data=image_file)
|
||||
self.images_client.store_image_file(image['id'], data=image_file)
|
||||
|
||||
self.assertEqual(min_img_ram, image['min_ram'])
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
import io
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common import waiters
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
@ -42,17 +41,11 @@ class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(ImagesMetadataTestJSON, cls).setup_clients()
|
||||
# Check if glance v1 is available to determine which client to use. We
|
||||
# prefer glance v1 for the compute API tests since the compute image
|
||||
# API proxy was written for glance v1.
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.glance_client = cls.os_primary.image_client
|
||||
elif CONF.image_feature_enabled.api_v2:
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
cls.glance_client = cls.os_primary.image_client_v2
|
||||
else:
|
||||
raise exceptions.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
cls.client = cls.compute_images_client
|
||||
|
||||
@classmethod
|
||||
@ -63,13 +56,9 @@ class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
|
||||
params = {
|
||||
'name': data_utils.rand_name('image'),
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'raw'
|
||||
'disk_format': 'raw',
|
||||
'visibility': 'private'
|
||||
}
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
params.update({'is_public': False})
|
||||
params = {'headers': common_image.image_meta_to_headers(**params)}
|
||||
else:
|
||||
params.update({'visibility': 'private'})
|
||||
|
||||
body = cls.glance_client.create_image(**params)
|
||||
body = body['image'] if 'image' in body else body
|
||||
@ -78,10 +67,7 @@ class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
|
||||
cls.glance_client.delete_image,
|
||||
cls.image_id)
|
||||
image_file = io.BytesIO((b'*' * 1024))
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.glance_client.update_image(cls.image_id, data=image_file)
|
||||
else:
|
||||
cls.glance_client.store_image_file(cls.image_id, data=image_file)
|
||||
cls.glance_client.store_image_file(cls.image_id, data=image_file)
|
||||
waiters.wait_for_image_status(cls.client, cls.image_id, 'ACTIVE')
|
||||
|
||||
def setUp(self):
|
||||
|
@ -19,7 +19,6 @@ import io
|
||||
import testtools
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common import waiters
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
@ -46,17 +45,11 @@ class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
|
||||
def setup_clients(cls):
|
||||
super(ListImageFiltersTestJSON, cls).setup_clients()
|
||||
cls.client = cls.compute_images_client
|
||||
# Check if glance v1 is available to determine which client to use. We
|
||||
# prefer glance v1 for the compute API tests since the compute image
|
||||
# API proxy was written for glance v1.
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.glance_client = cls.os_primary.image_client
|
||||
elif CONF.image_feature_enabled.api_v2:
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
cls.glance_client = cls.os_primary.image_client_v2
|
||||
else:
|
||||
raise exceptions.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
@ -66,14 +59,9 @@ class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
|
||||
params = {
|
||||
'name': data_utils.rand_name(cls.__name__ + '-image'),
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'raw'
|
||||
'disk_format': 'raw',
|
||||
'visibility': 'private'
|
||||
}
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
params.update({'is_public': False})
|
||||
params = {'headers':
|
||||
common_image.image_meta_to_headers(**params)}
|
||||
else:
|
||||
params.update({'visibility': 'private'})
|
||||
|
||||
body = cls.glance_client.create_image(**params)
|
||||
body = body['image'] if 'image' in body else body
|
||||
@ -86,10 +74,7 @@ class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
|
||||
# between created_at and updated_at.
|
||||
time.sleep(1)
|
||||
image_file = io.BytesIO((b'*' * 1024))
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.glance_client.update_image(image_id, data=image_file)
|
||||
else:
|
||||
cls.glance_client.store_image_file(image_id, data=image_file)
|
||||
cls.glance_client.store_image_file(image_id, data=image_file)
|
||||
waiters.wait_for_image_status(cls.client, image_id, 'ACTIVE')
|
||||
body = cls.client.show_image(image_id)['image']
|
||||
return body
|
||||
|
@ -569,17 +569,11 @@ class ServerActionsTestOtherB(ServerActionsBase):
|
||||
|
||||
# create the first and the second backup
|
||||
|
||||
# Check if glance v1 is available to determine which client to use. We
|
||||
# prefer glance v1 for the compute API tests since the compute image
|
||||
# API proxy was written for glance v1.
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
glance_client = self.os_primary.image_client
|
||||
elif CONF.image_feature_enabled.api_v2:
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
glance_client = self.os_primary.image_client_v2
|
||||
else:
|
||||
raise lib_exc.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
|
||||
backup1 = data_utils.rand_name('backup-1')
|
||||
resp = self.client.create_backup(self.server_id,
|
||||
@ -635,16 +629,9 @@ class ServerActionsTestOtherB(ServerActionsBase):
|
||||
'sort_key': 'created_at',
|
||||
'sort_dir': 'asc'
|
||||
}
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
for key, value in properties.items():
|
||||
params['property-%s' % key] = value
|
||||
image_list = glance_client.list_images(
|
||||
detail=True,
|
||||
**params)['images']
|
||||
else:
|
||||
# Additional properties are flattened in glance v2.
|
||||
params.update(properties)
|
||||
image_list = glance_client.list_images(params)['images']
|
||||
# Additional properties are flattened in glance v2.
|
||||
params.update(properties)
|
||||
image_list = glance_client.list_images(params)['images']
|
||||
|
||||
self.assertEqual(2, len(image_list))
|
||||
self.assertEqual((backup1, backup2),
|
||||
@ -668,11 +655,7 @@ class ServerActionsTestOtherB(ServerActionsBase):
|
||||
waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
|
||||
glance_client.wait_for_resource_deletion(image1_id)
|
||||
oldest_backup_exist = False
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
image_list = glance_client.list_images(
|
||||
detail=True, **params)['images']
|
||||
else:
|
||||
image_list = glance_client.list_images(params)['images']
|
||||
image_list = glance_client.list_images(params)['images']
|
||||
self.assertEqual(2, len(image_list),
|
||||
'Unexpected number of images for '
|
||||
'v2:test_create_backup; was the oldest backup not '
|
||||
@ -733,23 +716,16 @@ class ServerActionsTestOtherB(ServerActionsBase):
|
||||
"""Test shelving and unshelving server"""
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
glance_client = self.os_primary.image_client_v2
|
||||
elif CONF.image_feature_enabled.api_v1:
|
||||
glance_client = self.os_primary.image_client
|
||||
else:
|
||||
raise lib_exc.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
compute.shelve_server(self.client, self.server_id,
|
||||
force_shelve_offload=True)
|
||||
|
||||
server = self.client.show_server(self.server_id)['server']
|
||||
image_name = server['name'] + '-shelved'
|
||||
params = {'name': image_name}
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
images = glance_client.list_images(params)['images']
|
||||
elif CONF.image_feature_enabled.api_v1:
|
||||
images = glance_client.list_images(
|
||||
detail=True, **params)['images']
|
||||
images = glance_client.list_images(params)['images']
|
||||
self.assertEqual(1, len(images))
|
||||
self.assertEqual(image_name, images[0]['name'])
|
||||
|
||||
|
@ -508,10 +508,7 @@ class ServersNegativeTestJSON(base.BaseV2ComputeTest):
|
||||
|
||||
server = self.client.show_server(self.server_id)['server']
|
||||
image_name = server['name'] + '-shelved'
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
kwargs = {'name': image_name}
|
||||
else:
|
||||
kwargs = {'params': {'name': image_name}}
|
||||
kwargs = {'params': {'name': image_name}}
|
||||
images = self.images_client.list_images(**kwargs)['images']
|
||||
self.assertEqual(1, len(images))
|
||||
self.assertEqual(image_name, images[0]['name'])
|
||||
|
@ -12,10 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import io
|
||||
import time
|
||||
|
||||
from tempest.common import image as common_image
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib.common.utils import test_utils
|
||||
@ -56,17 +54,7 @@ class BaseImageTest(tempest.test.BaseTestCase):
|
||||
name = data_utils.rand_name(cls.__name__ + "-image")
|
||||
kwargs['name'] = name
|
||||
|
||||
params = cls._get_create_params(**kwargs)
|
||||
if data:
|
||||
# NOTE: On glance v1 API, the data should be passed on
|
||||
# a header. Then here handles the data separately.
|
||||
params['data'] = data
|
||||
|
||||
image = cls.client.create_image(**params)
|
||||
# Image objects returned by the v1 client have the image
|
||||
# data inside a dict that is keyed against 'image'.
|
||||
if 'image' in image:
|
||||
image = image['image']
|
||||
image = cls.client.create_image(**kwargs)
|
||||
cls.created_images.append(image['id'])
|
||||
cls.addClassResourceCleanup(cls.client.wait_for_resource_deletion,
|
||||
image['id'])
|
||||
@ -74,54 +62,6 @@ class BaseImageTest(tempest.test.BaseTestCase):
|
||||
cls.client.delete_image, image['id'])
|
||||
return image
|
||||
|
||||
@classmethod
|
||||
def _get_create_params(cls, **kwargs):
|
||||
return kwargs
|
||||
|
||||
|
||||
class BaseV1ImageTest(BaseImageTest):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseV1ImageTest, cls).skip_checks()
|
||||
if not CONF.image_feature_enabled.api_v1:
|
||||
msg = "Glance API v1 not supported"
|
||||
raise cls.skipException(msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseV1ImageTest, cls).setup_clients()
|
||||
cls.client = cls.os_primary.image_client
|
||||
|
||||
@classmethod
|
||||
def _get_create_params(cls, **kwargs):
|
||||
return {'headers': common_image.image_meta_to_headers(**kwargs)}
|
||||
|
||||
|
||||
class BaseV1ImageMembersTest(BaseV1ImageTest):
|
||||
|
||||
credentials = ['primary', 'alt']
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseV1ImageMembersTest, cls).setup_clients()
|
||||
cls.image_member_client = cls.os_primary.image_member_client
|
||||
cls.alt_image_member_client = cls.os_alt.image_member_client
|
||||
cls.alt_img_cli = cls.os_alt.image_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseV1ImageMembersTest, cls).resource_setup()
|
||||
cls.alt_tenant_id = cls.alt_image_member_client.tenant_id
|
||||
|
||||
def _create_image(self):
|
||||
image_file = io.BytesIO(data_utils.random_bytes())
|
||||
image = self.create_image(container_format='bare',
|
||||
disk_format='raw',
|
||||
is_public=False,
|
||||
data=image_file)
|
||||
return image['id']
|
||||
|
||||
|
||||
class BaseV2ImageTest(BaseImageTest):
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from tempest.api.image import base
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
class ImageMembersTest(base.BaseV1ImageMembersTest):
|
||||
"""Test image members"""
|
||||
|
||||
@decorators.idempotent_id('1d6ef640-3a20-4c84-8710-d95828fdb6ad')
|
||||
def test_add_image_member(self):
|
||||
"""Test adding member for image"""
|
||||
image = self._create_image()
|
||||
self.image_member_client.create_image_member(image, self.alt_tenant_id)
|
||||
body = self.image_member_client.list_image_members(image)
|
||||
members = body['members']
|
||||
members = [member['member_id'] for member in members]
|
||||
self.assertIn(self.alt_tenant_id, members)
|
||||
# get image as alt user
|
||||
self.alt_img_cli.show_image(image)
|
||||
|
||||
@decorators.idempotent_id('6a5328a5-80e8-4b82-bd32-6c061f128da9')
|
||||
def test_get_shared_images(self):
|
||||
"""Test getting shared images"""
|
||||
image = self._create_image()
|
||||
self.image_member_client.create_image_member(image, self.alt_tenant_id)
|
||||
share_image = self._create_image()
|
||||
self.image_member_client.create_image_member(share_image,
|
||||
self.alt_tenant_id)
|
||||
body = self.image_member_client.list_shared_images(
|
||||
self.alt_tenant_id)
|
||||
images = body['shared_images']
|
||||
images = [img['image_id'] for img in images]
|
||||
self.assertIn(share_image, images)
|
||||
self.assertIn(image, images)
|
||||
|
||||
@decorators.idempotent_id('a76a3191-8948-4b44-a9d6-4053e5f2b138')
|
||||
def test_remove_member(self):
|
||||
"""Test removing member from image"""
|
||||
image_id = self._create_image()
|
||||
self.image_member_client.create_image_member(image_id,
|
||||
self.alt_tenant_id)
|
||||
self.image_member_client.delete_image_member(image_id,
|
||||
self.alt_tenant_id)
|
||||
body = self.image_member_client.list_image_members(image_id)
|
||||
members = body['members']
|
||||
self.assertEmpty(members)
|
||||
self.assertRaises(
|
||||
lib_exc.NotFound, self.alt_img_cli.show_image, image_id)
|
@ -1,62 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tempest.api.image import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
class ImageMembersNegativeTest(base.BaseV1ImageMembersTest):
|
||||
"""Negative tests of image members"""
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('147a9536-18e3-45da-91ea-b037a028f364')
|
||||
def test_add_member_with_non_existing_image(self):
|
||||
"""Add member with non existing image"""
|
||||
non_exist_image = data_utils.rand_uuid()
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.image_member_client.create_image_member,
|
||||
non_exist_image, self.alt_tenant_id)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
|
||||
def test_delete_member_with_non_existing_image(self):
|
||||
"""Delete member with non existing image"""
|
||||
non_exist_image = data_utils.rand_uuid()
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.image_member_client.delete_image_member,
|
||||
non_exist_image, self.alt_tenant_id)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
|
||||
def test_delete_member_with_non_existing_tenant(self):
|
||||
"""Delete member from image with non existing tenant"""
|
||||
image_id = self._create_image()
|
||||
non_exist_tenant = data_utils.rand_uuid_hex()
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.image_member_client.delete_image_member,
|
||||
image_id, non_exist_tenant)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
|
||||
def test_get_image_without_membership(self):
|
||||
"""Get image without membership
|
||||
|
||||
Image is hidden from another tenants.
|
||||
"""
|
||||
image_id = self._create_image()
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.alt_img_cli.show_image,
|
||||
image_id)
|
@ -1,341 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import io
|
||||
|
||||
from tempest.api.image import base
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common import waiters
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
def get_container_and_disk_format():
|
||||
a_formats = ['ami', 'ari', 'aki']
|
||||
|
||||
container_format = CONF.image.container_formats[0]
|
||||
|
||||
# In v1, If container_format is one of ['ami', 'ari', 'aki'], then
|
||||
# disk_format must be same with container_format.
|
||||
# If they are of different item sequence in tempest.conf, such as:
|
||||
# container_formats = ami,ari,aki,bare
|
||||
# disk_formats = ari,ami,aki,vhd
|
||||
# we can select one in disk_format list that is same with container_format.
|
||||
if container_format in a_formats:
|
||||
if container_format in CONF.image.disk_formats:
|
||||
disk_format = container_format
|
||||
else:
|
||||
msg = ("The container format and the disk format don't match. "
|
||||
"Container format: %(container)s, Disk format: %(disk)s." %
|
||||
{'container': container_format, 'disk':
|
||||
CONF.image.disk_formats})
|
||||
raise exceptions.InvalidConfiguration(msg)
|
||||
else:
|
||||
disk_format = CONF.image.disk_formats[0]
|
||||
|
||||
return container_format, disk_format
|
||||
|
||||
|
||||
class CreateRegisterImagesTest(base.BaseV1ImageTest):
|
||||
"""Here we test the registration and creation of images."""
|
||||
|
||||
@decorators.idempotent_id('3027f8e6-3492-4a11-8575-c3293017af4d')
|
||||
def test_register_then_upload(self):
|
||||
"""Register, then upload an image"""
|
||||
properties = {'prop1': 'val1'}
|
||||
container_format, disk_format = get_container_and_disk_format()
|
||||
image = self.create_image(name='New Name',
|
||||
container_format=container_format,
|
||||
disk_format=disk_format,
|
||||
is_public=False,
|
||||
properties=properties)
|
||||
self.assertEqual('New Name', image.get('name'))
|
||||
self.assertFalse(image.get('is_public'))
|
||||
self.assertEqual('queued', image.get('status'))
|
||||
for key, val in properties.items():
|
||||
self.assertEqual(val, image.get('properties')[key])
|
||||
|
||||
# Now try uploading an image file
|
||||
image_file = io.BytesIO(data_utils.random_bytes())
|
||||
body = self.client.update_image(image['id'], data=image_file)['image']
|
||||
self.assertIn('size', body)
|
||||
self.assertEqual(1024, body.get('size'))
|
||||
|
||||
@decorators.idempotent_id('69da74d9-68a9-404b-9664-ff7164ccb0f5')
|
||||
def test_register_remote_image(self):
|
||||
"""Register a new remote image"""
|
||||
container_format, disk_format = get_container_and_disk_format()
|
||||
body = self.create_image(name='New Remote Image',
|
||||
container_format=container_format,
|
||||
disk_format=disk_format, is_public=False,
|
||||
location=CONF.image.http_image,
|
||||
properties={'key1': 'value1',
|
||||
'key2': 'value2'})
|
||||
self.assertEqual('New Remote Image', body.get('name'))
|
||||
self.assertFalse(body.get('is_public'))
|
||||
self.assertEqual('active', body.get('status'))
|
||||
properties = body.get('properties')
|
||||
self.assertEqual(properties['key1'], 'value1')
|
||||
self.assertEqual(properties['key2'], 'value2')
|
||||
|
||||
@decorators.idempotent_id('6d0e13a7-515b-460c-b91f-9f4793f09816')
|
||||
def test_register_http_image(self):
|
||||
"""Register a new image from an http image path url"""
|
||||
container_format, disk_format = get_container_and_disk_format()
|
||||
image = self.create_image(name='New Http Image',
|
||||
container_format=container_format,
|
||||
disk_format=disk_format, is_public=False,
|
||||
copy_from=CONF.image.http_image)
|
||||
self.assertEqual('New Http Image', image.get('name'))
|
||||
self.assertFalse(image.get('is_public'))
|
||||
waiters.wait_for_image_status(self.client, image['id'], 'active')
|
||||
self.client.show_image(image['id'])
|
||||
|
||||
@decorators.idempotent_id('05b19d55-140c-40d0-b36b-fafd774d421b')
|
||||
def test_register_image_with_min_ram(self):
|
||||
"""Register an image with min ram"""
|
||||
container_format, disk_format = get_container_and_disk_format()
|
||||
properties = {'prop1': 'val1'}
|
||||
body = self.create_image(name='New_image_with_min_ram',
|
||||
container_format=container_format,
|
||||
disk_format=disk_format,
|
||||
is_public=False,
|
||||
min_ram=40,
|
||||
properties=properties)
|
||||
self.assertEqual('New_image_with_min_ram', body.get('name'))
|
||||
self.assertFalse(body.get('is_public'))
|
||||
self.assertEqual('queued', body.get('status'))
|
||||
self.assertEqual(40, body.get('min_ram'))
|
||||
for key, val in properties.items():
|
||||
self.assertEqual(val, body.get('properties')[key])
|
||||
self.client.delete_image(body['id'])
|
||||
|
||||
|
||||
class ListImagesTest(base.BaseV1ImageTest):
|
||||
"""Here we test the listing of image information"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(ListImagesTest, cls).skip_checks()
|
||||
if (len(CONF.image.container_formats) < 2 or
|
||||
len(CONF.image.disk_formats) < 2):
|
||||
skip_msg = ("%s skipped as multiple container formats "
|
||||
"or disk formats are not available." % cls.__name__)
|
||||
raise cls.skipException(skip_msg)
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(ListImagesTest, cls).resource_setup()
|
||||
# We add a few images here to test the listing functionality of
|
||||
# the images API
|
||||
a_formats = ['ami', 'ari', 'aki']
|
||||
|
||||
(cls.container_format,
|
||||
container_format_alt) = CONF.image.container_formats[:2]
|
||||
cls.disk_format, cls.disk_format_alt = CONF.image.disk_formats[:2]
|
||||
if cls.container_format in a_formats:
|
||||
cls.disk_format = cls.container_format
|
||||
if container_format_alt in a_formats:
|
||||
cls.disk_format_alt = container_format_alt
|
||||
|
||||
img1 = cls._create_remote_image('one', cls.container_format,
|
||||
cls.disk_format)
|
||||
img2 = cls._create_remote_image('two', container_format_alt,
|
||||
cls.disk_format_alt)
|
||||
img3 = cls._create_remote_image('dup', cls.container_format,
|
||||
cls.disk_format)
|
||||
img4 = cls._create_remote_image('dup', cls.container_format,
|
||||
cls.disk_format)
|
||||
img5 = cls._create_standard_image('1', container_format_alt,
|
||||
cls.disk_format_alt, 42)
|
||||
img6 = cls._create_standard_image('2', container_format_alt,
|
||||
cls.disk_format_alt, 142)
|
||||
img7 = cls._create_standard_image('33', cls.container_format,
|
||||
cls.disk_format, 142)
|
||||
img8 = cls._create_standard_image('33', cls.container_format,
|
||||
cls.disk_format, 142)
|
||||
cls.created_set = set(cls.created_images)
|
||||
# same container format
|
||||
cls.same_container_format_set = set((img1, img3, img4, img7, img8))
|
||||
# same disk format
|
||||
cls.same_disk_format_set = set((img2, img5, img6))
|
||||
|
||||
# 1x with size 42
|
||||
cls.size42_set = set((img5,))
|
||||
# 3x with size 142
|
||||
cls.size142_set = set((img6, img7, img8,))
|
||||
# dup named
|
||||
cls.dup_set = set((img3, img4))
|
||||
|
||||
@classmethod
|
||||
def _create_remote_image(cls, name, container_format, disk_format):
|
||||
"""Create a new remote image and return newly-registered image-id"""
|
||||
|
||||
name = 'New Remote Image %s' % name
|
||||
location = CONF.image.http_image
|
||||
image = cls.create_image(name=name,
|
||||
container_format=container_format,
|
||||
disk_format=disk_format,
|
||||
is_public=False,
|
||||
location=location)
|
||||
return image['id']
|
||||
|
||||
@classmethod
|
||||
def _create_standard_image(cls, name, container_format,
|
||||
disk_format, size):
|
||||
"""Create a new standard image and return newly-registered image-id
|
||||
|
||||
Note that the size of the new image is a random number between
|
||||
1024 and 4096
|
||||
"""
|
||||
image_file = io.BytesIO(data_utils.random_bytes(size))
|
||||
name = 'New Standard Image %s' % name
|
||||
image = cls.create_image(name=name,
|
||||
container_format=container_format,
|
||||
disk_format=disk_format,
|
||||
is_public=False, data=image_file)
|
||||
return image['id']
|
||||
|
||||
@decorators.idempotent_id('246178ab-3b33-4212-9a4b-a7fe8261794d')
|
||||
def test_index_no_params(self):
|
||||
"""Simple test to see all fixture images returned"""
|
||||
images_list = self.client.list_images()['images']
|
||||
image_list = [image['id'] for image in images_list]
|
||||
for image_id in self.created_images:
|
||||
self.assertIn(image_id, image_list)
|
||||
|
||||
@decorators.idempotent_id('f1755589-63d6-4468-b098-589820eb4031')
|
||||
def test_index_disk_format(self):
|
||||
"""Test listing images by disk format"""
|
||||
images_list = self.client.list_images(
|
||||
disk_format=self.disk_format_alt)['images']
|
||||
for image in images_list:
|
||||
self.assertEqual(image['disk_format'], self.disk_format_alt)
|
||||
result_set = set(map(lambda x: x['id'], images_list))
|
||||
self.assertTrue(self.same_disk_format_set <= result_set)
|
||||
self.assertFalse(self.created_set - self.same_disk_format_set <=
|
||||
result_set)
|
||||
|
||||
@decorators.idempotent_id('2143655d-96d9-4bec-9188-8674206b4b3b')
|
||||
def test_index_container_format(self):
|
||||
"""Test listing images by container format"""
|
||||
images_list = self.client.list_images(
|
||||
container_format=self.container_format)['images']
|
||||
for image in images_list:
|
||||
self.assertEqual(image['container_format'], self.container_format)
|
||||
result_set = set(map(lambda x: x['id'], images_list))
|
||||
self.assertTrue(self.same_container_format_set <= result_set)
|
||||
self.assertFalse(self.created_set - self.same_container_format_set <=
|
||||
result_set)
|
||||
|
||||
@decorators.idempotent_id('feb32ac6-22bb-4a16-afd8-9454bb714b14')
|
||||
def test_index_max_size(self):
|
||||
"""Test listing images by max size"""
|
||||
images_list = self.client.list_images(size_max=42)['images']
|
||||
for image in images_list:
|
||||
self.assertLessEqual(image['size'], 42)
|
||||
result_set = set(map(lambda x: x['id'], images_list))
|
||||
self.assertTrue(self.size42_set <= result_set)
|
||||
self.assertFalse(self.created_set - self.size42_set <= result_set)
|
||||
|
||||
@decorators.idempotent_id('6ffc16d0-4cbf-4401-95c8-4ac63eac34d8')
|
||||
def test_index_min_size(self):
|
||||
"""Test listing images by min size"""
|
||||
images_list = self.client.list_images(size_min=142)['images']
|
||||
for image in images_list:
|
||||
self.assertGreaterEqual(image['size'], 142)
|
||||
result_set = set(map(lambda x: x['id'], images_list))
|
||||
self.assertTrue(self.size142_set <= result_set)
|
||||
self.assertFalse(self.size42_set <= result_set)
|
||||
|
||||
@decorators.idempotent_id('e5dc26d9-9aa2-48dd-bda5-748e1445da98')
|
||||
def test_index_status_active_detail(self):
|
||||
"""Test listing active images sorting by size in descending order"""
|
||||
images_list = self.client.list_images(detail=True,
|
||||
status='active',
|
||||
sort_key='size',
|
||||
sort_dir='desc')['images']
|
||||
top_size = images_list[0]['size'] # We have non-zero sized images
|
||||
for image in images_list:
|
||||
size = image['size']
|
||||
self.assertLessEqual(size, top_size)
|
||||
top_size = size
|
||||
self.assertEqual(image['status'], 'active')
|
||||
|
||||
@decorators.idempotent_id('097af10a-bae8-4342-bff4-edf89969ed2a')
|
||||
def test_index_name(self):
|
||||
"""Test listing images by its name"""
|
||||
images_list = self.client.list_images(
|
||||
detail=True,
|
||||
name='New Remote Image dup')['images']
|
||||
result_set = set(map(lambda x: x['id'], images_list))
|
||||
for image in images_list:
|
||||
self.assertEqual(image['name'], 'New Remote Image dup')
|
||||
self.assertTrue(self.dup_set <= result_set)
|
||||
self.assertFalse(self.created_set - self.dup_set <= result_set)
|
||||
|
||||
|
||||
class UpdateImageMetaTest(base.BaseV1ImageTest):
|
||||
"""Test image metadata"""
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(UpdateImageMetaTest, cls).resource_setup()
|
||||
container_format, disk_format = get_container_and_disk_format()
|
||||
cls.image_id = cls._create_standard_image('1', container_format,
|
||||
disk_format, 42)
|
||||
|
||||
@classmethod
|
||||
def _create_standard_image(cls, name, container_format,
|
||||
disk_format, size):
|
||||
"""Create a new standard image and return newly-registered image-id"""
|
||||
|
||||
image_file = io.BytesIO(data_utils.random_bytes(size))
|
||||
name = 'New Standard Image %s' % name
|
||||
image = cls.create_image(name=name,
|
||||
container_format=container_format,
|
||||
disk_format=disk_format,
|
||||
is_public=False, data=image_file,
|
||||
properties={'key1': 'value1'})
|
||||
return image['id']
|
||||
|
||||
@decorators.idempotent_id('01752c1c-0275-4de3-9e5b-876e44541928')
|
||||
def test_list_image_metadata(self):
|
||||
"""Test listing image metadata"""
|
||||
# All metadata key/value pairs for an image should be returned
|
||||
resp = self.client.check_image(self.image_id)
|
||||
resp_metadata = common_image.get_image_meta_from_headers(resp)
|
||||
expected = {'key1': 'value1'}
|
||||
self.assertEqual(expected, resp_metadata['properties'])
|
||||
|
||||
@decorators.idempotent_id('d6d7649c-08ce-440d-9ea7-e3dda552f33c')
|
||||
def test_update_image_metadata(self):
|
||||
"""Test updating image metadata"""
|
||||
# The metadata for the image should match the updated values
|
||||
req_metadata = {'key1': 'alt1', 'key2': 'value2'}
|
||||
resp = self.client.check_image(self.image_id)
|
||||
metadata = common_image.get_image_meta_from_headers(resp)
|
||||
self.assertEqual(metadata['properties'], {'key1': 'value1'})
|
||||
metadata['properties'].update(req_metadata)
|
||||
headers = common_image.image_meta_to_headers(
|
||||
properties=metadata['properties'])
|
||||
self.client.update_image(self.image_id, headers=headers)
|
||||
resp = self.client.check_image(self.image_id)
|
||||
resp_metadata = common_image.get_image_meta_from_headers(resp)
|
||||
self.assertEqual(req_metadata, resp_metadata['properties'])
|
@ -1,82 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from tempest.api.image import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest):
|
||||
"""Here are negative tests for the deletion and creation of images."""
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('036ede36-6160-4463-8c01-c781eee6369d')
|
||||
def test_register_with_invalid_container_format(self):
|
||||
"""Create image with invalid container format
|
||||
|
||||
Negative tests for invalid data supplied to POST /images
|
||||
"""
|
||||
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
|
||||
headers={'x-image-meta-name': 'test',
|
||||
'x-image-meta-container_format': 'wrong',
|
||||
'x-image-meta-disk_format': 'vhd'})
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
|
||||
def test_register_with_invalid_disk_format(self):
|
||||
"""Create image with invalid disk format"""
|
||||
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
|
||||
headers={'x-image-meta-name': 'test',
|
||||
'x-image-meta-container_format': 'bare',
|
||||
'x-image-meta-disk_format': 'wrong'})
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('ec652588-7e3c-4b67-a2f2-0fa96f57c8fc')
|
||||
def test_delete_non_existent_image(self):
|
||||
"""Return an error while trying to delete a non-existent image"""
|
||||
|
||||
non_existent_image_id = data_utils.rand_uuid()
|
||||
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
|
||||
non_existent_image_id)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('04f72aa3-fcec-45a3-81a3-308ef7cc82bc')
|
||||
def test_delete_image_blank_id(self):
|
||||
"""Return an error while trying to delete an image with blank Id"""
|
||||
self.assertRaises(lib_exc.NotFound, self.client.delete_image, '')
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('950e5054-a3c7-4dee-ada5-e576f1087abd')
|
||||
def test_delete_image_non_hex_string_id(self):
|
||||
"""Return an error while trying to delete an image with non hex id"""
|
||||
invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
|
||||
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
|
||||
invalid_image_id)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('4ed757cd-450c-44b1-9fd1-c819748c650d')
|
||||
def test_delete_image_negative_image_id(self):
|
||||
"""Return an error while trying to delete an image with negative id"""
|
||||
self.assertRaises(lib_exc.NotFound, self.client.delete_image, -1)
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('a4a448ab-3db2-4d2d-b9b2-6a1271241dfe')
|
||||
def test_delete_image_id_over_character_limit(self):
|
||||
"""Return an error while trying to delete image with id over limit"""
|
||||
overlimit_image_id = data_utils.rand_uuid() + "1"
|
||||
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
|
||||
overlimit_image_id)
|
@ -83,8 +83,6 @@ class Manager(clients.ServiceClients):
|
||||
|
||||
def _set_image_clients(self):
|
||||
if CONF.service_available.glance:
|
||||
self.image_client = self.image_v1.ImagesClient()
|
||||
self.image_member_client = self.image_v1.ImageMembersClient()
|
||||
self.image_client_v2 = self.image_v2.ImagesClient()
|
||||
self.image_member_client_v2 = self.image_v2.ImageMembersClient()
|
||||
self.image_cache_client = self.image_v2.ImageCacheClient()
|
||||
|
@ -118,25 +118,16 @@ def verify_glance_api_versions(os, update):
|
||||
# Since we want to verify that the configuration is correct, we cannot
|
||||
# rely on a specific version of the API being available.
|
||||
try:
|
||||
_, versions = os.image_v1.ImagesClient().get_versions()
|
||||
versions = os.image_v2.VersionsClient().list_versions()['versions']
|
||||
versions = [x['id'] for x in versions]
|
||||
except lib_exc.NotFound:
|
||||
# If not found, we use v2. The assumption is that either v1 or v2
|
||||
# are available, since glance is marked as available in the catalog.
|
||||
# If not, glance should be disabled in Tempest conf.
|
||||
try:
|
||||
versions = os.image_v2.VersionsClient().list_versions()['versions']
|
||||
versions = [x['id'] for x in versions]
|
||||
except lib_exc.NotFound:
|
||||
msg = ('Glance is available in the catalog, but no known version, '
|
||||
'(v1.x or v2.x) of Glance could be found, so Glance should '
|
||||
'be configured as not available')
|
||||
LOG.warning(msg)
|
||||
print_and_or_update('glance', 'service-available', False, update)
|
||||
return
|
||||
msg = ('Glance is available in the catalog, but no known version, '
|
||||
'of Glance could be found, so Glance should '
|
||||
'be configured as not available')
|
||||
LOG.warning(msg)
|
||||
print_and_or_update('glance', 'service-available', False, update)
|
||||
return
|
||||
|
||||
if CONF.image_feature_enabled.api_v1 != contains_version('v1.', versions):
|
||||
print_and_or_update('api_v1', 'image-feature-enabled',
|
||||
not CONF.image_feature_enabled.api_v1, update)
|
||||
if CONF.image_feature_enabled.api_v2 != contains_version('v2.', versions):
|
||||
print_and_or_update('api_v2', 'image-feature-enabled',
|
||||
not CONF.image_feature_enabled.api_v2, update)
|
||||
|
@ -16,12 +16,10 @@ import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tempest.common import image as common_image
|
||||
from tempest import config
|
||||
from tempest import exceptions
|
||||
from tempest.lib.common.utils import test_utils
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest.lib.services.image.v1 import images_client as images_v1_client
|
||||
|
||||
CONF = config.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -156,17 +154,7 @@ def wait_for_image_status(client, image_id, status):
|
||||
The client should have a show_image(image_id) method to get the image.
|
||||
The client should also have build_interval and build_timeout attributes.
|
||||
"""
|
||||
if isinstance(client, images_v1_client.ImagesClient):
|
||||
# The 'check_image' method is used here because the show_image method
|
||||
# returns image details plus the image itself which is very expensive.
|
||||
# The 'check_image' method returns just image details.
|
||||
def _show_image_v1(image_id):
|
||||
resp = client.check_image(image_id)
|
||||
return common_image.get_image_meta_from_headers(resp)
|
||||
|
||||
show_image = _show_image_v1
|
||||
else:
|
||||
show_image = client.show_image
|
||||
show_image = client.show_image
|
||||
|
||||
current_status = 'An unknown status'
|
||||
start = int(time.time())
|
||||
|
@ -712,14 +712,6 @@ ImageFeaturesGroup = [
|
||||
'are current one. In future, Tempest will '
|
||||
'test v2 APIs only so this config option '
|
||||
'will be removed.'),
|
||||
cfg.BoolOpt('api_v1',
|
||||
default=False,
|
||||
help="Is the v1 image API enabled",
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason='Glance v1 APIs are deprecated and v2 APIs '
|
||||
'are current one. In future, Tempest will '
|
||||
'test v2 APIs only so this config option '
|
||||
'will be removed.'),
|
||||
# Image import feature is setup in devstack victoria onwards.
|
||||
# Once all stable branches setup the same via glance standalone
|
||||
# mode or with uwsgi, we can remove this config option.
|
||||
|
@ -48,7 +48,6 @@ def tempest_modules():
|
||||
'placement': placement,
|
||||
'identity.v2': identity.v2,
|
||||
'identity.v3': identity.v3,
|
||||
'image.v1': image.v1,
|
||||
'image.v2': image.v2,
|
||||
'network': network,
|
||||
'object-storage': object_storage,
|
||||
|
@ -12,7 +12,6 @@
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
|
||||
from tempest.lib.services.image import v1
|
||||
from tempest.lib.services.image import v2
|
||||
|
||||
__all__ = ['v1', 'v2']
|
||||
__all__ = ['v2']
|
||||
|
@ -1,28 +0,0 @@
|
||||
# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
|
||||
import warnings
|
||||
|
||||
from tempest.lib.services.image.v1.image_members_client import \
|
||||
ImageMembersClient
|
||||
from tempest.lib.services.image.v1.images_client import ImagesClient
|
||||
|
||||
__all__ = ['ImageMembersClient', 'ImagesClient']
|
||||
|
||||
|
||||
warnings.warn(
|
||||
"The tempest.lib.services.image.v1 module (Image v1 APIs service "
|
||||
"clients) is deprecated in favor of tempest.lib.services.image.v2 "
|
||||
"(Image v2 APIs service clients) and will be removed once Tempest stop "
|
||||
"supporting stable Ussuri.", DeprecationWarning)
|
@ -1,66 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from tempest.lib.common import rest_client
|
||||
|
||||
|
||||
class ImageMembersClient(rest_client.RestClient):
|
||||
api_version = "v1"
|
||||
|
||||
def list_image_members(self, image_id):
|
||||
"""List all members of an image."""
|
||||
url = 'images/%s/members' % image_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_shared_images(self, tenant_id):
|
||||
"""List image memberships for the given tenant.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/#list-shared-images
|
||||
"""
|
||||
|
||||
url = 'shared-images/%s' % tenant_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def create_image_member(self, image_id, member_id, **kwargs):
|
||||
"""Add a member to an image.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/#add-member-to-image
|
||||
"""
|
||||
url = 'images/%s/members/%s' % (image_id, member_id)
|
||||
body = json.dumps({'member': kwargs})
|
||||
resp, __ = self.put(url, body)
|
||||
self.expected_success(204, resp.status)
|
||||
return rest_client.ResponseBody(resp)
|
||||
|
||||
def delete_image_member(self, image_id, member_id):
|
||||
"""Removes a membership from the image.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/#remove-member
|
||||
"""
|
||||
url = 'images/%s/members/%s' % (image_id, member_id)
|
||||
resp, __ = self.delete(url)
|
||||
self.expected_success(204, resp.status)
|
||||
return rest_client.ResponseBody(resp)
|
@ -1,155 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
from urllib import parse as urllib
|
||||
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from tempest.lib.common import rest_client
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
CHUNKSIZE = 1024 * 64 # 64kB
|
||||
|
||||
|
||||
class ImagesClient(rest_client.RestClient):
|
||||
api_version = "v1"
|
||||
|
||||
def _create_with_data(self, headers, data):
|
||||
# We are going to do chunked transfert, so split the input data
|
||||
# info fixed-sized chunks.
|
||||
headers['Content-Type'] = 'application/octet-stream'
|
||||
data = iter(functools.partial(data.read, CHUNKSIZE), b'')
|
||||
resp, body = self.request('POST', 'images',
|
||||
headers=headers, body=data, chunked=True)
|
||||
self._error_checker(resp, body)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def _update_with_data(self, image_id, headers, data):
|
||||
# We are going to do chunked transfert, so split the input data
|
||||
# info fixed-sized chunks.
|
||||
headers['Content-Type'] = 'application/octet-stream'
|
||||
data = iter(functools.partial(data.read, CHUNKSIZE), b'')
|
||||
url = 'images/%s' % image_id
|
||||
resp, body = self.request('PUT', url, headers=headers,
|
||||
body=data, chunked=True)
|
||||
self._error_checker(resp, body)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
@property
|
||||
def http(self):
|
||||
if self._http is None:
|
||||
self._http = self._get_http()
|
||||
return self._http
|
||||
|
||||
def create_image(self, data=None, headers=None):
|
||||
"""Create an image.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/index.html#create-image
|
||||
"""
|
||||
if headers is None:
|
||||
headers = {}
|
||||
|
||||
if data is not None:
|
||||
return self._create_with_data(headers, data)
|
||||
|
||||
resp, body = self.post('images', None, headers)
|
||||
self.expected_success(201, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def update_image(self, image_id, data=None, headers=None):
|
||||
"""Update an image.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/index.html#update-image
|
||||
"""
|
||||
if headers is None:
|
||||
headers = {}
|
||||
|
||||
if data is not None:
|
||||
return self._update_with_data(image_id, headers, data)
|
||||
|
||||
url = 'images/%s' % image_id
|
||||
resp, body = self.put(url, None, headers)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def delete_image(self, image_id):
|
||||
url = 'images/%s' % image_id
|
||||
resp, body = self.delete(url)
|
||||
self.expected_success(200, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_images(self, detail=False, **kwargs):
|
||||
"""Return a list of all images filtered by input parameters.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://docs.openstack.org/api-ref/image/v1/#list-images
|
||||
|
||||
Most parameters except the following are passed to the API without
|
||||
any changes.
|
||||
:param changes_since: The name is changed to changes-since
|
||||
"""
|
||||
url = 'images'
|
||||
|
||||
if detail:
|
||||
url += '/detail'
|
||||
|
||||
if 'changes_since' in kwargs:
|
||||
kwargs['changes-since'] = kwargs.pop('changes_since')
|
||||
|
||||
if kwargs:
|
||||
url += '?%s' % urllib.urlencode(kwargs)
|
||||
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def check_image(self, image_id):
|
||||
"""Check image metadata."""
|
||||
url = 'images/%s' % image_id
|
||||
resp, body = self.head(url)
|
||||
self.expected_success(200, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_image(self, image_id):
|
||||
"""Get image details plus the image itself."""
|
||||
url = 'images/%s' % image_id
|
||||
resp, body = self.get(url)
|
||||
self.expected_success(200, resp.status)
|
||||
return rest_client.ResponseBodyData(resp, body)
|
||||
|
||||
def is_resource_deleted(self, id):
|
||||
try:
|
||||
resp = self.check_image(id)
|
||||
if resp.response["x-image-meta-status"] == 'deleted':
|
||||
return True
|
||||
except lib_exc.NotFound:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def resource_type(self):
|
||||
"""Returns the primary type of resource this client works with."""
|
||||
return 'image_meta'
|
@ -25,7 +25,6 @@ from oslo_serialization import jsonutils as json
|
||||
from oslo_utils import netutils
|
||||
|
||||
from tempest.common import compute
|
||||
from tempest.common import image as common_image
|
||||
from tempest.common.utils.linux import remote_client
|
||||
from tempest.common.utils import net_utils
|
||||
from tempest.common import waiters
|
||||
@ -124,15 +123,11 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
"""This setup the service clients for the tests"""
|
||||
super(ScenarioTest, cls).setup_clients()
|
||||
if CONF.service_available.glance:
|
||||
# Check if glance v1 is available to determine which client to use.
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
cls.image_client = cls.os_primary.image_client
|
||||
elif CONF.image_feature_enabled.api_v2:
|
||||
if CONF.image_feature_enabled.api_v2:
|
||||
cls.image_client = cls.os_primary.image_client_v2
|
||||
else:
|
||||
raise lib_exc.InvalidConfiguration(
|
||||
'Either api_v1 or api_v2 must be True in '
|
||||
'[image-feature-enabled].')
|
||||
'api_v2 must be True in [image-feature-enabled].')
|
||||
|
||||
cls.setup_compute_client(cls)
|
||||
cls.setup_network_client(cls)
|
||||
@ -371,11 +366,7 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
if size is None:
|
||||
size = CONF.volume.volume_size
|
||||
if imageRef:
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
resp = self.image_client.check_image(imageRef)
|
||||
image = common_image.get_image_meta_from_headers(resp)
|
||||
else:
|
||||
image = self.image_client.show_image(imageRef)
|
||||
image = self.image_client.show_image(imageRef)
|
||||
min_disk = image.get('min_disk')
|
||||
size = max(size, min_disk)
|
||||
if name is None:
|
||||
@ -796,27 +787,18 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
'name': name,
|
||||
'container_format': img_container_format,
|
||||
'disk_format': img_disk_format or img_container_format,
|
||||
'visibility': 'private'
|
||||
}
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
params['is_public'] = 'False'
|
||||
if img_properties:
|
||||
params['properties'] = img_properties
|
||||
params = {'headers': common_image.image_meta_to_headers(**params)}
|
||||
else:
|
||||
params['visibility'] = 'private'
|
||||
# Additional properties are flattened out in the v2 API.
|
||||
if img_properties:
|
||||
params.update(img_properties)
|
||||
# Additional properties are flattened out in the v2 API.
|
||||
if img_properties:
|
||||
params.update(img_properties)
|
||||
params.update(kwargs)
|
||||
body = self.image_client.create_image(**params)
|
||||
image = body['image'] if 'image' in body else body
|
||||
self.addCleanup(self.image_client.delete_image, image['id'])
|
||||
self.assertEqual("queued", image['status'])
|
||||
with open(img_path, 'rb') as image_file:
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
self.image_client.update_image(image['id'], data=image_file)
|
||||
else:
|
||||
self.image_client.store_image_file(image['id'], image_file)
|
||||
self.image_client.store_image_file(image['id'], image_file)
|
||||
LOG.debug("image:%s", image['id'])
|
||||
return image['id']
|
||||
|
||||
@ -864,15 +846,9 @@ class ScenarioTest(tempest.test.BaseTestCase):
|
||||
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
|
||||
_image_client.delete_image, image_id)
|
||||
|
||||
if CONF.image_feature_enabled.api_v1:
|
||||
# In glance v1 the additional properties are stored in the headers
|
||||
resp = _image_client.check_image(image_id)
|
||||
snapshot_image = common_image.get_image_meta_from_headers(resp)
|
||||
image_props = snapshot_image.get('properties', {})
|
||||
else:
|
||||
# In glance v2 the additional properties are flattened.
|
||||
snapshot_image = _image_client.show_image(image_id)
|
||||
image_props = snapshot_image
|
||||
# In glance v2 the additional properties are flattened.
|
||||
snapshot_image = _image_client.show_image(image_id)
|
||||
image_props = snapshot_image
|
||||
|
||||
bdm = image_props.get('block_device_mapping')
|
||||
if bdm:
|
||||
|
@ -178,13 +178,13 @@ class TestDiscovery(base.TestCase):
|
||||
def test_verify_glance_version_no_v2_with_v1_1(self):
|
||||
# This test verifies that wrong config api_v2 = True is detected
|
||||
class FakeClient(object):
|
||||
def get_versions(self):
|
||||
return (None, ['v1.1'])
|
||||
def list_versions(self):
|
||||
return {'versions': [{'id': 'v1.1'}]}
|
||||
|
||||
fake_os = mock.MagicMock()
|
||||
fake_module = mock.MagicMock()
|
||||
fake_module.ImagesClient = FakeClient
|
||||
fake_os.image_v1 = fake_module
|
||||
fake_module.VersionsClient = FakeClient
|
||||
fake_os.image_v2 = fake_module
|
||||
with mock.patch.object(verify_tempest_config,
|
||||
'print_and_or_update') as print_mock:
|
||||
verify_tempest_config.verify_glance_api_versions(fake_os, True)
|
||||
@ -194,53 +194,28 @@ class TestDiscovery(base.TestCase):
|
||||
def test_verify_glance_version_no_v2_with_v1_0(self):
|
||||
# This test verifies that wrong config api_v2 = True is detected
|
||||
class FakeClient(object):
|
||||
def get_versions(self):
|
||||
return (None, ['v1.0'])
|
||||
def list_versions(self):
|
||||
return {'versions': [{'id': 'v1.0'}]}
|
||||
|
||||
fake_os = mock.MagicMock()
|
||||
fake_module = mock.MagicMock()
|
||||
fake_module.ImagesClient = FakeClient
|
||||
fake_os.image_v1 = fake_module
|
||||
fake_module.VersionsClient = FakeClient
|
||||
fake_os.image_v2 = fake_module
|
||||
with mock.patch.object(verify_tempest_config,
|
||||
'print_and_or_update') as print_mock:
|
||||
verify_tempest_config.verify_glance_api_versions(fake_os, True)
|
||||
print_mock.assert_called_with('api_v2', 'image-feature-enabled',
|
||||
False, True)
|
||||
|
||||
def test_verify_glance_version_no_v1(self):
|
||||
# This test verifies that wrong config api_v1 = True is detected
|
||||
class FakeClient(object):
|
||||
def get_versions(self):
|
||||
raise lib_exc.NotFound()
|
||||
|
||||
def list_versions(self):
|
||||
return {'versions': [{'id': 'v2.0'}]}
|
||||
|
||||
fake_os = mock.MagicMock()
|
||||
fake_module = mock.MagicMock()
|
||||
fake_module.ImagesClient = FakeClient
|
||||
fake_module.VersionsClient = FakeClient
|
||||
fake_os.image_v1 = fake_module
|
||||
fake_os.image_v2 = fake_module
|
||||
with mock.patch.object(verify_tempest_config,
|
||||
'print_and_or_update') as print_mock:
|
||||
verify_tempest_config.verify_glance_api_versions(fake_os, True)
|
||||
print_mock.assert_not_called()
|
||||
|
||||
def test_verify_glance_version_no_version(self):
|
||||
# This test verifies that wrong config api_v1 = True is detected
|
||||
# This test verifies that wrong config api_v2 = True is detected
|
||||
class FakeClient(object):
|
||||
def get_versions(self):
|
||||
raise lib_exc.NotFound()
|
||||
|
||||
def list_versions(self):
|
||||
raise lib_exc.NotFound()
|
||||
|
||||
fake_os = mock.MagicMock()
|
||||
fake_module = mock.MagicMock()
|
||||
fake_module.ImagesClient = FakeClient
|
||||
fake_module.VersionsClient = FakeClient
|
||||
fake_os.image_v1 = fake_module
|
||||
fake_os.image_v2 = fake_module
|
||||
with mock.patch.object(verify_tempest_config,
|
||||
'print_and_or_update') as print_mock:
|
||||
|
@ -1,84 +0,0 @@
|
||||
# Copyright 2016 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tempest.lib.services.image.v1 import image_members_client
|
||||
from tempest.tests.lib import fake_auth_provider
|
||||
from tempest.tests.lib.services import base
|
||||
|
||||
|
||||
class TestImageMembersClient(base.BaseServiceTest):
|
||||
FAKE_LIST_IMAGE_MEMBERS = {
|
||||
"members": [
|
||||
{
|
||||
"created_at": "2013-10-07T17:58:03Z",
|
||||
"image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
|
||||
"member_id": "123456789",
|
||||
"status": "pending",
|
||||
"updated_at": "2013-10-07T17:58:03Z"
|
||||
},
|
||||
{
|
||||
"created_at": "2013-10-07T17:58:55Z",
|
||||
"image_id": "dbc999e3-c52f-4200-bedd-3b18fe7f87fe",
|
||||
"member_id": "987654321",
|
||||
"status": "accepted",
|
||||
"updated_at": "2013-10-08T12:08:55Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestImageMembersClient, self).setUp()
|
||||
fake_auth = fake_auth_provider.FakeAuthProvider()
|
||||
self.client = image_members_client.ImageMembersClient(fake_auth,
|
||||
'image',
|
||||
'regionOne')
|
||||
|
||||
def _test_list_image_members(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.list_image_members,
|
||||
'tempest.lib.common.rest_client.RestClient.get',
|
||||
self.FAKE_LIST_IMAGE_MEMBERS,
|
||||
bytes_body,
|
||||
image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e")
|
||||
|
||||
def _test_create_image_member(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.create_image_member,
|
||||
'tempest.lib.common.rest_client.RestClient.put',
|
||||
{},
|
||||
bytes_body,
|
||||
image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
|
||||
member_id="8989447062e04a818baf9e073fd04fa7",
|
||||
status=204)
|
||||
|
||||
def test_list_image_members_with_str_body(self):
|
||||
self._test_list_image_members()
|
||||
|
||||
def test_list_image_members_with_bytes_body(self):
|
||||
self._test_list_image_members(bytes_body=True)
|
||||
|
||||
def test_create_image_member_with_str_body(self):
|
||||
self._test_create_image_member()
|
||||
|
||||
def test_create_image_member_with_bytes_body(self):
|
||||
self._test_create_image_member(bytes_body=True)
|
||||
|
||||
def test_delete_image_member(self):
|
||||
self.check_service_client_function(
|
||||
self.client.delete_image_member,
|
||||
'tempest.lib.common.rest_client.RestClient.delete',
|
||||
{},
|
||||
image_id="0ae74cc5-5147-4239-9ce2-b0c580f7067e",
|
||||
member_id="8989447062e04a818baf9e073fd04fa7",
|
||||
status=204)
|
@ -37,7 +37,7 @@ class RegistryFixture(fixtures.Fixture):
|
||||
def __init__(self):
|
||||
"""Initialise the registry fixture"""
|
||||
self.services = set(['compute', 'identity.v2', 'identity.v3',
|
||||
'image.v1', 'image.v2', 'network', 'placement',
|
||||
'image.v2', 'network', 'placement',
|
||||
'volume.v2', 'volume.v3', 'object-storage'])
|
||||
|
||||
def _setUp(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user