Merge "Move `call_until_true` to tempest/lib"
diff --git a/README.rst b/README.rst
index 13f4f61..3c0463b 100644
--- a/README.rst
+++ b/README.rst
@@ -124,6 +124,9 @@
Release Versioning
------------------
+`Tempest Release Notes <http://docs.openstack.org/releasenotes/tempest>`_
+shows what changes have been released on each version.
+
Tempest's released versions are broken into 2 sets of information. Depending on
how you intend to consume tempest you might need
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index e4b104f..6c55015 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -135,7 +135,10 @@
It is worth pointing out that each set of credentials in the accounts.yaml
should have a unique project. This is required to provide proper isolation
to the tests using the credentials, and failure to do this will likely cause
-unexpected failures in some tests.
+unexpected failures in some tests. Also, ensure that these projects and users
+used do not have any pre-existing resources created. Tempest assumes all
+tenants it's using are empty and may sporadically fail if there are unexpected
+resources present.
When the keystone in the target cloud requires domain scoped tokens to
perform admin actions, all pre-provisioned admin users must have a role
diff --git a/releasenotes/notes/add-new-identity-clients-3c3afd674a395bde.yaml b/releasenotes/notes/add-new-identity-clients-3c3afd674a395bde.yaml
index b8dcfce..3ec8b56 100644
--- a/releasenotes/notes/add-new-identity-clients-3c3afd674a395bde.yaml
+++ b/releasenotes/notes/add-new-identity-clients-3c3afd674a395bde.yaml
@@ -1,10 +1,13 @@
---
features:
- |
- Define identity service clients as libraries
+ Define identity service clients as libraries.
The following identity service clients are defined as library interface,
so the other projects can use these modules as stable libraries without
any maintenance changes.
* endpoints_client(v3)
* policies_client (v3)
+ * regions_client(v3)
+ * services_client(v3)
+ * projects_client(v3)
diff --git a/setup.cfg b/setup.cfg
index 91ede20..50bf891 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -17,6 +17,7 @@
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
+ Programming Language :: Python :: 3.5
[files]
packages =
diff --git a/tempest/api/baremetal/admin/base.py b/tempest/api/baremetal/admin/base.py
index 2d3f190..ac5986c 100644
--- a/tempest/api/baremetal/admin/base.py
+++ b/tempest/api/baremetal/admin/base.py
@@ -99,7 +99,7 @@
def create_chassis(cls, description=None):
"""Wrapper utility for creating test chassis.
- :param description: A description of the chassis. if not supplied,
+ :param description: A description of the chassis. If not supplied,
a random value will be generated.
:return: Created chassis.
@@ -114,6 +114,7 @@
memory_mb=4096):
"""Wrapper utility for creating test baremetal nodes.
+ :param chassis_id: The unique identifier of the chassis.
:param cpu_arch: CPU architecture of the node. Default: x86.
:param cpus: Number of CPUs. Default: 8.
:param local_gb: Disk size. Default: 10.
@@ -133,6 +134,7 @@
def create_port(cls, node_id, address, extra=None, uuid=None):
"""Wrapper utility for creating test ports.
+ :param node_id: The unique identifier of the node.
:param address: MAC address of the port.
:param extra: Meta data of the port. If not supplied, an empty
dictionary will be created.
@@ -150,7 +152,7 @@
def delete_chassis(cls, chassis_id):
"""Deletes a chassis having the specified UUID.
- :param uuid: The unique identifier of the chassis.
+ :param chassis_id: The unique identifier of the chassis.
:return: Server response.
"""
@@ -166,7 +168,7 @@
def delete_node(cls, node_id):
"""Deletes a node having the specified UUID.
- :param uuid: The unique identifier of the node.
+ :param node_id: The unique identifier of the node.
:return: Server response.
"""
@@ -182,7 +184,7 @@
def delete_port(cls, port_id):
"""Deletes a port having the specified UUID.
- :param uuid: The unique identifier of the port.
+ :param port_id: The unique identifier of the port.
:return: Server response.
"""
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 5ff5d58..db6e682 100755
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -34,6 +34,7 @@
cls.client = cls.os_adm.servers_client
cls.non_adm_client = cls.servers_client
cls.flavors_client = cls.os_adm.flavors_client
+ cls.quotas_client = cls.os_adm.quotas_client
@classmethod
def resource_setup(cls):
@@ -64,8 +65,8 @@
self.useFixture(fixtures.LockFixture('compute_quotas'))
flavor_name = data_utils.rand_name("flavor")
flavor_id = self._get_unused_flavor_id()
- quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
- ['quota_set'])
+ quota_set = self.quotas_client.show_quota_set(
+ self.tenant_id)['quota_set']
ram = int(quota_set['ram'])
if ram == -1:
raise self.skipException("default ram quota set is -1,"
@@ -93,8 +94,8 @@
flavor_name = data_utils.rand_name("flavor")
flavor_id = self._get_unused_flavor_id()
ram = 512
- quota_set = (self.quotas_client.show_default_quota_set(self.tenant_id)
- ['quota_set'])
+ quota_set = self.quotas_client.show_quota_set(
+ self.tenant_id)['quota_set']
vcpus = int(quota_set['cores'])
if vcpus == -1:
raise self.skipException("default cores quota set is -1,"
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index e8df52d..78f0db4 100755
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -139,7 +139,7 @@
hostname = linux_client.get_hostname()
msg = ('Failed while verifying servername equals hostname. Expected '
'hostname "%s" but got "%s".' % (self.name, hostname))
- self.assertEqual(self.name, hostname, msg)
+ self.assertEqual(self.name.lower(), hostname, msg)
@test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
def test_create_server_with_scheduler_hint_group(self):
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 55d847c..26cbb090 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -271,14 +271,9 @@
msg = 'fixed_network_name needs to be configured to run this test'
raise self.skipException(msg)
self.s1 = self.client.show_server(self.s1['id'])['server']
- for addr_spec in self.s1['addresses'][self.fixed_network_name]:
- ip = addr_spec['addr']
- if addr_spec['version'] == 4:
- params = {'ip': ip}
- break
- else:
- msg = "Skipped until bug 1450859 is resolved"
- raise self.skipException(msg)
+ # Get first ip address inspite of v4 or v6
+ addr_spec = self.s1['addresses'][self.fixed_network_name][0]
+ params = {'ip': addr_spec['addr']}
body = self.client.list_servers(**params)
servers = body['servers']
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 4a5bab5..062e920 100755
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -270,6 +270,9 @@
'SHUTOFF')
self.client.resize_server(self.server_id, self.flavor_ref_alt)
+ # NOTE(jlk): Explicitly delete the server to get a new one for later
+ # tests. Avoids resize down race issues.
+ self.addCleanup(self.delete_server, self.server_id)
waiters.wait_for_server_status(self.client, self.server_id,
'VERIFY_RESIZE')
@@ -285,10 +288,6 @@
# NOTE(mriedem): tearDown requires the server to be started.
self.client.start_server(self.server_id)
- # NOTE(jlk): Explicitly delete the server to get a new one for later
- # tests. Avoids resize down race issues.
- self.addCleanup(self.delete_server, self.server_id)
-
@test.idempotent_id('1499262a-9328-4eda-9068-db1ac57498d2')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
@@ -309,6 +308,9 @@
# values after a resize is reverted
self.client.resize_server(self.server_id, self.flavor_ref_alt)
+ # NOTE(zhufl): Explicitly delete the server to get a new one for later
+ # tests. Avoids resize down race issues.
+ self.addCleanup(self.delete_server, self.server_id)
waiters.wait_for_server_status(self.client, self.server_id,
'VERIFY_RESIZE')
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 78e53b4..e7a46b0 100755
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -144,6 +144,18 @@
cls.resource_types_client = cls.os.resource_types_client
cls.schemas_client = cls.os.schemas_client
+ def create_namespace(cls, namespace_name=None, visibility='public',
+ description='Tempest', protected=False,
+ **kwargs):
+ if not namespace_name:
+ namespace_name = data_utils.rand_name('test-ns')
+ kwargs.setdefault('display_name', namespace_name)
+ namespace = cls.namespaces_client.create_namespace(
+ namespace=namespace_name, visibility=visibility,
+ description=description, protected=protected, **kwargs)
+ cls.addCleanup(cls.namespaces_client.delete_namespace, namespace_name)
+ return namespace
+
class BaseV2MemberImageTest(BaseV2ImageTest):
diff --git a/tempest/api/image/v2/test_images_metadefs_resource_types.py b/tempest/api/image/v2/test_images_metadefs_resource_types.py
new file mode 100644
index 0000000..a5143a1
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_resource_types.py
@@ -0,0 +1,54 @@
+# Copyright 2016 Ericsson India Global Services Private Limited
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.image import base
+from tempest import test
+
+
+class MetadataResourceTypesTest(base.BaseV2ImageTest):
+ """Test the Metadata definition ressource types basic functionality"""
+
+ @test.idempotent_id('6f358a4e-5ef0-11e6-a795-080027d0d606')
+ def test_basic_meta_def_resource_type_association(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']
+ # 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)
+ self.assertEqual(body['name'], resource_name)
+ # NOTE(raiesmh08): Here intentionally I have not added addcleanup
+ # method for resource type dissociation because its a metadata add and
+ # being cleaned as soon as namespace is cleaned at test case level.
+ # When namespace cleans, resource type associaion will automatically
+ # clean without any error or dependency.
+
+ # List resource type associations and validate creation
+ rs_type_associations = [
+ rs_type_association['name'] for rs_type_association in
+ self.resource_types_client.list_resource_type_association(
+ namespace['namespace'])['resource_type_associations']]
+ self.assertIn(resource_name, rs_type_associations)
+ # Delete resource type association
+ self.resource_types_client.delete_resource_type_association(
+ namespace['namespace'], resource_name)
+ # List resource type associations and validate deletion
+ rs_type_associations = [
+ rs_type_association['name'] for rs_type_association in
+ self.resource_types_client.list_resource_type_association(
+ namespace['namespace'])['resource_type_associations']]
+ self.assertNotIn(resource_name, rs_type_associations)
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index baeaa0c..2686af2 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -26,6 +26,13 @@
credentials = ['primary', 'alt', 'admin']
@classmethod
+ def skip_checks(cls):
+ super(FloatingIPAdminTestJSON, cls).skip_checks()
+ if not test.is_extension_enabled('router', 'network'):
+ msg = "router extension not enabled."
+ raise cls.skipException(msg)
+
+ @classmethod
def setup_clients(cls):
super(FloatingIPAdminTestJSON, cls).setup_clients()
cls.alt_floating_ips_client = cls.alt_manager.floating_ips_client
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 9e7c795..5c67d68 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -270,7 +270,7 @@
"""Wrapper utility that returns a test metering label."""
body = cls.admin_metering_labels_client.create_metering_label(
description=description,
- name=data_utils.rand_name("metering-label"))
+ name=name)
metering_label = body['metering_label']
cls.metering_labels.append(metering_label)
return metering_label
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
new file mode 100644
index 0000000..8a21853
--- /dev/null
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -0,0 +1,79 @@
+# Copyright 2016 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 operator
+
+from tempest.api.volume import base
+from tempest import test
+
+
+class BackendsCapabilitiesAdminV2TestsJSON(base.BaseVolumeAdminTest):
+
+ CAPABILITIES = ('namespace',
+ 'vendor_name',
+ 'volume_backend_name',
+ 'pool_name',
+ 'driver_version',
+ 'storage_protocol',
+ 'display_name',
+ 'description',
+ 'visibility',
+ 'properties')
+
+ @classmethod
+ def resource_setup(cls):
+ super(BackendsCapabilitiesAdminV2TestsJSON, cls).resource_setup()
+ # Get host list, formation: host@backend-name
+ cls.hosts = [
+ pool['name'] for pool in
+ cls.admin_volume_client.show_pools()['pools']
+ ]
+
+ @test.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
+ def test_get_capabilities_backend(self):
+ # Test backend properties
+ backend = self.admin_volume_client.show_backend_capabilities(
+ self.hosts[0])
+
+ # Verify getting capabilities parameters from a backend
+ for key in self.CAPABILITIES:
+ self.assertIn(key, backend)
+
+ @test.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
+ def test_compare_volume_stats_values(self):
+ # Test values comparison between show_backend_capabilities
+ # to show_pools
+ VOLUME_STATS = ('vendor_name',
+ 'volume_backend_name',
+ 'storage_protocol')
+
+ # Get list backend capabilities using show_pools
+ cinder_pools = [
+ pool['capabilities'] for pool in
+ self.admin_volume_client.show_pools(detail=True)['pools']
+ ]
+
+ # Get list backends capabilities using show_backend_capabilities
+ capabilities = [
+ self.admin_volume_client.show_backend_capabilities(
+ host=host) for host in self.hosts
+ ]
+
+ # Returns a tuple of VOLUME_STATS values
+ expected_list = map(operator.itemgetter(*VOLUME_STATS),
+ cinder_pools)
+ observed_list = map(operator.itemgetter(*VOLUME_STATS),
+ capabilities)
+ self.assertEqual(expected_list, observed_list)
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 755365d..165874b 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -79,7 +79,7 @@
services = (self.admin_volume_services_client.list_services(
host=self.host_name, binary=self.binary_name))['services']
- self.assertEqual(1, len(services))
+ self.assertNotEqual(0, len(services))
self.assertEqual(self.host_name, _get_host(services[0]['host']))
self.assertEqual(self.binary_name, services[0]['binary'])
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index f38a068..7529dc2 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -23,6 +23,12 @@
class VolumesCloneTest(base.BaseVolumeTest):
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesCloneTest, cls).skip_checks()
+ if not CONF.volume_feature_enabled.clone:
+ raise cls.skipException("Cinder volume clones are disabled")
+
@test.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
def test_create_from_volume(self):
# Creates a volume from another volume passing a size different from
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index ee51e00..d1bedb4 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -24,6 +24,12 @@
class VolumesCloneTest(base.BaseVolumeTest):
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesCloneTest, cls).skip_checks()
+ if not CONF.volume_feature_enabled.clone:
+ raise cls.skipException("Cinder volume clones are disabled")
+
@test.idempotent_id('9adae371-a257-43a5-459a-dc7c88e66e0e')
def test_create_from_volume_decreasing_size(self):
# Creates a volume from another volume passing a size different from
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index c501ffc..60a35b0 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -42,11 +42,13 @@
super(VolumesV2ListTestJSON, cls).resource_setup()
# Create 3 test volumes
- cls.volume_id_list = []
cls.metadata = {'Type': 'work'}
+ # NOTE(zhufl): When using pre-provisioned credentials, the project
+ # may have volumes other than those created below.
+ existing_volumes = cls.client.list_volumes()['volumes']
+ cls.volume_id_list = [vol['id'] for vol in existing_volumes]
for i in range(3):
volume = cls.create_volume(metadata=cls.metadata)
- volume = cls.client.show_volume(volume['id'])['volume']
cls.volume_id_list.append(volume['id'])
@test.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index c9b9db1..04c9645 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -401,9 +401,18 @@
except lib_exc.NotFound:
LOG.warning("user with name: %s not found for delete" %
creds.username)
+ # NOTE(zhufl): Only when neutron's security_group ext is
+ # enabled, _cleanup_default_secgroup will not raise error. But
+ # here cannot use test.is_extension_enabled for it will cause
+ # "circular dependency". So here just use try...except to
+ # ensure tenant deletion without big changes.
try:
if CONF.service_available.neutron:
self._cleanup_default_secgroup(creds.tenant_id)
+ except lib_exc.NotFound:
+ LOG.warning("failed to cleanup tenant %s's secgroup" %
+ creds.tenant_name)
+ try:
self.creds_client.delete_project(creds.tenant_id)
except lib_exc.NotFound:
LOG.warning("tenant with name: %s not found for delete" %
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 1857a43..83aa405 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -689,6 +689,10 @@
"""Credentials are equal if attributes in self.ATTRIBUTES are equal"""
return str(self) == str(other)
+ def __ne__(self, other):
+ """Contrary to the __eq__"""
+ return not self.__eq__(other)
+
def __getattr__(self, key):
# If an attribute is set, __getattr__ is not invoked
# If an attribute is not set, and it is a known one, return None
diff --git a/tempest/lib/base.py b/tempest/lib/base.py
index f687343..33a32ee 100644
--- a/tempest/lib/base.py
+++ b/tempest/lib/base.py
@@ -42,7 +42,7 @@
def setUp(self):
super(BaseTestCase, self).setUp()
if not self.setUpClassCalled:
- raise RuntimeError("setUpClass does not calls the super's"
+ raise RuntimeError("setUpClass does not calls the super's "
"setUpClass in the "
+ self.__class__.__name__)
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 5c5d3c2..8507f8a 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -235,8 +235,8 @@
raise TypeError("'read_code' must be an int instead of (%s)"
% type(read_code))
- assert_msg = ("This function only allowed to use for HTTP status"
- "codes which explicitly defined in the RFC 7231 & 4918."
+ assert_msg = ("This function only allowed to use for HTTP status "
+ "codes which explicitly defined in the RFC 7231 & 4918. "
"{0} is not a defined Success Code!"
).format(expected_code)
if isinstance(expected_code, list):
@@ -893,11 +893,11 @@
cls=JSONSCHEMA_VALIDATOR,
format_checker=FORMAT_CHECKER)
except jsonschema.ValidationError as ex:
- msg = ("HTTP response body is invalid (%s)") % ex
+ msg = ("HTTP response body is invalid (%s)" % ex)
raise exceptions.InvalidHTTPResponseBody(msg)
else:
if body:
- msg = ("HTTP response body should not exist (%s)") % body
+ msg = ("HTTP response body should not exist (%s)" % body)
raise exceptions.InvalidHTTPResponseBody(msg)
# Check the header of a response
@@ -908,7 +908,7 @@
cls=JSONSCHEMA_VALIDATOR,
format_checker=FORMAT_CHECKER)
except jsonschema.ValidationError as ex:
- msg = ("HTTP response header is invalid (%s)") % ex
+ msg = ("HTTP response header is invalid (%s)" % ex)
raise exceptions.InvalidHTTPResponseHeader(msg)
diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py
index a831dbd..c13f41a 100644
--- a/tempest/lib/common/ssh.py
+++ b/tempest/lib/common/ssh.py
@@ -77,7 +77,7 @@
self.username, self.host)
return ssh
except (EOFError,
- socket.error,
+ socket.error, socket.timeout,
paramiko.SSHException) as e:
if self._is_timed_out(_start_time):
LOG.exception("Failed to establish authenticated ssh"
@@ -121,7 +121,6 @@
channel.fileno() # Register event pipe
channel.exec_command(cmd)
channel.shutdown_write()
- exit_status = channel.recv_exit_status()
# If the executing host is linux-based, poll the channel
if self._can_system_poll():
@@ -162,6 +161,8 @@
out_data = out_data.decode(encoding)
err_data = err_data.decode(encoding)
+ exit_status = channel.recv_exit_status()
+
if 0 != exit_status:
raise exceptions.SSHExecCommandFailed(
command=cmd, exit_status=exit_status,
diff --git a/tempest/lib/services/compute/images_client.py b/tempest/lib/services/compute/images_client.py
index da8a61e..3dc3749 100644
--- a/tempest/lib/services/compute/images_client.py
+++ b/tempest/lib/services/compute/images_client.py
@@ -61,7 +61,6 @@
def show_image(self, image_id):
"""Return the details of a single image."""
resp, body = self.get("images/%s" % image_id)
- self.expected_success(200, resp.status)
body = json.loads(body)
self.validate_response(schema.get_image, resp, body)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/roles_client.py b/tempest/lib/services/identity/v2/roles_client.py
index 15c8834..aaa75f1 100644
--- a/tempest/lib/services/identity/v2/roles_client.py
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -66,7 +66,7 @@
Available params: see http://developer.openstack.org/
api-ref-identity-v2-ext.html#deleteRole
"""
- resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
+ resp, body = self.delete('OS-KSADM/roles/%s' % role_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/projects_client.py b/tempest/lib/services/identity/v3/projects_client.py
similarity index 97%
rename from tempest/services/identity/v3/json/projects_client.py
rename to tempest/lib/services/identity/v3/projects_client.py
index 97e43df..ce2f38d 100644
--- a/tempest/services/identity/v3/json/projects_client.py
+++ b/tempest/lib/services/identity/v3/projects_client.py
@@ -68,6 +68,6 @@
def delete_project(self, project_id):
"""Delete a project."""
- resp, body = self.delete('projects/%s' % str(project_id))
+ resp, body = self.delete('projects/%s' % project_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/regions_client.py b/tempest/lib/services/identity/v3/regions_client.py
similarity index 100%
rename from tempest/services/identity/v3/json/regions_client.py
rename to tempest/lib/services/identity/v3/regions_client.py
diff --git a/tempest/services/identity/v3/json/services_client.py b/tempest/lib/services/identity/v3/services_client.py
similarity index 100%
rename from tempest/services/identity/v3/json/services_client.py
rename to tempest/lib/services/identity/v3/services_client.py
diff --git a/tempest/lib/services/image/v2/resource_types_client.py b/tempest/lib/services/image/v2/resource_types_client.py
index 1349c63..8f2a977 100644
--- a/tempest/lib/services/image/v2/resource_types_client.py
+++ b/tempest/lib/services/image/v2/resource_types_client.py
@@ -22,8 +22,54 @@
api_version = "v2"
def list_resource_types(self):
+ """Lists all resource types.
+
+ Available params: see http://developer.openstack.org/
+ api-ref/image/v2/metadefs-index.html?expanded=#
+ list-resource-types
+ """
url = 'metadefs/resource_types'
resp, body = self.get(url)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+
+ def create_resource_type_association(self, namespace_id, **kwargs):
+ """Creates a resource type association in given namespace.
+
+ Available params: see http://developer.openstack.org/
+ api-ref/image/v2/metadefs-index.html?expanded=#
+ create-resource-type-association
+ """
+ url = 'metadefs/namespaces/%s/resource_types' % namespace_id
+ 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 list_resource_type_association(self, namespace_id):
+ """Lists resource type associations in given namespace.
+
+ Available params: see http://developer.openstack.org/
+ api-ref/image/v2/metadefs-index.html?expanded=#
+ list-resource-type-associations
+ """
+ url = 'metadefs/namespaces/%s/resource_types' % namespace_id
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_resource_type_association(self, namespace_id, resource_name):
+ """Removes resource type association in given namespace.
+
+ Available params: see http://developer.openstack.org/
+ api-ref/image/v2/metadefs-index.html?expanded=#
+ remove-resource-type-association
+ """
+ url = 'metadefs/namespaces/%s/resource_types/%s' % (namespace_id,
+ resource_name)
+ resp, _ = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/network/agents_client.py b/tempest/lib/services/network/agents_client.py
index c5d4c66..9bdf090 100644
--- a/tempest/lib/services/network/agents_client.py
+++ b/tempest/lib/services/network/agents_client.py
@@ -44,7 +44,7 @@
# link to api-site.
# LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526670
uri = '/agents/%s/l3-routers' % agent_id
- return self.create_resource(uri, kwargs)
+ return self.create_resource(uri, kwargs, expect_empty_body=True)
def delete_router_from_l3_agent(self, agent_id, router_id):
uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
@@ -65,4 +65,4 @@
# link to api-site.
# LP: https://bugs.launchpad.net/openstack-api-site/+bug/1526212
uri = '/agents/%s/dhcp-networks' % agent_id
- return self.create_resource(uri, kwargs)
+ return self.create_resource(uri, kwargs, expect_empty_body=True)
diff --git a/tempest/lib/services/network/base.py b/tempest/lib/services/network/base.py
index 620e0f1..b6f9c91 100644
--- a/tempest/lib/services/network/base.py
+++ b/tempest/lib/services/network/base.py
@@ -63,6 +63,8 @@
# body. Otherwise we returns the body as it is.
if not expect_empty_body:
body = json.loads(body)
+ else:
+ body = None
self.expected_success(201, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -75,5 +77,7 @@
# body. Otherwise we returns the body as it is.
if not expect_empty_body:
body = json.loads(body)
+ else:
+ body = None
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index c060c61..45c38f6 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -22,7 +22,7 @@
class BaremetalBasicOps(manager.BaremetalScenarioTest):
- """This smoke test tests the pxe_ssh Ironic driver.
+ """This test tests the pxe_ssh Ironic driver.
It follows this basic set of operations:
* Creates a keypair
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index e4b699e..a89147a 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -58,7 +58,7 @@
security_groups = [{'name': security_group['name']}]
network, subnet, router = self.create_networks()
public_network_id = CONF.network.public_network_id
- server_name = data_utils.rand_name('server-smoke')
+ server_name = data_utils.rand_name('server')
server = self.create_server(
name=server_name,
networks=[{'uuid': network['id']}],
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index 6ad8ef2..c77c78d 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__init__.py
@@ -14,20 +14,20 @@
from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient
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.services_client import ServicesClient
from tempest.lib.services.identity.v3.token_client import V3TokenClient
from tempest.services.identity.v3.json.credentials_client import \
CredentialsClient
from tempest.services.identity.v3.json.domains_client import DomainsClient
from tempest.services.identity.v3.json.groups_client import GroupsClient
from tempest.services.identity.v3.json.identity_client import IdentityClient
-from tempest.services.identity.v3.json.projects_client import ProjectsClient
-from tempest.services.identity.v3.json.regions_client import RegionsClient
from tempest.services.identity.v3.json.roles_client import RolesClient
-from tempest.services.identity.v3.json.services_client import ServicesClient
from tempest.services.identity.v3.json.trusts_client import TrustsClient
from tempest.services.identity.v3.json.users_clients import UsersClient
-__all__ = ['EndPointsClient', 'PoliciesClient', 'V3TokenClient',
+__all__ = ['EndPointsClient', 'PoliciesClient', 'ProjectsClient',
+ 'RegionsClient', 'ServicesClient', 'V3TokenClient',
'CredentialsClient', 'DomainsClient', 'GroupsClient',
- 'IdentityClient', 'ProjectsClient', 'RegionsClient', 'RolesClient',
- 'ServicesClient', 'TrustsClient', 'UsersClient', ]
+ 'IdentityClient', 'RolesClient', 'TrustsClient', 'UsersClient', ]
diff --git a/tempest/services/identity/v3/json/domains_client.py b/tempest/services/identity/v3/json/domains_client.py
index d129a0a..fe929a5 100644
--- a/tempest/services/identity/v3/json/domains_client.py
+++ b/tempest/services/identity/v3/json/domains_client.py
@@ -38,7 +38,7 @@
def delete_domain(self, domain_id):
"""Deletes a domain."""
- resp, body = self.delete('domains/%s' % str(domain_id))
+ resp, body = self.delete('domains/%s' % domain_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/groups_client.py b/tempest/services/identity/v3/json/groups_client.py
index 628b3e1..3674496 100644
--- a/tempest/services/identity/v3/json/groups_client.py
+++ b/tempest/services/identity/v3/json/groups_client.py
@@ -73,7 +73,7 @@
def delete_group(self, group_id):
"""Delete a group."""
- resp, body = self.delete('groups/%s' % str(group_id))
+ resp, body = self.delete('groups/%s' % group_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v3/json/roles_client.py b/tempest/services/identity/v3/json/roles_client.py
index bdb0490..aab203f 100644
--- a/tempest/services/identity/v3/json/roles_client.py
+++ b/tempest/services/identity/v3/json/roles_client.py
@@ -34,7 +34,7 @@
def show_role(self, role_id):
"""GET a Role."""
- resp, body = self.get('roles/%s' % str(role_id))
+ resp, body = self.get('roles/%s' % role_id)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
@@ -53,14 +53,14 @@
api-ref-identity-v3.html#updateRole
"""
post_body = json.dumps({'role': kwargs})
- resp, body = self.patch('roles/%s' % str(role_id), post_body)
+ resp, body = self.patch('roles/%s' % role_id, post_body)
self.expected_success(200, resp.status)
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
def delete_role(self, role_id):
"""Delete a role."""
- resp, body = self.delete('roles/%s' % str(role_id))
+ resp, body = self.delete('roles/%s' % role_id)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index 0988373..a7db30e 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -42,12 +42,6 @@
self.expected_success(201, resp.status)
return resp, body
- def update_object(self, container, object_name, data):
- """Upload data to replace current storage object."""
- resp, body = self.create_object(container, object_name, data)
- self.expected_success(201, resp.status)
- return resp, body
-
def delete_object(self, container, object_name, params=None):
"""Delete storage object."""
url = "%s/%s" % (str(container), str(object_name))
diff --git a/tempest/services/volume/base/admin/base_types_client.py b/tempest/services/volume/base/admin/base_types_client.py
index 2effaae..83870ae 100755
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ b/tempest/services/volume/base/admin/base_types_client.py
@@ -202,42 +202,3 @@
"/types/%s/encryption/provider" % volume_type_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
-
- 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
- """
- post_body = json.dumps({'addProjectAccess': kwargs})
- url = 'types/%s/action' % volume_type_id
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- 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
- """
- post_body = json.dumps({'removeProjectAccess': kwargs})
- url = 'types/%s/action' % volume_type_id
- resp, body = self.post(url, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- 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
- """
- url = 'types/%s/os-volume-type-access' % volume_type_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_backups_client.py b/tempest/services/volume/base/base_backups_client.py
index fc247a9..a57e628 100644
--- a/tempest/services/volume/base/base_backups_client.py
+++ b/tempest/services/volume/base/base_backups_client.py
@@ -51,13 +51,13 @@
def delete_backup(self, backup_id):
"""Delete a backup of volume."""
- resp, body = self.delete('backups/%s' % (str(backup_id)))
+ resp, body = self.delete('backups/%s' % backup_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
def show_backup(self, backup_id):
"""Returns the details of a single backup."""
- url = "backups/%s" % str(backup_id)
+ url = "backups/%s" % backup_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
diff --git a/tempest/services/volume/base/base_qos_client.py b/tempest/services/volume/base/base_qos_client.py
index 2d9f02a..e00ac68 100644
--- a/tempest/services/volume/base/base_qos_client.py
+++ b/tempest/services/volume/base/base_qos_client.py
@@ -82,7 +82,7 @@
def delete_qos(self, qos_id, force=False):
"""Delete the specified QoS specification."""
resp, body = self.delete(
- "qos-specs/%s?force=%s" % (str(qos_id), force))
+ "qos-specs/%s?force=%s" % (qos_id, force))
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -96,7 +96,7 @@
def show_qos(self, qos_id):
"""Get the specified QoS specification."""
- url = "qos-specs/%s" % str(qos_id)
+ url = "qos-specs/%s" % qos_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -128,7 +128,7 @@
def associate_qos(self, qos_id, vol_type_id):
"""Associate the specified QoS with specified volume-type."""
- url = "qos-specs/%s/associate" % str(qos_id)
+ url = "qos-specs/%s/associate" % qos_id
url += "?vol_type_id=%s" % vol_type_id
resp, body = self.get(url)
self.expected_success(202, resp.status)
@@ -136,7 +136,7 @@
def show_association_qos(self, qos_id):
"""Get the association of the specified QoS specification."""
- url = "qos-specs/%s/associations" % str(qos_id)
+ url = "qos-specs/%s/associations" % qos_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -144,7 +144,7 @@
def disassociate_qos(self, qos_id, vol_type_id):
"""Disassociate the specified QoS with specified volume-type."""
- url = "qos-specs/%s/disassociate" % str(qos_id)
+ url = "qos-specs/%s/disassociate" % qos_id
url += "?vol_type_id=%s" % vol_type_id
resp, body = self.get(url)
self.expected_success(202, resp.status)
@@ -152,7 +152,7 @@
def disassociate_all_qos(self, qos_id):
"""Disassociate the specified QoS with all associations."""
- url = "qos-specs/%s/disassociate_all" % str(qos_id)
+ url = "qos-specs/%s/disassociate_all" % qos_id
resp, body = self.get(url)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_snapshots_client.py b/tempest/services/volume/base/base_snapshots_client.py
index 7a8e12b..38a6dc7 100755
--- a/tempest/services/volume/base/base_snapshots_client.py
+++ b/tempest/services/volume/base/base_snapshots_client.py
@@ -45,7 +45,7 @@
Available params: see http://developer.openstack.org/
api-ref-blockstorage-v2.html#showSnapshot
"""
- url = "snapshots/%s" % str(snapshot_id)
+ url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -81,7 +81,7 @@
Available params: see http://developer.openstack.org/
api-ref-blockstorage-v2.html#deleteSnapshot
"""
- resp, body = self.delete("snapshots/%s" % str(snapshot_id))
+ resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -112,7 +112,7 @@
# Bug https://bugs.launchpad.net/openstack-api-site/+bug/1532645
post_body = json.dumps({'os-update_snapshot_status': kwargs})
- url = 'snapshots/%s/action' % str(snapshot_id)
+ url = 'snapshots/%s/action' % snapshot_id
resp, body = self.post(url, post_body)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -120,7 +120,7 @@
def create_snapshot_metadata(self, snapshot_id, metadata):
"""Create metadata for the snapshot."""
put_body = json.dumps({'metadata': metadata})
- url = "snapshots/%s/metadata" % str(snapshot_id)
+ url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.post(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -133,7 +133,7 @@
api-ref-blockstorage-v2.html#
showSnapshotMetadata
"""
- url = "snapshots/%s/metadata" % str(snapshot_id)
+ url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -147,7 +147,7 @@
updateSnapshotMetadata
"""
put_body = json.dumps(kwargs)
- url = "snapshots/%s/metadata" % str(snapshot_id)
+ url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.put(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -160,7 +160,7 @@
# link to api-site.
# LP: https://bugs.launchpad.net/openstack-api-site/+bug/1529064
put_body = json.dumps(kwargs)
- url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+ url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
resp, body = self.put(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -168,7 +168,7 @@
def delete_snapshot_metadata_item(self, snapshot_id, id):
"""Delete metadata item for the snapshot."""
- url = "snapshots/%s/metadata/%s" % (str(snapshot_id), str(id))
+ url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
resp, body = self.delete(url)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/base/base_volumes_client.py b/tempest/services/volume/base/base_volumes_client.py
index d694c53..8130089 100755
--- a/tempest/services/volume/base/base_volumes_client.py
+++ b/tempest/services/volume/base/base_volumes_client.py
@@ -63,7 +63,11 @@
return rest_client.ResponseBody(resp, body)
def show_pools(self, detail=False):
- # List all the volumes pools (hosts)
+ """List all the volumes pools (hosts).
+
+ Output params: see http://developer.openstack.org/
+ api-ref-blockstorage-v2.html#listPools
+ """
url = 'scheduler-stats/get_pools'
if detail:
url += '?detail=True'
@@ -73,9 +77,22 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
+ 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
+ """
+ url = 'capabilities/%s' % host
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
def show_volume(self, volume_id):
"""Returns the details of a single volume."""
- url = "volumes/%s" % str(volume_id)
+ url = "volumes/%s" % volume_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -109,7 +126,7 @@
def delete_volume(self, volume_id):
"""Deletes the Specified Volume."""
- resp, body = self.delete("volumes/%s" % str(volume_id))
+ resp, body = self.delete("volumes/%s" % volume_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -201,22 +218,6 @@
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
- def volume_begin_detaching(self, volume_id):
- """Volume Begin Detaching."""
- # ref cinder/api/contrib/volume_actions.py#L158
- post_body = json.dumps({'os-begin_detaching': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def volume_roll_detaching(self, volume_id):
- """Volume Roll Detaching."""
- # cinder/api/contrib/volume_actions.py#L170
- post_body = json.dumps({'os-roll_detaching': {}})
- resp, body = self.post('volumes/%s/action' % volume_id, post_body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
def create_volume_transfer(self, **kwargs):
"""Create a volume transfer.
@@ -231,7 +232,7 @@
def show_volume_transfer(self, transfer_id):
"""Returns the details of a volume transfer."""
- url = "os-volume-transfer/%s" % str(transfer_id)
+ url = "os-volume-transfer/%s" % transfer_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -253,7 +254,7 @@
def delete_volume_transfer(self, transfer_id):
"""Delete a volume transfer."""
- resp, body = self.delete("os-volume-transfer/%s" % str(transfer_id))
+ resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -288,7 +289,7 @@
def create_volume_metadata(self, volume_id, metadata):
"""Create metadata for the volume."""
put_body = json.dumps({'metadata': metadata})
- url = "volumes/%s/metadata" % str(volume_id)
+ url = "volumes/%s/metadata" % volume_id
resp, body = self.post(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -296,7 +297,7 @@
def show_volume_metadata(self, volume_id):
"""Get metadata of the volume."""
- url = "volumes/%s/metadata" % str(volume_id)
+ url = "volumes/%s/metadata" % volume_id
resp, body = self.get(url)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -305,7 +306,7 @@
def update_volume_metadata(self, volume_id, metadata):
"""Update metadata for the volume."""
put_body = json.dumps({'metadata': metadata})
- url = "volumes/%s/metadata" % str(volume_id)
+ url = "volumes/%s/metadata" % volume_id
resp, body = self.put(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -314,7 +315,7 @@
def update_volume_metadata_item(self, volume_id, id, meta_item):
"""Update metadata item for the volume."""
put_body = json.dumps({'meta': meta_item})
- url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ url = "volumes/%s/metadata/%s" % (volume_id, id)
resp, body = self.put(url, put_body)
body = json.loads(body)
self.expected_success(200, resp.status)
@@ -322,7 +323,7 @@
def delete_volume_metadata_item(self, volume_id, id):
"""Delete metadata item for the volume."""
- url = "volumes/%s/metadata/%s" % (str(volume_id), str(id))
+ url = "volumes/%s/metadata/%s" % (volume_id, id)
resp, body = self.delete(url)
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/v2/json/admin/types_client.py b/tempest/services/volume/v2/json/admin/types_client.py
index ecf5131..f76e8dc 100644
--- a/tempest/services/volume/v2/json/admin/types_client.py
+++ b/tempest/services/volume/v2/json/admin/types_client.py
@@ -13,9 +13,51 @@
# 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
from tempest.services.volume.base.admin import base_types_client
class TypesClient(base_types_client.BaseTypesClient):
"""Client class to send CRUD Volume V2 API requests"""
api_version = "v2"
+
+ 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
+ """
+ post_body = json.dumps({'addProjectAccess': kwargs})
+ url = 'types/%s/action' % volume_type_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ 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
+ """
+ post_body = json.dumps({'removeProjectAccess': kwargs})
+ url = 'types/%s/action' % volume_type_id
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ 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
+ """
+ url = 'types/%s/os-volume-type-access' % volume_type_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index b7cc05d..e7b9a4c 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -27,13 +27,13 @@
v2_tenants_client
from tempest.lib.services.identity.v2 import token_client as v2_token_client
from tempest.lib.services.identity.v2 import users_client as v2_users_client
+from tempest.lib.services.identity.v3 import projects_client as \
+ v3_projects_client
from tempest.lib.services.identity.v3 import token_client as v3_token_client
from tempest.lib.services.network import routers_client
from tempest.services.identity.v2.json import identity_client as v2_iden_client
from tempest.services.identity.v3.json import domains_client
from tempest.services.identity.v3.json import identity_client as v3_iden_client
-from tempest.services.identity.v3.json import projects_client as \
- v3_projects_client
from tempest.services.identity.v3.json import roles_client as v3_roles_client
from tempest.services.identity.v3.json import users_clients as v3_users_client
from tempest.tests import base
diff --git a/tempest/tests/lib/services/identity/v3/test_projects_client.py b/tempest/tests/lib/services/identity/v3/test_projects_client.py
new file mode 100644
index 0000000..6ffbcde
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_projects_client.py
@@ -0,0 +1,178 @@
+# 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 projects_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestProjectsClient(base.BaseServiceTest):
+ FAKE_CREATE_PROJECT = {
+ "project": {
+ "description": "My new project",
+ "domain_id": "default",
+ "enabled": True,
+ "is_domain": False,
+ "name": "myNewProject"
+ }
+ }
+
+ FAKE_PROJECT_INFO = {
+ "project": {
+ "is_domain": False,
+ "description": None,
+ "domain_id": "default",
+ "enabled": True,
+ "id": "0c4e939acacf4376bdcd1129f1a054ad",
+ "links": {
+ "self": "http://example.com/identity/v3/projects/0c4e" +
+ "939acacf4376bdcd1129f1a054ad"
+ },
+ "name": "admin",
+ "parent_id": "default"
+ }
+ }
+
+ FAKE_LIST_PROJECTS = {
+ "links": {
+ "next": None,
+ "previous": None,
+ "self": "http://example.com/identity/v3/projects"
+ },
+ "projects": [
+ {
+ "is_domain": False,
+ "description": None,
+ "domain_id": "default",
+ "enabled": True,
+ "id": "0c4e939acacf4376bdcd1129f1a054ad",
+ "links": {
+ "self": "http://example.com/identity/v3/projects" +
+ "/0c4e939acacf4376bdcd1129f1a054ad"
+ },
+ "name": "admin",
+ "parent_id": None
+ },
+ {
+ "is_domain": False,
+ "description": None,
+ "domain_id": "default",
+ "enabled": True,
+ "id": "0cbd49cbf76d405d9c86562e1d579bd3",
+ "links": {
+ "self": "http://example.com/identity/v3/projects" +
+ "/0cbd49cbf76d405d9c86562e1d579bd3"
+ },
+ "name": "demo",
+ "parent_id": None
+ },
+ {
+ "is_domain": False,
+ "description": None,
+ "domain_id": "default",
+ "enabled": True,
+ "id": "2db68fed84324f29bb73130c6c2094fb",
+ "links": {
+ "self": "http://example.com/identity/v3/projects" +
+ "/2db68fed84324f29bb73130c6c2094fb"
+ },
+ "name": "swifttenanttest2",
+ "parent_id": None
+ },
+ {
+ "is_domain": False,
+ "description": None,
+ "domain_id": "default",
+ "enabled": True,
+ "id": "3d594eb0f04741069dbbb521635b21c7",
+ "links": {
+ "self": "http://example.com/identity/v3/projects" +
+ "/3d594eb0f04741069dbbb521635b21c7"
+ },
+ "name": "service",
+ "parent_id": None
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestProjectsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = projects_client.ProjectsClient(fake_auth,
+ 'identity',
+ 'regionOne')
+
+ def _test_create_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_project,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_PROJECT,
+ bytes_body,
+ name=self.FAKE_CREATE_PROJECT["project"]["name"],
+ status=201)
+
+ def _test_show_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_project,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_PROJECT_INFO,
+ bytes_body,
+ project_id="0c4e939acacf4376bdcd1129f1a054ad")
+
+ def _test_list_projects(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_projects,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_PROJECTS,
+ bytes_body)
+
+ def _test_update_project(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_project,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_PROJECT_INFO,
+ bytes_body,
+ project_id="0c4e939acacf4376bdcd1129f1a054ad")
+
+ def test_create_project_with_str_body(self):
+ self._test_create_project()
+
+ def test_create_project_with_bytes_body(self):
+ self._test_create_project(bytes_body=True)
+
+ def test_show_project_with_str_body(self):
+ self._test_show_project()
+
+ def test_show_project_with_bytes_body(self):
+ self._test_show_project(bytes_body=True)
+
+ def test_list_projects_with_str_body(self):
+ self._test_list_projects()
+
+ def test_list_projects_with_bytes_body(self):
+ self._test_list_projects(bytes_body=True)
+
+ def test_update_project_with_str_body(self):
+ self._test_update_project()
+
+ def test_update_project_with_bytes_body(self):
+ self._test_update_project(bytes_body=True)
+
+ def test_delete_project(self):
+ self.check_service_client_function(
+ self.client.delete_project,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ project_id="0c4e939acacf4376bdcd1129f1a054ad",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_regions_client.py b/tempest/tests/lib/services/identity/v3/test_regions_client.py
new file mode 100644
index 0000000..a2cb86f
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_regions_client.py
@@ -0,0 +1,125 @@
+# 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 regions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRegionsClient(base.BaseServiceTest):
+ FAKE_CREATE_REGION = {
+ "region": {
+ "description": "My subregion",
+ "id": "RegionOneSubRegion",
+ "parent_region_id": "RegionOne"
+ }
+ }
+
+ FAKE_REGION_INFO = {
+ "region": {
+ "description": "My subregion 3",
+ "id": "RegionThree",
+ "links": {
+ "self": "http://example.com/identity/v3/regions/RegionThree"
+ },
+ "parent_region_id": "RegionOne"
+ }
+ }
+
+ FAKE_LIST_REGIONS = {
+ "links": {
+ "next": None,
+ "previous": None,
+ "self": "http://example.com/identity/v3/regions"
+ },
+ "regions": [
+ {
+ "description": "",
+ "id": "RegionOne",
+ "links": {
+ "self": "http://example.com/identity/v3/regions/RegionOne"
+ },
+ "parent_region_id": None
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestRegionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = regions_client.RegionsClient(fake_auth, 'identity',
+ 'regionOne')
+
+ def _test_create_region(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_region,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_REGION,
+ bytes_body,
+ status=201)
+
+ def _test_show_region(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_region,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_REGION_INFO,
+ bytes_body,
+ region_id="RegionThree")
+
+ def _test_list_regions(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_regions,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_REGIONS,
+ bytes_body)
+
+ def _test_update_region(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_region,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_REGION_INFO,
+ bytes_body,
+ region_id="RegionThree")
+
+ def test_create_region_with_str_body(self):
+ self._test_create_region()
+
+ def test_create_region_with_bytes_body(self):
+ self._test_create_region(bytes_body=True)
+
+ def test_show_region_with_str_body(self):
+ self._test_show_region()
+
+ def test_show_region_with_bytes_body(self):
+ self._test_show_region(bytes_body=True)
+
+ def test_list_regions_with_str_body(self):
+ self._test_list_regions()
+
+ def test_list_regions_with_bytes_body(self):
+ self._test_list_regions(bytes_body=True)
+
+ def test_update_region_with_str_body(self):
+ self._test_update_region()
+
+ def test_update_region_with_bytes_body(self):
+ self._test_update_region(bytes_body=True)
+
+ def test_delete_region(self):
+ self.check_service_client_function(
+ self.client.delete_region,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ region_id="RegionThree",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_services_client.py b/tempest/tests/lib/services/identity/v3/test_services_client.py
new file mode 100644
index 0000000..f87fcce
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_services_client.py
@@ -0,0 +1,149 @@
+# 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 services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+ FAKE_CREATE_SERVICE = {
+ "service": {
+ "type": "compute",
+ "name": "compute2",
+ "description": "Compute service 2"
+ }
+ }
+
+ FAKE_SERVICE_INFO = {
+ "service": {
+ "description": "Keystone Identity Service",
+ "enabled": True,
+ "id": "686766",
+ "links": {
+ "self": "http://example.com/identity/v3/services/686766"
+ },
+ "name": "keystone",
+ "type": "identity"
+ }
+ }
+
+ FAKE_LIST_SERVICES = {
+ "links": {
+ "next": None,
+ "previous": None,
+ "self": "http://example.com/identity/v3/services"
+ },
+ "services": [
+ {
+ "description": "Nova Compute Service",
+ "enabled": True,
+ "id": "1999c3",
+ "links": {
+ "self": "http://example.com/identity/v3/services/1999c3"
+ },
+ "name": "nova",
+ "type": "compute"
+ },
+ {
+ "description": "Cinder Volume Service V2",
+ "enabled": True,
+ "id": "392166",
+ "links": {
+ "self": "http://example.com/identity/v3/services/392166"
+ },
+ "name": "cinderv2",
+ "type": "volumev2"
+ },
+ {
+ "description": "Neutron Service",
+ "enabled": True,
+ "id": "4fe41a",
+ "links": {
+ "self": "http://example.com/identity/v3/services/4fe41a"
+ },
+ "name": "neutron",
+ "type": "network"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestServicesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = services_client.ServicesClient(fake_auth, 'identity',
+ 'regionOne')
+
+ def _test_create_service(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_service,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_SERVICE,
+ bytes_body,
+ status=201)
+
+ def _test_show_service(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_service,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_SERVICE_INFO,
+ bytes_body,
+ service_id="686766")
+
+ def _test_list_services(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_services,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_SERVICES,
+ bytes_body)
+
+ def _test_update_service(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_service,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_SERVICE_INFO,
+ bytes_body,
+ service_id="686766")
+
+ def test_create_service_with_str_body(self):
+ self._test_create_service()
+
+ def test_create_service_with_bytes_body(self):
+ self._test_create_service(bytes_body=True)
+
+ def test_show_service_with_str_body(self):
+ self._test_show_service()
+
+ def test_show_service_with_bytes_body(self):
+ self._test_show_service(bytes_body=True)
+
+ def test_list_services_with_str_body(self):
+ self._test_list_services()
+
+ def test_list_services_with_bytes_body(self):
+ self._test_list_services(bytes_body=True)
+
+ def test_update_service_with_str_body(self):
+ self._test_update_service()
+
+ def test_update_service_with_bytes_body(self):
+ self._test_update_service(bytes_body=True)
+
+ def test_delete_service(self):
+ self.check_service_client_function(
+ self.client.delete_service,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ service_id="686766",
+ status=204)
diff --git a/tox.ini b/tox.ini
index 111557f..a621492 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py34,py27
+envlist = pep8,py35,py34,py27
minversion = 2.3.1
skipsdist = True