Merge "Drop unused admin client in server metadata tests"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index f1712ac..bf0b308 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -119,13 +119,13 @@
# Timeout for association of Nova instance and Ironic node
# (integer value)
-#association_timeout=10
+#association_timeout=30
# Timeout for Ironic power transitions. (integer value)
-#power_timeout=20
+#power_timeout=60
# Timeout for unprovisioning an Ironic node. (integer value)
-#unprovision_timeout=20
+#unprovision_timeout=60
[boto]
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index e1dc685..f147b9c 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -44,6 +44,7 @@
# TODO(afazekas): Add dedicated tenant to the skiped quota tests
# it can be moved into the setUpClass as well
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_cpu_quota_is_full(self):
# Disallow server creation when tenant's vcpu quota is full
@@ -57,8 +58,9 @@
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
cores=default_vcpu_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_memory_quota_is_full(self):
# Disallow server creation when tenant's memory quota is full
@@ -72,8 +74,9 @@
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
ram=default_mem_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_instances_quota_is_full(self):
# Once instances quota limit is reached, disallow server creation
@@ -86,7 +89,7 @@
instances=instances_quota)
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
instances=default_instances_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
@test.skip_because(bug="1186354",
condition=CONF.service_available.neutron)
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 8b3a0b5..15025ba 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -54,6 +54,7 @@
flavor_id = data_utils.rand_int_id(start=1000)
return flavor_id
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_resize_server_using_overlimit_ram(self):
flavor_name = data_utils.rand_name("flavor-")
@@ -67,11 +68,12 @@
ram, vcpus, disk,
flavor_id)
self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises(exceptions.Unauthorized,
self.client.resize,
self.servers[0]['id'],
flavor_ref['id'])
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_resize_server_using_overlimit_vcpus(self):
flavor_name = data_utils.rand_name("flavor-")
@@ -85,7 +87,7 @@
ram, vcpus, disk,
flavor_id)
self.addCleanup(self.flavors_client.delete_flavor, flavor_id)
- self.assertRaises(exceptions.OverLimit,
+ self.assertRaises(exceptions.Unauthorized,
self.client.resize,
self.servers[0]['id'],
flavor_ref['id'])
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index e135eca..279dc51 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -102,6 +102,28 @@
self.password)
self.assertTrue(linux_client.hostname_equals_servername(self.name))
+ @test.skip_because(bug="1306367", interface="xml")
+ @test.attr(type='gate')
+ def test_create_server_with_scheduler_hint_group(self):
+ # Create a server with the scheduler hint "group".
+ name = data_utils.rand_name('server_group')
+ policies = ['affinity']
+ resp, body = self.client.create_server_group(name=name,
+ policies=policies)
+ self.assertEqual(200, resp.status)
+ group_id = body['id']
+ self.addCleanup(self.client.delete_server_group, group_id)
+
+ hints = {'group': group_id}
+ resp, server = self.create_test_server(sched_hints=hints,
+ wait_until='ACTIVE')
+ self.assertEqual(202, resp.status)
+
+ # Check a server is in the group
+ resp, server_group = self.client.get_server_group(group_id)
+ self.assertEqual(200, resp.status)
+ self.assertIn(server['id'], server_group['members'])
+
class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
disk_config = 'AUTO'
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d0fd876..71fcbff 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -14,6 +14,7 @@
# under the License.
import base64
+import logging
import testtools
import urlparse
@@ -27,6 +28,8 @@
CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
class ServerActionsTestJSON(base.BaseV2ComputeTest):
run_ssh = CONF.compute.run_ssh
@@ -267,7 +270,14 @@
# the oldest one should be deleted automatically in this test
def _clean_oldest_backup(oldest_backup):
if oldest_backup_exist:
- self.os.image_client.delete_image(oldest_backup)
+ try:
+ self.os.image_client.delete_image(oldest_backup)
+ except exceptions.NotFound:
+ pass
+ else:
+ LOG.warning("Deletion of oldest backup %s should not have "
+ "been successful as it should have been "
+ "deleted during rotation." % oldest_backup)
image1_id = data_utils.parse_image_id(resp['location'])
self.addCleanup(_clean_oldest_backup, image1_id)
diff --git a/tempest/api/compute/v3/admin/test_quotas_negative.py b/tempest/api/compute/v3/admin/test_quotas_negative.py
index 307462f..7739f09 100644
--- a/tempest/api/compute/v3/admin/test_quotas_negative.py
+++ b/tempest/api/compute/v3/admin/test_quotas_negative.py
@@ -34,6 +34,7 @@
# TODO(afazekas): Add dedicated tenant to the skiped quota tests
# it can be moved into the setUpClass as well
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_cpu_quota_is_full(self):
# Disallow server creation when tenant's vcpu quota is full
@@ -47,8 +48,9 @@
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
cores=default_vcpu_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_memory_quota_is_full(self):
# Disallow server creation when tenant's memory quota is full
@@ -62,7 +64,7 @@
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
ram=default_mem_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
@test.attr(type=['negative', 'gate'])
def test_update_quota_normal_user(self):
@@ -71,6 +73,7 @@
self.demo_tenant_id,
ram=0)
+ @test.skip_because(bug="1298131")
@test.attr(type=['negative', 'gate'])
def test_create_server_when_instances_quota_is_full(self):
# Once instances quota limit is reached, disallow server creation
@@ -83,4 +86,4 @@
instances=instances_quota)
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
instances=default_instances_quota)
- self.assertRaises(exceptions.OverLimit, self.create_test_server)
+ self.assertRaises(exceptions.Unauthorized, self.create_test_server)
diff --git a/tempest/api/compute/v3/servers/test_server_actions.py b/tempest/api/compute/v3/servers/test_server_actions.py
index e098311..3ee8050 100644
--- a/tempest/api/compute/v3/servers/test_server_actions.py
+++ b/tempest/api/compute/v3/servers/test_server_actions.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import logging
+
import testtools
import urlparse
@@ -25,6 +27,8 @@
CONF = config.CONF
+LOG = logging.getLogger(__name__)
+
class ServerActionsV3Test(base.BaseV3ComputeTest):
run_ssh = CONF.compute.run_ssh
@@ -260,7 +264,14 @@
# the oldest one should be deleted automatically in this test
def _clean_oldest_backup(oldest_backup):
if oldest_backup_exist:
- self.images_client.delete_image(oldest_backup)
+ try:
+ self.images_client.delete_image(oldest_backup)
+ except exceptions.NotFound:
+ pass
+ else:
+ LOG.warning("Deletion of oldest backup %s should not have "
+ "been successful as it should have been "
+ "deleted during rotation." % oldest_backup)
image1_id = data_utils.parse_image_id(resp['location'])
self.addCleanup(_clean_oldest_backup, image1_id)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 9981292..02d391b 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -142,9 +142,9 @@
def _create_image(self):
name = data_utils.rand_name('image')
- resp, image = self.os_img_client.create_image(name,
- container_format='bare',
- disk_format='raw')
+ _, image = self.os_img_client.create_image(name,
+ container_format='bare',
+ disk_format='raw')
image_id = image['id']
self.addCleanup(self.os_img_client.delete_image, image_id)
return image_id
diff --git a/tempest/api/image/v1/test_image_members.py b/tempest/api/image/v1/test_image_members.py
index 4cbb62f..f91cb69 100644
--- a/tempest/api/image/v1/test_image_members.py
+++ b/tempest/api/image/v1/test_image_members.py
@@ -22,27 +22,21 @@
@test.attr(type='gate')
def test_add_image_member(self):
image = self._create_image()
- resp = self.client.add_member(self.alt_tenant_id, image)
- self.assertEqual(204, resp.status)
- resp, body = self.client.get_image_membership(image)
- self.assertEqual(200, resp.status)
+ self.client.add_member(self.alt_tenant_id, image)
+ _, body = self.client.get_image_membership(image)
members = body['members']
members = map(lambda x: x['member_id'], members)
self.assertIn(self.alt_tenant_id, members)
# get image as alt user
- resp, body = self.alt_img_cli.get_image(image)
- self.assertEqual(200, resp.status)
+ self.alt_img_cli.get_image(image)
@test.attr(type='gate')
def test_get_shared_images(self):
image = self._create_image()
- resp = self.client.add_member(self.alt_tenant_id, image)
- self.assertEqual(204, resp.status)
+ self.client.add_member(self.alt_tenant_id, image)
share_image = self._create_image()
- resp = self.client.add_member(self.alt_tenant_id, share_image)
- self.assertEqual(204, resp.status)
- resp, body = self.client.get_shared_images(self.alt_tenant_id)
- self.assertEqual(200, resp.status)
+ self.client.add_member(self.alt_tenant_id, share_image)
+ _, body = self.client.get_shared_images(self.alt_tenant_id)
images = body['shared_images']
images = map(lambda x: x['image_id'], images)
self.assertIn(share_image, images)
@@ -51,11 +45,8 @@
@test.attr(type='gate')
def test_remove_member(self):
image_id = self._create_image()
- resp = self.client.add_member(self.alt_tenant_id, image_id)
- self.assertEqual(204, resp.status)
- resp = self.client.delete_member(self.alt_tenant_id, image_id)
- self.assertEqual(204, resp.status)
- resp, body = self.client.get_image_membership(image_id)
- self.assertEqual(200, resp.status)
+ self.client.add_member(self.alt_tenant_id, image_id)
+ self.client.delete_member(self.alt_tenant_id, image_id)
+ _, body = self.client.get_image_membership(image_id)
members = body['members']
self.assertEqual(0, len(members), str(members))
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 2cc2009..8528e42 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -30,11 +30,11 @@
def test_register_then_upload(self):
# Register, then upload an image
properties = {'prop1': 'val1'}
- resp, body = self.create_image(name='New Name',
- container_format='bare',
- disk_format='raw',
- is_public=False,
- properties=properties)
+ _, body = self.create_image(name='New Name',
+ container_format='bare',
+ disk_format='raw',
+ is_public=False,
+ properties=properties)
self.assertIn('id', body)
image_id = body.get('id')
self.assertEqual('New Name', body.get('name'))
@@ -45,19 +45,19 @@
# Now try uploading an image file
image_file = StringIO.StringIO(('*' * 1024))
- resp, body = self.client.update_image(image_id, data=image_file)
+ _, body = self.client.update_image(image_id, data=image_file)
self.assertIn('size', body)
self.assertEqual(1024, body.get('size'))
@test.attr(type='gate')
def test_register_remote_image(self):
# Register a new remote image
- resp, body = self.create_image(name='New Remote Image',
- container_format='bare',
- disk_format='raw', is_public=False,
- location=CONF.image.http_image,
- properties={'key1': 'value1',
- 'key2': 'value2'})
+ _, body = self.create_image(name='New Remote Image',
+ container_format='bare',
+ disk_format='raw', is_public=False,
+ location=CONF.image.http_image,
+ properties={'key1': 'value1',
+ 'key2': 'value2'})
self.assertIn('id', body)
self.assertEqual('New Remote Image', body.get('name'))
self.assertFalse(body.get('is_public'))
@@ -68,28 +68,27 @@
@test.attr(type='gate')
def test_register_http_image(self):
- resp, body = self.create_image(name='New Http Image',
- container_format='bare',
- disk_format='raw', is_public=False,
- copy_from=CONF.image.http_image)
+ _, body = self.create_image(name='New Http Image',
+ container_format='bare',
+ disk_format='raw', is_public=False,
+ copy_from=CONF.image.http_image)
self.assertIn('id', body)
image_id = body.get('id')
self.assertEqual('New Http Image', body.get('name'))
self.assertFalse(body.get('is_public'))
self.client.wait_for_image_status(image_id, 'active')
- resp, body = self.client.get_image(image_id)
- self.assertEqual(resp['status'], '200')
+ self.client.get_image(image_id)
@test.attr(type='gate')
def test_register_image_with_min_ram(self):
# Register an image with min ram
properties = {'prop1': 'val1'}
- resp, body = self.create_image(name='New_image_with_min_ram',
- container_format='bare',
- disk_format='raw',
- is_public=False,
- min_ram=40,
- properties=properties)
+ _, body = self.create_image(name='New_image_with_min_ram',
+ container_format='bare',
+ disk_format='raw',
+ is_public=False,
+ min_ram=40,
+ properties=properties)
self.assertIn('id', body)
self.assertEqual('New_image_with_min_ram', body.get('name'))
self.assertFalse(body.get('is_public'))
@@ -97,8 +96,7 @@
self.assertEqual(40, body.get('min_ram'))
for key, val in properties.items():
self.assertEqual(val, body.get('properties')[key])
- resp, body = self.client.delete_image(body['id'])
- self.assertEqual('200', resp['status'])
+ self.client.delete_image(body['id'])
class ListImagesTest(base.BaseV1ImageTest):
@@ -143,11 +141,11 @@
"""
name = 'New Remote Image %s' % name
location = CONF.image.http_image
- resp, image = cls.create_image(name=name,
- container_format=container_format,
- disk_format=disk_format,
- is_public=False,
- location=location)
+ _, image = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
+ is_public=False,
+ location=location)
image_id = image['id']
return image_id
@@ -161,26 +159,24 @@
"""
image_file = StringIO.StringIO('*' * size)
name = 'New Standard Image %s' % name
- resp, image = cls.create_image(name=name,
- container_format=container_format,
- disk_format=disk_format,
- is_public=False, data=image_file)
+ _, image = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
+ is_public=False, data=image_file)
image_id = image['id']
return image_id
@test.attr(type='gate')
def test_index_no_params(self):
# Simple test to see all fixture images returned
- resp, images_list = self.client.image_list()
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list()
image_list = map(lambda x: x['id'], images_list)
for image_id in self.created_images:
self.assertIn(image_id, image_list)
@test.attr(type='gate')
def test_index_disk_format(self):
- resp, images_list = self.client.image_list(disk_format='ami')
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(disk_format='ami')
for image in images_list:
self.assertEqual(image['disk_format'], 'ami')
result_set = set(map(lambda x: x['id'], images_list))
@@ -189,8 +185,7 @@
@test.attr(type='gate')
def test_index_container_format(self):
- resp, images_list = self.client.image_list(container_format='bare')
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(container_format='bare')
for image in images_list:
self.assertEqual(image['container_format'], 'bare')
result_set = set(map(lambda x: x['id'], images_list))
@@ -199,8 +194,7 @@
@test.attr(type='gate')
def test_index_max_size(self):
- resp, images_list = self.client.image_list(size_max=42)
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(size_max=42)
for image in images_list:
self.assertTrue(image['size'] <= 42)
result_set = set(map(lambda x: x['id'], images_list))
@@ -209,8 +203,7 @@
@test.attr(type='gate')
def test_index_min_size(self):
- resp, images_list = self.client.image_list(size_min=142)
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(size_min=142)
for image in images_list:
self.assertTrue(image['size'] >= 142)
result_set = set(map(lambda x: x['id'], images_list))
@@ -219,10 +212,9 @@
@test.attr(type='gate')
def test_index_status_active_detail(self):
- resp, images_list = self.client.image_list_detail(status='active',
- sort_key='size',
- sort_dir='desc')
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list_detail(status='active',
+ sort_key='size',
+ sort_dir='desc')
top_size = images_list[0]['size'] # We have non-zero sized images
for image in images_list:
size = image['size']
@@ -232,9 +224,8 @@
@test.attr(type='gate')
def test_index_name(self):
- resp, images_list = self.client.image_list_detail(
+ _, images_list = self.client.image_list_detail(
name='New Remote Image dup')
- self.assertEqual(resp['status'], '200')
result_set = set(map(lambda x: x['id'], images_list))
for image in images_list:
self.assertEqual(image['name'], 'New Remote Image dup')
@@ -266,10 +257,10 @@
cls.snapshot_set = set((cls.snapshot,))
image_file = StringIO.StringIO('*' * 42)
- resp, image = cls.create_image(name="Standard Image",
- container_format='ami',
- disk_format='ami',
- is_public=False, data=image_file)
+ _, image = cls.create_image(name="Standard Image",
+ container_format='ami',
+ disk_format='ami',
+ is_public=False, data=image_file)
cls.image_id = image['id']
cls.client.wait_for_image_status(image['id'], 'active')
@@ -281,7 +272,7 @@
@classmethod
def _create_snapshot(cls, name, image_id, flavor, **kwargs):
- resp, server = cls.servers_client.create_server(
+ _, server = cls.servers_client.create_server(
name, image_id, flavor, **kwargs)
cls.servers.append(server)
cls.servers_client.wait_for_server_status(
@@ -297,9 +288,8 @@
@test.services('compute')
def test_index_server_id(self):
# The images should contain images filtered by server id
- resp, images = self.client.image_list_detail(
+ _, images = self.client.image_list_detail(
{'instance_uuid': self.servers[0]['id']})
- self.assertEqual(200, resp.status)
result_set = set(map(lambda x: x['id'], images))
self.assertEqual(self.snapshot_set, result_set)
@@ -308,9 +298,8 @@
def test_index_type(self):
# The list of servers should be filtered by image type
params = {'image_type': 'snapshot'}
- resp, images = self.client.image_list_detail(params)
+ _, images = self.client.image_list_detail(params)
- self.assertEqual(200, resp.status)
result_set = set(map(lambda x: x['id'], images))
self.assertIn(self.snapshot, result_set)
@@ -318,9 +307,8 @@
@test.services('compute')
def test_index_limit(self):
# Verify only the expected number of results are returned
- resp, images = self.client.image_list_detail(limit=1)
+ _, images = self.client.image_list_detail(limit=1)
- self.assertEqual(200, resp.status)
self.assertEqual(1, len(images))
@test.attr(type='gate')
@@ -329,13 +317,11 @@
# Verify an update image is returned
# Becoming ACTIVE will modify the updated time
# Filter by the image's created time
- resp, image = self.client.get_image_meta(self.snapshot)
- self.assertEqual(200, resp.status)
+ _, image = self.client.get_image_meta(self.snapshot)
self.assertEqual(self.snapshot, image['id'])
- resp, images = self.client.image_list_detail(
+ _, images = self.client.image_list_detail(
changes_since=image['updated_at'])
- self.assertEqual(200, resp.status)
result_set = set(map(lambda x: x['id'], images))
self.assertIn(self.image_id, result_set)
self.assertNotIn(self.snapshot, result_set)
@@ -357,18 +343,18 @@
"""
image_file = StringIO.StringIO('*' * size)
name = 'New Standard Image %s' % name
- resp, image = cls.create_image(name=name,
- container_format=container_format,
- disk_format=disk_format,
- is_public=False, data=image_file,
- properties={'key1': 'value1'})
+ _, image = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
+ is_public=False, data=image_file,
+ properties={'key1': 'value1'})
image_id = image['id']
return image_id
@test.attr(type='gate')
def test_list_image_metadata(self):
# All metadata key/value pairs for an image should be returned
- resp, resp_metadata = self.client.get_image_meta(self.image_id)
+ _, resp_metadata = self.client.get_image_meta(self.image_id)
expected = {'key1': 'value1'}
self.assertEqual(expected, resp_metadata['properties'])
@@ -376,13 +362,12 @@
def test_update_image_metadata(self):
# The metadata for the image should match the updated values
req_metadata = {'key1': 'alt1', 'key2': 'value2'}
- resp, metadata = self.client.get_image_meta(self.image_id)
- self.assertEqual(200, resp.status)
+ _, metadata = self.client.get_image_meta(self.image_id)
self.assertEqual(metadata['properties'], {'key1': 'value1'})
metadata['properties'].update(req_metadata)
- resp, metadata = self.client.update_image(
+ _, metadata = self.client.update_image(
self.image_id, properties=metadata['properties'])
- resp, resp_metadata = self.client.get_image_meta(self.image_id)
+ _, resp_metadata = self.client.get_image_meta(self.image_id)
expected = {'key1': 'alt1', 'key2': 'value2'}
self.assertEqual(expected, resp_metadata['properties'])
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 37dc163..ae777eb 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -37,11 +37,11 @@
uuid = '00000000-1111-2222-3333-444455556666'
image_name = data_utils.rand_name('image')
- resp, body = self.create_image(name=image_name,
- container_format='bare',
- disk_format='raw',
- visibility='private',
- ramdisk_id=uuid)
+ _, body = self.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='private',
+ ramdisk_id=uuid)
self.assertIn('id', body)
image_id = body.get('id')
self.assertIn('name', body)
@@ -54,12 +54,10 @@
# Now try uploading an image file
file_content = '*' * 1024
image_file = StringIO.StringIO(file_content)
- resp, body = self.client.store_image(image_id, image_file)
- self.assertEqual(resp.status, 204)
+ self.client.store_image(image_id, image_file)
# Now try to get image details
- resp, body = self.client.get_image(image_id)
- self.assertEqual(200, resp.status)
+ _, body = self.client.get_image(image_id)
self.assertEqual(image_id, body['id'])
self.assertEqual(image_name, body['name'])
self.assertEqual(uuid, body['ramdisk_id'])
@@ -67,8 +65,7 @@
self.assertEqual(1024, body.get('size'))
# Now try get image file
- resp, body = self.client.get_image_file(image_id)
- self.assertEqual(200, resp.status)
+ _, body = self.client.get_image_file(image_id)
self.assertEqual(file_content, body)
@test.attr(type='gate')
@@ -77,11 +74,10 @@
# Create image
image_name = data_utils.rand_name('image')
- resp, body = self.client.create_image(name=image_name,
- container_format='bare',
- disk_format='raw',
- visibility='private')
- self.assertEqual(201, resp.status)
+ _, body = self.client.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='private')
image_id = body['id']
# Delete Image
@@ -89,8 +85,7 @@
self.client.wait_for_resource_deletion(image_id)
# Verifying deletion
- resp, images = self.client.image_list()
- self.assertEqual(resp.status, 200)
+ _, images = self.client.image_list()
self.assertNotIn(image_id, images)
@test.attr(type='gate')
@@ -99,11 +94,10 @@
# Create image
image_name = data_utils.rand_name('image')
- resp, body = self.client.create_image(name=image_name,
- container_format='bare',
- disk_format='iso',
- visibility='private')
- self.assertEqual(201, resp.status)
+ _, body = self.client.create_image(name=image_name,
+ container_format='bare',
+ disk_format='iso',
+ visibility='private')
self.addCleanup(self.client.delete_image, body['id'])
self.assertEqual('queued', body['status'])
image_id = body['id']
@@ -111,20 +105,16 @@
# Now try uploading an image file
file_content = '*' * 1024
image_file = StringIO.StringIO(file_content)
- resp, body = self.client.store_image(image_id, image_file)
- self.assertEqual(204, resp.status)
+ self.client.store_image(image_id, image_file)
# Update Image
new_image_name = data_utils.rand_name('new-image')
- resp, body = self.client.update_image(image_id, [
+ _, body = self.client.update_image(image_id, [
dict(replace='/name', value=new_image_name)])
- self.assertEqual(200, resp.status)
-
# Verifying updating
- resp, body = self.client.get_image(image_id)
- self.assertEqual(200, resp.status)
+ _, body = self.client.get_image(image_id)
self.assertEqual(image_id, body['id'])
self.assertEqual(new_image_name, body['name'])
@@ -157,12 +147,12 @@
"""
image_file = StringIO.StringIO('*' * random.randint(1024, 4096))
name = data_utils.rand_name('image-')
- resp, body = cls.create_image(name=name,
- container_format=container_format,
- disk_format=disk_format,
- visibility='private')
+ _, body = cls.create_image(name=name,
+ container_format=container_format,
+ disk_format=disk_format,
+ visibility='private')
image_id = body['id']
- resp, body = cls.client.store_image(image_id, data=image_file)
+ cls.client.store_image(image_id, data=image_file)
return image_id
@@ -170,8 +160,7 @@
"""
Perform list action with given params and validates result.
"""
- resp, images_list = self.client.image_list(params=params)
- self.assertEqual(200, resp.status)
+ _, images_list = self.client.image_list(params=params)
# Validating params of fetched images
for image in images_list:
for key in params:
@@ -181,8 +170,7 @@
@test.attr(type='gate')
def test_index_no_params(self):
# Simple test to see all fixture images returned
- resp, images_list = self.client.image_list()
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list()
image_list = map(lambda x: x['id'], images_list)
for image in self.created_images:
@@ -211,8 +199,7 @@
# Test to get all images by size
image_id = self.created_images[1]
# Get image metadata
- resp, image = self.client.get_image(image_id)
- self.assertEqual(resp['status'], '200')
+ _, image = self.client.get_image(image_id)
params = {"size": image['size']}
self._list_by_param_value_and_assert(params)
@@ -222,13 +209,11 @@
# Test to get all images with size between 2000 to 3000
image_id = self.created_images[1]
# Get image metadata
- resp, image = self.client.get_image(image_id)
- self.assertEqual(resp['status'], '200')
+ _, image = self.client.get_image(image_id)
size = image['size']
params = {"size_min": size - 500, "size_max": size + 500}
- resp, images_list = self.client.image_list(params=params)
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(params=params)
image_size_list = map(lambda x: x['size'], images_list)
for image_size in image_size_list:
@@ -246,8 +231,7 @@
def test_list_images_param_limit(self):
# Test to get images by limit
params = {"limit": 2}
- resp, images_list = self.client.image_list(params=params)
- self.assertEqual(resp['status'], '200')
+ _, images_list = self.client.image_list(params=params)
self.assertEqual(len(images_list), params['limit'],
"Failed to get images by limit")
@@ -256,14 +240,12 @@
def test_get_image_schema(self):
# Test to get image schema
schema = "image"
- resp, body = self.client.get_schema(schema)
- self.assertEqual(200, resp.status)
+ _, body = self.client.get_schema(schema)
self.assertEqual("image", body['name'])
@test.attr(type='gate')
def test_get_images_schema(self):
# Test to get images schema
schema = "images"
- resp, body = self.client.get_schema(schema)
- self.assertEqual(200, resp.status)
+ _, body = self.client.get_schema(schema)
self.assertEqual("images", body['name'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index f80c818..5aaf578 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -20,9 +20,8 @@
@test.attr(type='gate')
def test_image_share_accept(self):
image_id = self._create_image()
- resp, member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
- self.assertEqual(200, resp.status)
+ _, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
@@ -31,8 +30,7 @@
self.alt_tenant_id,
'accepted')
self.assertIn(image_id, self._list_image_ids_as_alt())
- resp, body = self.os_img_client.get_image_membership(image_id)
- self.assertEqual(200, resp.status)
+ _, body = self.os_img_client.get_image_membership(image_id)
members = body['members']
member = members[0]
self.assertEqual(len(members), 1, str(members))
@@ -43,17 +41,15 @@
@test.attr(type='gate')
def test_image_share_reject(self):
image_id = self._create_image()
- resp, member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
- self.assertEqual(200, resp.status)
+ _, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['member_id'], self.alt_tenant_id)
self.assertEqual(member['image_id'], image_id)
self.assertEqual(member['status'], 'pending')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
- resp, _ = self.alt_img_client.update_member_status(image_id,
- self.alt_tenant_id,
- 'rejected')
- self.assertEqual(200, resp.status)
+ self.alt_img_client.update_member_status(image_id,
+ self.alt_tenant_id,
+ 'rejected')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
@test.attr(type='gate')
@@ -66,9 +62,8 @@
'accepted')
self.assertIn(image_id, self._list_image_ids_as_alt())
- resp, member = self.os_img_client.get_member(image_id,
- self.alt_tenant_id)
- self.assertEqual(200, resp.status)
+ _, member = self.os_img_client.get_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(self.alt_tenant_id, member['member_id'])
self.assertEqual(image_id, member['image_id'])
self.assertEqual('accepted', member['status'])
@@ -83,18 +78,15 @@
'accepted')
self.assertIn(image_id, self._list_image_ids_as_alt())
- resp = self.os_img_client.remove_member(image_id, self.alt_tenant_id)
- self.assertEqual(204, resp.status)
+ self.os_img_client.remove_member(image_id, self.alt_tenant_id)
self.assertNotIn(image_id, self._list_image_ids_as_alt())
@test.attr(type='gate')
def test_get_image_member_schema(self):
- resp, body = self.os_img_client.get_schema("member")
- self.assertEqual(200, resp.status)
+ _, body = self.os_img_client.get_schema("member")
self.assertEqual("member", body['name'])
@test.attr(type='gate')
def test_get_image_members_schema(self):
- resp, body = self.os_img_client.get_schema("members")
- self.assertEqual(200, resp.status)
+ _, body = self.os_img_client.get_schema("members")
self.assertEqual("members", body['name'])
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index 98ef649..7da6e65 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -21,8 +21,8 @@
@test.attr(type=['negative', 'gate'])
def test_image_share_invalid_status(self):
image_id = self._create_image()
- resp, member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ _, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertRaises(exceptions.BadRequest,
self.alt_img_client.update_member_status,
@@ -31,8 +31,8 @@
@test.attr(type=['negative', 'gate'])
def test_image_share_owner_cannot_accept(self):
image_id = self._create_image()
- resp, member = self.os_img_client.add_member(image_id,
- self.alt_tenant_id)
+ _, member = self.os_img_client.add_member(image_id,
+ self.alt_tenant_id)
self.assertEqual(member['status'], 'pending')
self.assertNotIn(image_id, self._list_image_ids_as_alt())
self.assertRaises(exceptions.Unauthorized,
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 27ba39c..722929e 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -52,11 +52,10 @@
def test_get_delete_deleted_image(self):
# get and delete the deleted image
# create and delete image
- resp, body = self.client.create_image(name='test',
- container_format='bare',
- disk_format='raw')
+ _, body = self.client.create_image(name='test',
+ container_format='bare',
+ disk_format='raw')
image_id = body['id']
- self.assertEqual(201, resp.status)
self.client.delete_image(image_id)
self.client.wait_for_resource_deletion(image_id)
diff --git a/tempest/api/image/v2/test_images_tags.py b/tempest/api/image/v2/test_images_tags.py
index dec3353..a9db24b 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -21,23 +21,19 @@
@test.attr(type='gate')
def test_update_delete_tags_for_image(self):
- resp, body = self.create_image(container_format='bare',
- disk_format='raw',
- visibility='private')
+ _, body = self.create_image(container_format='bare',
+ disk_format='raw',
+ visibility='private')
image_id = body['id']
tag = data_utils.rand_name('tag-')
self.addCleanup(self.client.delete_image, image_id)
# Creating image tag and verify it.
- resp, body = self.client.add_image_tag(image_id, tag)
- self.assertEqual(resp.status, 204)
- resp, body = self.client.get_image(image_id)
- self.assertEqual(resp.status, 200)
+ self.client.add_image_tag(image_id, tag)
+ _, body = self.client.get_image(image_id)
self.assertIn(tag, body['tags'])
# Deleting image tag and verify it.
- resp = self.client.delete_image_tag(image_id, tag)
- self.assertEqual(resp.status, 204)
- resp, body = self.client.get_image(image_id)
- self.assertEqual(resp.status, 200)
+ self.client.delete_image_tag(image_id, tag)
+ _, body = self.client.get_image(image_id)
self.assertNotIn(tag, body['tags'])
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 13cfa0a..8e42b7c 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -33,10 +33,10 @@
@test.attr(type=['negative', 'gate'])
def test_delete_non_existing_tag(self):
# Delete non existing tag.
- resp, body = self.create_image(container_format='bare',
- disk_format='raw',
- visibility='private'
- )
+ _, body = self.create_image(container_format='bare',
+ disk_format='raw',
+ visibility='private'
+ )
image_id = body['id']
tag = data_utils.rand_name('non-exist-tag-')
self.addCleanup(self.client.delete_image, image_id)
diff --git a/tempest/api/object_storage/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index 53ca20d..73b4f3b 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -54,14 +54,18 @@
self.assertEqual(resp['status'], '200')
self.assertHeaders(resp, 'Object', 'HEAD')
self.assertIn('x-delete-at', resp)
+ # we want to ensure that we will sleep long enough for things to
+ # actually expire, so figure out how many secs in the future that is.
+ sleepy_time = int(resp['x-delete-at']) - int(time.time())
+
resp, body = self.object_client.get_object(self.container_name,
self.object_name)
self.assertEqual(resp['status'], '200')
self.assertHeaders(resp, 'Object', 'GET')
self.assertIn('x-delete-at', resp)
- # sleep for over 5 seconds, so that object expires
- time.sleep(5)
+ # add a couple of seconds for safety.
+ time.sleep(sleepy_time + 3)
# object should not be there anymore
self.assertRaises(exceptions.NotFound, self.object_client.get_object,
@@ -69,10 +73,12 @@
@test.attr(type='gate')
def test_get_object_after_expiry_time(self):
- metadata = {'X-Delete-After': '3'}
+ # the 10s is important, because the get calls can take 3s each
+ # some times
+ metadata = {'X-Delete-After': '10'}
self._test_object_expiry(metadata)
@test.attr(type='gate')
def test_get_object_at_expiry_time(self):
- metadata = {'X-Delete-At': str(int(time.time()) + 3)}
+ metadata = {'X-Delete-At': str(int(time.time()) + 10)}
self._test_object_expiry(metadata)
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 06e63a4..1ef9aa1 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -13,11 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+import cStringIO as StringIO
import hashlib
import random
import re
from six import moves
import time
+import zlib
from tempest.api.object_storage import base
from tempest.common import custom_matchers
@@ -61,7 +63,7 @@
return object_name, data_segments
- @test.attr(type='smoke')
+ @test.attr(type='gate')
def test_create_object(self):
# create object
object_name = data_utils.rand_name(name='TestObject')
@@ -76,7 +78,242 @@
self.assertEqual(resp['status'], '201')
self.assertHeaders(resp, 'Object', 'PUT')
- @test.attr(type='smoke')
+ # check uploaded content
+ _, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_content_disposition(self):
+ # create object with content_disposition
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata = {}
+ metadata['content-disposition'] = 'inline'
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(
+ self.container_name,
+ object_name,
+ metadata=None)
+ self.assertIn('content-disposition', resp)
+ self.assertEqual(resp['content-disposition'], 'inline')
+ self.assertEqual(body, data)
+
+ @test.attr(type='gate')
+ def test_create_object_with_content_encoding(self):
+ # create object with content_encoding
+ object_name = data_utils.rand_name(name='TestObject')
+
+ # put compressed string
+ data_before = 'x' * 2000
+ data = zlib.compress(data_before)
+ metadata = {}
+ metadata['content-encoding'] = 'deflate'
+
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ # download compressed object
+ metadata = {}
+ metadata['accept-encoding'] = 'deflate'
+ resp, body = self.object_client.get_object(
+ self.container_name,
+ object_name,
+ metadata=metadata)
+ self.assertEqual(body, data_before)
+
+ @test.attr(type='gate')
+ def test_create_object_with_etag(self):
+ # create object with etag
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ md5 = hashlib.md5(data).hexdigest()
+ metadata = {'Etag': md5}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ # check uploaded content
+ _, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_expect_continue(self):
+ # create object with expect_continue
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata = {'Expect': '100-continue'}
+ resp = self.custom_object_client.create_object_continue(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+
+ self.assertIn('status', resp)
+ self.assertEqual(resp['status'], '100')
+
+ self.custom_object_client.create_object_continue(
+ self.container_name,
+ object_name,
+ data,
+ metadata=None)
+
+ # check uploaded content
+ _, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_transfer_encoding(self):
+ # create object with transfer_encoding
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string(1024)
+ status, _, resp_headers = self.object_client.put_object_with_chunk(
+ container=self.container_name,
+ name=object_name,
+ contents=StringIO.StringIO(data),
+ chunk_size=512)
+ self.assertEqual(status, 201)
+ self.assertHeaders(resp_headers, 'Object', 'PUT')
+
+ # check uploaded content
+ _, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_x_fresh_metadata(self):
+ # create object with x_fresh_metadata
+ object_name_base = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata_1 = {'X-Object-Meta-test-meta': 'Meta'}
+ self.object_client.create_object(self.container_name,
+ object_name_base,
+ data,
+ metadata=metadata_1)
+ object_name = data_utils.rand_name(name='TestObject')
+ metadata_2 = {'X-Copy-From': '%s/%s' % (self.container_name,
+ object_name_base),
+ 'X-Fresh-Metadata': 'true'}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ '',
+ metadata=metadata_2)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertNotIn('x-object-meta-test-meta', resp)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_x_object_meta(self):
+ # create object with object_meta
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata = {'X-Object-Meta-test-meta': 'Meta'}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertIn('x-object-meta-test-meta', resp)
+ self.assertEqual(resp['x-object-meta-test-meta'], 'Meta')
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_x_object_metakey(self):
+ # create object with the blank value of metadata
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata = {'X-Object-Meta-test-meta': ''}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertIn('x-object-meta-test-meta', resp)
+ self.assertEqual(resp['x-object-meta-test-meta'], '')
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_x_remove_object_meta(self):
+ # create object with x_remove_object_meta
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
+ self.object_client.create_object(self.container_name,
+ object_name,
+ data,
+ metadata=metadata_add)
+ metadata_remove = {'X-Remove-Object-Meta-test-meta': 'Meta'}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata_remove)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertNotIn('x-object-meta-test-meta', resp)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
+ def test_create_object_with_x_remove_object_metakey(self):
+ # create object with the blank value of remove metadata
+ object_name = data_utils.rand_name(name='TestObject')
+ data = data_utils.arbitrary_string()
+ metadata_add = {'X-Object-Meta-test-meta': 'Meta'}
+ self.object_client.create_object(self.container_name,
+ object_name,
+ data,
+ metadata=metadata_add)
+ metadata_remove = {'X-Remove-Object-Meta-test-meta': ''}
+ resp, _ = self.object_client.create_object(
+ self.container_name,
+ object_name,
+ data,
+ metadata=metadata_remove)
+ self.assertEqual(resp['status'], '201')
+ self.assertHeaders(resp, 'Object', 'PUT')
+
+ resp, body = self.object_client.get_object(self.container_name,
+ object_name)
+ self.assertNotIn('x-object-meta-test-meta', resp)
+ self.assertEqual(data, body)
+
+ @test.attr(type='gate')
def test_delete_object(self):
# create object
object_name = data_utils.rand_name(name='TestObject')
diff --git a/tempest/api_schema/compute/agents.py b/tempest/api_schema/compute/agents.py
index b04cf64..b9ad240 100644
--- a/tempest/api_schema/compute/agents.py
+++ b/tempest/api_schema/compute/agents.py
@@ -38,3 +38,24 @@
'required': ['agents']
}
}
+
+common_create_agent = {
+ 'type': 'object',
+ 'properties': {
+ 'agent': {
+ 'type': 'object',
+ 'properties': {
+ 'agent_id': {'type': ['integer', 'string']},
+ 'hypervisor': {'type': 'string'},
+ 'os': {'type': 'string'},
+ 'architecture': {'type': 'string'},
+ 'version': {'type': 'string'},
+ 'url': {'type': 'string', 'format': 'uri'},
+ 'md5hash': {'type': 'string'}
+ },
+ 'required': ['agent_id', 'hypervisor', 'os', 'architecture',
+ 'version', 'url', 'md5hash']
+ }
+ },
+ 'required': ['agent']
+}
diff --git a/tempest/api_schema/compute/v2/agents.py b/tempest/api_schema/compute/v2/agents.py
index 837731f..30f999f 100644
--- a/tempest/api_schema/compute/v2/agents.py
+++ b/tempest/api_schema/compute/v2/agents.py
@@ -12,6 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api_schema.compute import agents
+
+create_agent = {
+ 'status_code': [200],
+ 'response_body': agents.common_create_agent
+}
+
delete_agent = {
'status_code': [200]
}
diff --git a/tempest/api_schema/compute/v3/agents.py b/tempest/api_schema/compute/v3/agents.py
index 63d1c46..597a089 100644
--- a/tempest/api_schema/compute/v3/agents.py
+++ b/tempest/api_schema/compute/v3/agents.py
@@ -12,6 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api_schema.compute import agents
+
+create_agent = {
+ 'status_code': [201],
+ 'response_body': agents.common_create_agent
+}
+
delete_agent = {
'status_code': [204]
}
diff --git a/tempest/auth.py b/tempest/auth.py
index 9c51edb..830dca9 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -213,7 +213,7 @@
# build authenticated request
# returns new request, it does not touch the original values
_headers = copy.deepcopy(headers) if headers is not None else {}
- _headers['X-Auth-Token'] = token
+ _headers['X-Auth-Token'] = str(token)
if url is None or url == "":
_url = base_url
else:
@@ -223,7 +223,7 @@
parts[2] = re.sub("/{2,}", "/", parts[2])
_url = urlparse.urlunparse(parts)
# no change to method or body
- return _url, _headers, body
+ return str(_url), _headers, body
def _auth_client(self):
raise NotImplementedError
diff --git a/tempest/cli/__init__.py b/tempest/cli/__init__.py
index 6aa98c4..0571f4f 100644
--- a/tempest/cli/__init__.py
+++ b/tempest/cli/__init__.py
@@ -83,6 +83,12 @@
return self.cmd_with_auth(
'cinder', action, flags, params, admin, fail_ok)
+ def swift(self, action, flags='', params='', admin=True, fail_ok=False):
+ """Executes swift command for the given action."""
+ flags += ' --os-endpoint-type %s' % CONF.object_storage.endpoint_type
+ return self.cmd_with_auth(
+ 'swift', action, flags, params, admin, fail_ok)
+
def neutron(self, action, flags='', params='', admin=True, fail_ok=False):
"""Executes neutron command for the given action."""
flags += ' --endpoint-type %s' % CONF.network.endpoint_type
diff --git a/tempest/cli/simple_read_only/test_swift.py b/tempest/cli/simple_read_only/test_swift.py
new file mode 100644
index 0000000..6d6caa7
--- /dev/null
+++ b/tempest/cli/simple_read_only/test_swift.py
@@ -0,0 +1,95 @@
+# Copyright 2014 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 re
+import subprocess
+
+import tempest.cli
+from tempest import config
+
+CONF = config.CONF
+
+
+class SimpleReadOnlySwiftClientTest(tempest.cli.ClientTestBase):
+ """Basic, read-only tests for Swift CLI client.
+
+ Checks return values and output of read-only commands.
+ These tests do not presume any content, nor do they create
+ their own. They only verify the structure of output if present.
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ if not CONF.service_available.swift:
+ msg = ("%s skipped as Swift is not available" % cls.__name__)
+ raise cls.skipException(msg)
+ super(SimpleReadOnlySwiftClientTest, cls).setUpClass()
+
+ def test_swift_fake_action(self):
+ self.assertRaises(subprocess.CalledProcessError,
+ self.swift,
+ 'this-does-not-exist')
+
+ def test_swift_list(self):
+ self.swift('list')
+
+ def test_swift_stat(self):
+ output = self.swift('stat')
+ entries = ['Account', 'Containers', 'Objects', 'Bytes', 'Content-Type',
+ 'X-Timestamp', 'X-Trans-Id']
+ for entry in entries:
+ self.assertTrue(entry in output)
+
+ def test_swift_capabilities(self):
+ output = self.swift('capabilities')
+ entries = ['account_listing_limit', 'container_listing_limit',
+ 'max_file_size', 'Additional middleware']
+ for entry in entries:
+ self.assertTrue(entry in output)
+
+ def test_swift_help(self):
+ help_text = self.swift('', flags='--help')
+ lines = help_text.split('\n')
+ self.assertFirstLineStartsWith(lines, 'Usage: swift')
+
+ commands = []
+ cmds_start = lines.index('Positional arguments:')
+ cmds_end = lines.index('Examples:')
+ command_pattern = re.compile('^ {4}([a-z0-9\-\_]+)')
+ for line in lines[cmds_start:cmds_end]:
+ match = command_pattern.match(line)
+ if match:
+ commands.append(match.group(1))
+ commands = set(commands)
+ wanted_commands = set(('stat', 'list', 'delete',
+ 'download', 'post', 'upload'))
+ self.assertFalse(wanted_commands - commands)
+
+ # Optional arguments:
+
+ def test_swift_version(self):
+ self.swift('', flags='--version')
+
+ def test_swift_debug_list(self):
+ self.swift('list', flags='--debug')
+
+ def test_swift_retries_list(self):
+ self.swift('list', flags='--retries 3')
+
+ def test_swift_region_list(self):
+ region = CONF.object_storage.region
+ if not region:
+ region = CONF.identity.region
+ self.swift('list', flags='--os-region-name ' + region)
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 20ee63e..1d46028 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -35,6 +35,7 @@
from tempest.services.image.v2.json import image_client
from tempest.services.object_storage import container_client
from tempest.services.object_storage import object_client
+from tempest.services.volume.json import volumes_client
OPTS = {}
USERS = {}
@@ -60,6 +61,7 @@
self.containers = container_client.ContainerClient(_auth)
self.images = image_client.ImageClientV2JSON(_auth)
self.flavors = flavors_client.FlavorsClientJSON(_auth)
+ self.volumes = volumes_client.VolumesClientJSON(_auth)
def load_resources(fname):
@@ -190,6 +192,7 @@
self.check_users()
self.check_objects()
self.check_servers()
+ self.check_volumes()
def check_users(self):
"""Check that the users we expect to exist, do.
@@ -235,6 +238,21 @@
"Server %s is not pingable at %s" % (
server['name'], addr))
+ def check_volumes(self):
+ """Check that the volumes are still there and attached."""
+ for volume in self.res['volumes']:
+ client = client_for_user(volume['owner'])
+ found = _get_volume_by_name(client, volume['name'])
+ self.assertIsNotNone(
+ found,
+ "Couldn't find expected volume %s" % volume['name'])
+
+ # Verify that a volume's attachment retrieved
+ server_id = _get_server_by_name(client, volume['server'])['id']
+ attachment = self.client.get_attachment_from_volume(volume)
+ self.assertEqual(volume['id'], attachment['volume_id'])
+ self.assertEqual(server_id, attachment['server_id'])
+
#######################
#
@@ -339,6 +357,40 @@
#######################
#
+# VOLUMES
+#
+#######################
+
+def _get_volume_by_name(client, name):
+ r, body = client.volumes.list_volumes()
+ for volume in body['volumes']:
+ if name == volume['name']:
+ return volume
+ return None
+
+
+def create_volumes(volumes):
+ for volume in volumes:
+ client = client_for_user(volume['owner'])
+
+ # only create a volume if the name isn't here
+ r, body = client.volumes.list_volumes()
+ if any(item['name'] == volume['name'] for item in body):
+ continue
+
+ client.volumes.create_volume(volume['name'], volume['size'])
+
+
+def attach_volumes(volumes):
+ for volume in volumes:
+ client = client_for_user(volume['owner'])
+
+ server_id = _get_server_by_name(client, volume['server'])['id']
+ client.volumes.attach_volume(volume['name'], server_id)
+
+
+#######################
+#
# MAIN LOGIC
#
#######################
@@ -355,6 +407,8 @@
create_objects(RES['objects'])
create_images(RES['images'])
create_servers(RES['servers'])
+ create_volumes(RES['volumes'])
+ attach_volumes(RES['volumes'])
def get_options():
diff --git a/tempest/cmd/resources.yaml b/tempest/cmd/resources.yaml
index f7cb8a9..a1f567b 100644
--- a/tempest/cmd/resources.yaml
+++ b/tempest/cmd/resources.yaml
@@ -32,9 +32,14 @@
aki: cirros-0.3.2-x86_64-vmlinuz
ari: cirros-0.3.2-x86_64-initrd
volumes:
- - assegai:
- - owner: javelin
- - gb: 1
+ - name: assegai
+ server: peltast
+ owner: javelin
+ size: 1
+ - name: pifpouf
+ server: hoplite
+ owner: javelin
+ size: 2
servers:
- name: peltast
owner: javelin
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 9273437..9e0f4d3 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -372,7 +372,7 @@
# The warning is normal for SHOULD/SHOULD NOT case
# Likely it will cause an error
- if not resp_body and resp.status >= 400:
+ if method != 'HEAD' and not resp_body and resp.status >= 400:
self.LOG.warning("status >= 400 response with empty body")
def _request(self, method, url, headers=None, body=None):
diff --git a/tempest/config.py b/tempest/config.py
index 4ea0702..7d871cb 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -924,14 +924,14 @@
default=300,
help="Timeout for Ironic node to completely provision"),
cfg.IntOpt('association_timeout',
- default=10,
+ default=30,
help="Timeout for association of Nova instance and Ironic "
"node"),
cfg.IntOpt('power_timeout',
- default=20,
+ default=60,
help="Timeout for Ironic power transitions."),
cfg.IntOpt('unprovision_timeout',
- default=20,
+ default=60,
help="Timeout for unprovisioning an Ironic node.")
]
diff --git a/tempest/openstack/common/config/generator.py b/tempest/openstack/common/config/generator.py
index 8156cc5..664200e 100644
--- a/tempest/openstack/common/config/generator.py
+++ b/tempest/openstack/common/config/generator.py
@@ -150,7 +150,7 @@
def _is_in_group(opt, group):
- "Check if opt is in group."
+ """Check if opt is in group."""
for value in group._opts.values():
# NOTE(llu): Temporary workaround for bug #1262148, wait until
# newly released oslo.config support '==' operator.
@@ -159,7 +159,7 @@
return False
-def _guess_groups(opt, mod_obj):
+def _guess_groups(opt):
# is it in the DEFAULT group?
if _is_in_group(opt, cfg.CONF):
return 'DEFAULT'
@@ -193,7 +193,7 @@
ret = {}
for opt in opts:
- ret.setdefault(_guess_groups(opt, obj), []).append(opt)
+ ret.setdefault(_guess_groups(opt), []).append(opt)
return ret.items()
@@ -223,6 +223,8 @@
def _sanitize_default(name, value):
"""Set up a reasonably sensible default for pybasedir, my_ip and host."""
+ hostname = socket.gethostname()
+ fqdn = socket.getfqdn()
if value.startswith(sys.prefix):
# NOTE(jd) Don't use os.path.join, because it is likely to think the
# second part is an absolute pathname and therefore drop the first
@@ -234,8 +236,13 @@
return value.replace(BASEDIR, '')
elif value == _get_my_ip():
return '10.0.0.1'
- elif value in (socket.gethostname(), socket.getfqdn()) and 'host' in name:
- return 'tempest'
+ elif value in (hostname, fqdn):
+ if 'host' in name:
+ return 'tempest'
+ elif value.endswith(hostname):
+ return value.replace(hostname, 'tempest')
+ elif value.endswith(fqdn):
+ return value.replace(fqdn, 'tempest')
elif value.strip() != value:
return '"%s"' % value
return value
@@ -246,7 +253,6 @@
if not opt_help:
sys.stderr.write('WARNING: "%s" is missing help string.\n' % opt_name)
opt_help = ""
- opt_type = None
try:
opt_type = OPTION_REGEX.search(str(type(opt))).group(0)
except (ValueError, AttributeError) as err:
diff --git a/tempest/openstack/common/gettextutils.py b/tempest/openstack/common/gettextutils.py
index 17f66f7..6102e67 100644
--- a/tempest/openstack/common/gettextutils.py
+++ b/tempest/openstack/common/gettextutils.py
@@ -32,24 +32,113 @@
from babel import localedata
import six
-_localedir = os.environ.get('tempest'.upper() + '_LOCALEDIR')
-_t = gettext.translation('tempest', localedir=_localedir, fallback=True)
-
-# We use separate translation catalogs for each log level, so set up a
-# mapping between the log level name and the translator. The domain
-# for the log level is project_name + "-log-" + log_level so messages
-# for each level end up in their own catalog.
-_t_log_levels = dict(
- (level, gettext.translation('tempest' + '-log-' + level,
- localedir=_localedir,
- fallback=True))
- for level in ['info', 'warning', 'error', 'critical']
-)
-
_AVAILABLE_LANGUAGES = {}
+
+# FIXME(dhellmann): Remove this when moving to oslo.i18n.
USE_LAZY = False
+class TranslatorFactory(object):
+ """Create translator functions
+ """
+
+ def __init__(self, domain, lazy=False, localedir=None):
+ """Establish a set of translation functions for the domain.
+
+ :param domain: Name of translation domain,
+ specifying a message catalog.
+ :type domain: str
+ :param lazy: Delays translation until a message is emitted.
+ Defaults to False.
+ :type lazy: Boolean
+ :param localedir: Directory with translation catalogs.
+ :type localedir: str
+ """
+ self.domain = domain
+ self.lazy = lazy
+ if localedir is None:
+ localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
+ self.localedir = localedir
+
+ def _make_translation_func(self, domain=None):
+ """Return a new translation function ready for use.
+
+ Takes into account whether or not lazy translation is being
+ done.
+
+ The domain can be specified to override the default from the
+ factory, but the localedir from the factory is always used
+ because we assume the log-level translation catalogs are
+ installed in the same directory as the main application
+ catalog.
+
+ """
+ if domain is None:
+ domain = self.domain
+ if self.lazy:
+ return functools.partial(Message, domain=domain)
+ t = gettext.translation(
+ domain,
+ localedir=self.localedir,
+ fallback=True,
+ )
+ if six.PY3:
+ return t.gettext
+ return t.ugettext
+
+ @property
+ def primary(self):
+ "The default translation function."
+ return self._make_translation_func()
+
+ def _make_log_translation_func(self, level):
+ return self._make_translation_func(self.domain + '-log-' + level)
+
+ @property
+ def log_info(self):
+ "Translate info-level log messages."
+ return self._make_log_translation_func('info')
+
+ @property
+ def log_warning(self):
+ "Translate warning-level log messages."
+ return self._make_log_translation_func('warning')
+
+ @property
+ def log_error(self):
+ "Translate error-level log messages."
+ return self._make_log_translation_func('error')
+
+ @property
+ def log_critical(self):
+ "Translate critical-level log messages."
+ return self._make_log_translation_func('critical')
+
+
+# NOTE(dhellmann): When this module moves out of the incubator into
+# oslo.i18n, these global variables can be moved to an integration
+# module within each application.
+
+# Create the global translation functions.
+_translators = TranslatorFactory('tempest')
+
+# The primary translation function using the well-known name "_"
+_ = _translators.primary
+
+# Translators for log levels.
+#
+# The abbreviated names are meant to reflect the usual use of a short
+# name like '_'. The "L" is for "log" and the other letter comes from
+# the level.
+_LI = _translators.log_info
+_LW = _translators.log_warning
+_LE = _translators.log_error
+_LC = _translators.log_critical
+
+# NOTE(dhellmann): End of globals that will move to the application's
+# integration module.
+
+
def enable_lazy():
"""Convenience function for configuring _() to use lazy gettext
@@ -58,41 +147,18 @@
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
- global USE_LAZY
+ # FIXME(dhellmann): This function will be removed in oslo.i18n,
+ # because the TranslatorFactory makes it superfluous.
+ global _, _LI, _LW, _LE, _LC, USE_LAZY
+ tf = TranslatorFactory('tempest', lazy=True)
+ _ = tf.primary
+ _LI = tf.log_info
+ _LW = tf.log_warning
+ _LE = tf.log_error
+ _LC = tf.log_critical
USE_LAZY = True
-def _(msg):
- if USE_LAZY:
- return Message(msg, domain='tempest')
- else:
- if six.PY3:
- return _t.gettext(msg)
- return _t.ugettext(msg)
-
-
-def _log_translation(msg, level):
- """Build a single translation of a log message
- """
- if USE_LAZY:
- return Message(msg, domain='tempest' + '-log-' + level)
- else:
- translator = _t_log_levels[level]
- if six.PY3:
- return translator.gettext(msg)
- return translator.ugettext(msg)
-
-# Translators for log levels.
-#
-# The abbreviated names are meant to reflect the usual use of a short
-# name like '_'. The "L" is for "log" and the other letter comes from
-# the level.
-_LI = functools.partial(_log_translation, level='info')
-_LW = functools.partial(_log_translation, level='warning')
-_LE = functools.partial(_log_translation, level='error')
-_LC = functools.partial(_log_translation, level='critical')
-
-
def install(domain, lazy=False):
"""Install a _() function using the given translation domain.
@@ -112,26 +178,9 @@
any available locale.
"""
if lazy:
- # NOTE(mrodden): Lazy gettext functionality.
- #
- # The following introduces a deferred way to do translations on
- # messages in OpenStack. We override the standard _() function
- # and % (format string) operation to build Message objects that can
- # later be translated when we have more information.
- def _lazy_gettext(msg):
- """Create and return a Message object.
-
- Lazy gettext function for a given domain, it is a factory method
- for a project/module to get a lazy gettext function for its own
- translation domain (i.e. nova, glance, cinder, etc.)
-
- Message encapsulates a string so that we can translate
- it later when needed.
- """
- return Message(msg, domain=domain)
-
from six import moves
- moves.builtins.__dict__['_'] = _lazy_gettext
+ tf = TranslatorFactory(domain, lazy=True)
+ moves.builtins.__dict__['_'] = tf.primary
else:
localedir = '%s_LOCALEDIR' % domain.upper()
if six.PY3:
@@ -274,13 +323,14 @@
def __radd__(self, other):
return self.__add__(other)
- def __str__(self):
- # NOTE(luisg): Logging in python 2.6 tries to str() log records,
- # and it expects specifically a UnicodeError in order to proceed.
- msg = _('Message objects do not support str() because they may '
- 'contain non-ascii characters. '
- 'Please use unicode() or translate() instead.')
- raise UnicodeError(msg)
+ if six.PY2:
+ def __str__(self):
+ # NOTE(luisg): Logging in python 2.6 tries to str() log records,
+ # and it expects specifically a UnicodeError in order to proceed.
+ msg = _('Message objects do not support str() because they may '
+ 'contain non-ascii characters. '
+ 'Please use unicode() or translate() instead.')
+ raise UnicodeError(msg)
def get_available_languages(domain):
diff --git a/tempest/openstack/common/importutils.py b/tempest/openstack/common/importutils.py
index 6c0d3b2..d5dd22f 100644
--- a/tempest/openstack/common/importutils.py
+++ b/tempest/openstack/common/importutils.py
@@ -24,10 +24,10 @@
def import_class(import_str):
"""Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
+ __import__(mod_str)
try:
- __import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
- except (ValueError, AttributeError):
+ except AttributeError:
raise ImportError('Class %s cannot be found (%s)' %
(class_str,
traceback.format_exception(*sys.exc_info())))
diff --git a/tempest/services/compute/json/agents_client.py b/tempest/services/compute/json/agents_client.py
index 98d8896..4f6602f 100644
--- a/tempest/services/compute/json/agents_client.py
+++ b/tempest/services/compute/json/agents_client.py
@@ -46,7 +46,9 @@
"""Create an agent build."""
post_body = json.dumps({'agent': kwargs})
resp, body = self.post('os-agents', post_body)
- return resp, self._parse_resp(body)
+ body = json.loads(body)
+ self.validate_response(schema.create_agent, resp, body)
+ return resp, body['agent']
def delete_agent(self, agent_id):
"""Delete an existing agent build."""
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 23c1e64..e1661c0 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -432,8 +432,9 @@
return self.action(server_id, 'shelveOffload', None, **kwargs)
def get_console_output(self, server_id, length):
+ kwargs = {'length': length} if length else {}
return self.action(server_id, 'os-getConsoleOutput', 'output',
- common_schema.get_console_output, length=length)
+ common_schema.get_console_output, **kwargs)
def list_virtual_interfaces(self, server_id):
"""
diff --git a/tempest/services/compute/v3/json/agents_client.py b/tempest/services/compute/v3/json/agents_client.py
index 48be54c..31314b7 100644
--- a/tempest/services/compute/v3/json/agents_client.py
+++ b/tempest/services/compute/v3/json/agents_client.py
@@ -43,7 +43,9 @@
"""Create an agent build."""
post_body = json.dumps({'agent': kwargs})
resp, body = self.post('os-agents', post_body)
- return resp, self._parse_resp(body)
+ body = json.loads(body)
+ self.validate_response(schema.create_agent, resp, body)
+ return resp, body['agent']
def delete_agent(self, agent_id):
"""Delete an existing agent build."""
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index 626e655..156d889 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -594,8 +594,9 @@
return resp, body
def get_console_output(self, server_id, length):
+ kwargs = {'length': length} if length else {}
return self.action(server_id, 'os-getConsoleOutput', 'output',
- length=length)
+ **kwargs)
def list_virtual_interfaces(self, server_id):
"""
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index e22cd9c..4a7c163 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -22,6 +22,7 @@
from tempest.common import glance_http
from tempest.common import rest_client
+from tempest.common.utils import misc as misc_utils
from tempest import config
from tempest import exceptions
from tempest.openstack.common import log as logging
@@ -153,6 +154,7 @@
return self._create_with_data(headers, kwargs.get('data'))
resp, body = self.post('v1/images', None, headers)
+ self.expected_success(201, resp.status)
body = json.loads(body)
return resp, body['image']
@@ -176,12 +178,15 @@
url = 'v1/images/%s' % image_id
resp, body = self.put(url, data, headers)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body['image']
def delete_image(self, image_id):
url = 'v1/images/%s' % image_id
- return self.delete(url)
+ resp, body = self.delete(url)
+ self.expected_success(200, resp.status)
+ return resp, body
def image_list(self, **kwargs):
url = 'v1/images'
@@ -190,6 +195,7 @@
url += '?%s' % urllib.urlencode(kwargs)
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body['images']
@@ -210,18 +216,21 @@
url += '?%s' % urllib.urlencode(kwargs)
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body['images']
def get_image_meta(self, image_id):
url = 'v1/images/%s' % image_id
resp, __ = self.head(url)
+ self.expected_success(200, resp.status)
body = self._image_meta_from_headers(resp)
return resp, body
def get_image(self, image_id):
url = 'v1/images/%s' % image_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
return resp, body
def is_resource_deleted(self, id):
@@ -234,12 +243,14 @@
def get_image_membership(self, image_id):
url = 'v1/images/%s/members' % image_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body
def get_shared_images(self, member_id):
url = 'v1/shared-images/%s' % member_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body
@@ -249,20 +260,15 @@
if can_share:
body = json.dumps({'member': {'can_share': True}})
resp, __ = self.put(url, body)
+ self.expected_success(204, resp.status)
return resp
def delete_member(self, member_id, image_id):
url = 'v1/images/%s/members/%s' % (image_id, member_id)
resp, __ = self.delete(url)
+ self.expected_success(204, resp.status)
return resp
- def replace_membership_list(self, image_id, member_list):
- url = 'v1/images/%s/members' % image_id
- body = json.dumps({'membership': member_list})
- resp, data = self.put(url, body)
- data = json.loads(data)
- return resp, data
-
# NOTE(afazekas): just for the wait function
def _get_image_status(self, image_id):
resp, meta = self.get_image_meta(image_id)
@@ -292,6 +298,9 @@
'while waiting for %s, '
'but we got %s.' %
(self.build_timeout, status, value))
+ caller = misc_utils.find_test_caller()
+ if caller:
+ message = '(%s) %s' % (caller, message)
raise exceptions.TimeoutException(message)
time.sleep(self.build_interval)
old_value = value
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 201869e..c420df9 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -61,6 +61,7 @@
headers = {"Content-Type": "application/openstack-images-v2.0"
"-json-patch"}
resp, body = self.patch('v2/images/%s' % image_id, data, headers)
+ self.expected_success(200, resp.status)
return resp, self._parse_resp(body)
def create_image(self, name, container_format, disk_format, **kwargs):
@@ -81,12 +82,14 @@
self._validate_schema(data)
resp, body = self.post('v2/images', data)
+ self.expected_success(201, resp.status)
body = json.loads(body)
return resp, body
def delete_image(self, image_id):
url = 'v2/images/%s' % image_id
- self.delete(url)
+ resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
def image_list(self, params=None):
url = 'v2/images'
@@ -95,6 +98,7 @@
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
self._validate_schema(body, type='images')
return resp, body['images']
@@ -102,6 +106,7 @@
def get_image(self, image_id):
url = 'v2/images/%s' % image_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body
@@ -117,36 +122,40 @@
headers = {'Content-Type': 'application/octet-stream'}
resp, body = self.http.raw_request('PUT', url, headers=headers,
body=data)
+ self.expected_success(204, resp.status)
return resp, body
def get_image_file(self, image_id):
url = 'v2/images/%s/file' % image_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
return resp, body
def add_image_tag(self, image_id, tag):
url = 'v2/images/%s/tags/%s' % (image_id, tag)
resp, body = self.put(url, body=None)
+ self.expected_success(204, resp.status)
return resp, body
def delete_image_tag(self, image_id, tag):
url = 'v2/images/%s/tags/%s' % (image_id, tag)
resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
return resp
def get_image_membership(self, image_id):
url = 'v2/images/%s/members' % image_id
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
- self.expected_success(200, resp)
return resp, body
def add_member(self, image_id, member_id):
url = 'v2/images/%s/members' % image_id
data = json.dumps({'member': member_id})
resp, body = self.post(url, data)
+ self.expected_success(200, resp.status)
body = json.loads(body)
- self.expected_success(200, resp)
return resp, body
def update_member_status(self, image_id, member_id, status):
@@ -154,24 +163,25 @@
url = 'v2/images/%s/members/%s' % (image_id, member_id)
data = json.dumps({'status': status})
resp, body = self.put(url, data)
+ self.expected_success(200, resp.status)
body = json.loads(body)
- self.expected_success(200, resp)
return resp, body
def get_member(self, image_id, member_id):
url = 'v2/images/%s/members/%s' % (image_id, member_id)
resp, body = self.get(url)
- self.expected_success(200, resp)
+ self.expected_success(200, resp.status)
return resp, json.loads(body)
def remove_member(self, image_id, member_id):
url = 'v2/images/%s/members/%s' % (image_id, member_id)
resp, _ = self.delete(url)
- self.expected_success(204, resp)
+ self.expected_success(204, resp.status)
return resp
def get_schema(self, schema):
url = 'v2/schemas/%s' % schema
resp, body = self.get(url)
+ self.expected_success(200, resp.status)
body = json.loads(body)
return resp, body
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index f3f4eb6..b2f8205 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -13,7 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import httplib
import urllib
+import urlparse
from tempest.common import http
from tempest.common import rest_client
@@ -143,6 +145,31 @@
resp, body = self.put(url, data)
return resp, body
+ def put_object_with_chunk(self, container, name, contents, chunk_size):
+ """
+ Put an object with Transfer-Encoding header
+ """
+ if self.base_url is None:
+ self._set_auth()
+
+ headers = {'Transfer-Encoding': 'chunked'}
+ if self.token:
+ headers['X-Auth-Token'] = self.token
+
+ conn = put_object_connection(self.base_url, container, name, contents,
+ chunk_size, headers)
+
+ resp = conn.getresponse()
+ body = resp.read()
+
+ resp_headers = {}
+ for header, value in resp.getheaders():
+ resp_headers[header.lower()] = value
+
+ self._error_checker('PUT', None, headers, contents, resp, body)
+
+ return resp.status, resp.reason, resp_headers
+
class ObjectClientCustomizedHeader(rest_client.RestClient):
@@ -220,3 +247,89 @@
url = "%s/%s" % (str(container), str(object_name))
resp, body = self.delete(url, headers=headers)
return resp, body
+
+ def create_object_continue(self, container, object_name,
+ data, metadata=None):
+ """Create storage object."""
+ headers = {}
+ if metadata:
+ for key in metadata:
+ headers[str(key)] = metadata[key]
+
+ if not data:
+ headers['content-length'] = '0'
+
+ if self.base_url is None:
+ self._set_auth()
+ headers['X-Auth-Token'] = self.token
+
+ conn = put_object_connection(self.base_url, str(container),
+ str(object_name), data, None, headers)
+
+ response = conn.response_class(conn.sock,
+ strict=conn.strict,
+ method=conn._method)
+ version, status, reason = response._read_status()
+ resp = {'version': version,
+ 'status': str(status),
+ 'reason': reason}
+
+ return resp
+
+
+def put_object_connection(base_url, container, name, contents=None,
+ chunk_size=65536, headers=None, query_string=None):
+ """
+ Helper function to make connection to put object with httplib
+ :param base_url: base_url of an object client
+ :param container: container name that the object is in
+ :param name: object name to put
+ :param contents: a string or a file like object to read object data
+ from; if None, a zero-byte put will be done
+ :param chunk_size: chunk size of data to write; it defaults to 65536;
+ used only if the the contents object has a 'read'
+ method, eg. file-like objects, ignored otherwise
+ :param headers: additional headers to include in the request, if any
+ :param query_string: if set will be appended with '?' to generated path
+ """
+ parsed = urlparse.urlparse(base_url)
+ if parsed.scheme == 'https':
+ conn = httplib.HTTPSConnection(parsed.netloc)
+ else:
+ conn = httplib.HTTPConnection(parsed.netloc)
+ path = str(parsed.path) + "/"
+ path += "%s/%s" % (str(container), str(name))
+
+ if query_string:
+ path += '?' + query_string
+ if headers:
+ headers = dict(headers)
+ else:
+ headers = {}
+ if hasattr(contents, 'read'):
+ conn.putrequest('PUT', path)
+ for header, value in headers.iteritems():
+ conn.putheader(header, value)
+ if 'Content-Length' not in headers:
+ if 'Transfer-Encoding' not in headers:
+ conn.putheader('Transfer-Encoding', 'chunked')
+ conn.endheaders()
+ chunk = contents.read(chunk_size)
+ while chunk:
+ conn.send('%x\r\n%s\r\n' % (len(chunk), chunk))
+ chunk = contents.read(chunk_size)
+ conn.send('0\r\n\r\n')
+ else:
+ conn.endheaders()
+ left = headers['Content-Length']
+ while left > 0:
+ size = chunk_size
+ if size > left:
+ size = left
+ chunk = contents.read(size)
+ conn.send(chunk)
+ left -= len(chunk)
+ else:
+ conn.request('PUT', path, contents, headers)
+
+ return conn
diff --git a/test-requirements.txt b/test-requirements.txt
index 215f28b..13ef291 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -8,3 +8,4 @@
mock>=1.0
coverage>=3.6
oslotest
+stevedore>=0.14
diff --git a/tools/config/check_uptodate.sh b/tools/config/check_uptodate.sh
index 528bd5b..0f0d77e 100755
--- a/tools/config/check_uptodate.sh
+++ b/tools/config/check_uptodate.sh
@@ -16,6 +16,10 @@
trap "rm -rf $TEMPDIR" EXIT
tools/config/generate_sample.sh -b ./ -p ${PROJECT_NAME} -o ${TEMPDIR}
+if [ $? != 0 ]
+then
+ exit 1
+fi
if ! diff -u ${TEMPDIR}/${CFGFILE_NAME} ${CFGFILE}
then
diff --git a/tools/config/generate_sample.sh b/tools/config/generate_sample.sh
index 20ddfbb..d22b2f0 100755
--- a/tools/config/generate_sample.sh
+++ b/tools/config/generate_sample.sh
@@ -1,5 +1,15 @@
#!/usr/bin/env bash
+# Generate sample configuration for your project.
+#
+# Aside from the command line flags, it also respects a config file which
+# should be named oslo.config.generator.rc and be placed in the same directory.
+#
+# You can then export the following variables:
+# TEMPEST_CONFIG_GENERATOR_EXTRA_MODULES: list of modules to interrogate for options.
+# TEMPEST_CONFIG_GENERATOR_EXTRA_LIBRARIES: list of libraries to discover.
+# TEMPEST_CONFIG_GENERATOR_EXCLUDED_FILES: list of files to remove from automatic listing.
+
print_hint() {
echo "Try \`${0##*/} --help' for more information." >&2
}
@@ -95,6 +105,10 @@
source "$RC_FILE"
fi
+for filename in ${TEMPEST_CONFIG_GENERATOR_EXCLUDED_FILES}; do
+ FILES="${FILES[@]/$filename/}"
+done
+
for mod in ${TEMPEST_CONFIG_GENERATOR_EXTRA_MODULES}; do
MODULES="$MODULES -m $mod"
done
@@ -111,6 +125,11 @@
MODULEPATH=${MODULEPATH:-$DEFAULT_MODULEPATH}
OUTPUTFILE=$OUTPUTDIR/$PACKAGENAME.conf.sample
python -m $MODULEPATH $MODULES $LIBRARIES $FILES > $OUTPUTFILE
+if [ $? != 0 ]
+then
+ echo "Can not generate $OUTPUTFILE"
+ exit 1
+fi
# Hook to allow projects to append custom config file snippets
CONCAT_FILES=$(ls $BASEDIR/tools/config/*.conf.sample 2>/dev/null)
diff --git a/tools/config/oslo.config.generator.rc b/tools/config/oslo.config.generator.rc
new file mode 100644
index 0000000..303e156
--- /dev/null
+++ b/tools/config/oslo.config.generator.rc
@@ -0,0 +1 @@
+MODULEPATH=tempest.common.generate_sample_tempest
diff --git a/tools/generate_sample.sh b/tools/generate_sample.sh
deleted file mode 100755
index 9b312c9..0000000
--- a/tools/generate_sample.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-MODULEPATH=tempest.common.generate_sample_tempest tools/config/generate_sample.sh $@
diff --git a/tox.ini b/tox.ini
index c1acde9..73546ec 100644
--- a/tox.ini
+++ b/tox.ini
@@ -85,7 +85,6 @@
-r{toxinidir}/test-requirements.txt
[testenv:pep8]
-setenv = MODULEPATH=tempest.common.generate_sample_tempest
commands =
flake8 {posargs}
{toxinidir}/tools/config/check_uptodate.sh