Merge "Remove duplication of skip_tracker"
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index adbd2dc..b516055 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -1,13 +1,83 @@
-===================================
-How To Implement Microversion Tests
-===================================
+=================================
+Microversion Testing With Tempest
+=================================
-Tempest provides stable interfaces to test API Microversion.
-For Details, see: `API Microversion testing Framework`_
-This document explains how to implement Microversion tests using those
-interfaces.
+Many OpenStack Services provide their APIs with `microversion`_
+support and want to test them in Tempest.
-.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+.. _microversion: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html
+
+This document covers how to test microversions for each project and
+whether tests should live in Tempest or on project side.
+
+Tempest Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+APIs microversions for any OpenStack service grow rapidly and
+testing each and every microversion in Tempest is not feasible and
+efficient way.
+Also not every API microversion changes the complete system behavior
+and many of them only change the API or DB layer to accept and return more
+data on API.
+
+Tempest is an integration test suite, but not all API microversion testing fall under this category.
+As a result, Tempest mainly covers integration test cases for microversions, Other testing coverage
+for microversion should be hosted on project side as functional tests or via Tempest plugin as per
+project guidelines.
+
+.. note:: Integration tests are those tests which involve more than one service to
+ verify the expected behavior by single or combination of API requests.
+ If a test is just to verify the API behavior as success and failure cases
+ or verify its expected response object, then it does not fall under integration
+ tests.
+
+Tempest will cover only integration testing of applicable microversions with
+below exceptions:
+
+ #. Test covers a feature which is important for interoperability. This covers tests requirement
+ from Defcore.
+ #. Test needed to fill Schema gaps.
+ Tempest validates API responses with defined JSON schema. API responses can be different on
+ each microversion and the JSON schemas need to be defined separately for the microversion.
+ While implementing new integration tests for a specific microversion, there
+ may be a gap in the JSON schemas (caused by previous microversions) implemented
+ in Tempest.
+ Filling that gap while implementing the new integration test cases is not efficient due to
+ many reasons:
+
+ * Hard to review
+ * Sync between multiple integration tests patches which try to fill the same schema gap at same
+ time
+ * Might delay the microversion change on project side where project team wants Tempest
+ tests to verify the results.
+
+ Tempest will allow to fill the schema gaps at the end of each cycle, or more
+ often if required.
+ Schema gap can be filled with testing those with a minimal set of tests. Those
+ tests might not be integration tests and might be already covered on project
+ side also.
+ This exception is needed because:
+
+ * Allow to create microversion response schema in Tempest at the same time that projects are
+ implementing their API microversions. This will make implementation easier for adding
+ required tests before a new microversion change can be merged in the corresponding project
+ and hence accelerate the development of microversions.
+ * New schema must be verified by at least one test case which exercises such schema.
+
+ For example:
+ If any projects implemented 4 API microversion say- v2.3, v2.4, v2.5, v2.6
+ Assume microversion v2.3, v2.4, v2.6 change the API Response which means Tempest
+ needs to add JSON schema for v2.3, v2.4, v2.6.
+ In that case if only 1 or 2 tests can verify all new schemas then we do not need
+ separate tests for each new schemas. In worst case, we have to add 3 separate tests.
+ #. Test covers service behavior at large scale with involvement of more deep layer like hypervisor
+ etc not just API/DB layer. This type of tests will be added case by case basis and
+ with project team consultation about why it cannot be covered on project side and worth to test
+ in Tempest.
+
+Project Scope For Microversion Testing
+""""""""""""""""""""""""""""""""""""""
+All microversions testing which are not covered under Tempest as per above section, should be
+tested on project side as functional tests or as Tempest plugin as per project decision.
Configuration options for Microversion
@@ -36,6 +106,14 @@
How To Implement Microversion Tests
"""""""""""""""""""""""""""""""""""
+Tempest provides stable interfaces to test API Microversion.
+For Details, see: `API Microversion testing Framework`_
+This document explains how to implement Microversion tests using those
+interfaces.
+
+.. _API Microversion testing Framework: http://docs.openstack.org/developer/tempest/library/api_microversion_testing.html
+
+
Step1: Add skip logic based on configured Microversion range
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
index 63ff61c..8488fb1 100644
--- a/doc/source/write_tests.rst
+++ b/doc/source/write_tests.rst
@@ -188,7 +188,7 @@
+-------------------+---------------------+
By default cls.os is available since it is allocated in the base tempest test
-class. (located in tempest/test.py) If your TestCase inherits from a different
+class (located in tempest/test.py). If your TestCase inherits from a different
direct parent class (it'll still inherit from the BaseTestCase, just not
directly) be sure to check if that class overrides allocated credentials.
@@ -199,10 +199,10 @@
automatically setup when a tenant is created. Since tempest needs isolated
tenants to function properly it also needs to handle network allocation. By
default the base test class will allocate a network, subnet, and router
-automatically. (this depends on the configured credential provider, for more
-details see: :ref:`tempest_conf_network_allocation`) However, there are
-situations where you do no need all of these resources allocated. (or your
-TestCase inherits from a class that overrides the default in tempest/test.py)
+automatically (this depends on the configured credential provider, for more
+details see: :ref:`tempest_conf_network_allocation`). However, there are
+situations where you do no need all of these resources allocated (or your
+TestCase inherits from a class that overrides the default in tempest/test.py).
There is a class level mechanism to override this allocation and specify which
resources you need. To do this you need to call `cls.set_network_resources()`
in the `setup_credentials()` method before the `super()`. For example::
@@ -256,8 +256,8 @@
The primary interface with which you interact with both credentials and
API clients is the client manager object. These objects are created
-automatically by the base test class as part of credential setup. (for more
-details see the previous :ref:`credentials` section) Each manager object is
+automatically by the base test class as part of credential setup (for more
+details see the previous :ref:`credentials` section). Each manager object is
initialized with a set of credentials and has each client object already setup
to use that set of credentials for making all the API requests. Each client is
accessible as a top level attribute on the manager object. So to start making
@@ -281,9 +281,9 @@
Credentials Objects
-------------------
-In certain cases you need direct access to the credentials. (the most common
+In certain cases you need direct access to the credentials (the most common
use case would be an API request that takes a user or project id in the request
-body) If you're in a situation where you need to access this you'll need to
+body). If you're in a situation where you need to access this you'll need to
access the ``credentials`` object which is allocated from the configured
credential provider in the base test class. This is accessible from the manager
object via the manager's ``credentials`` attribute. For example::
diff --git a/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
new file mode 100644
index 0000000..8e85d3a
--- /dev/null
+++ b/releasenotes/notes/add-list-volume-transfers-with-detail-to-transfers-client-80169bf78cf4fa66.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add list volume transfers with details API to v2 transfers_client library.
+ This feature enables the possibility to list volume transfers with details.
diff --git a/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
new file mode 100644
index 0000000..2b63402
--- /dev/null
+++ b/releasenotes/notes/deprecate-config-forbid_global_implied_dsr-e64cfa66e6e3ded5.yaml
@@ -0,0 +1,5 @@
+---
+deprecations:
+ - The config option ``forbid_global_implied_dsr`` from the ``IdentityFeature``
+ group is now deprecated. This feature flag was introduced to support
+ testing of old OpenStack versions which are not supported anymore.
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 750f9f9..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -42,9 +42,7 @@
@decorators.idempotent_id('162ba316-f18b-4987-8c0c-fd9140cd63ed')
def test_tenant_delete_by_unauthorized_user(self):
# Non-administrator user should not be able to delete a tenant
- tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.delete_tenant,
tenant['id'])
@@ -53,9 +51,7 @@
@decorators.idempotent_id('e450db62-2e9d-418f-893a-54772d6386b1')
def test_tenant_delete_request_without_token(self):
# Request to delete a tenant without a valid token should fail
- tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
@@ -75,10 +71,7 @@
def test_tenant_create_duplicate(self):
# Tenant names should be unique
tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- tenant1_id = body.get('id')
-
- self.addCleanup(self.tenants_client.delete_tenant, tenant1_id)
+ self.setup_test_tenant(name=tenant_name)
self.assertRaises(lib_exc.Conflict, self.tenants_client.create_tenant,
name=tenant_name)
@@ -131,9 +124,7 @@
@decorators.idempotent_id('41704dc5-c5f7-4f79-abfa-76e6fedc570b')
def test_tenant_update_by_unauthorized_user(self):
# Non-administrator user should not be able to update a tenant
- tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.update_tenant,
tenant['id'])
@@ -142,9 +133,7 @@
@decorators.idempotent_id('7a421573-72c7-4c22-a98e-ce539219c657')
def test_tenant_update_request_without_token(self):
# Request to update a tenant without a valid token should fail
- tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
token = self.client.auth_provider.get_token()
self.client.delete_token(token)
self.assertRaises(lib_exc.Unauthorized,
diff --git a/tempest/api/identity/admin/v2/test_tenants.py b/tempest/api/identity/admin/v2/test_tenants.py
index 6b7413c..ad9b983 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -15,7 +15,6 @@
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
@@ -26,12 +25,7 @@
# Create several tenants and delete them
tenants = []
for _ in range(3):
- tenant_name = data_utils.rand_name(name='tenant-new')
- tenant = self.tenants_client.create_tenant(
- name=tenant_name)['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
tenants.append(tenant)
tenant_ids = [tn['id'] for tn in tenants]
body = self.tenants_client.list_tenants()['tenants']
@@ -48,14 +42,8 @@
@decorators.idempotent_id('d25e9f24-1310-4d29-b61b-d91299c21d6d')
def test_tenant_create_with_description(self):
# Create tenant with a description
- tenant_name = data_utils.rand_name(name='tenant')
tenant_desc = data_utils.rand_name(name='desc')
- body = self.tenants_client.create_tenant(name=tenant_name,
- description=tenant_desc)
- tenant = body['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant(description=tenant_desc)
tenant_id = tenant['id']
desc1 = tenant['description']
self.assertEqual(desc1, tenant_desc, 'Description should have '
@@ -69,13 +57,7 @@
@decorators.idempotent_id('670bdddc-1cd7-41c7-b8e2-751cfb67df50')
def test_tenant_create_enabled(self):
# Create a tenant that is enabled
- tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(name=tenant_name,
- enabled=True)
- tenant = body['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant(enabled=True)
tenant_id = tenant['id']
en1 = tenant['enabled']
self.assertTrue(en1, 'Enable should be True in response')
@@ -87,13 +69,7 @@
@decorators.idempotent_id('3be22093-b30f-499d-b772-38340e5e16fb')
def test_tenant_create_not_enabled(self):
# Create a tenant that is not enabled
- tenant_name = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(name=tenant_name,
- enabled=False)
- tenant = body['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant(enabled=False)
tenant_id = tenant['id']
en1 = tenant['enabled']
self.assertEqual('false', str(en1).lower(),
@@ -108,14 +84,9 @@
def test_tenant_update_name(self):
# Update name attribute of a tenant
t_name1 = data_utils.rand_name(name='tenant')
- body = self.tenants_client.create_tenant(name=t_name1)['tenant']
- tenant = body
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
-
- t_id = body['id']
- resp1_name = body['name']
+ tenant = self.setup_test_tenant(name=t_name1)
+ t_id = tenant['id']
+ resp1_name = tenant['name']
t_name2 = data_utils.rand_name(name='tenant2')
body = self.tenants_client.update_tenant(t_id, name=t_name2)['tenant']
@@ -134,15 +105,8 @@
@decorators.idempotent_id('859fcfe1-3a03-41ef-86f9-b19a47d1cd87')
def test_tenant_update_desc(self):
# Update description attribute of a tenant
- t_name = data_utils.rand_name(name='tenant')
t_desc = data_utils.rand_name(name='desc')
- body = self.tenants_client.create_tenant(name=t_name,
- description=t_desc)
- tenant = body['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
-
+ tenant = self.setup_test_tenant(description=t_desc)
t_id = tenant['id']
resp1_desc = tenant['description']
@@ -164,14 +128,8 @@
@decorators.idempotent_id('8fc8981f-f12d-4c66-9972-2bdcf2bc2e1a')
def test_tenant_update_enable(self):
# Update the enabled attribute of a tenant
- t_name = data_utils.rand_name(name='tenant')
t_en = False
- body = self.tenants_client.create_tenant(name=t_name, enabled=t_en)
- tenant = body['tenant']
- # Add the tenant to the cleanup list
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.tenants_client.delete_tenant, tenant['id'])
-
+ tenant = self.setup_test_tenant(enabled=t_en)
t_id = tenant['id']
resp1_en = tenant['enabled']
diff --git a/tempest/api/identity/admin/v2/test_tokens.py b/tempest/api/identity/admin/v2/test_tokens.py
index b288705..b4c9389 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -26,10 +26,7 @@
user_name = data_utils.rand_name(name='user')
user_password = data_utils.rand_password()
# first:create a tenant
- tenant_name = data_utils.rand_name(name='tenant')
- tenant = self.tenants_client.create_tenant(name=tenant_name)['tenant']
- # Delete the tenant at the end of the test
- self.addCleanup(self.tenants_client.delete_tenant, tenant['id'])
+ tenant = self.setup_test_tenant()
# second:create a user
user = self.create_test_user(name=user_name,
password=user_password,
@@ -70,16 +67,10 @@
# Create a couple tenants.
tenant1_name = data_utils.rand_name(name='tenant')
- tenant1 = self.tenants_client.create_tenant(
- name=tenant1_name)['tenant']
- # Delete the tenant at the end of the test
- self.addCleanup(self.tenants_client.delete_tenant, tenant1['id'])
+ tenant1 = self.setup_test_tenant(name=tenant1_name)
tenant2_name = data_utils.rand_name(name='tenant')
- tenant2 = self.tenants_client.create_tenant(
- name=tenant2_name)['tenant']
- # Delete the tenant at the end of the test
- self.addCleanup(self.tenants_client.delete_tenant, tenant2['id'])
+ tenant2 = self.setup_test_tenant(name=tenant2_name)
# Create a role
role = self.setup_test_role()
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 0f17c92..aff3238 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -152,11 +152,13 @@
user = self.create_test_user(tenantId=tenant['id'], password=password)
return user
- def setup_test_tenant(self):
+ def setup_test_tenant(self, **kwargs):
"""Set up a test tenant."""
- tenant = self.projects_client.create_tenant(
- name=data_utils.rand_name('test_tenant'),
- description=data_utils.rand_name('desc'))['tenant']
+ if 'name' not in kwargs:
+ kwargs['name'] = data_utils.rand_name('test_tenant')
+ if 'description' not in kwargs:
+ kwargs['description'] = data_utils.rand_name('desc')
+ tenant = self.projects_client.create_tenant(**kwargs)['tenant']
# Delete the tenant at the end of the test
self.addCleanup(
test_utils.call_and_ignore_notfound_exc,
diff --git a/tempest/api/image/admin/__init__.py b/tempest/api/image/admin/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/image/admin/__init__.py
+++ /dev/null
diff --git a/tempest/api/image/admin/v2/__init__.py b/tempest/api/image/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/image/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
deleted file mode 100644
index fc5ed79..0000000
--- a/tempest/api/image/admin/v2/test_images.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2015 Red Hat, Inc.
-# 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 six
-import testtools
-
-from tempest.api.image import base
-from tempest import config
-from tempest.lib.common.utils import data_utils
-from tempest.lib import decorators
-from tempest.lib import exceptions as lib_exc
-
-CONF = config.CONF
-
-
-class BasicAdminOperationsImagesTest(base.BaseV2ImageAdminTest):
- """Here we test admin operations of images"""
-
- @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
- 'deactivate-image is not available.')
- @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
- def test_admin_deactivate_reactivate_image(self):
- # Create image by non-admin tenant
- image_name = data_utils.rand_name('image')
- image = self.create_image(name=image_name,
- container_format='bare',
- disk_format='raw',
- visibility='private')
- # upload an image file
- content = data_utils.random_bytes()
- image_file = six.BytesIO(content)
- self.client.store_image_file(image['id'], image_file)
- # deactivate image
- self.admin_client.deactivate_image(image['id'])
- body = self.client.show_image(image['id'])
- self.assertEqual("deactivated", body['status'])
- # non-admin user unable to download deactivated image
- self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
- image['id'])
- # reactivate image
- self.admin_client.reactivate_image(image['id'])
- body = self.client.show_image(image['id'])
- self.assertEqual("active", body['status'])
- # non-admin user able to download image after reactivation by admin
- body = self.client.show_image_file(image['id'])
- self.assertEqual(content, body.data)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 8310001..a5d9773 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -18,11 +18,14 @@
import six
+import testtools
+
from oslo_log import log as logging
from tempest.api.image import base
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -125,6 +128,40 @@
self.assertEqual(image['id'], body['id'])
self.assertEqual(new_image_name, body['name'])
+ @testtools.skipUnless(CONF.image_feature_enabled.deactivate_image,
+ 'deactivate-image is not available.')
+ @decorators.idempotent_id('951ebe01-969f-4ea9-9898-8a3f1f442ab0')
+ def test_deactivate_reactivate_image(self):
+ # Create image
+ image_name = data_utils.rand_name('image')
+ image = self.create_image(name=image_name,
+ container_format='bare',
+ disk_format='raw',
+ visibility='private')
+
+ # Upload an image file
+ content = data_utils.random_bytes()
+ image_file = six.BytesIO(content)
+ self.client.store_image_file(image['id'], image_file)
+
+ # Deactivate image
+ self.client.deactivate_image(image['id'])
+ body = self.client.show_image(image['id'])
+ self.assertEqual("deactivated", body['status'])
+
+ # User unable to download deactivated image
+ self.assertRaises(lib_exc.Forbidden, self.client.show_image_file,
+ image['id'])
+
+ # Reactivate image
+ self.client.reactivate_image(image['id'])
+ body = self.client.show_image(image['id'])
+ self.assertEqual("active", body['status'])
+
+ # User able to download image after reactivation
+ body = self.client.show_image_file(image['id'])
+ self.assertEqual(content, body.data)
+
class ListUserImagesTest(base.BaseV2ImageTest):
"""Here we test the listing of image information"""
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index e7460af..85b2472 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -27,11 +27,7 @@
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
- _agent_mode = 'legacy'
-
- """
- Tests the following operations in the Neutron API using the REST client for
- Neutron:
+ """Tests the following operations in the Neutron API:
List routers that the given L3 agent is hosting.
List L3 agents hosting the given router.
@@ -52,14 +48,10 @@
@classmethod
def resource_setup(cls):
super(L3AgentSchedulerTestJSON, cls).resource_setup()
- body = cls.admin_agents_client.list_agents()
- agents = body['agents']
+ agents = cls.admin_agents_client.list_agents(
+ agent_type=AGENT_TYPE)['agents']
for agent in agents:
- # TODO(armax): falling back on default _agent_mode can be
- # dropped as soon as Icehouse is dropped.
- agent_mode = (
- agent['configurations'].get('agent_mode', cls._agent_mode))
- if agent['agent_type'] == AGENT_TYPE and agent_mode in AGENT_MODES:
+ if agent['configurations']['agent_mode'] in AGENT_MODES:
cls.agent = agent
break
else:
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 6784a55..f955d34 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -82,11 +82,13 @@
"""Test that a user cannot modify or remove a quota on its account."""
# Not able to remove quota
- self.assertRaises(lib_exc.Forbidden,
- self.account_client.create_account_metadata,
- {"Quota-Bytes": ""})
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.account_client.create_update_or_delete_account_metadata,
+ create_update_metadata={"Quota-Bytes": ""})
# Not able to modify quota
- self.assertRaises(lib_exc.Forbidden,
- self.account_client.create_account_metadata,
- {"Quota-Bytes": "100"})
+ self.assertRaises(
+ lib_exc.Forbidden,
+ self.account_client.create_update_or_delete_account_metadata,
+ create_update_metadata={"Quota-Bytes": "100"})
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index d0a3f10..e54b6e7 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -272,13 +272,15 @@
# set metadata to account
metadata = {'test-account-meta1': 'Meta1',
'test-account-meta2': 'Meta2'}
- resp, _ = self.account_client.create_account_metadata(metadata)
+ resp, _ = self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
resp, _ = self.account_client.list_account_metadata()
self.assertHeaders(resp, 'Account', 'HEAD')
self.assertIn('x-account-meta-test-account-meta1', resp)
self.assertIn('x-account-meta-test-account-meta2', resp)
- self.account_client.delete_account_metadata(metadata)
+ self.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata)
@decorators.idempotent_id('b904c2e3-24c2-4dba-ad7d-04e90a761be5')
def test_list_no_account_metadata(self):
@@ -291,7 +293,8 @@
def test_update_account_metadata_with_create_metadata(self):
# add metadata to account
metadata = {'test-account-meta1': 'Meta1'}
- resp, _ = self.account_client.create_account_metadata(metadata)
+ resp, _ = self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
self.assertHeaders(resp, 'Account', 'POST')
resp, body = self.account_client.list_account_metadata()
@@ -299,14 +302,17 @@
self.assertEqual(resp['x-account-meta-test-account-meta1'],
metadata['test-account-meta1'])
- self.account_client.delete_account_metadata(metadata)
+ self.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata)
@decorators.idempotent_id('9f60348d-c46f-4465-ae06-d51dbd470953')
def test_update_account_metadata_with_delete_metadata(self):
# delete metadata from account
metadata = {'test-account-meta1': 'Meta1'}
- self.account_client.create_account_metadata(metadata)
- resp, _ = self.account_client.delete_account_metadata(metadata)
+ self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
+ resp, _ = self.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
@@ -317,7 +323,8 @@
# if the value of metadata is not set, the metadata is not
# registered at a server
metadata = {'test-account-meta1': ''}
- resp, _ = self.account_client.create_account_metadata(metadata)
+ resp, _ = self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
@@ -328,9 +335,11 @@
# Although the value of metadata is not set, the feature of
# deleting metadata is valid
metadata_1 = {'test-account-meta1': 'Meta1'}
- self.account_client.create_account_metadata(metadata_1)
+ self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata_1)
metadata_2 = {'test-account-meta1': ''}
- resp, _ = self.account_client.delete_account_metadata(metadata_2)
+ resp, _ = self.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata_2)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
@@ -340,11 +349,13 @@
def test_update_account_metadata_with_create_and_delete_metadata(self):
# Send a request adding and deleting metadata requests simultaneously
metadata_1 = {'test-account-meta1': 'Meta1'}
- self.account_client.create_account_metadata(metadata_1)
+ self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata_1)
metadata_2 = {'test-account-meta2': 'Meta2'}
- resp, body = self.account_client.create_and_delete_account_metadata(
- metadata_2,
- metadata_1)
+ resp, body = (
+ self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata_2,
+ delete_metadata=metadata_1))
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
@@ -353,4 +364,5 @@
self.assertEqual(resp['x-account-meta-test-account-meta2'],
metadata_2['test-account-meta2'])
- self.account_client.delete_account_metadata(metadata_2)
+ self.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata_2)
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index c100629..3a2233a 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -37,7 +37,8 @@
cls.key = 'Meta'
cls.metadata = {'Temp-URL-Key': cls.key}
- cls.account_client.create_account_metadata(metadata=cls.metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=cls.metadata)
def setUp(self):
super(ObjectFormPostTest, self).setUp()
@@ -53,7 +54,8 @@
@classmethod
def resource_cleanup(cls):
- cls.account_client.delete_account_metadata(metadata=cls.metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=cls.metadata)
cls.delete_containers()
super(ObjectFormPostTest, cls).resource_cleanup()
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index cfa1875..c56d91a 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -38,7 +38,8 @@
cls.key = 'Meta'
cls.metadata = {'Temp-URL-Key': cls.key}
- cls.account_client.create_account_metadata(metadata=cls.metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=cls.metadata)
def setUp(self):
super(ObjectFormPostNegativeTest, self).setUp()
@@ -54,7 +55,8 @@
@classmethod
def resource_cleanup(cls):
- cls.account_client.delete_account_metadata(metadata=cls.metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=cls.metadata)
cls.delete_containers()
super(ObjectFormPostNegativeTest, cls).resource_cleanup()
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 912deab..4b506f8 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -37,7 +37,8 @@
cls.metadatas = []
metadata = {'Temp-URL-Key': cls.key}
cls.metadatas.append(metadata)
- cls.account_client.create_account_metadata(metadata=metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
# create an object
cls.object_name, cls.content = cls.create_object(cls.container_name)
@@ -45,8 +46,8 @@
@classmethod
def resource_cleanup(cls):
for metadata in cls.metadatas:
- cls.account_client.delete_account_metadata(
- metadata=metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=metadata)
cls.delete_containers()
@@ -110,7 +111,8 @@
def test_get_object_using_temp_url_key_2(self):
key2 = 'Meta2-'
metadata = {'Temp-URL-Key-2': key2}
- self.account_client.create_account_metadata(metadata=metadata)
+ self.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=metadata)
self.metadatas.append(metadata)
# make sure the metadata has been set
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index 578249b..f4d63fd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -39,14 +39,15 @@
# update account metadata
cls.key = 'Meta'
cls.metadata = {'Temp-URL-Key': cls.key}
- cls.account_client.create_account_metadata(metadata=cls.metadata)
+ cls.account_client.create_update_or_delete_account_metadata(
+ create_update_metadata=cls.metadata)
cls.account_client_metadata, _ = \
cls.account_client.list_account_metadata()
@classmethod
def resource_cleanup(cls):
- resp, _ = cls.account_client.delete_account_metadata(
- metadata=cls.metadata)
+ resp, _ = cls.account_client.create_update_or_delete_account_metadata(
+ delete_metadata=cls.metadata)
cls.delete_containers()
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index a19af5d..767b3c7 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -65,7 +65,6 @@
def setup_clients(cls):
super(BaseVolumeTest, cls).setup_clients()
cls.servers_client = cls.os.servers_client
- cls.compute_networks_client = cls.os.compute_networks_client
cls.compute_images_client = cls.os.compute_images_client
cls.snapshots_client = cls.os.snapshots_v2_client
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index afcffc2..75f2a73 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -73,19 +73,20 @@
volume['id'])
# Create a volume transfer
- body = self.client.create_volume_transfer(
- volume_id=volume['id'])['transfer']
- transfer_id = body['id']
+ transfer_id = self.client.create_volume_transfer(
+ volume_id=volume['id'])['transfer']['id']
waiters.wait_for_volume_resource_status(
self.volumes_client, volume['id'], 'awaiting-transfer')
- # List all volume transfers (looking for the one we created)
- body = self.client.list_volume_transfers()['transfers']
- for transfer in body:
- if volume['id'] == transfer['volume_id']:
- break
- else:
- self.fail('Transfer not found for volume %s' % volume['id'])
+ # List all volume transfers with details, check the detail-specific
+ # elements, and look for the created transfer.
+ transfers = self.client.list_volume_transfers(detail=True)['transfers']
+ self.assertNotEmpty(transfers)
+ for transfer in transfers:
+ self.assertIn('created_at', transfer)
+ volume_list = [transfer['volume_id'] for transfer in transfers]
+ self.assertIn(volume['id'], volume_list,
+ 'Transfer not found for volume %s' % volume['id'])
# Delete a volume transfer
self.client.delete_volume_transfer(transfer_id)
diff --git a/tempest/config.py b/tempest/config.py
index 00c69b0..fe2ba54 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -223,7 +223,11 @@
cfg.BoolOpt('forbid_global_implied_dsr',
default=False,
help='Does the environment forbid global roles implying '
- 'domain specific ones?'),
+ 'domain specific ones?',
+ deprecated_for_removal=True,
+ deprecated_reason="This feature flag was introduced to "
+ "support testing of old OpenStack versions, "
+ "which are not supported anymore"),
cfg.BoolOpt('security_compliance',
default=False,
help='Does the environment have the security compliance '
diff --git a/tempest/lib/services/volume/v2/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
index 853948e..2dfbe7b 100644
--- a/tempest/lib/services/volume/v2/transfers_client.py
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -44,14 +44,17 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
- def list_volume_transfers(self, **params):
+ def list_volume_transfers(self, detail=False, **params):
"""List all the volume transfers created.
For a full list of available parameters, please refer to the official
API reference:
https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers
+ https://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-with-details
"""
url = 'os-volume-transfer'
+ if detail:
+ url += '/detail'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 469e58d..5a1737e 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -23,6 +23,32 @@
class AccountClient(rest_client.RestClient):
+ def create_update_or_delete_account_metadata(
+ self,
+ create_update_metadata=None,
+ delete_metadata=None,
+ create_update_metadata_prefix='X-Account-Meta-',
+ delete_metadata_prefix='X-Remove-Account-Meta-'):
+ """Creates, Updates or deletes an account metadata entry.
+
+ Account Metadata can be created, updated or deleted based on
+ metadata header or value. For detailed info, please refer to the
+ official API reference:
+ http://developer.openstack.org/api-ref/object-storage/?expanded=create-update-or-delete-account-metadata-detail
+ """
+ headers = {}
+ if create_update_metadata:
+ for key in create_update_metadata:
+ metadata_header_name = create_update_metadata_prefix + key
+ headers[metadata_header_name] = create_update_metadata[key]
+ if delete_metadata:
+ for key in delete_metadata:
+ headers[delete_metadata_prefix + key] = delete_metadata[key]
+
+ resp, body = self.post('', headers=headers, body=None)
+ self.expected_success([200, 204], resp.status)
+ return resp, body
+
def list_account_metadata(self):
"""HEAD on the storage URL
@@ -32,46 +58,6 @@
self.expected_success(204, resp.status)
return resp, body
- def create_account_metadata(self, metadata,
- metadata_prefix='X-Account-Meta-'):
- """Creates an account metadata entry."""
- headers = {}
- if metadata:
- for key in metadata:
- headers[metadata_prefix + key] = metadata[key]
-
- resp, body = self.post('', headers=headers, body=None)
- self.expected_success([200, 204], resp.status)
- return resp, body
-
- def delete_account_metadata(self, metadata,
- metadata_prefix='X-Remove-Account-Meta-'):
- """Deletes an account metadata entry."""
-
- headers = {}
- for item in metadata:
- headers[metadata_prefix + item] = metadata[item]
- resp, body = self.post('', headers=headers, body=None)
- self.expected_success(204, resp.status)
- return resp, body
-
- def create_and_delete_account_metadata(
- self,
- create_metadata=None,
- delete_metadata=None,
- create_metadata_prefix='X-Account-Meta-',
- delete_metadata_prefix='X-Remove-Account-Meta-'):
- """Creates and deletes an account metadata entry."""
- headers = {}
- for key in create_metadata:
- headers[create_metadata_prefix + key] = create_metadata[key]
- for key in delete_metadata:
- headers[delete_metadata_prefix + key] = delete_metadata[key]
-
- resp, body = self.post('', headers=headers, body=None)
- self.expected_success(204, resp.status)
- return resp, body
-
def list_account_containers(self, params=None):
"""GET on the (base) storage URL
diff --git a/tempest/test.py b/tempest/test.py
index 2ebdc60..e63e08f 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -455,7 +455,9 @@
"""Returns a credentials provider
If no credential provider exists yet creates one.
- It uses self.identity_version if defined, or the configuration value
+ It always use the configuration value from identity.auth_version,
+ since we always want to provision accounts with the current version
+ of the identity API.
"""
if (not hasattr(cls, '_creds_provider') or not cls._creds_provider or
not cls._creds_provider.name == cls.__name__):
@@ -464,8 +466,7 @@
cls._creds_provider = credentials.get_credentials_provider(
name=cls.__name__, network_resources=cls.network_resources,
- force_tenant_isolation=force_tenant_isolation,
- identity_version=cls.get_identity_version())
+ force_tenant_isolation=force_tenant_isolation)
return cls._creds_provider
@classmethod
diff --git a/tempest/tests/lib/services/volume/v2/test_transfers_client.py b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
new file mode 100644
index 0000000..0c59bf2
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_transfers_client.py
@@ -0,0 +1,61 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.volume.v2 import transfers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTransfersClient(base.BaseServiceTest):
+
+ FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL = {
+ "transfers": [{
+ "created_at": "2017-04-18T09:10:03.000000",
+ "volume_id": "47bf04ef-1ea5-4c5f-a375-430a086d6747",
+ "id": "0e89cdd1-6249-421b-96d8-25fac0623d42",
+ "links": [
+ {
+ "href": "fake-url-1",
+ "rel": "self"
+ },
+ {
+ "href": "fake-url-2",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "fake-volume-transfer"
+ }]
+ }
+
+ def setUp(self):
+ super(TestTransfersClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = transfers_client.TransfersClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_list_volume_transfers_with_detail(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_volume_transfers,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_VOLUME_TRANSFERS_WITH_DETAIL,
+ bytes_body,
+ detail=True)
+
+ def test_list_volume_transfers_with_detail_with_str_body(self):
+ self._test_list_volume_transfers_with_detail()
+
+ def test_list_volume_transfers_with_detail_with_bytes_body(self):
+ self._test_list_volume_transfers_with_detail(bytes_body=True)