Merge "Make Identity v2 service clients as available module"
diff --git a/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
new file mode 100644
index 0000000..7e40fd4
--- /dev/null
+++ b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ As in the [doc]:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html,
+ there are some apis are not included, add them.
+
+ * namespace_properties_client(v2)
+
diff --git a/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
new file mode 100644
index 0000000..a1edcc5
--- /dev/null
+++ b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - Define the identity service role_assignments_client as a library.
+ Add role_assignments_client to the library interface so the other
+ projects can use this module as a stable library without any
+ maintenance changes.
diff --git a/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
new file mode 100644
index 0000000..e6e53af
--- /dev/null
+++ b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+ - The *bootable* config option in the *volume_feature_enabled* group is
+ removed because the corresponding feature os-set_bootable has been
+ implemented 2.5 years ago and all OpenStack versions which are supported
+ by Tempest should support the feature.
diff --git a/requirements.txt b/requirements.txt
index ea73180..d9a9ebb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr>=1.8 # Apache-2.0
-cliff>=2.2.0 # Apache-2.0
+cliff>=2.3.0 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
paramiko>=2.0 # LGPLv2.1+
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 609eae6..85aa301 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -177,6 +177,5 @@
def test_aggregate_remove_nonexistent_host(self):
aggregate = self._create_test_aggregate()
- non_exist_host = data_utils.rand_name('nonexist_host')
self.assertRaises(lib_exc.NotFound, self.client.remove_host,
- aggregate['id'], host=non_exist_host)
+ aggregate['id'], host='nonexist_host')
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index b0ab9be..7eb4bbf 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -102,11 +102,10 @@
@test.attr(type=['negative'])
@test.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
def test_flavor_unset_nonexistent_key(self):
- nonexistent_key = data_utils.rand_name('flavor_key')
self.assertRaises(lib_exc.NotFound,
self.client.unset_flavor_extra_spec,
self.flavor['id'],
- nonexistent_key)
+ 'nonexistent_key')
@test.attr(type=['negative'])
@test.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 220ea39..0f35e14 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -122,12 +122,10 @@
@test.attr(type=['negative'])
@test.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
def test_search_nonexistent_hypervisor(self):
- nonexistent_hypervisor_name = data_utils.rand_name('test_hypervisor')
-
self.assertRaises(
lib_exc.NotFound,
self.client.search_hypervisor,
- nonexistent_hypervisor_name)
+ 'nonexistent_hypervisor_name')
@test.attr(type=['negative'])
@test.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index f71f046..31cf39c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -23,7 +23,6 @@
class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
- server_id = None
@classmethod
def setup_clients(cls):
@@ -38,15 +37,14 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
# Generating a nonexistent floatingIP id
- cls.floating_ip_ids = []
body = cls.client.list_floating_ips()['floating_ips']
- for i in range(len(body)):
- cls.floating_ip_ids.append(body[i]['id'])
+ floating_ip_ids = [floating_ip['id'] for floating_ip in body]
while True:
- cls.non_exist_id = data_utils.rand_int_id(start=999)
if CONF.service_available.neutron:
cls.non_exist_id = data_utils.rand_uuid()
- if cls.non_exist_id not in cls.floating_ip_ids:
+ else:
+ cls.non_exist_id = data_utils.rand_int_id(start=999)
+ if cls.non_exist_id not in floating_ip_ids:
break
@test.attr(type=['negative'])
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index fc6a20f..8342a3e 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -50,30 +50,6 @@
cls.subnets_client = cls.os.subnets_client
cls.ports_client = cls.os.ports_client
- def wait_for_interface_status(self, server, port_id, status):
- """Waits for an interface to reach a given status."""
- body = (self.interfaces_client.show_interface(server, port_id)
- ['interfaceAttachment'])
- interface_status = body['port_state']
- start = int(time.time())
-
- while(interface_status != status):
- time.sleep(self.build_interval)
- body = (self.interfaces_client.show_interface(server, port_id)
- ['interfaceAttachment'])
- interface_status = body['port_state']
-
- timed_out = int(time.time()) - start >= self.build_timeout
-
- if interface_status != status and timed_out:
- message = ('Interface %s failed to reach %s status '
- '(current %s) within the required time (%s s).' %
- (port_id, status, interface_status,
- self.build_timeout))
- raise lib_exc.TimeoutException(message)
-
- return body
-
# TODO(mriedem): move this into a common waiters utility module
def wait_for_port_detach(self, port_id):
"""Waits for the port's device_id to be unset.
@@ -118,16 +94,16 @@
server = self.create_test_server(wait_until='ACTIVE')
ifs = (self.interfaces_client.list_interfaces(server['id'])
['interfaceAttachments'])
- body = self.wait_for_interface_status(
- server['id'], ifs[0]['port_id'], 'ACTIVE')
+ body = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
ifs[0]['port_state'] = body['port_state']
return server, ifs
def _test_create_interface(self, server):
iface = (self.interfaces_client.create_interface(server['id'])
['interfaceAttachment'])
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface)
return iface
@@ -135,8 +111,8 @@
network_id = ifs[0]['net_id']
iface = self.interfaces_client.create_interface(
server['id'], net_id=network_id)['interfaceAttachment']
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, network_id=network_id)
return iface
@@ -147,8 +123,8 @@
self.addCleanup(self.ports_client.delete_port, port_id)
iface = self.interfaces_client.create_interface(
server['id'], port_id=port_id)['interfaceAttachment']
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, port_id=port_id)
return iface
@@ -166,8 +142,8 @@
server['id'], net_id=network_id,
fixed_ips=fixed_ips)['interfaceAttachment']
self.addCleanup(self.ports_client.delete_port, iface['port_id'])
- iface = self.wait_for_interface_status(
- server['id'], iface['port_id'], 'ACTIVE')
+ iface = waiters.wait_for_interface_status(
+ self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
self._check_interface(iface, fixed_ip=ip_list[0])
return iface
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 50910ec..0a94d5e 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -13,8 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-
+from oslo_log import log as logging
from six.moves.urllib import parse as urlparse
import testtools
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 460c882..01718cc 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common import waiters
@@ -39,6 +41,8 @@
cls.snapshots_client = cls.snapshots_extensions_client
@test.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
+ @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+ 'Cinder volume snapshots are disabled')
def test_volume_snapshot_create_get_list_delete(self):
volume = self.create_volume()
self.addCleanup(self.delete_volume, volume['id'])
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index c60fcca..7d76731 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_log import log as logging
+
from tempest.api.compute import base
from tempest.common.utils import data_utils
from tempest.common import waiters
@@ -20,6 +22,7 @@
from tempest import test
CONF = config.CONF
+LOG = logging.getLogger(__name__)
class VolumesTestJSON(base.BaseV2ComputeTest):
@@ -59,21 +62,16 @@
volume = cls.client.show_volume(volume['id'])['volume']
cls.volume_list.append(volume)
cls.volume_id_list.append(volume['id'])
- except Exception:
+ except Exception as exc:
+ LOG.exception(exc)
if cls.volume_list:
# We could not create all the volumes, though we were able
# to create *some* of the volumes. This is typically
# because the backing file size of the volume group is
- # too small. So, here, we clean up whatever we did manage
- # to create and raise a SkipTest
+ # too small.
for volume in cls.volume_list:
cls.delete_volume(volume['id'])
- msg = ("Failed to create ALL necessary volumes to run "
- "test. This typically means that the backing file "
- "size of the nova-volumes group is too small to "
- "create the 3 volumes needed by this test case")
- raise cls.skipException(msg)
- raise
+ raise exc
@classmethod
def resource_cleanup(cls):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 5fe4cb3..c4041cb 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -66,7 +66,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
- def test_create_volume_with_out_passing_size(self):
+ def test_create_volume_without_passing_size(self):
# Negative: Should not be able to create volume without passing size
# in request
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 955b6fb..c7e8411 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -168,15 +168,16 @@
self.domain['id'], self.user['id'], src_role['id'])
# List "effective" role assignments from user on the parent project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- self.project['id'], self.user['id']))['role_assignments']
+ params = {'scope.project.id': self.project['id'],
+ 'user.id': self.user['id']}
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# List "effective" role assignments from user on the leaf project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = leaf_project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# Revoke role from domain
@@ -185,16 +186,16 @@
# List "effective" role assignments from user on the parent project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- self.project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = self.project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
# List "effective" role assignments from user on the leaf project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params['scope.project.id'] = leaf_project['id']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
@test.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
@@ -217,9 +218,10 @@
self.project['id'], self.user['id'], src_role['id'])
# List "effective" role assignments from user on the leaf project
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ params = {'scope.project.id': leaf_project['id'],
+ 'user.id': self.user['id']}
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertNotEmpty(assignments)
# Revoke role from parent project
@@ -228,7 +230,6 @@
# List "effective" role assignments from user on the leaf project
# should return an empty list
- assignments = (
- self.role_assignments.list_user_project_effective_assignments(
- leaf_project['id'], self.user['id']))['role_assignments']
+ assignments = self.role_assignments.list_role_assignments(
+ effective=True, **params)['role_assignments']
self.assertEmpty(assignments)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 1cc3fa2..812c436 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -141,6 +141,7 @@
cls.client = cls.os.image_client_v2
cls.namespaces_client = cls.os.namespaces_client
cls.resource_types_client = cls.os.resource_types_client
+ cls.namespace_properties_client = cls.os.namespace_properties_client
cls.schemas_client = cls.os.schemas_client
def create_namespace(cls, namespace_name=None, visibility='public',
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 5cf8084..42912f0 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -129,7 +129,6 @@
class ListImagesTest(base.BaseV2ImageTest):
- """Here we test the listing of image information"""
@classmethod
def resource_setup(cls):
@@ -157,23 +156,49 @@
"""
size = random.randint(1024, 4096)
image_file = six.BytesIO(data_utils.random_bytes(size))
+ tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
image = cls.create_image(container_format=container_format,
disk_format=disk_format,
- visibility='private')
+ visibility='private',
+ tags=tags)
cls.client.store_image_file(image['id'], data=image_file)
+ # Keep the data of one test image so it can be used to filter lists
+ cls.test_data = image
+ cls.test_data['size'] = size
return image['id']
+
+class ListUserImagesTest(ListImagesTest):
+ """Here we test the listing of image information"""
+
def _list_by_param_value_and_assert(self, params):
"""Perform list action with given params and validates result."""
-
+ # Retrieve the list of images that meet the filter
images_list = self.client.list_images(params=params)['images']
# Validating params of fetched images
+ msg = 'No images were found that met the filter criteria.'
+ self.assertNotEmpty(images_list, msg)
for image in images_list:
for key in params:
msg = "Failed to list images by %s" % key
self.assertEqual(params[key], image[key], msg)
+ def _list_sorted_by_image_size_and_assert(self, params, desc=False):
+ """Validate an image list that has been sorted by size
+
+ Perform list action with given params and validates the results are
+ sorted by image size in either ascending or descending order.
+ """
+ # Retrieve the list of images that meet the filter
+ images_list = self.client.list_images(params=params)['images']
+ # Validate that the list was fetched sorted accordingly
+ msg = 'No images were found that met the filter criteria.'
+ self.assertNotEmpty(images_list, msg)
+ sorted_list = [image['size'] for image in images_list]
+ msg = 'The list of images was not sorted correctly.'
+ self.assertEqual(sorted(sorted_list, reverse=desc), sorted_list, msg)
+
@test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
def test_list_no_params(self):
# Simple test to see all fixture images returned
@@ -185,8 +210,8 @@
@test.idempotent_id('9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e')
def test_list_images_param_container_format(self):
- # Test to get all images with container_format='bare'
- params = {"container_format": "bare"}
+ # Test to get all images with a specific container_format
+ params = {"container_format": self.test_data['container_format']}
self._list_by_param_value_and_assert(params)
@test.idempotent_id('4a4735a7-f22f-49b6-b0d9-66e1ef7453eb')
@@ -254,6 +279,37 @@
params = {"owner": image['owner']}
self._list_by_param_value_and_assert(params)
+ @test.idempotent_id('55c8f5f5-bfed-409d-a6d5-4caeda985d7b')
+ def test_list_images_param_name(self):
+ # Test to get images by name
+ params = {'name': self.test_data['name']}
+ self._list_by_param_value_and_assert(params)
+
+ @test.idempotent_id('aa8ac4df-cff9-418b-8d0f-dd9c67b072c9')
+ def test_list_images_param_tag(self):
+ # Test to get images matching a tag
+ params = {'tag': self.test_data['tags'][0]}
+ images_list = self.client.list_images(params=params)['images']
+ # Validating properties of fetched images
+ self.assertNotEmpty(images_list)
+ for image in images_list:
+ msg = ("The image {image_name} does not have the expected tag "
+ "{expected_tag} among its tags: {observerd_tags}."
+ .format(image_name=image['name'],
+ expected_tag=self.test_data['tags'][0],
+ observerd_tags=image['tags']))
+ self.assertIn(self.test_data['tags'][0], image['tags'], msg)
+
+ @test.idempotent_id('eeadce49-04e0-43b7-aec7-52535d903e7a')
+ def test_list_images_param_sort(self):
+ params = {'sort': 'size:desc'}
+ self._list_sorted_by_image_size_and_assert(params, desc=True)
+
+ @test.idempotent_id('9faaa0c2-c3a5-43e1-8f61-61c54b409a49')
+ def test_list_images_param_sort_key_dir(self):
+ params = {'sort_key': 'size', 'sort_dir': 'desc'}
+ self._list_sorted_by_image_size_and_assert(params, desc=True)
+
@test.idempotent_id('622b925c-479f-4736-860d-adeaf13bc371')
def test_get_image_schema(self):
# Test to get image schema
@@ -267,3 +323,32 @@
schema = "images"
body = self.schemas_client.show_schema(schema)
self.assertEqual("images", body['name'])
+
+
+class ListSharedImagesTest(ListImagesTest):
+ """Here we test the listing of a shared image information"""
+
+ credentials = ['primary', 'alt']
+
+ @classmethod
+ def setup_clients(cls):
+ super(ListSharedImagesTest, cls).setup_clients()
+ cls.image_member_client = cls.os.image_member_client_v2
+ cls.alt_img_client = cls.os_alt.image_client_v2
+
+ @test.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
+ def test_list_images_param_member_status(self):
+ # Share one of the images created with the alt user
+ self.image_member_client.create_image_member(
+ image_id=self.test_data['id'],
+ member=self.alt_img_client.tenant_id)
+ # Update the info on the test data so it remains accurate
+ self.test_data['updated_at'] = self.client.show_image(
+ self.test_data['id'])['updated_at']
+ # As an image consumer you need to provide the member_status parameter
+ # along with the visibility=shared parameter in order for it to show
+ # results
+ params = {'member_status': 'pending', 'visibility': 'shared'}
+ fetched_images = self.alt_img_client.list_images(params)['images']
+ self.assertEqual(1, len(fetched_images))
+ self.assertEqual(self.test_data['id'], fetched_images[0]['id'])
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
new file mode 100644
index 0000000..7113db4
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -0,0 +1,57 @@
+# 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.common.utils import data_utils
+from tempest import test
+
+
+class MetadataNamespacePropertiesTest(base.BaseV2ImageTest):
+ """Test the Metadata definition namespace property basic functionality"""
+
+ @test.idempotent_id('b1a3765e-3a5d-4f6d-a3a7-3ca3476ae768')
+ def test_basic_meta_def_namespace_property(self):
+ # Get the available resource types and use one resource_type
+ body = self.resource_types_client.list_resource_types()
+ resource_name = body['resource_types'][0]['name']
+ enum = ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"]
+ # Create a namespace
+ namespace = self.create_namespace()
+ # Create resource type association
+ body = self.resource_types_client.create_resource_type_association(
+ namespace['namespace'], name=resource_name)
+ # Create a property
+ property_title = data_utils.rand_name('property')
+ body = self.namespace_properties_client.create_namespace_property(
+ namespace=namespace['namespace'], title=property_title,
+ name=resource_name, type="string", enum=enum)
+ self.assertEqual(property_title, body['title'])
+ # Show namespace property
+ body = self.namespace_properties_client.show_namespace_properties(
+ namespace['namespace'], resource_name)
+ self.assertEqual(resource_name, body['name'])
+ # Update namespace property
+ update_property_title = data_utils.rand_name('update-property')
+ body = self.namespace_properties_client.update_namespace_properties(
+ namespace['namespace'], resource_name,
+ title=update_property_title, type="string",
+ enum=enum, name=resource_name)
+ self.assertEqual(update_property_title, body['title'])
+ # Delete namespace property
+ self.namespace_properties_client.delete_namespace_property(
+ namespace['namespace'], resource_name)
+ # List namespace properties and validate deletion
+ namespace_property = [
+ namespace_property['title'] for namespace_property in
+ self.namespace_properties_client.list_namespace_properties(
+ namespace['namespace'])['properties']]
+ self.assertNotIn(update_property_title, namespace_property)
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index ed6a302..e989b69 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -49,41 +49,31 @@
@test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
def test_create_show_list_update_delete_router(self):
# Create a router
- # NOTE(salv-orlando): Do not invoke self.create_router
- # as we need to check the response code
- name = data_utils.rand_name('router-')
- create_body = self.routers_client.create_router(
- name=name, external_gateway_info={
- "network_id": CONF.network.public_network_id},
- admin_state_up=False)
- self.addCleanup(self._delete_router, create_body['router']['id'])
- self.assertEqual(create_body['router']['name'], name)
+ router = self._create_router(
+ admin_state_up=False,
+ external_network_id=CONF.network.public_network_id)
+ self.assertEqual(router['admin_state_up'], False)
self.assertEqual(
- create_body['router']['external_gateway_info']['network_id'],
+ router['external_gateway_info']['network_id'],
CONF.network.public_network_id)
- self.assertEqual(create_body['router']['admin_state_up'], False)
# Show details of the created router
- show_body = self.routers_client.show_router(
- create_body['router']['id'])
- self.assertEqual(show_body['router']['name'], name)
+ router_show = self.routers_client.show_router(
+ router['id'])['router']
+ self.assertEqual(router_show['name'], router['name'])
self.assertEqual(
- show_body['router']['external_gateway_info']['network_id'],
+ router_show['external_gateway_info']['network_id'],
CONF.network.public_network_id)
- self.assertEqual(show_body['router']['admin_state_up'], False)
# List routers and verify if created router is there in response
- list_body = self.routers_client.list_routers()
- routers_list = list()
- for router in list_body['routers']:
- routers_list.append(router['id'])
- self.assertIn(create_body['router']['id'], routers_list)
+ routers = self.routers_client.list_routers()['routers']
+ self.assertIn(router['id'], map(lambda x: x['id'], routers))
# Update the name of router and verify if it is updated
- updated_name = 'updated ' + name
- update_body = self.routers_client.update_router(
- create_body['router']['id'], name=updated_name)
- self.assertEqual(update_body['router']['name'], updated_name)
- show_body = self.routers_client.show_router(
- create_body['router']['id'])
- self.assertEqual(show_body['router']['name'], updated_name)
+ updated_name = 'updated' + router['name']
+ router_update = self.routers_client.update_router(
+ router['id'], name=updated_name)['router']
+ self.assertEqual(router_update['name'], updated_name)
+ router_show = self.routers_client.show_router(
+ router['id'])['router']
+ self.assertEqual(router_show['name'], updated_name)
@test.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
def test_create_router_setting_project_id(self):
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index 7049db0..e6c53ec 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -92,47 +92,39 @@
def test_get_metadata_headers_with_invalid_container_name(self):
# Attempts to retrieve metadata headers with an invalid
# container name.
- invalid_name = data_utils.rand_name(name="TestInvalidContainer")
-
self.assertRaises(exceptions.NotFound,
self.container_client.list_container_metadata,
- invalid_name)
+ 'invalid_container_name')
@test.attr(type=["negative"])
@test.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
def test_update_metadata_with_nonexistent_container_name(self):
# Attempts to update metadata using a nonexistent container name.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
metadata = {'animal': 'penguin'}
self.assertRaises(exceptions.NotFound,
self.container_client.update_container_metadata,
- nonexistent_name, metadata)
+ 'nonexistent_container_name', metadata)
@test.attr(type=["negative"])
@test.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
def test_delete_with_nonexistent_container_name(self):
# Attempts to delete metadata using a nonexistent container name.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
metadata = {'animal': 'penguin'}
self.assertRaises(exceptions.NotFound,
self.container_client.delete_container_metadata,
- nonexistent_name, metadata)
+ 'nonexistent_container_name', metadata)
@test.attr(type=["negative"])
@test.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
def test_list_all_container_objects_with_nonexistent_container(self):
# Attempts to get a listing of all objects on a container
# that doesn't exist.
- nonexistent_name = data_utils.rand_name(
- name="TestNonexistentContainer")
params = {'limit': 9999, 'format': 'json'}
self.assertRaises(exceptions.NotFound,
self.container_client.list_container_contents,
- nonexistent_name, params)
+ 'nonexistent_container_name', params)
@test.attr(type=["negative"])
@test.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index a3792b4..bffcb64 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -10,10 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-import logging
-
import netaddr
+from oslo_log import log as logging
from tempest.api.orchestration import base
from tempest.common.utils import data_utils
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 6db3807..f32c84a 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -116,6 +116,12 @@
if 'size' not in kwargs:
kwargs['size'] = CONF.volume.volume_size
+ if 'imageRef' in kwargs:
+ image = cls.compute_images_client.show_image(
+ kwargs['imageRef'])['image']
+ min_disk = image.get('minDisk')
+ kwargs['size'] = max(kwargs['size'], min_disk)
+
name_field = cls.special_fields['name_field']
if name_field not in kwargs:
name = data_utils.rand_name(cls.__name__ + '-Volume')
@@ -151,6 +157,18 @@
client.delete_volume(volume_id)
client.wait_for_resource_deletion(volume_id)
+ def attach_volume(self, server_id, volume_id):
+ """Attachs a volume to a server"""
+ self.servers_client.attach_volume(
+ server_id, volumeId=volume_id,
+ device='/dev/%s' % CONF.compute.volume_device_name)
+ waiters.wait_for_volume_status(self.volumes_client,
+ volume_id, 'in-use')
+ self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+ volume_id, 'available')
+ self.addCleanup(self.servers_client.detach_volume, server_id,
+ self.volume_origin['id'])
+
@classmethod
def clear_volumes(cls):
for volume in cls.volumes:
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 737ce5e..38f1082 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -12,7 +12,6 @@
# 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 testtools
from tempest.api.volume import base
from tempest.common.utils import data_utils
@@ -67,8 +66,6 @@
self.volume['id'], 'available')
@test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
- @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
- 'Update bootable status of a volume is not enabled.')
def test_volume_bootable(self):
# Verify that a volume bootable flag is retrieved
for bool_bootable in [True, False]:
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index fda0dda..6ed6b9c 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -62,7 +62,7 @@
@test.attr(type=['negative'])
@test.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
- def test_create_volume_with_out_passing_size(self):
+ def test_create_volume_without_passing_size(self):
# Should not be able to create volume without passing size
# in request
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 3c05d3e..3c7a2c8 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,7 +12,6 @@
from tempest.api.volume import base
from tempest.common.utils import data_utils
-from tempest.common import waiters
from tempest import config
from tempest import test
@@ -46,21 +45,53 @@
# Create a snapshot when volume status is in-use
# Create a test instance
server = self.create_server(wait_until='ACTIVE')
- self.servers_client.attach_volume(
- server['id'], volumeId=self.volume_origin['id'],
- device='/dev/%s' % CONF.compute.volume_device_name)
- waiters.wait_for_volume_status(self.volumes_client,
- self.volume_origin['id'], 'in-use')
- self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
- self.volume_origin['id'], 'available')
- self.addCleanup(self.servers_client.detach_volume, server['id'],
- self.volume_origin['id'])
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
# Snapshot a volume even if it's attached to an instance
snapshot = self.create_snapshot(self.volume_origin['id'],
force=True)
# Delete the snapshot
self.cleanup_snapshot(snapshot)
+ @test.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
+ @test.services('compute')
+ def test_snapshot_delete_with_volume_in_use(self):
+ # Create a test instance
+ server = self.create_server(wait_until='ACTIVE')
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
+ # Snapshot a volume attached to an instance
+ snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+ # Delete the snapshots. Some snapshot implementations can take
+ # different paths according to order they are deleted.
+ self.cleanup_snapshot(snapshot1)
+ self.cleanup_snapshot(snapshot3)
+ self.cleanup_snapshot(snapshot2)
+
+ @test.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
+ @test.services('compute')
+ def test_snapshot_create_offline_delete_online(self):
+
+ # Create a snapshot while it is not attached
+ snapshot1 = self.create_snapshot(self.volume_origin['id'])
+
+ # Create a server and attach it
+ server = self.create_server(wait_until='ACTIVE')
+ self.attach_volume(server['id'], self.volume_origin['id'])
+
+ # Now that the volume is attached, create another snapshots
+ snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+ snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+ # Delete the snapshots. Some snapshot implementations can take
+ # different paths according to order they are deleted.
+ self.cleanup_snapshot(snapshot3)
+ self.cleanup_snapshot(snapshot1)
+ self.cleanup_snapshot(snapshot2)
+
@test.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
def test_snapshot_create_get_list_update_delete(self):
# Create a snapshot
diff --git a/tempest/clients.py b/tempest/clients.py
index d131dc4..5d535f6 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -17,7 +17,6 @@
from oslo_log import log as logging
-from tempest.common import negative_rest_client
from tempest import config
from tempest.lib import auth
from tempest.lib import exceptions as lib_exc
@@ -82,8 +81,6 @@
build_interval=CONF.orchestration.build_interval,
build_timeout=CONF.orchestration.build_timeout,
**self.default_params)
- self.negative_client = negative_rest_client.NegativeRestClient(
- self.auth_provider, service, **self.default_params)
def _prepare_configuration(self):
"""Map values from CONF into Manager parameters
@@ -141,6 +138,8 @@
self.namespaces_client = self.image_v2.NamespacesClient()
self.resource_types_client = self.image_v2.ResourceTypesClient()
self.schemas_client = self.image_v2.SchemasClient()
+ self.namespace_properties_client = \
+ self.image_v2.NamespacePropertiesClient()
def _set_compute_clients(self):
self.agents_client = self.compute.AgentsClient()
diff --git a/tempest/cmd/main.py b/tempest/cmd/main.py
index 641d11c..1090c41 100644
--- a/tempest/cmd/main.py
+++ b/tempest/cmd/main.py
@@ -11,11 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import sys
from cliff import app
from cliff import commandmanager
+from oslo_log import log as logging
from pbr import version
diff --git a/tempest/common/negative_rest_client.py b/tempest/common/negative_rest_client.py
deleted file mode 100644
index 3495a24..0000000
--- a/tempest/common/negative_rest_client.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-# Copyright 2014 Red Hat, Inc.
-# Copyright 2014 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 import config
-from tempest.lib.common import rest_client
-
-CONF = config.CONF
-
-
-class NegativeRestClient(rest_client.RestClient):
- """Version of RestClient that does not raise exceptions."""
- def __init__(self, auth_provider, service, **kwargs):
- region, endpoint_type = self._get_region_and_endpoint_type(service)
- super(NegativeRestClient, self).__init__(
- auth_provider, service, region, endpoint_type=endpoint_type,
- **kwargs)
-
- def _get_region_and_endpoint_type(self, service):
- """Returns the region for a specific service"""
- service_region = None
- service_endpoint_type = None
- for cfgname in dir(CONF._config):
- # Find all config.FOO.catalog_type and assume FOO is a service.
- cfg = getattr(CONF, cfgname)
- catalog_type = getattr(cfg, 'catalog_type', None)
- if catalog_type == service:
- service_region = getattr(cfg, 'region', None)
- service_endpoint_type = getattr(cfg, 'endpoint_type', None)
- if not service_region:
- service_region = CONF.identity.region
- return service_region, service_endpoint_type
-
- def _error_checker(self, method, url,
- headers, body, resp, resp_body):
- pass
-
- def send_request(self, method, url_template, resources, body=None):
- url = url_template % tuple(resources)
- if method == "GET":
- resp, body = self.get(url)
- elif method == "POST":
- resp, body = self.post(url, body)
- elif method == "PUT":
- resp, body = self.put(url, body)
- elif method == "PATCH":
- resp, body = self.patch(url, body)
- elif method == "HEAD":
- resp, body = self.head(url)
- elif method == "DELETE":
- resp, body = self.delete(url)
- elif method == "COPY":
- resp, body = self.copy(url)
- else:
- assert False
-
- return resp, body
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 0cf8154..981a922 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -290,3 +290,28 @@
if int(time.time()) - start_time >= client.build_timeout:
raise lib_exc.TimeoutException
time.sleep(client.build_interval)
+
+
+def wait_for_interface_status(client, server, port_id, status):
+ """Waits for an interface to reach a given status."""
+ body = (client.show_interface(server, port_id)
+ ['interfaceAttachment'])
+ interface_status = body['port_state']
+ start = int(time.time())
+
+ while(interface_status != status):
+ time.sleep(client.build_interval)
+ body = (client.show_interface(server, port_id)
+ ['interfaceAttachment'])
+ interface_status = body['port_state']
+
+ timed_out = int(time.time()) - start >= client.build_timeout
+
+ if interface_status != status and timed_out:
+ message = ('Interface %s failed to reach %s status '
+ '(current %s) within the required time (%s s).' %
+ (port_id, status, interface_status,
+ client.build_timeout))
+ raise lib_exc.TimeoutException(message)
+
+ return body
diff --git a/tempest/config.py b/tempest/config.py
index bc9215c..70ede55 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -397,7 +397,10 @@
cfg.BoolOpt('nova_cert',
default=False,
help='Does the test environment have the nova cert running?',
- deprecated_for_removal=True),
+ deprecated_for_removal=True,
+ deprecated_reason="On Nova side, the nova-cert service is "
+ "deprecated and the service will be removed "
+ "as early as Ocata."),
cfg.BoolOpt('personality',
default=False,
help='Does the test environment support server personality'),
@@ -780,11 +783,6 @@
cfg.BoolOpt('api_v3',
default=False,
help="Is the v3 volume API enabled"),
- cfg.BoolOpt('bootable',
- default=True,
- help='Update bootable status of a volume '
- 'Not implemented on icehouse ',
- deprecated_for_removal=True),
# TODO(ynesenenko): Remove volume_services once liberty-eol happens.
cfg.BoolOpt('volume_services',
default=False,
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
new file mode 100644
index 0000000..c2dc3eb
--- /dev/null
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -0,0 +1,47 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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 six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class RoleAssignmentsClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def list_role_assignments(self, effective=False, **kwargs):
+ """List role assignments.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/?expanded=list-effective-role-assignments-detail
+
+ :param effective: If True, returns the effective assignments, including
+ any assignments gained by virtue of group membership.
+ """
+ url = 'role_assignments'
+ if kwargs:
+ # NOTE(rodrigods): "effective" is a key-only query parameter and
+ # is treated below.
+ if 'effective' in kwargs:
+ del kwargs['effective']
+ url += '?%s' % urllib.urlencode(kwargs)
+ if effective:
+ url += '&effective'
+
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 32bad8b..d359d4b 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -15,10 +15,12 @@
from tempest.lib.services.image.v2.image_members_client import \
ImageMembersClient
from tempest.lib.services.image.v2.images_client import ImagesClient
+from tempest.lib.services.image.v2.namespace_properties_client import \
+ NamespacePropertiesClient
from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
from tempest.lib.services.image.v2.schemas_client import SchemasClient
-__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacesClient',
- 'ResourceTypesClient', 'SchemasClient']
+__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacePropertiesClient',
+ 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
diff --git a/tempest/lib/services/image/v2/namespace_properties_client.py b/tempest/lib/services/image/v2/namespace_properties_client.py
new file mode 100644
index 0000000..1236b2b
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_properties_client.py
@@ -0,0 +1,91 @@
+# Copyright 2016 EasyStack.
+# 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class NamespacePropertiesClient(rest_client.RestClient):
+ api_version = "v2"
+
+ def list_namespace_properties(self, namespace):
+ """Lists property definitions in a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-properties
+ """
+ url = 'metadefs/namespaces/%s/properties' % namespace
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_namespace_property(self, namespace, **kwargs):
+ """Creates a property definition in a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-property
+ """
+ url = 'metadefs/namespaces/%s/properties' % namespace
+ data = json.dumps(kwargs)
+ resp, body = self.post(url, data)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_namespace_properties(self, namespace, property_name):
+ """Shows the definition for a property.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#show-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_namespace_properties(self, namespace, property_name, **kwargs):
+ """Updates a property definition.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ data = json.dumps(kwargs)
+ resp, body = self.put(url, data)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_namespace_property(self, namespace, property_name):
+ """Removes a property definition from a namespace.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#remove-property-definition
+ """
+ url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+ property_name)
+ resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/volume/v1/snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
index 1881078..3433e68 100644
--- a/tempest/lib/services/volume/v1/snapshots_client.py
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -25,8 +25,9 @@
def list_snapshots(self, detail=False, **params):
"""List all the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#listSnapshots
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-snapshots-with-details-v1
"""
url = 'snapshots'
if detail:
@@ -42,8 +43,9 @@
def show_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#showSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-details-v1
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -54,8 +56,9 @@
def create_snapshot(self, **kwargs):
"""Creates a new snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#createSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-snapshot-v1
"""
post_body = json.dumps({'snapshot': kwargs})
resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#deleteSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#delete-snapshot-v1
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -117,9 +121,9 @@
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-v1
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -130,9 +134,9 @@
def show_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- showSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-metadata-v1
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -143,9 +147,9 @@
def update_snapshot_metadata(self, snapshot_id, **kwargs):
"""Update metadata for the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-metadata-v1
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v1/types_client.py b/tempest/lib/services/volume/v1/types_client.py
index dce728d..4ae9935 100644
--- a/tempest/lib/services/volume/v1/types_client.py
+++ b/tempest/lib/services/volume/v1/types_client.py
@@ -38,8 +38,9 @@
def list_volume_types(self, **params):
"""List all the volume_types created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#listVolumeTypes
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-types-v1
"""
url = 'types'
if params:
@@ -53,8 +54,9 @@
def show_volume_type(self, volume_type_id):
"""Returns the details of a single volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#showVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#show-volume-type-v1
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -65,8 +67,9 @@
def create_volume_type(self, **kwargs):
"""Create volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#createVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-type-v1
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -77,8 +80,9 @@
def delete_volume_type(self, volume_type_id):
"""Deletes the Specified Volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v1.html#deleteVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#delete-volume-type-v1
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -131,8 +135,9 @@
def update_volume_type(self, volume_type_id, **kwargs):
"""Updates volume type name, description, and/or is_public.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-volume-type-v1
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -148,9 +153,9 @@
extra_spec_name: Name of the extra spec to be updated.
extra_spec: A dictionary of with key as extra_spec_name and the
updated value.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateVolumeTypeExtraSpecs
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-extra-specs-for-a-volume-type-v1
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
index cc98c91..7a25697 100644
--- a/tempest/lib/services/volume/v1/volumes_client.py
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -61,8 +61,9 @@
def create_volume(self, **kwargs):
"""Creates a new Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -73,8 +74,9 @@
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#update-volume
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -100,8 +102,9 @@
def attach_volume(self, volume_id, **kwargs):
"""Attaches a volume to a given instance on a given mountpoint.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#attachVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#attach-volume
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -156,8 +159,9 @@
def extend_volume(self, volume_id, **kwargs):
"""Extend a volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#extendVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#extend-volume
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -168,8 +172,9 @@
def reset_volume_status(self, volume_id, **kwargs):
"""Reset the Specified Volume's Status.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#resetVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#reset-volume-status
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -179,8 +184,9 @@
def create_volume_transfer(self, **kwargs):
"""Create a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-transfer
"""
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
@@ -199,8 +205,9 @@
def list_volume_transfers(self, **params):
"""List all the volume transfers created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-transfers
"""
url = 'os-volume-transfer'
if params:
@@ -219,8 +226,9 @@
def accept_volume_transfer(self, transfer_id, **kwargs):
"""Accept a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#acceptVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v1/#accept-volume-transfer
"""
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index 61f865d..ab5eefd 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -26,8 +26,9 @@
def create_backup(self, **kwargs):
"""Creates a backup of volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-blockstorage-v2.html#createBackup
"""
post_body = json.dumps({'backup': kwargs})
resp, body = self.post('backups', post_body)
@@ -38,8 +39,9 @@
def restore_backup(self, backup_id, **kwargs):
"""Restore volume from backup.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#restoreBackup
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-blockstorage-v2.html#restoreBackup
"""
post_body = json.dumps({'restore': kwargs})
resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index c84e557..6f51b51 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -25,8 +25,9 @@
def list_snapshots(self, detail=False, **params):
"""List all the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listSnapshots
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
"""
url = 'snapshots'
if detail:
@@ -42,8 +43,9 @@
def show_snapshot(self, snapshot_id):
"""Returns the details of a single snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -54,8 +56,9 @@
def create_snapshot(self, **kwargs):
"""Creates a new snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
"""
post_body = json.dumps({'snapshot': kwargs})
resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
def update_snapshot(self, snapshot_id, **kwargs):
"""Updates a snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -78,8 +82,9 @@
def delete_snapshot(self, snapshot_id):
"""Delete Snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#deleteSnapshot
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -129,9 +134,9 @@
def show_snapshot_metadata(self, snapshot_id):
"""Get metadata of the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- showSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -142,9 +147,9 @@
def update_snapshot_metadata(self, snapshot_id, **kwargs):
"""Update metadata for the snapshot.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateSnapshotMetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index d399e99..31597d7 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -39,8 +39,9 @@
def list_volume_types(self, **params):
"""List all the volume_types created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showVolumeTypes
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
"""
url = 'types'
if params:
@@ -54,8 +55,9 @@
def show_volume_type(self, volume_type_id):
"""Returns the details of a single volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#showVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -66,8 +68,9 @@
def create_volume_type(self, **kwargs):
"""Create volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -78,8 +81,9 @@
def delete_volume_type(self, volume_type_id):
"""Deletes the Specified Volume_type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#deleteVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -132,8 +136,9 @@
def update_volume_type(self, volume_type_id, **kwargs):
"""Updates volume type name, description, and/or is_public.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolumeType
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -149,9 +154,9 @@
extra_spec_name: Name of the extra spec to be updated.
extra_spec: A dictionary of with key as extra_spec_name and the
updated value.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- updateVolumeTypeExtraSpecs
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
@@ -163,9 +168,9 @@
def add_type_access(self, volume_type_id, **kwargs):
"""Adds volume type access for the given project.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #createVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
"""
post_body = json.dumps({'addProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -176,9 +181,9 @@
def remove_type_access(self, volume_type_id, **kwargs):
"""Removes volume type access for the given project.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #removeVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
"""
post_body = json.dumps({'removeProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -189,9 +194,9 @@
def list_type_access(self, volume_type_id):
"""Print access information about the given volume type.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#
- listVolumeTypeAccessExt
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
"""
url = 'types/%s/os-volume-type-access' % volume_type_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index b1930e1..ce97adb 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -62,8 +62,9 @@
def create_volume(self, **kwargs):
"""Creates a new Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -74,8 +75,9 @@
def update_volume(self, volume_id, **kwargs):
"""Updates the Specified Volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#updateVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -101,8 +103,9 @@
def attach_volume(self, volume_id, **kwargs):
"""Attaches a volume to a given instance on a given mountpoint.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#attachVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -157,8 +160,9 @@
def extend_volume(self, volume_id, **kwargs):
"""Extend a volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#extendVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -169,8 +173,9 @@
def reset_volume_status(self, volume_id, **kwargs):
"""Reset the Specified Volume's Status.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#resetVolume
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
"""
post_body = json.dumps({'os-reset_status': kwargs})
resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -180,8 +185,9 @@
def create_volume_transfer(self, **kwargs):
"""Create a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#createVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
"""
post_body = json.dumps({'transfer': kwargs})
resp, body = self.post('os-volume-transfer', post_body)
@@ -200,8 +206,9 @@
def list_volume_transfers(self, **params):
"""List all the volume transfers created.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
"""
url = 'os-volume-transfer'
if params:
@@ -220,8 +227,9 @@
def accept_volume_transfer(self, transfer_id, **kwargs):
"""Accept a volume transfer.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#acceptVolumeTransfer
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
"""
url = 'os-volume-transfer/%s/accept' % transfer_id
post_body = json.dumps({'accept': kwargs})
@@ -296,9 +304,9 @@
def update_volume_image_metadata(self, volume_id, **kwargs):
"""Update image metadata for the volume.
- Available params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #setVolumeimagemetadata
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
"""
post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
url = "volumes/%s/action" % (volume_id)
@@ -329,9 +337,9 @@
def show_backend_capabilities(self, host):
"""Shows capabilities for a storage back end.
- Output params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #showBackendCapabilities
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
"""
url = 'capabilities/%s' % host
resp, body = self.get(url)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8b86267..73544d9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -219,6 +219,10 @@
imageRef=None, volume_type=None):
if size is None:
size = CONF.volume.volume_size
+ if imageRef:
+ image = self.compute_images_client.show_image(imageRef)['image']
+ min_disk = image.get('minDisk')
+ size = max(size, min_disk)
if name is None:
name = data_utils.rand_name(self.__class__.__name__ + "-volume")
kwargs = {'display_name': name,
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index c66128d..3aab2b8 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -16,6 +16,7 @@
import json
import re
+from tempest.common import waiters
from tempest import config
from tempest import exceptions
from tempest.lib.common.utils import test_utils
@@ -134,3 +135,5 @@
self.verify_metadata_on_config_drive()
self.verify_networkdata_on_config_drive()
self.servers_client.delete_server(self.instance['id'])
+ waiters.wait_for_server_termination(
+ self.servers_client, self.instance['id'], ignore_error=False)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index 9b40b77..6e64a7d 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__init__.py
@@ -22,14 +22,14 @@
from tempest.lib.services.identity.v3.policies_client import PoliciesClient
from tempest.lib.services.identity.v3.projects_client import ProjectsClient
from tempest.lib.services.identity.v3.regions_client import RegionsClient
+from tempest.lib.services.identity.v3.role_assignments_client import \
+ RoleAssignmentsClient
from tempest.lib.services.identity.v3.roles_client import RolesClient
from tempest.lib.services.identity.v3.services_client import ServicesClient
from tempest.lib.services.identity.v3.token_client import V3TokenClient
from tempest.lib.services.identity.v3.trusts_client import TrustsClient
from tempest.lib.services.identity.v3.users_client import UsersClient
from tempest.services.identity.v3.json.domains_client import DomainsClient
-from tempest.services.identity.v3.json.role_assignments_client import \
- RoleAssignmentsClient
__all__ = ['CredentialsClient', 'EndPointsClient', 'GroupsClient',
'IdentityClient', 'InheritedRolesClient', 'PoliciesClient',
diff --git a/tempest/services/identity/v3/json/role_assignments_client.py b/tempest/services/identity/v3/json/role_assignments_client.py
deleted file mode 100644
index 9fd7736..0000000
--- a/tempest/services/identity/v3/json/role_assignments_client.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2016 Red Hat, Inc.
-#
-# 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 RoleAssignmentsClient(rest_client.RestClient):
- api_version = "v3"
-
- def list_user_project_effective_assignments(
- self, project_id, user_id):
- """List the effective role assignments for a user in a project."""
- resp, body = self.get(
- "role_assignments?scope.project.id=%s&user.id=%s&effective" %
- (project_id, user_id))
- self.expected_success(200, resp.status)
- body = json.loads(body)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
new file mode 100644
index 0000000..7d304c1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
@@ -0,0 +1,206 @@
+# Copyright 2016 Red Hat, Inc.
+#
+# 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.identity.v3 import role_assignments_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRoleAssignmentsClient(base.BaseServiceTest):
+
+ FAKE_USER_ID = "313234"
+ FAKE_GROUP_ID = "101112"
+
+ FAKE_ROLE1_ID = "123456"
+ FAKE_ROLE2_ID = "123457"
+
+ FAKE_PROJECT_ID = "456789"
+ FAKE_DOMAIN_ID = "102030"
+
+ FAKE_USER_PROJECT_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/users/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_USER_ID,
+ FAKE_ROLE2_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE2_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_GROUP_PROJECT_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_GROUP_ID,
+ FAKE_ROLE1_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "group": {
+ "id": FAKE_GROUP_ID
+ }
+ }
+
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/projects/"
+ "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+ FAKE_GROUP_ID,
+ FAKE_ROLE1_ID),
+ "membership": "http://example.com/identity/v3/groups/"
+ "%s/users/%s" % (FAKE_GROUP_ID, FAKE_USER_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "project": {
+ "id": FAKE_PROJECT_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_USER_DOMAIN_ASSIGNMENT = {
+ "links": {
+ "assignment": "http://example.com/identity/v3/domains/"
+ "%s/users/%s/roles/%s" % (FAKE_DOMAIN_ID,
+ FAKE_USER_ID,
+ FAKE_ROLE1_ID)
+ },
+ "role": {
+ "id": FAKE_ROLE1_ID
+ },
+ "scope": {
+ "domain": {
+ "id": FAKE_DOMAIN_ID
+ }
+ },
+ "user": {
+ "id": FAKE_USER_ID
+ }
+ }
+
+ FAKE_GROUP_PROJECT_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_GROUP_PROJECT_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.project.id=%s&group.id=%s&effective" % (
+ FAKE_PROJECT_ID, FAKE_GROUP_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_USER_PROJECT_ASSIGNMENT,
+ FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.project.id=%s&user.id=%s&effective" % (
+ FAKE_PROJECT_ID, FAKE_USER_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ FAKE_USER_DOMAIN_ASSIGNMENTS = {
+ "role_assignments": [
+ FAKE_USER_DOMAIN_ASSIGNMENT
+ ],
+ "links": {
+ "self": "http://example.com/identity/v3/role_assignments?"
+ "scope.domain.id=%s&user.id=%s&effective" % (
+ FAKE_DOMAIN_ID, FAKE_USER_ID),
+ "previous": None,
+ "next": None
+ }
+ }
+
+ def setUp(self):
+ super(TestRoleAssignmentsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = role_assignments_client.RoleAssignmentsClient(
+ fake_auth, 'identity', 'regionOne')
+
+ def _test_list_user_project_effective_assignments(self, bytes_body=False):
+ params = {'scope.project.id': self.FAKE_PROJECT_ID,
+ 'user.id': self.FAKE_USER_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS,
+ bytes_body,
+ effective=True,
+ **params)
+
+ def test_list_user_project_effective_assignments_with_str_body(self):
+ self._test_list_user_project_effective_assignments()
+
+ def test_list_user_project_effective_assignments_with_bytes_body(self):
+ self._test_list_user_project_effective_assignments(bytes_body=True)
+
+ def _test_list_group_project_assignments(self, bytes_body=False):
+ params = {'scope.project.id': self.FAKE_PROJECT_ID,
+ 'group.id': self.FAKE_GROUP_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+ bytes_body,
+ **params)
+
+ def test_list_group_project_assignments_with_str_body(self):
+ self._test_list_group_project_assignments()
+
+ def test_list_group_project_assignments_with_bytes_body(self):
+ self._test_list_group_project_assignments(bytes_body=True)
+
+ def _test_list_user_domain_assignments(self, bytes_body=False):
+ params = {'scope.domain.id': self.FAKE_DOMAIN_ID,
+ 'user.id': self.FAKE_USER_ID}
+ self.check_service_client_function(
+ self.client.list_role_assignments,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+ bytes_body,
+ **params)
+
+ def test_list_user_domain_assignments_with_str_body(self):
+ self._test_list_user_domain_assignments()
+
+ def test_list_user_domain_assignments_with_bytes_body(self):
+ self._test_list_user_domain_assignments(bytes_body=True)
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
deleted file mode 100644
index 05f9f3e..0000000
--- a/tempest/tests/test_negative_rest_client.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# (c) 2015 Deutsche Telekom AG
-# Copyright 2015 Red Hat, Inc.
-# Copyright 2015 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.
-
-import mock
-from oslotest import mockpatch
-
-from tempest.common import negative_rest_client
-from tempest import config
-from tempest.tests import base
-from tempest.tests import fake_config
-from tempest.tests.lib import fake_auth_provider
-
-
-class TestNegativeRestClient(base.TestCase):
-
- url = 'fake_endpoint'
-
- def setUp(self):
- super(TestNegativeRestClient, self).setUp()
- self.useFixture(fake_config.ConfigFixture())
- self.patchobject(config, 'TempestConfigPrivate',
- fake_config.FakePrivate)
- self.negative_rest_client = negative_rest_client.NegativeRestClient(
- fake_auth_provider.FakeAuthProvider(), None)
- self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
- '_log_request'))
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.post',
- return_value=(mock.Mock(), mock.Mock()))
- def test_post(self, mock_post):
- __, return_dict = self.negative_rest_client.send_request('POST',
- self.url,
- [], {})
- mock_post.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.get',
- return_value=(mock.Mock(), mock.Mock()))
- def test_get(self, mock_get):
- __, return_dict = self.negative_rest_client.send_request('GET',
- self.url,
- [])
- mock_get.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.delete',
- return_value=(mock.Mock(), mock.Mock()))
- def test_delete(self, mock_delete):
- __, return_dict = self.negative_rest_client.send_request('DELETE',
- self.url,
- [])
- mock_delete.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.patch',
- return_value=(mock.Mock(), mock.Mock()))
- def test_patch(self, mock_patch):
- __, return_dict = self.negative_rest_client.send_request('PATCH',
- self.url,
- [], {})
- mock_patch.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.put',
- return_value=(mock.Mock(), mock.Mock()))
- def test_put(self, mock_put):
- __, return_dict = self.negative_rest_client.send_request('PUT',
- self.url,
- [], {})
- mock_put.assert_called_once_with(self.url, {})
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.head',
- return_value=(mock.Mock(), mock.Mock()))
- def test_head(self, mock_head):
- __, return_dict = self.negative_rest_client.send_request('HEAD',
- self.url,
- [])
- mock_head.assert_called_once_with(self.url)
-
- @mock.patch('tempest.lib.common.rest_client.RestClient.copy',
- return_value=(mock.Mock(), mock.Mock()))
- def test_copy(self, mock_copy):
- __, return_dict = self.negative_rest_client.send_request('COPY',
- self.url,
- [])
- mock_copy.assert_called_once_with(self.url)
-
- def test_other(self):
- self.assertRaises(AssertionError,
- self.negative_rest_client.send_request,
- 'OTHER', self.url, [])
diff --git a/test-requirements.txt b/test-requirements.txt
index 3260915..475fb16 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-hacking>=0.12.0,<0.13 # Apache-2.0
+hacking<0.13,>=0.12.0 # Apache-2.0
# needed for doc build
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0