Merge "cinder backup force-delete when backup is error"
diff --git a/HACKING.rst b/HACKING.rst
index f97f97a..c0a857c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -246,7 +246,7 @@
to the config variables in tempest/config.py then the sample config file must be
regenerated. This can be done running::
- tox -egenconfig
+ tox -e genconfig
Unit Tests
----------
diff --git a/README.rst b/README.rst
index c1c6a10..ac93992 100644
--- a/README.rst
+++ b/README.rst
@@ -172,7 +172,7 @@
You can generate a new sample tempest.conf file, run the following
command from the top level of the Tempest directory::
- $ tox -egenconfig
+ $ tox -e genconfig
The most important pieces that are needed are the user ids, openstack
endpoint, and basic flavors and images needed to run tests.
@@ -258,11 +258,11 @@
Tox also contains several existing job configurations. For example::
- $ tox -efull
+ $ tox -e full
which will run the same set of tests as the OpenStack gate. (it's exactly how
the gate invokes Tempest) Or::
- $ tox -esmoke
+ $ tox -e smoke
to run the tests tagged as smoke.
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 2597f04..cbfcc09 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -1,3 +1,16 @@
+# 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.
+
# Tempest documentation build configuration file, created by
# sphinx-quickstart on Tue May 21 17:43:32 2013.
#
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 18269bf..2314222 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -9,6 +9,8 @@
config file which explains the purpose of each individual option. You can see
the sample config file here: :ref:`tempest-sampleconf`
+.. _tempest_cred_provider_conf:
+
Test Credentials
----------------
@@ -232,6 +234,9 @@
Enabling Remote Access to Created Servers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. _tempest_conf_network_allocation:
+
Network Creation/Usage for Servers
""""""""""""""""""""""""""""""""""
When Tempest creates servers for testing, some tests require being able to
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 896cd98..1264ecc 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -67,7 +67,8 @@
HACKING
REVIEWING
microversion_testing
- test-removal
+ test_removal
+ write_tests
-------
Plugins
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/plugin.rst b/doc/source/plugin.rst
index 6b30825..b3af92f 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -1,3 +1,5 @@
+.. _tempest_plugin:
+
=============================
Tempest Test Plugin Interface
=============================
diff --git a/doc/source/test-removal.rst b/doc/source/test_removal.rst
similarity index 100%
rename from doc/source/test-removal.rst
rename to doc/source/test_removal.rst
diff --git a/doc/source/write_tests.rst b/doc/source/write_tests.rst
new file mode 100644
index 0000000..8488fb1
--- /dev/null
+++ b/doc/source/write_tests.rst
@@ -0,0 +1,311 @@
+.. _tempest_test_writing:
+
+Tempest Test Writing Guide
+##########################
+
+This guide serves as a starting point for developers working on writing new
+Tempest tests. At a high level tests in Tempest are just tests that conform to
+the standard python `unit test`_ framework. But there are several aspects of
+that are unique to tempest and it's role as an integration test suite running
+against a real cloud.
+
+.. _unit test: https://docs.python.org/3.6/library/unittest.html
+
+.. note:: This guide is for writing tests in the tempest repository. While many
+ parts of this guide are also applicable to tempest plugins, not all
+ the APIs mentioned are considered stable or recommended for use in
+ plugins. Please refer to :ref:`tempest_plugin` for details about
+ writing plugins
+
+
+Adding a New TestCase
+=====================
+
+The base unit of testing in Tempest is the `TestCase`_ (also called the test
+class). Each TestCase contains test methods which are the individual tests that
+will be executed by the test runner. But, the TestCase is the smallest self
+contained unit for tests from the tempest perspective. It's also the level at
+which tempest is parallel safe. In other words, multiple TestCases can be
+executed in parallel, but individual test methods in the same TestCase can not.
+Also, all test methods within a TestCase are assumed to be executed serially. As
+such you can use the test case to store variables that are shared between
+methods.
+
+.. _TestCase: https://docs.python.org/3.6/library/unittest.html#unittest.TestCase
+
+In standard unittest the lifecycle of a TestCase can be described in the
+following phases:
+
+ #. setUpClass
+ #. setUp
+ #. Test Execution
+ #. tearDown
+ #. doCleanups
+ #. tearDownClass
+
+setUpClass
+----------
+
+The setUpClass phase is the first phase executed by the test runner and is used
+to perform any setup required for all the test methods to be executed. In
+Tempest this is a very important step and will automatically do the necessary
+setup for interacting with the configured cloud.
+
+To accomplish this you do **not** define a setUpClass function, instead there
+are a number of predefined phases to setUpClass that are used. The phases are:
+
+ * skip_checks
+ * setup_credentials
+ * setup_clients
+ * resource_setup
+
+which is executed in that order. An example of a TestCase which defines all
+of these would be::
+
+ from tempest import config
+ from tempest import test
+
+ CONF = config.CONF
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def skip_checks(cls):
+ """This section is used to evaluate config early and skip all test
+ methods based on these checks
+ """
+ super(TestExampleCase, cls).skip_checks()
+ if not CONF.section.foo
+ cls.skip('A helpful message')
+
+ @classmethod
+ def setup_credentials(cls):
+ """This section is used to do any manual credential allocation and also
+ in the case of dynamic credentials to override the default network
+ resource creation/auto allocation
+ """
+ # This call is used to tell the credential allocator to not create any
+ # network resources for this test case. It also enables selective
+ # creation of other neutron resources. NOTE: it must go before the
+ # super call
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+ @classmethod
+ def setup_clients(cls):
+ """This section is used to setup client aliases from the manager object
+ or to initialize any additional clients. Except in a few very
+ specific situations you should not need to use this.
+ """
+ super(TestExampleCase, cls).setup_clients()
+ cls.servers_client = cls.os.servers_client
+
+ @classmethod
+ def resource_setup(cls):
+ """This section is used to create any resources or objects which are
+ going to be used and shared by **all** test methods in the
+ TestCase. Note then anything created in this section must also be
+ destroyed in the corresponding resource_cleanup() method (which will
+ be run during tearDownClass())
+ """
+ super(TestExampleCase, cls).resource_setup()
+ cls.shared_server = cls.servers_client.create_server(...)
+
+.. _credentials:
+
+Allocating Credentials
+''''''''''''''''''''''
+
+Since Tempest tests are all about testing a running cloud, every test will need
+credentials to be able to make API requests against the cloud. Since this is
+critical to operation and, when running in parallel, easy to make a mistake,
+the base TestCase class will automatically allocate a regular user for each
+TestCase during the setup_credentials() phase. During this process it will also
+initialize a client manager object using those credentials, which will be your
+entry point into interacting with the cloud. For more details on how credentials
+are allocated the :ref:`tempest_cred_provider_conf` section of the Tempest
+Configuration Guide provides more details on the operation of this.
+
+There are some cases when you need more than a single set of credentials, or
+credentials with a more specialized set of roles. To accomplish this you have
+to set a class variable ``credentials`` on the TestCase directly. For example::
+
+ from tempest import test
+
+ class TestExampleAdmin(test.BaseTestCase):
+
+ credentials = ['primary', 'admin']
+
+ @classmethod
+ def skip_checks(cls):
+ ...
+
+In this example the ``TestExampleAdmin`` TestCase will allocate 2 sets of
+credentials, one regular user and one admin user. The corresponding manager
+objects will be set as class variables cls.os and cls.os_adm respectively. You
+can also allocate a second user by putting **'alt'** in the list too. A set of
+alt credentials are the same as primary but can be used for tests cases that
+need a second user/project.
+
+You can also specify credentials with specific roles assigned. This is useful
+for cases where there are specific RBAC requirements hard coded into an API.
+The canonical example of this are swift tests which often want to test swift's
+concepts of operator and reseller_admin. An actual example from tempest on how
+to do this is::
+
+ class PublicObjectTest(base.BaseObjectTest):
+
+ credentials = [['operator', CONF.object_storage.operator_role],
+ ['operator_alt', CONF.object_storage.operator_role]]
+
+ @classmethod
+ def setup_credentials(cls):
+ super(PublicObjectTest, cls).setup_credentials()
+ ...
+
+In this case the manager objects will be set to ``cls.os_roles_operator`` and
+``cls.os_roles_operator_alt`` respectively.
+
+
+There is no limit to how many credentials you can allocate in this manner,
+however in almost every case you should **not** need more than 3 sets of
+credentials per test case.
+
+To figure out the mapping of manager objects set on the TestCase and the
+requested credentials you can reference:
+
++-------------------+---------------------+
+| Credentials Entry | Manager Variable |
++===================+=====================+
+| primary | cls.os |
++-------------------+---------------------+
+| admin | cls.os_adm |
++-------------------+---------------------+
+| alt | cls.os_alt |
++-------------------+---------------------+
+| [$label, $role] | cls.os_roles_$label |
++-------------------+---------------------+
+
+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
+direct parent class (it'll still inherit from the BaseTestCase, just not
+directly) be sure to check if that class overrides allocated credentials.
+
+Dealing with Network Allocation
+'''''''''''''''''''''''''''''''
+
+When neutron is enabled and a testing requires networking this isn't normally
+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).
+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::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources(network=True, subnet=True, router=False)
+ super(TestExampleCase, cls).setup_credentials()
+
+There are 2 quirks with the usage here. First for the set_network_resources
+function to work properly it **must be called before super()**. This is so
+that children classes' settings are always used instead of a parent classes'.
+The other quirk here is that if you do not want to allocate any network
+resources for your test class simply call `set_network_resources()` without
+any arguments. For example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+
+ @classmethod
+ def setup_credentials(cls):
+ cls.set_network_resources()
+ super(TestExampleCase, cls).setup_credentials()
+
+This will not allocate any networking resources. This is because by default all
+the arguments default to False.
+
+It's also worth pointing out that it is common for base test classes for
+different services (and scenario tests) to override this setting. When
+inheriting from classes other than the base TestCase in tempest/test.py it is
+worth checking the immediate parent for what is set to determine if your
+class needs to override that setting.
+
+Interacting with Credentials and Clients
+========================================
+
+Once you have your basic TestCase setup you'll want to start writing tests. To
+do that you need to interact with an OpenStack deployment. This section will
+cover how credentials and clients are used inside of Tempest tests.
+
+
+Manager Objects
+---------------
+
+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
+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
+API requests you just access the client's method for making that call and the
+credentials are already setup for you. For example if you wanted to make an API
+call to create a server in Nova::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+ def test_example_create_server(self):
+ self.os.servers_client.create_server(...)
+
+is all you need to do. As described previously, in the above example the ``self.os``
+is created automatically because the base test class sets the ``credentials``
+attribute to allocate a primary credential set and initializes the client
+manager as ``self.os``. This same access pattern can be used for all of the
+clients in Tempest.
+
+Credentials Objects
+-------------------
+
+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
+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::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+ def test_example_create_server(self):
+ credentials = self.os.credentials
+
+The credentials object provides access to all of the credential information you
+would need to make API requests. For example, building off the previous
+example::
+
+ from tempest import test
+
+
+ class TestExampleCase(test.BaseTestCase):
+ def test_example_create_server(self):
+ credentials = self.os.credentials
+ username = credentials.username
+ user_id = credentials.user_id
+ password = credentials.password
+ tenant_id = credentials.tenant_id
diff --git a/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
new file mode 100644
index 0000000..6b45666
--- /dev/null
+++ b/releasenotes/notes/16.0.0-add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Add a new client to handle the OAUTH consumers feature from the identity API.
diff --git a/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
new file mode 100644
index 0000000..01136c6
--- /dev/null
+++ b/releasenotes/notes/16.0.0-add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add missing API call, list all role inference rules,
+ to the roles_client library. This feature enables the
+ possibility of listing all role inference rules in the
+ system.
diff --git a/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
new file mode 100644
index 0000000..6801858
--- /dev/null
+++ b/releasenotes/notes/16.0.0-add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add cascade parameter to volumes_client.
+ This option provides the ability to delete a volume and have Cinder
+ handle deletion of snapshots associated with that volume by passing
+ an additional argument to volume delete, "cascade=True".
diff --git a/releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml b/releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
similarity index 100%
rename from releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
rename to releasenotes/notes/16.0.0-add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
diff --git a/releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
similarity index 100%
rename from releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml
rename to releasenotes/notes/16.0.0-add-content-type-without-spaces-b2c9b91b257814f3.yaml
diff --git a/releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml b/releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
similarity index 100%
rename from releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml
rename to releasenotes/notes/16.0.0-add-list-auth-project-client-5905076d914a3943.yaml
diff --git a/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
similarity index 100%
rename from releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
rename to releasenotes/notes/16.0.0-add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
diff --git a/releasenotes/notes/add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml b/releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
similarity index 100%
rename from releasenotes/notes/add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
rename to releasenotes/notes/16.0.0-add-list-security-groups-by-servers-to-servers-client-library-088df48f6d81f4be.yaml
diff --git a/releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml b/releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
similarity index 100%
rename from releasenotes/notes/add-list-version-to-identity-client-944cb7396088a575.yaml
rename to releasenotes/notes/16.0.0-add-list-version-to-identity-client-944cb7396088a575.yaml
diff --git a/releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml b/releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
similarity index 100%
rename from releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
rename to releasenotes/notes/16.0.0-add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
diff --git a/releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml b/releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
similarity index 100%
rename from releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
rename to releasenotes/notes/16.0.0-add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
diff --git a/releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml b/releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
rename to releasenotes/notes/16.0.0-add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
diff --git a/releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml b/releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
similarity index 100%
rename from releasenotes/notes/add-tempest-run-combine-option-e94c1049ba8985d5.yaml
rename to releasenotes/notes/16.0.0-add-tempest-run-combine-option-e94c1049ba8985d5.yaml
diff --git a/releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
new file mode 100644
index 0000000..c95e77c
--- /dev/null
+++ b/releasenotes/notes/16.0.0-add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add update encryption type API to the v2 encryption_types_client library.
+ This feature enables the possibility to update an encryption type for an
+ existing volume type.
diff --git a/releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
new file mode 100644
index 0000000..3453050
--- /dev/null
+++ b/releasenotes/notes/16.0.0-add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
@@ -0,0 +1,9 @@
+---
+features:
+ - |
+ Add the unmanage volume API service method in v2 volumes_client library.
+ Define v2 volume_manage_client client for the volume service as library
+ interfaces, allowing other projects to use this module as stable libraries
+ without maintenance changes.
+
+ * volume_manage_client(v2)
diff --git a/releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml b/releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
similarity index 100%
rename from releasenotes/notes/create-server-tags-client-8c0042a77e859af6.yaml
rename to releasenotes/notes/16.0.0-create-server-tags-client-8c0042a77e859af6.yaml
diff --git a/releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml b/releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml
rename to releasenotes/notes/16.0.0-deprecate-deactivate_image-config-7a282c471937bbcb.yaml
diff --git a/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
rename to releasenotes/notes/16.0.0-deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
diff --git a/releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml b/releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
rename to releasenotes/notes/16.0.0-deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
diff --git a/releasenotes/notes/deprecate-resources-prefix-option-ad490c0a30a0266b.yaml b/releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
rename to releasenotes/notes/16.0.0-deprecate-resources-prefix-option-ad490c0a30a0266b.yaml
diff --git a/releasenotes/notes/deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml b/releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
rename to releasenotes/notes/16.0.0-deprecate-skip_unless_attr-decorator-450a1ed727494724.yaml
diff --git a/releasenotes/notes/deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml b/releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
similarity index 100%
rename from releasenotes/notes/deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
rename to releasenotes/notes/16.0.0-deprecate-skip_unless_config-decorator-64c32d588043ab12.yaml
diff --git a/releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml b/releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
similarity index 100%
rename from releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
rename to releasenotes/notes/16.0.0-deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
diff --git a/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
new file mode 100644
index 0000000..4081f6a
--- /dev/null
+++ b/releasenotes/notes/16.0.0-dreprecate_client_parameters-cb8d069e62957f7e.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+ - |
+ Deprecate the client_parameters argument in
+ `tempest.lib.services.clients.ServiceClients`. The parameter is actually
+ not honoured already - see https://bugs.launchpad.net/tempest/+bug/1680915
diff --git a/releasenotes/notes/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml b/releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
similarity index 100%
rename from releasenotes/notes/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
rename to releasenotes/notes/16.0.0-fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
diff --git a/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml b/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
new file mode 100644
index 0000000..24ec512
--- /dev/null
+++ b/releasenotes/notes/16.0.0-mitaka-eol-88ff8355fff81b55.yaml
@@ -0,0 +1,13 @@
+---
+prelude: >
+ This release indicates end of support for Mitaka in Tempest.
+other:
+ - |
+ OpenStack Releases Supported after this release are **Newton**
+ and **Ocata**
+
+ The release under current development as of this tag is Pike,
+ meaning that every Tempest commit is also tested against master branch
+ during the Pike cycle. However, this does not necessarily mean that
+ using Tempest as of this tag will work against Pike (or future
+ releases) cloud.
diff --git a/releasenotes/notes/remove-call_until_true-of-test-de9c13bc8f969921.yaml b/releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
similarity index 100%
rename from releasenotes/notes/remove-call_until_true-of-test-de9c13bc8f969921.yaml
rename to releasenotes/notes/16.0.0-remove-call_until_true-of-test-de9c13bc8f969921.yaml
diff --git a/releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml b/releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
similarity index 100%
rename from releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
rename to releasenotes/notes/16.0.0-remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
new file mode 100644
index 0000000..9d7102f
--- /dev/null
+++ b/releasenotes/notes/16.0.0-remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - |
+ The deprecated config option 'allow_port_security_disabled' from compute_feature_enabled
+ group has been removed.
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
new file mode 100644
index 0000000..b4e4dd1
--- /dev/null
+++ b/releasenotes/notes/16.0.0-remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+ - |
+ Below deprecated config options from compute group have been removed.
+ Corresponding config options already been available in validation group.
+
+ - ``compute.image_ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``compute.ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``scenario.ssh_user`` (available as ``validation.image_ssh_user``)
+ - ``compute.network_for_ssh`` (available as ``validation.network_for_ssh``)
+ - ``compute.ping_timeout `` (available as ``validation.ping_timeout``)
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
new file mode 100644
index 0000000..889e862
--- /dev/null
+++ b/releasenotes/notes/16.0.0-remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - |
+ The deprecated config option 'dvr_extra_resources' from network group has been removed.
+ This option was for extra resources which were provisioned to bind a router to Neutron
+ L3 agent. The extra resources need to be provisioned in Liberty release or older,
+ and are not required since Mitaka release. Current Tempest doesn't support Liberty, so
+ this option has been removed from Tempest.
diff --git a/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
new file mode 100644
index 0000000..8085694
--- /dev/null
+++ b/releasenotes/notes/16.0.0-remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+ - |
+ The deprecated config option 'reseller' from identity_feature_enabled group has been removed.
diff --git a/releasenotes/notes/remove-sahara-service-available-44a642aa9c634ab4.yaml b/releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
similarity index 100%
rename from releasenotes/notes/remove-sahara-service-available-44a642aa9c634ab4.yaml
rename to releasenotes/notes/16.0.0-remove-sahara-service-available-44a642aa9c634ab4.yaml
diff --git a/releasenotes/notes/remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml b/releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
similarity index 100%
rename from releasenotes/notes/remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
rename to releasenotes/notes/16.0.0-remove-volume_feature_enabled.volume_services-c6aa142cc1021297.yaml
diff --git a/releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml b/releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
similarity index 100%
rename from releasenotes/notes/use-keystone-v3-api-935860d30ddbb8e9.yaml
rename to releasenotes/notes/16.0.0-use-keystone-v3-api-935860d30ddbb8e9.yaml
diff --git a/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
new file mode 100644
index 0000000..e5e479b
--- /dev/null
+++ b/releasenotes/notes/16.0.0-volume-transfers-client-e5ed3f5464c0cdc0.yaml
@@ -0,0 +1,18 @@
+---
+features:
+ - |
+ Define volume transfers service clients as libraries.
+ The following volume transfers service clients are defined as library interface.
+
+ * transfers_client(v2)
+deprecations:
+ - |
+ Deprecate volume v2 transfers resource methods from volumes_client(v2) libraries.
+ Same methods are available in new transfers service client: transfers_client(v2)
+ The following methods of volume v2 volumes_clients have been deprecated:
+
+ * create_volume_transfer (v2.volumes_client)
+ * show_volume_transfer (v2.volumes_client)
+ * list_volume_transfers (v2.volumes_client)
+ * delete_volume_transfer (v2.volumes_client)
+ * accept_volume_transfer (v2.volumes_client)
diff --git a/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml b/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml
new file mode 100644
index 0000000..5653681
--- /dev/null
+++ b/releasenotes/notes/add-domain-configuration-client-tempest-tests-e383efabdbb9ad03.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add a new client to handle the domain configuration feature from the
+ identity v3 API.
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/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml b/releasenotes/notes/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml
new file mode 100644
index 0000000..0c33b69
--- /dev/null
+++ b/releasenotes/notes/api_v2_admin_flag-dea5ca9bc2ce63bc.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ A new configuration flag api_v2_admin is introduced in the identity
+ feature flag group to allow for enabling/disabling all identity v2
+ admin tests. The new flag only applies when the existing api_v2 flag
+ is set to True
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/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml b/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml
new file mode 100644
index 0000000..58d45cc
--- /dev/null
+++ b/releasenotes/notes/move-attr-decorator-to-lib-a1e80c42ba9c5392.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ A new ``attr`` decorator has been added in the ``tempest.lib.decorators``
+ module. For example, use it to tag specific tests, which could be leveraged
+ by test runners to run only a subset of Tempest tests.
diff --git a/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml b/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml
new file mode 100644
index 0000000..8c420c8
--- /dev/null
+++ b/releasenotes/notes/move-related_bug-decorator-to-lib-dbfd5c543bbb2805.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ A new ``related_bug`` decorator has been added to
+ ``tempest.lib.decorators``. Use it to decorate and tag a test that was
+ added in relation to a launchpad bug report.
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index eec42cd..97e3a4d 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# tempest Release Notes documentation build configuration file, created by
+# Tempest Release Notes documentation build configuration file, created by
# sphinx-quickstart on Tue Nov 3 17:40:50 2015.
#
# This file is execfile()d with the current directory set to its
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index cea76b4..adec7a7 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
:maxdepth: 1
unreleased
+ v16.0.0
v15.0.0
v14.0.0
v13.0.0
diff --git a/releasenotes/source/v16.0.0.rst b/releasenotes/source/v16.0.0.rst
new file mode 100644
index 0000000..ae29599
--- /dev/null
+++ b/releasenotes/source/v16.0.0.rst
@@ -0,0 +1,6 @@
+=====================
+v16.0.0 Release Notes
+=====================
+
+.. release-notes:: 16.0.0 Release Notes
+ :version: 16.0.0
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 92825a7..14c42b3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
-pbr>=2.0.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
cliff>=2.3.0 # Apache-2.0
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
testtools>=1.4.0 # MIT
@@ -10,7 +10,7 @@
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config>=3.22.0 # Apache-2.0
-oslo.log>=3.11.0 # Apache-2.0
+oslo.log>=3.22.0 # Apache-2.0
oslo.serialization>=1.10.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0
six>=1.9.0 # MIT
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index e682570..69689a7 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class AggregatesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -47,7 +46,7 @@
self.addCleanup(self.client.delete_aggregate, aggregate['id'])
return aggregate
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('86a1cb14-da37-4a70-b056-903fd56dfe29')
def test_aggregate_create_as_user(self):
# Regular user is not allowed to create an aggregate.
@@ -56,7 +55,7 @@
self.user_client.create_aggregate,
name=aggregate_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3b8a1929-3793-4e92-bcb4-dfa572ee6c1d')
def test_aggregate_create_aggregate_name_length_less_than_1(self):
# the length of aggregate name should >= 1 and <=255
@@ -64,7 +63,7 @@
self.client.create_aggregate,
name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4c194563-543b-4e70-a719-557bbe947fac')
def test_aggregate_create_aggregate_name_length_exceeds_255(self):
# the length of aggregate name should >= 1 and <=255
@@ -73,7 +72,7 @@
self.client.create_aggregate,
name=aggregate_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9c23a291-b0b1-487b-b464-132e061151b3')
def test_aggregate_create_with_existent_aggregate_name(self):
# creating an aggregate with existent aggregate name is forbidden
@@ -82,7 +81,7 @@
self.client.create_aggregate,
name=aggregate['name'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('cd6de795-c15d-45f1-8d9e-813c6bb72a3d')
def test_aggregate_delete_as_user(self):
# Regular user is not allowed to delete an aggregate.
@@ -91,14 +90,14 @@
self.user_client.delete_aggregate,
aggregate['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b7d475a6-5dcd-4ff4-b70a-cd9de66a6672')
def test_aggregate_list_as_user(self):
# Regular user is not allowed to list aggregates.
self.assertRaises(lib_exc.Forbidden,
self.user_client.list_aggregates)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('557cad12-34c9-4ff4-95f0-22f0dfbaf7dc')
def test_aggregate_get_details_as_user(self):
# Regular user is not allowed to get aggregate details.
@@ -107,21 +106,21 @@
self.user_client.show_aggregate,
aggregate['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c74f4bf1-4708-4ff2-95a0-f49eaca951bd')
def test_aggregate_delete_with_invalid_id(self):
# Delete an aggregate with invalid id should raise exceptions.
self.assertRaises(lib_exc.NotFound,
self.client.delete_aggregate, -1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3c916244-2c46-49a4-9b55-b20bb0ae512c')
def test_aggregate_get_details_with_invalid_id(self):
# Get aggregate details with invalid id should raise exceptions.
self.assertRaises(lib_exc.NotFound,
self.client.show_aggregate, -1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0ef07828-12b4-45ba-87cc-41425faf5711')
def test_aggregate_add_non_exist_host(self):
# Adding a non-exist host to an aggregate should raise exceptions.
@@ -135,7 +134,7 @@
self.assertRaises(lib_exc.NotFound, self.client.add_host,
aggregate['id'], host=non_exist_host)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7324c334-bd13-4c93-8521-5877322c3d51')
def test_aggregate_add_host_as_user(self):
# Regular user is not allowed to add a host to an aggregate.
@@ -144,7 +143,7 @@
self.user_client.add_host,
aggregate['id'], host=self.host)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('19dd44e1-c435-4ee1-a402-88c4f90b5950')
def test_aggregate_add_existent_host(self):
self.useFixture(fixtures.LockFixture('availability_zone'))
@@ -157,7 +156,7 @@
self.assertRaises(lib_exc.Conflict, self.client.add_host,
aggregate['id'], host=self.host)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7a53af20-137a-4e44-a4ae-e19260e626d9')
def test_aggregate_remove_host_as_user(self):
# Regular user is not allowed to remove a host from an aggregate.
@@ -172,7 +171,7 @@
self.user_client.remove_host,
aggregate['id'], host=self.host)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('95d6a6fa-8da9-4426-84d0-eec0329f2e4d')
def test_aggregate_remove_nonexistent_host(self):
aggregate = self._create_test_aggregate()
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
index faeb811..a58c22c 100644
--- a/tempest/api/compute/admin/test_availability_zone_negative.py
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -15,7 +15,6 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -26,7 +25,7 @@
super(AZAdminNegativeTestJSON, cls).setup_clients()
cls.non_adm_client = cls.availability_zone_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bf34dca2-fdc3-4073-9c02-7648d9eae0d7')
def test_get_availability_zone_list_detail_with_non_admin_user(self):
# List of availability zones and available services with
diff --git a/tempest/api/compute/admin/test_baremetal_nodes.py b/tempest/api/compute/admin/test_baremetal_nodes.py
deleted file mode 100644
index 7726ed4..0000000
--- a/tempest/api/compute/admin/test_baremetal_nodes.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2015 NEC Corporation. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from tempest.api.compute import base
-from tempest import config
-from tempest.lib import decorators
-from tempest import test
-
-CONF = config.CONF
-
-
-class BaremetalNodesAdminTestJSON(base.BaseV2ComputeAdminTest):
- """Tests Baremetal API"""
-
- @classmethod
- def resource_setup(cls):
- super(BaremetalNodesAdminTestJSON, cls).resource_setup()
- if not getattr(CONF.service_available, 'ironic', False):
- skip_msg = ('%s skipped as Ironic is not available' % cls.__name__)
- raise cls.skipException(skip_msg)
- cls.client = cls.os_adm.baremetal_nodes_client
- cls.ironic_client = cls.os_adm.baremetal_client
-
- @test.attr(type=['baremetal'])
- @decorators.idempotent_id('e475aa6e-416d-4fa4-b3af-28d5e84250fb')
- def test_list_get_baremetal_nodes(self):
- # Create some test nodes in Ironic directly
- test_nodes = []
- for _ in range(0, 3):
- _, node = self.ironic_client.create_node()
- test_nodes.append(node)
- self.addCleanup(self.ironic_client.delete_node, node['uuid'])
-
- # List all baremetal nodes and ensure our created test nodes are
- # listed
- bm_node_ids = set([n['id'] for n in
- self.client.list_baremetal_nodes()['nodes']])
- test_node_ids = set([n['uuid'] for n in test_nodes])
- self.assertTrue(test_node_ids.issubset(bm_node_ids))
-
- # Test getting each individually
- for node in test_nodes:
- baremetal_node = self.client.show_baremetal_node(node['uuid'])
- self.assertEqual(node['uuid'], baremetal_node['node']['id'])
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 073f152..5134b81 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -49,14 +49,14 @@
if cls.ip:
break
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9f17f47d-daad-4adc-986e-12370c93e407')
@test.services('network')
def test_list_fixed_ip_details_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.show_fixed_ip, self.ip)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
@test.services('network')
def test_set_reserve_with_non_admin_user(self):
@@ -64,7 +64,7 @@
self.non_admin_client.reserve_fixed_ip,
self.ip, reserve="None")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f1f7a35b-0390-48c5-9803-5f27461439db')
@test.services('network')
def test_set_unreserve_with_non_admin_user(self):
@@ -72,7 +72,7 @@
self.non_admin_client.reserve_fixed_ip,
self.ip, unreserve="None")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f51cf464-7fc5-4352-bc3e-e75cfa2cb717')
@test.services('network')
def test_set_reserve_with_invalid_ip(self):
@@ -85,7 +85,7 @@
self.client.reserve_fixed_ip,
"my.invalid.ip", reserve="None")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fd26ef50-f135-4232-9d32-281aab3f9176')
@test.services('network')
def test_fixed_ip_with_invalid_action(self):
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 12e4587..be165cb 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -43,7 +43,7 @@
cls.vcpus = 1
cls.disk = 10
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0621c53e-d45d-40e7-951d-43e5e257b272')
def test_flavor_access_list_with_public_flavor(self):
# Test to list flavor access with exceptions by querying public flavor
@@ -53,7 +53,7 @@
self.admin_flavors_client.list_flavor_access,
flavor['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('41eaaade-6d37-4f28-9c74-f21b46ca67bd')
def test_flavor_non_admin_add(self):
# Test to add flavor access as a user without admin privileges.
@@ -64,7 +64,7 @@
flavor['id'],
self.tenant_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('073e79a6-c311-4525-82dc-6083d919cb3a')
def test_flavor_non_admin_remove(self):
# Test to remove flavor access as a user without admin privileges.
@@ -81,7 +81,7 @@
flavor['id'],
self.tenant_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f3592cc0-0306-483c-b210-9a7b5346eddc')
def test_add_flavor_access_duplicate(self):
# Create a new flavor.
@@ -101,7 +101,7 @@
flavor['id'],
self.tenant_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1f710927-3bc7-4381-9f82-0ca6e42644b7')
def test_remove_flavor_access_not_found(self):
# Create a new flavor.
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index a728711..f39feb9 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -62,7 +62,7 @@
cls.admin_flavors_client.wait_for_resource_deletion(cls.flavor['id'])
super(FlavorsExtraSpecsNegativeTestJSON, cls).resource_cleanup()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a00a3b81-5641-45a8-ab2b-4a8ec41e1d7d')
def test_flavor_non_admin_set_keys(self):
# Test to SET flavor extra spec as a user without admin privileges.
@@ -71,7 +71,7 @@
self.flavor['id'],
key1="value1", key2="value2")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1ebf4ef8-759e-48fe-a801-d451d80476fb')
def test_flavor_non_admin_update_specific_key(self):
# non admin user is not allowed to update flavor extra spec
@@ -85,7 +85,7 @@
'key1',
key1='value1_new')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('28f12249-27c7-44c1-8810-1f382f316b11')
def test_flavor_non_admin_unset_keys(self):
self.admin_flavors_client.set_flavor_extra_spec(
@@ -96,7 +96,7 @@
self.flavor['id'],
'key1')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
def test_flavor_unset_nonexistent_key(self):
self.assertRaises(lib_exc.NotFound,
@@ -104,7 +104,7 @@
self.flavor['id'],
'nonexistent_key')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
def test_flavor_get_nonexistent_key(self):
self.assertRaises(lib_exc.NotFound,
@@ -112,7 +112,7 @@
self.flavor['id'],
"nonexistent_key")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('25b822b8-9f49-44f6-80de-d99f0482e5cb')
def test_flavor_update_mismatch_key(self):
# the key will be updated should be match the key in the body
@@ -122,7 +122,7 @@
"key2",
key1="value")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f5889590-bf66-41cc-b4b1-6e6370cfd93f')
def test_flavor_update_more_key(self):
# there should be just one item in the request body
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index 07ab8c5..684f656 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -15,7 +15,6 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class HostsAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -35,26 +34,26 @@
raise lib_exc.NotFound("no host found")
cls.hostname = hosts[0]['host_name']
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dd032027-0210-4d9c-860e-69b1b8deed5f')
def test_list_hosts_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.list_hosts)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e75b0a1a-041f-47a1-8b4a-b72a6ff36d3f')
def test_show_host_detail_with_nonexistent_hostname(self):
self.assertRaises(lib_exc.NotFound,
self.client.show_host, 'nonexistent_hostname')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
def test_show_host_detail_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.show_host,
self.hostname)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e40c72b1-0239-4ed6-ba21-81a184df1f7c')
def test_update_host_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
@@ -63,7 +62,7 @@
status='enable',
maintenance_mode='enable')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fbe2bf3e-3246-4a95-a59f-94e4e298ec77')
def test_update_host_with_invalid_status(self):
# 'status' can only be 'enable' or 'disable'
@@ -73,7 +72,7 @@
status='invalid',
maintenance_mode='enable')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ab1e230e-5e22-41a9-8699-82b9947915d4')
def test_update_host_with_invalid_maintenance_mode(self):
# 'maintenance_mode' can only be 'enable' or 'disable'
@@ -83,7 +82,7 @@
status='enable',
maintenance_mode='invalid')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0cd85f75-6992-4a4a-b1bd-d11e37fd0eee')
def test_update_host_without_param(self):
# 'status' or 'maintenance_mode' needed for host update
@@ -91,7 +90,7 @@
self.client.update_host,
self.hostname)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('23c92146-2100-4d68-b2d6-c7ade970c9c1')
def test_update_nonexistent_host(self):
self.assertRaises(lib_exc.NotFound,
@@ -100,42 +99,42 @@
status='enable',
maintenance_mode='enable')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0d981ac3-4320-4898-b674-82b61fbb60e4')
def test_startup_nonexistent_host(self):
self.assertRaises(lib_exc.NotFound,
self.client.startup_host,
'nonexistent_hostname')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9f4ebb7e-b2ae-4e5b-a38f-0fd1bb0ddfca')
def test_startup_host_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.startup_host,
self.hostname)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9e637444-29cf-4244-88c8-831ae82c31b6')
def test_shutdown_nonexistent_host(self):
self.assertRaises(lib_exc.NotFound,
self.client.shutdown_host,
'nonexistent_hostname')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a803529c-7e3f-4d3c-a7d6-8e1c203d27f6')
def test_shutdown_host_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.shutdown_host,
self.hostname)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f86bfd7b-0b13-4849-ae29-0322e83ee58b')
def test_reboot_nonexistent_host(self):
self.assertRaises(lib_exc.NotFound,
self.client.reboot_host,
'nonexistent_hostname')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('02d79bb9-eb57-4612-abf6-2cb38897d2f8')
def test_reboot_host_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 4637024..e0e5a29 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -104,7 +104,7 @@
try:
uptime = (self.client.show_hypervisor_uptime(hyper['id'])
['hypervisor'])
- if len(uptime) > 0:
+ if uptime:
has_valid_uptime = True
break
except Exception:
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index b8a67b9..2addce9 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class HypervisorAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -34,7 +33,7 @@
hypers = self.client.list_hypervisors()['hypervisors']
return hypers
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c136086a-0f67-4b2b-bc61-8482bd68989f')
def test_show_nonexistent_hypervisor(self):
nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -44,7 +43,7 @@
self.client.show_hypervisor,
nonexistent_hypervisor_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('51e663d0-6b89-4817-a465-20aca0667d03')
def test_show_hypervisor_with_non_admin_user(self):
hypers = self._list_hypervisors()
@@ -55,7 +54,7 @@
self.non_adm_client.show_hypervisor,
hypers[0]['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('2a0a3938-832e-4859-95bf-1c57c236b924')
def test_show_servers_with_non_admin_user(self):
hypers = self._list_hypervisors()
@@ -66,7 +65,7 @@
self.non_adm_client.list_servers_on_hypervisor,
hypers[0]['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('02463d69-0ace-4d33-a4a8-93d7883a2bba')
def test_show_servers_with_nonexistent_hypervisor(self):
nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -76,14 +75,14 @@
self.client.list_servers_on_hypervisor,
nonexistent_hypervisor_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e2b061bb-13f9-40d8-9d6e-d5bf17595849')
def test_get_hypervisor_stats_with_non_admin_user(self):
self.assertRaises(
lib_exc.Forbidden,
self.non_adm_client.show_hypervisor_statistics)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f60aa680-9a3a-4c7d-90e1-fae3a4891303')
def test_get_nonexistent_hypervisor_uptime(self):
nonexistent_hypervisor_id = data_utils.rand_uuid()
@@ -93,7 +92,7 @@
self.client.show_hypervisor_uptime,
nonexistent_hypervisor_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6c3461f9-c04c-4e2a-bebb-71dc9cb47df2')
def test_get_hypervisor_uptime_with_non_admin_user(self):
hypers = self._list_hypervisors()
@@ -104,7 +103,7 @@
self.non_adm_client.show_hypervisor_uptime,
hypers[0]['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('51b3d536-9b14-409c-9bce-c6f7c794994e')
def test_get_hypervisor_list_with_non_admin_user(self):
# List of hypervisor and available services with non admin user
@@ -112,7 +111,7 @@
lib_exc.Forbidden,
self.non_adm_client.list_hypervisors)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dc02db05-e801-4c5f-bc8e-d915290ab345')
def test_get_hypervisor_list_details_with_non_admin_user(self):
# List of hypervisor details and available services with non admin user
@@ -120,7 +119,7 @@
lib_exc.Forbidden,
self.non_adm_client.list_hypervisors, detail=True)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
def test_search_nonexistent_hypervisor(self):
self.assertRaises(
@@ -128,7 +127,7 @@
self.client.search_hypervisor,
'nonexistent_hypervisor_name')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
def test_search_hypervisor_with_non_admin_user(self):
hypers = self._list_hypervisors()
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index 16c39c4..92235cc 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -20,7 +20,6 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -30,7 +29,7 @@
super(InstanceUsageAuditLogNegativeTestJSON, cls).setup_clients()
cls.adm_client = cls.os_adm.instance_usages_audit_log_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a9d33178-d2c9-4131-ad3b-f4ca8d0308a2')
def test_instance_usage_audit_logs_with_nonadmin_user(self):
# the instance_usage_audit_logs API just can be accessed by admin user
@@ -43,7 +42,7 @@
show_instance_usage_audit_log,
urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9b952047-3641-41c7-ba91-a809fc5974c8')
def test_get_instance_usage_audit_logs_with_invalid_time(self):
self.assertRaises(lib_exc.BadRequest,
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 3ffd238..4d0f12a 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-
+from oslo_log import log as logging
import testtools
from tempest.api.compute import base
@@ -23,6 +23,7 @@
from tempest import test
CONF = config.CONF
+LOG = logging.getLogger(__name__)
class LiveBlockMigrationTestJSON(base.BaseV2ComputeAdminTest):
@@ -80,6 +81,22 @@
if host != target_host:
return target_host
+ def _live_migrate(self, server_id, target_host, state,
+ volume_backed=False):
+ self._migrate_server_to(server_id, target_host, volume_backed)
+ waiters.wait_for_server_status(self.servers_client, server_id, state)
+ migration_list = (self.admin_migration_client.list_migrations()
+ ['migrations'])
+
+ msg = ("Live Migration failed. Migrations list for Instance "
+ "%s: [" % server_id)
+ for live_migration in migration_list:
+ if (live_migration['instance_uuid'] == server_id):
+ msg += "\n%s" % live_migration
+ msg += "]"
+ self.assertEqual(target_host, self._get_host_for_server(server_id),
+ msg)
+
def _test_live_migration(self, state='ACTIVE', volume_backed=False):
"""Tests live migration between two hosts.
@@ -94,27 +111,23 @@
# Live migrate an instance to another host
server_id = self.create_test_server(wait_until="ACTIVE",
volume_backed=volume_backed)['id']
- actual_host = self._get_host_for_server(server_id)
- target_host = self._get_host_other_than(actual_host)
+ source_host = self._get_host_for_server(server_id)
+ destination_host = self._get_host_other_than(source_host)
if state == 'PAUSED':
self.admin_servers_client.pause_server(server_id)
waiters.wait_for_server_status(self.admin_servers_client,
server_id, state)
- self._migrate_server_to(server_id, target_host, volume_backed)
- waiters.wait_for_server_status(self.servers_client, server_id, state)
- migration_list = (self.admin_migration_client.list_migrations()
- ['migrations'])
-
- msg = ("Live Migration failed. Migrations list for Instance "
- "%s: [" % server_id)
- for live_migration in migration_list:
- if (live_migration['instance_uuid'] == server_id):
- msg += "\n%s" % live_migration
- msg += "]"
- self.assertEqual(target_host, self._get_host_for_server(server_id),
- msg)
+ LOG.info("Live migrate from source %s to destination %s",
+ source_host, destination_host)
+ self._live_migrate(server_id, destination_host, state, volume_backed)
+ if CONF.compute_feature_enabled.live_migrate_back_and_forth:
+ # If live_migrate_back_and_forth is enabled it is a grenade job.
+ # Therefore test should validate whether LM is compatible in both
+ # ways, so live migrate VM back to the source host
+ LOG.info("Live migrate back to source %s", source_host)
+ self._live_migrate(server_id, source_host, state, volume_backed)
@decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
def test_live_block_migration(self):
@@ -149,11 +162,17 @@
# Attach the volume to the server
self.attach_volume(server, volume, device='/dev/xvdb')
-
+ server = self.admin_servers_client.show_server(server_id)['server']
+ volume_id1 = server["os-extended-volumes:volumes_attached"][0]["id"]
self._migrate_server_to(server_id, target_host)
waiters.wait_for_server_status(self.servers_client,
server_id, 'ACTIVE')
+
+ server = self.admin_servers_client.show_server(server_id)['server']
+ volume_id2 = server["os-extended-volumes:volumes_attached"][0]["id"]
+
self.assertEqual(target_host, self._get_host_for_server(server_id))
+ self.assertEqual(volume_id1, volume_id2)
class LiveAutoBlockMigrationV225TestJSON(LiveBlockMigrationTestJSON):
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index a1dd2e7..cc4654d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -51,7 +51,7 @@
self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
**{quota_item: default_quota_value})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
def test_update_quota_normal_user(self):
self.assertRaises(lib_exc.Forbidden,
@@ -61,7 +61,7 @@
# TODO(afazekas): Add dedicated tenant to the skipped quota tests.
# It can be moved into the setUpClass as well.
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
def test_create_server_when_cpu_quota_is_full(self):
# Disallow server creation when tenant's vcpu quota is full
@@ -69,7 +69,7 @@
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.create_test_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
def test_create_server_when_memory_quota_is_full(self):
# Disallow server creation when tenant's memory quota is full
@@ -77,7 +77,7 @@
self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
self.create_test_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
def test_create_server_when_instances_quota_is_full(self):
# Once instances quota limit is reached, disallow server creation
@@ -87,7 +87,7 @@
@decorators.skip_because(bug="1186354",
condition=CONF.service_available.neutron)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7c6c8f3b-2bf6-4918-b240-57b136a66aa0')
@test.services('network')
def test_security_groups_exceed_limit(self):
@@ -106,7 +106,7 @@
@decorators.skip_because(bug="1186354",
condition=CONF.service_available.neutron)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6e9f436d-f1ed-4f8e-a493-7275dfaa4b4d')
@test.services('network')
def test_security_groups_rules_exceed_limit(self):
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 79777d0..aff61bf 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -18,7 +18,6 @@
from tempest.common import waiters
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
@@ -35,14 +34,15 @@
super(ServersAdminTestJSON, cls).resource_setup()
cls.s1_name = data_utils.rand_name(cls.__name__ + '-server')
- server = cls.create_test_server(name=cls.s1_name,
- wait_until='ACTIVE')
+ server = cls.create_test_server(name=cls.s1_name)
cls.s1_id = server['id']
cls.s2_name = data_utils.rand_name(cls.__name__ + '-server')
server = cls.create_test_server(name=cls.s2_name,
wait_until='ACTIVE')
cls.s2_id = server['id']
+ waiters.wait_for_server_status(cls.non_admin_client,
+ cls.s1_id, 'ACTIVE')
@decorators.idempotent_id('06f960bb-15bb-48dc-873d-f96e89be7870')
def test_list_servers_filter_by_error_status(self):
@@ -92,7 +92,7 @@
self.assertIn(self.s1_name, servers_name)
self.assertIn(self.s2_name, servers_name)
- @test.related_bug('1659811')
+ @decorators.related_bug('1659811')
@decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
def test_list_servers_by_admin_with_specified_tenant(self):
# In nova v2, tenant_id is ignored unless all_tenants is specified
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index ebdceef..677972e 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -21,7 +21,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -47,7 +46,7 @@
@decorators.idempotent_id('28dcec23-f807-49da-822c-56a92ea3c687')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resize_server_using_overlimit_ram(self):
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
self.useFixture(fixtures.LockFixture('compute_quotas'))
@@ -69,7 +68,7 @@
@decorators.idempotent_id('7368a427-2f26-4ad9-9ba9-911a0ec2b0db')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resize_server_using_overlimit_vcpus(self):
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
self.useFixture(fixtures.LockFixture('compute_quotas'))
@@ -88,27 +87,27 @@
self.servers[0]['id'],
flavor_ref['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b0b4d8af-1256-41ef-9ee7-25f1c19dde80')
def test_reset_state_server_invalid_state(self):
self.assertRaises(lib_exc.BadRequest,
self.client.reset_state, self.s1_id,
state='invalid')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4cdcc984-fab0-4577-9a9d-6d558527ee9d')
def test_reset_state_server_invalid_type(self):
self.assertRaises(lib_exc.BadRequest,
self.client.reset_state, self.s1_id,
state=1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e741298b-8df2-46f0-81cb-8f814ff2504c')
def test_reset_state_server_nonexistent_server(self):
self.assertRaises(lib_exc.NotFound,
self.client.reset_state, '999', state='error')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
def test_get_server_diagnostics_by_non_admin(self):
# Non-admin user can not view server diagnostics according to policy
@@ -116,7 +115,7 @@
self.non_adm_client.show_server_diagnostics,
self.s1_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
def test_migrate_non_existent_server(self):
# migrate a non existent server
@@ -129,7 +128,7 @@
'Resize not available.')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_migrate_server_invalid_state(self):
# create server.
server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 9447a09..1edfc89 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -15,7 +15,6 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ServicesAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -27,13 +26,13 @@
cls.client = cls.os_adm.services_client
cls.non_admin_client = cls.services_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1126d1f8-266e-485f-a687-adc547492646')
def test_list_services_with_non_admin_user(self):
self.assertRaises(lib_exc.Forbidden,
self.non_admin_client.list_services)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d0884a69-f693-4e79-a9af-232d15643bf7')
def test_get_service_by_invalid_params(self):
# return all services if send the request with invalid parameter
@@ -42,7 +41,7 @@
['services'])
self.assertEqual(len(services), len(services_xxx))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1e966d4a-226e-47c7-b601-0b18a27add54')
def test_get_service_by_invalid_service_and_valid_host(self):
services = self.client.list_services()['services']
@@ -51,7 +50,7 @@
binary='xxx')['services']
self.assertEqual(0, len(services))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('64e7e7fb-69e8-4cb6-a71d-8d5eb0c98655')
def test_get_service_with_valid_service_and_invalid_host(self):
services = self.client.list_services()['services']
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index 35762d6..222d28c 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -18,7 +18,6 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class TenantUsagesNegativeTestJSON(base.BaseV2ComputeAdminTest):
@@ -41,7 +40,7 @@
# Returns formatted datetime
return at.strftime('%Y-%m-%dT%H:%M:%S.%f')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8b21e135-d94b-4991-b6e9-87059609c8ed')
def test_get_usage_tenant_with_empty_tenant_id(self):
# Get usage for a specific tenant empty
@@ -51,7 +50,7 @@
self.adm_client.show_tenant_usage,
'', **params)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4079dd2a-9e8d-479f-869d-6fa985ce45b6')
def test_get_usage_tenant_with_invalid_date(self):
# Get usage for tenant with invalid date
@@ -61,7 +60,7 @@
self.adm_client.show_tenant_usage,
self.client.tenant_id, **params)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bbe6fe2c-15d8-404c-a0a2-44fad0ad5cc7')
def test_list_usage_all_tenants_with_non_admin_user(self):
# Get usage for all tenants with non admin user
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index 984f1a9..22a5bc4 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -10,10 +10,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from tempest.api.compute import base
from tempest.common import waiters
from tempest import config
from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
from tempest import test
CONF = config.CONF
@@ -41,6 +44,41 @@
if not CONF.compute_feature_enabled.swap_volume:
raise cls.skipException("Swapping volumes is not supported.")
+ def _wait_for_server_volume_swap(self, server_id, old_volume_id,
+ new_volume_id):
+ """Waits for a server to swap the old volume to a new one."""
+ volume_attachments = self.servers_client.list_volume_attachments(
+ server_id)['volumeAttachments']
+ attached_volume_ids = [attachment['volumeId']
+ for attachment in volume_attachments]
+ start = int(time.time())
+
+ while (old_volume_id in attached_volume_ids) \
+ or (new_volume_id not in attached_volume_ids):
+ time.sleep(self.servers_client.build_interval)
+ volume_attachments = self.servers_client.list_volume_attachments(
+ server_id)['volumeAttachments']
+ attached_volume_ids = [attachment['volumeId']
+ for attachment in volume_attachments]
+
+ if int(time.time()) - start >= self.servers_client.build_timeout:
+ old_vol_bdm_status = 'in BDM' \
+ if old_volume_id in attached_volume_ids else 'not in BDM'
+ new_vol_bdm_status = 'in BDM' \
+ if new_volume_id in attached_volume_ids else 'not in BDM'
+ message = ('Failed to swap old volume %(old_volume_id)s '
+ '(current %(old_vol_bdm_status)s) to new volume '
+ '%(new_volume_id)s (current %(new_vol_bdm_status)s)'
+ ' on server %(server_id)s within the required time '
+ '(%(timeout)s s)' %
+ {'old_volume_id': old_volume_id,
+ 'old_vol_bdm_status': old_vol_bdm_status,
+ 'new_volume_id': new_volume_id,
+ 'new_vol_bdm_status': new_vol_bdm_status,
+ 'server_id': server_id,
+ 'timeout': self.servers_client.build_timeout})
+ raise lib_exc.TimeoutException(message)
+
@decorators.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
@test.services('volume')
def test_volume_swap(self):
@@ -61,6 +99,8 @@
volume1['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume2['id'], 'in-use')
+ self._wait_for_server_volume_swap(server['id'], volume1['id'],
+ volume2['id'])
# Verify "volume2" is attached to the server
vol_attachments = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
@@ -74,6 +114,8 @@
volume2['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume1['id'], 'in-use')
+ self._wait_for_server_volume_swap(server['id'], volume2['id'],
+ volume1['id'])
# Verify "volume1" is attached to the server
vol_attachments = self.servers_client.list_volume_attachments(
server['id'])['volumeAttachments']
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 06b0893..4a7f36f 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -36,7 +35,7 @@
super(VolumesAdminNegativeTest, cls).resource_setup()
cls.server = cls.create_test_server(wait_until='ACTIVE')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
volume = self.create_volume()
@@ -46,8 +45,8 @@
self.server['id'], nonexistent_volume,
volumeId=volume['id'])
- @test.related_bug('1629110', status_code=400)
- @test.attr(type=['negative'])
+ @decorators.related_bug('1629110', status_code=400)
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
def test_update_attached_volume_with_nonexistent_volume_in_body(self):
volume = self.create_volume()
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ef13eef..1736463 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -94,10 +94,7 @@
cls.os.security_group_default_rules_client)
cls.versions_client = cls.os.compute_versions_client
- if CONF.volume_feature_enabled.api_v1:
- cls.volumes_client = cls.os.volumes_client
- else:
- cls.volumes_client = cls.os.volumes_v2_client
+ cls.volumes_client = cls.os.volumes_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 89051c1..bf4c958 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -15,14 +15,13 @@
from tempest.api.compute import base
from tempest.lib import decorators
-from tempest import test
class FlavorsV2TestJSON(base.BaseV2ComputeTest):
_min_disk = 'minDisk'
_min_ram = 'minRam'
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('e36c0eaa-dff5-4082-ad1f-3f9a80aa3f59')
def test_list_flavors(self):
# List of all flavors should contain the expected flavor
@@ -39,7 +38,7 @@
flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
self.assertIn(flavor, flavors)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('1f12046b-753d-40d2-abb6-d8eb8b30cb2f')
def test_get_flavor(self):
# The expected flavor details should be returned
diff --git a/tempest/api/compute/flavors/test_flavors_negative.py b/tempest/api/compute/flavors/test_flavors_negative.py
index eee6bd2..b2c33e2 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -42,7 +42,7 @@
'Either api_v1 or api_v2 must be True in '
'[image-feature-enabled].')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@test.services('image')
@decorators.idempotent_id('90f0d93a-91c1-450c-91e6-07d18172cefe')
def test_boot_with_low_ram(self):
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index b3c3e17..cce9856 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -48,7 +48,7 @@
if cls.non_exist_id not in floating_ip_ids:
break
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6e0f059b-e4dd-48fb-8207-06e3bba5b074')
@test.services('network')
def test_allocate_floating_ip_from_nonexistent_pool(self):
@@ -58,7 +58,7 @@
self.client.create_floating_ip,
pool="non_exist_pool")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ae1c55a8-552b-44d4-bfb6-2a115a15d0ba')
@test.services('network')
def test_delete_nonexistent_floating_ip(self):
@@ -69,7 +69,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_floating_ip,
self.non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('595fa616-1a71-4670-9614-46564ac49a4c')
@test.services('network')
def test_associate_nonexistent_floating_ip(self):
@@ -80,7 +80,7 @@
self.client.associate_floating_ip_to_server,
"0.0.0.0", self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0a081a66-e568-4e6b-aa62-9587a876dca8')
@test.services('network')
def test_dissociate_nonexistent_floating_ip(self):
@@ -90,7 +90,7 @@
self.client.disassociate_floating_ip_from_server,
"0.0.0.0", self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('804b4fcb-bbf5-412f-925d-896672b61eb3')
@test.services('network')
def test_associate_ip_to_server_without_passing_floating_ip(self):
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index 388db0b..9d70bf7 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -30,7 +30,7 @@
super(FloatingIPDetailsNegativeTestJSON, cls).setup_clients()
cls.client = cls.floating_ips_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7ab18834-4a4b-4f28-a2c5-440579866695')
@test.services('network')
def test_get_nonexistent_floating_ip_details(self):
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 9cb4b57..03d0789 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
@@ -27,7 +26,7 @@
super(ImagesMetadataNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('94069db2-792f-4fa8-8bd3-2271a6e0c095')
def test_list_nonexistent_image_metadata(self):
# Negative test: List on nonexistent image
@@ -35,7 +34,7 @@
self.assertRaises(lib_exc.NotFound, self.client.list_image_metadata,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a403ef9e-9f95-427c-b70a-3ce3388796f1')
def test_update_nonexistent_image_metadata(self):
# Negative test:An update should not happen for a non-existent image
@@ -44,7 +43,7 @@
self.client.update_image_metadata,
data_utils.rand_uuid(), meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('41ae052c-6ee6-405c-985e-5712393a620d')
def test_get_nonexistent_image_metadata_item(self):
# Negative test: Get on non-existent image should not happen
@@ -52,7 +51,7 @@
self.client.show_image_metadata_item,
data_utils.rand_uuid(), 'os_version')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dc64f2ce-77e8-45b0-88c8-e15041d08eaf')
def test_set_nonexistent_image_metadata(self):
# Negative test: Metadata should not be set to a non-existent image
@@ -60,7 +59,7 @@
self.assertRaises(lib_exc.NotFound, self.client.set_image_metadata,
data_utils.rand_uuid(), meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('2154fd03-ab54-457c-8874-e6e3eb56e9cf')
def test_set_nonexistent_image_metadata_item(self):
# Negative test: Metadata item should not be set to a
@@ -71,7 +70,7 @@
data_utils.rand_uuid(), 'os_distro',
meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('848e157f-6bcf-4b2e-a5dd-5124025a8518')
def test_delete_nonexistent_image_metadata_item(self):
# Negative test: Shouldn't be able to delete metadata
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 86013d4..e292389 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -41,7 +40,7 @@
super(ImagesNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6cd5a89d-5b47-46a7-93bc-3916f0d84973')
def test_create_image_from_deleted_server(self):
# An image should not be created if the server instance is removed
@@ -56,7 +55,7 @@
self.create_image_from_server,
server['id'], metadata=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('82c5b0c4-9dbd-463c-872b-20c4755aae7f')
def test_create_image_from_invalid_server(self):
# An image should not be created with invalid server id
@@ -65,7 +64,7 @@
self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
data_utils.rand_name('invalid'), metadata=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ec176029-73dc-4037-8d72-2e4ff60cf538')
def test_create_image_specify_uuid_35_characters_or_less(self):
# Return an error if Image ID passed is 35 characters or less
@@ -74,7 +73,7 @@
self.assertRaises(lib_exc.NotFound, self.client.create_image,
test_uuid, name=snapshot_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('36741560-510e-4cc2-8641-55fe4dfb2437')
def test_create_image_specify_uuid_37_characters_or_more(self):
# Return an error if Image ID passed is 37 characters or more
@@ -83,14 +82,14 @@
self.assertRaises(lib_exc.NotFound, self.client.create_image,
test_uuid, name=snapshot_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('381acb65-785a-4942-94ce-d8f8c84f1f0f')
def test_delete_image_with_invalid_image_id(self):
# An image should not be deleted with invalid image id
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
data_utils.rand_name('invalid'))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('137aef61-39f7-44a1-8ddf-0adf82511701')
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
@@ -99,13 +98,13 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e6e41425-af5c-4fe6-a4b5-7b7b963ffda5')
def test_delete_image_blank_id(self):
# Return an error while trying to delete an image with blank Id
self.assertRaises(lib_exc.NotFound, self.client.delete_image, '')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
@@ -113,13 +112,13 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
invalid_image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
def test_delete_image_negative_image_id(self):
# Return an error while trying to delete an image with negative id
self.assertRaises(lib_exc.NotFound, self.client.delete_image, -1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
def test_delete_image_with_id_over_character_limit(self):
# Return an error while trying to delete image with id over limit
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 68563bd..cf32ba3 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -22,7 +22,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -77,7 +76,7 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('55d1d38c-dd66-4933-9c8e-7d92aeb60ddc')
def test_create_image_specify_invalid_metadata(self):
# Return an error when creating image with invalid metadata
@@ -85,7 +84,7 @@
self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
self.server_id, metadata=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
def test_create_image_specify_metadata_over_limits(self):
# Return an error when creating image with meta data over 255 chars
@@ -93,7 +92,7 @@
self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
self.server_id, metadata=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0460efcf-ee88-4f94-acef-1bf658695456')
def test_create_second_image_when_first_image_is_being_saved(self):
# Disallow creating another image when first image is being saved
@@ -110,7 +109,7 @@
self.client.delete_image(image_id)
self.images.remove(image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
def test_create_image_specify_name_over_character_limit(self):
# Return an error if snapshot name over 255 characters is passed
@@ -119,7 +118,7 @@
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
self.server_id, name=snapshot_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0894954d-2db2-4195-a45b-ffec0bc0187e')
def test_delete_image_that_is_not_yet_active(self):
# Return an error while trying to delete an image what is creating
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index 403961f..d37f8fc 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -36,7 +35,7 @@
super(ListImageFiltersNegativeTestJSON, cls).setup_clients()
cls.client = cls.compute_images_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('391b0440-432c-4d4b-b5da-c5096aa247eb')
def test_get_nonexistent_image(self):
# Check raises a NotFound
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 8b5a35b..205076c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -18,11 +18,10 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class KeyPairsNegativeTestJSON(base.BaseKeypairTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('29cca892-46ae-4d48-bc32-8fe7e731eb81')
def test_keypair_create_with_invalid_pub_key(self):
# Keypair should not be created with a non RSA public key
@@ -30,7 +29,7 @@
self.assertRaises(lib_exc.BadRequest,
self.create_keypair, pub_key=pub_key)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7cc32e47-4c42-489d-9623-c5e2cb5a2fa5')
def test_keypair_delete_nonexistent_key(self):
# Non-existent key deletion should throw a proper error
@@ -38,7 +37,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_keypair,
k_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dade320e-69ca-42a9-ba4a-345300f127e0')
def test_create_keypair_with_empty_public_key(self):
# Keypair should not be created with an empty public key
@@ -46,7 +45,7 @@
self.assertRaises(lib_exc.BadRequest, self.create_keypair,
pub_key=pub_key)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fc100c19-2926-4b9c-8fdc-d0589ee2f9ff')
def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
# Keypair should not be created when public key bits are too long
@@ -54,7 +53,7 @@
self.assertRaises(lib_exc.BadRequest, self.create_keypair,
pub_key=pub_key)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0359a7f1-f002-4682-8073-0c91e4011b7c')
def test_create_keypair_with_duplicate_name(self):
# Keypairs with duplicate names should not be created
@@ -65,14 +64,14 @@
k_name)
self.client.delete_keypair(k_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1398abe1-4a84-45fb-9294-89f514daff00')
def test_create_keypair_with_empty_name_string(self):
# Keypairs with name being an empty string should not be created
self.assertRaises(lib_exc.BadRequest, self.create_keypair,
'')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3faa916f-779f-4103-aca7-dc3538eee1b7')
def test_create_keypair_with_long_keynames(self):
# Keypairs with name longer than 255 chars should not be created
@@ -80,7 +79,7 @@
self.assertRaises(lib_exc.BadRequest, self.create_keypair,
k_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('45fbe5e0-acb5-49aa-837a-ff8d0719db91')
def test_create_keypair_invalid_name(self):
# Keypairs with name being an invalid name should not be created
diff --git a/tempest/api/compute/limits/test_absolute_limits_negative.py b/tempest/api/compute/limits/test_absolute_limits_negative.py
index 21b4b1c..bef4eb5 100644
--- a/tempest/api/compute/limits/test_absolute_limits_negative.py
+++ b/tempest/api/compute/limits/test_absolute_limits_negative.py
@@ -17,7 +17,6 @@
from tempest.common import tempest_fixtures as fixtures
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class AbsoluteLimitsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -32,7 +31,7 @@
super(AbsoluteLimitsNegativeTestJSON, cls).setup_clients()
cls.client = cls.limits_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('215cd465-d8ae-49c9-bf33-9c911913a5c8')
def test_max_image_meta_exceed_limit(self):
# We should not create vm with image meta over maxImageMeta limit
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index b82fa3b..3378ef8 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -14,12 +14,9 @@
# under the License.
from tempest.api.compute.security_groups import base
-from tempest import config
from tempest.lib import decorators
from tempest import test
-CONF = config.CONF
-
class SecurityGroupRulesTestJSON(base.BaseSecurityGroupsTest):
@@ -56,7 +53,7 @@
self.assertEqual(self.expected[key], actual_rule[key],
"Miss-matched key is %s" % key)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('850795d7-d4d3-4e55-b527-a774c0123d3a')
@test.services('network')
def test_security_group_rules_create(self):
@@ -126,7 +123,7 @@
'name': group_name}
self._check_expected_response(rule)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('a6154130-5a55-4850-8be4-5e9e796dbf17')
@test.services('network')
def test_security_group_rules_list(self):
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 6b8dfdf..4efb8b7 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -27,7 +27,7 @@
super(SecurityGroupRulesNegativeTestJSON, cls).setup_clients()
cls.rules_client = cls.security_group_rules_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1d507e98-7951-469b-82c3-23f1e6b8c254')
@test.services('network')
def test_create_security_group_rule_with_non_existent_id(self):
@@ -44,7 +44,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('2244d7e4-adb7-4ecb-9930-2d77e123ce4f')
@test.services('network')
def test_create_security_group_rule_with_invalid_id(self):
@@ -61,7 +61,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8bd56d02-3ffa-4d67-9933-b6b9a01d6089')
@test.services('network')
def test_create_security_group_rule_duplicate(self):
@@ -86,7 +86,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('84c81249-9f6e-439c-9bbf-cbb0d2cddbdf')
@test.services('network')
def test_create_security_group_rule_with_invalid_ip_protocol(self):
@@ -106,7 +106,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('12bbc875-1045-4f7a-be46-751277baedb9')
@test.services('network')
def test_create_security_group_rule_with_invalid_from_port(self):
@@ -125,7 +125,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ff88804d-144f-45d1-bf59-dd155838a43a')
@test.services('network')
def test_create_security_group_rule_with_invalid_to_port(self):
@@ -144,7 +144,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('00296fa9-0576-496a-ae15-fbab843189e0')
@test.services('network')
def test_create_security_group_rule_with_invalid_port_range(self):
@@ -163,7 +163,7 @@
ip_protocol=ip_protocol, from_port=from_port,
to_port=to_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('56fddcca-dbb8-4494-a0db-96e9f869527c')
@test.services('network')
def test_delete_security_group_rule_with_non_existent_id(self):
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index ed0e722..930a58e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -28,7 +28,7 @@
super(SecurityGroupsTestJSON, cls).setup_clients()
cls.client = cls.security_groups_client
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('eb2b087d-633d-4d0d-a7bd-9e6ba35b32de')
@test.services('network')
def test_security_groups_create_list_delete(self):
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 3593237..207778a 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -32,7 +32,7 @@
super(SecurityGroupsNegativeTestJSON, cls).setup_clients()
cls.client = cls.security_groups_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('673eaec1-9b3e-48ed-bdf1-2786c1b9661c')
@test.services('network')
def test_security_group_get_nonexistent_group(self):
@@ -44,7 +44,7 @@
@decorators.skip_because(bug="1161411",
condition=CONF.service_available.neutron)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1759c3cb-b0fc-44b7-86ce-c99236be911d')
@test.services('network')
def test_security_group_create_with_invalid_group_name(self):
@@ -67,7 +67,7 @@
@decorators.skip_because(bug="1161411",
condition=CONF.service_available.neutron)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('777b6f14-aca9-4758-9e84-38783cfa58bc')
@test.services('network')
def test_security_group_create_with_invalid_group_description(self):
@@ -84,7 +84,7 @@
@decorators.idempotent_id('9fdb4abc-6b66-4b27-b89c-eb215a956168')
@testtools.skipIf(CONF.service_available.neutron,
"Neutron allows duplicate names for security groups")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@test.services('network')
def test_security_group_create_with_duplicate_name(self):
# Negative test:Security Group with duplicate name should not
@@ -97,7 +97,7 @@
self.client.create_security_group,
name=s_name, description=s_description)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('36a1629f-c6da-4a26-b8b8-55e7e5d5cd58')
@test.services('network')
def test_delete_the_default_security_group(self):
@@ -113,7 +113,7 @@
self.client.delete_security_group,
default_security_group_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6727c00b-214c-4f9e-9a52-017ac3e98411')
@test.services('network')
def test_delete_nonexistent_security_group(self):
@@ -122,7 +122,7 @@
self.assertRaises(lib_exc.NotFound,
self.client.delete_security_group, non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1438f330-8fa4-4aeb-8a94-37c250106d7f')
@test.services('network')
def test_delete_security_group_without_passing_id(self):
@@ -134,7 +134,7 @@
@decorators.idempotent_id('00579617-fe04-4e1c-9d08-ca7467d2e34b')
@testtools.skipIf(CONF.service_available.neutron,
"Neutron does not check the security group ID")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@test.services('network')
def test_update_security_group_with_invalid_sg_id(self):
# Update security_group with invalid sg_id should fail
@@ -149,7 +149,7 @@
@decorators.idempotent_id('cda8d8b4-59f8-4087-821d-20cf5a03b3b1')
@testtools.skipIf(CONF.service_available.neutron,
"Neutron does not check the security group name")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@test.services('network')
def test_update_security_group_with_invalid_sg_name(self):
# Update security_group with invalid sg_name should fail
@@ -165,7 +165,7 @@
@decorators.idempotent_id('97d12b1c-a610-4194-93f1-ba859e718b45')
@testtools.skipIf(CONF.service_available.neutron,
"Neutron does not check the security group description")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@test.services('network')
def test_update_security_group_with_invalid_sg_des(self):
# Update security_group with invalid sg_des should fail
@@ -178,7 +178,7 @@
self.client.update_security_group,
securitygroup_id, description=s_new_des)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('27edee9c-873d-4da6-a68a-3c256efebe8f')
@test.services('network')
def test_update_non_existent_security_group(self):
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index e0c8887..06a47d1 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -219,7 +219,7 @@
_ifs = self._test_delete_interface(server, ifs)
self.assertEqual(len(ifs) - 1, len(_ifs))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
@test.services('network')
def test_add_remove_fixed_ip(self):
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index eb23e4b..d54c3ec 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -77,7 +77,7 @@
subnet['subnet']['id'])
return net
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('5de47127-9977-400a-936f-abcfbec1218f')
def test_verify_server_details(self):
# Verify the specified server attributes are set correctly
@@ -91,7 +91,7 @@
self.assertEqual(self.flavor_ref, self.server['flavor']['id'])
self.assertEqual(self.meta, self.server['metadata'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('9a438d88-10c6-4bcd-8b5b-5b6e25e1346f')
def test_list_servers(self):
# The created server should be in the list of all servers
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 512bd1e..1d3a790 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class InstanceActionsNegativeTestJSON(base.BaseV2ComputeTest):
@@ -32,7 +31,7 @@
super(InstanceActionsNegativeTestJSON, cls).resource_setup()
cls.server = cls.create_test_server(wait_until='ACTIVE')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
def test_list_instance_actions_non_existent_server(self):
# List actions of the non-existent server id
@@ -41,7 +40,7 @@
self.client.list_instance_actions,
non_existent_server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0269f40a-6f18-456c-b336-c03623c897f1')
def test_get_instance_action_invalid_request(self):
# Get the action details of the provided server with invalid request
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 12eb5e1..1ad153a 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -48,23 +48,25 @@
cls.fixed_network_name = None
network_kwargs = fixed_network.set_networks_kwarg(network)
cls.s1_name = data_utils.rand_name(cls.__name__ + '-instance')
- cls.s1 = cls.create_test_server(name=cls.s1_name,
- wait_until='ACTIVE',
- **network_kwargs)
+ cls.s1 = cls.create_test_server(name=cls.s1_name, **network_kwargs)
cls.s2_name = data_utils.rand_name(cls.__name__ + '-instance')
# If image_ref_alt is "" or None then we still want to boot a server
# but we rely on `testtools.skipUnless` decorator to actually skip
# the irrelevant tests.
cls.s2 = cls.create_test_server(
- name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref,
- wait_until='ACTIVE')
+ name=cls.s2_name, image_id=cls.image_ref_alt or cls.image_ref)
cls.s3_name = data_utils.rand_name(cls.__name__ + '-instance')
cls.s3 = cls.create_test_server(name=cls.s3_name,
flavor=cls.flavor_ref_alt,
wait_until='ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s1['id'],
+ 'ACTIVE')
+ waiters.wait_for_server_status(cls.client, cls.s2['id'],
+ 'ACTIVE')
+
@decorators.idempotent_id('05e8a8e7-9659-459a-989d-92c2f501f4ba')
@testtools.skipUnless(CONF.compute.image_ref != CONF.compute.image_ref_alt,
"Need distinct images to run this test")
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index 3010caf..d89f44c 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -17,7 +17,6 @@
from tempest.common import waiters
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ListServersNegativeTestJSON(base.BaseV2ComputeTest):
@@ -36,15 +35,14 @@
# servers are cleaned up automatically in the
# tearDownClass method of the super-class.
cls.deleted_fixtures = []
- for _ in range(2):
- srv = cls.create_test_server(wait_until='ACTIVE')
+ cls.create_test_server(wait_until='ACTIVE', min_count=2)
srv = cls.create_test_server(wait_until='ACTIVE')
cls.client.delete_server(srv['id'])
waiters.wait_for_server_termination(cls.client, srv['id'])
cls.deleted_fixtures.append(srv)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f')
def test_list_servers_with_a_deleted_server(self):
# Verify deleted servers do not show by default in list servers
@@ -56,7 +54,7 @@
if srv['id'] in deleted_ids]
self.assertEqual([], actual)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
def test_list_servers_by_non_existing_image(self):
# Listing servers for a non existing image returns empty list
@@ -64,7 +62,7 @@
servers = body['servers']
self.assertEqual([], servers)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
def test_list_servers_by_non_existing_flavor(self):
# Listing servers by non existing flavor returns empty list
@@ -72,7 +70,7 @@
servers = body['servers']
self.assertEqual([], servers)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
def test_list_servers_by_non_existing_server_name(self):
# Listing servers for a non existent server name returns empty list
@@ -80,7 +78,7 @@
servers = body['servers']
self.assertEqual([], servers)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
def test_list_servers_status_non_existing(self):
# Return an empty list when invalid status is specified
@@ -94,7 +92,7 @@
body = self.client.list_servers(limit=1)
self.assertEqual(1, len([x for x in body['servers'] if 'id' in x]))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
def test_list_servers_by_limits_greater_than_actual_count(self):
# Gather the complete list of servers in the project for reference
@@ -104,21 +102,21 @@
body = self.client.list_servers(limit=limit)
self.assertEqual(len(full_list), len(body['servers']))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
def test_list_servers_by_limits_pass_string(self):
# Return an error if a string value is passed for limit
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
limit='testing')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('62610dd9-4713-4ee0-8beb-fd2c1aa7f950')
def test_list_servers_by_limits_pass_negative_value(self):
# Return an error if a negative value for limit is passed
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
limit=-1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('87d12517-e20a-4c9c-97b6-dd1628d6d6c9')
def test_list_servers_by_changes_since_invalid_date(self):
# Return an error when invalid date format is passed
@@ -126,7 +124,7 @@
self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
**params)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('74745ad8-b346-45b5-b9b8-509d7447fc1f')
def test_list_servers_by_changes_since_future_date(self):
# Return an empty list when a date in the future is passed
@@ -134,7 +132,7 @@
body = self.client.list_servers(**changes_since)
self.assertEqual(0, len(body['servers']))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('93055106-2d34-46fe-af68-d9ddbf7ee570')
def test_list_servers_detail_server_is_deleted(self):
# Server details are not listed for a deleted server
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index 675cbee..422510f 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -16,40 +16,39 @@
from tempest.api.compute import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
def test_min_count_less_than_one(self):
invalid_min_count = 0
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
def test_min_count_non_integer(self):
invalid_min_count = 2.5
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
min_count=invalid_min_count)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
def test_max_count_less_than_one(self):
invalid_max_count = 0
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
def test_max_count_non_integer(self):
invalid_max_count = 2.5
self.assertRaises(lib_exc.BadRequest, self.create_test_server,
max_count=invalid_max_count)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('476da616-f1ef-4271-a9b1-b9fc87727cdf')
def test_max_count_less_than_min_count(self):
min_count = 3
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index a72df5e..90b0665 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -13,14 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import socket
import struct
import six
-from six.moves.urllib import parse as urlparse
import urllib3
from tempest.api.compute import base
+from tempest.common import compute
from tempest import config
from tempest.lib import decorators
@@ -83,7 +82,7 @@
"""Verify we can connect to novnc and do the websocket connection."""
# Turn the Socket into a WebSocket to do the communication
data = self._websocket.receive_frame()
- self.assertFalse(data is None or len(data) == 0,
+ self.assertFalse(data is None or not data,
'Token must be invalid because the connection '
'closed.')
# Parse the RFB version from the data to make sure it is valid
@@ -158,14 +157,6 @@
self._websocket.response.find(b'Server: WebSockify') > 0,
'Did not get the expected WebSocket HTTP Response.')
- def _create_websocket(self, url):
- url = urlparse.urlparse(url)
- client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- client_socket.connect((url.hostname, url.port))
- # Turn the Socket into a WebSocket to do the communication
- return _WebSocket(client_socket, url)
-
@decorators.idempotent_id('c640fdff-8ab4-45a4-a5d8-7e6146cbd0dc')
def test_novnc(self):
body = self.client.get_vnc_console(self.server['id'],
@@ -174,7 +165,7 @@
# Do the initial HTTP Request to novncproxy to get the NoVNC JavaScript
self._validate_novnc_html(body['url'])
# Do the WebSockify HTTP Request to novncproxy to do the RFB connection
- self._websocket = self._create_websocket(body['url'])
+ self._websocket = compute.create_websocket(body['url'])
# Validate that we succesfully connected and upgraded to Web Sockets
self._validate_websocket_upgrade()
# Validate the RFB Negotiation to determine if a valid VNC session
@@ -187,84 +178,9 @@
self.assertEqual('novnc', body['type'])
# Do the WebSockify HTTP Request to novncproxy with a bad token
url = body['url'].replace('token=', 'token=bad')
- self._websocket = self._create_websocket(url)
+ self._websocket = compute.create_websocket(url)
# Make sure the novncproxy rejected the connection and closed it
data = self._websocket.receive_frame()
- self.assertTrue(data is None or len(data) == 0,
+ self.assertTrue(data is None or not data,
"The novnc proxy actually sent us some data, but we "
"expected it to close the connection.")
-
-
-class _WebSocket(object):
- def __init__(self, client_socket, url):
- """Contructor for the WebSocket wrapper to the socket."""
- self._socket = client_socket
- # Upgrade the HTTP connection to a WebSocket
- self._upgrade(url)
-
- def receive_frame(self):
- """Wrapper for receiving data to parse the WebSocket frame format"""
- # We need to loop until we either get some bytes back in the frame
- # or no data was received (meaning the socket was closed). This is
- # done to handle the case where we get back some empty frames
- while True:
- header = self._socket.recv(2)
- # If we didn't receive any data, just return None
- if len(header) == 0:
- return None
- # We will make the assumption that we are only dealing with
- # frames less than 125 bytes here (for the negotiation) and
- # that only the 2nd byte contains the length, and since the
- # server doesn't do masking, we can just read the data length
- if ord_func(header[1]) & 127 > 0:
- return self._socket.recv(ord_func(header[1]) & 127)
-
- def send_frame(self, data):
- """Wrapper for sending data to add in the WebSocket frame format."""
- frame_bytes = list()
- # For the first byte, want to say we are sending binary data (130)
- frame_bytes.append(130)
- # Only sending negotiation data so don't need to worry about > 125
- # We do need to add the bit that says we are masking the data
- frame_bytes.append(len(data) | 128)
- # We don't really care about providing a random mask for security
- # So we will just hard-code a value since a test program
- mask = [7, 2, 1, 9]
- for i in range(len(mask)):
- frame_bytes.append(mask[i])
- # Mask each of the actual data bytes that we are going to send
- for i in range(len(data)):
- frame_bytes.append(ord_func(data[i]) ^ mask[i % 4])
- # Convert our integer list to a binary array of bytes
- frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
- self._socket.sendall(frame_bytes)
-
- def close(self):
- """Helper method to close the connection."""
- # Close down the real socket connection and exit the test program
- if self._socket is not None:
- self._socket.shutdown(1)
- self._socket.close()
- self._socket = None
-
- def _upgrade(self, url):
- """Upgrade the HTTP connection to a WebSocket and verify."""
- # The real request goes to the /websockify URI always
- reqdata = 'GET /websockify HTTP/1.1\r\n'
- reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
- # Tell the HTTP Server to Upgrade the connection to a WebSocket
- reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
- # The token=xxx is sent as a Cookie not in the URI
- reqdata += 'Cookie: %s\r\n' % url.query
- # Use a hard-coded WebSocket key since a test program
- reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
- reqdata += 'Sec-WebSocket-Version: 13\r\n'
- # We are choosing to use binary even though browser may do Base64
- reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
- # Send the HTTP GET request and get the response back
- self._socket.sendall(reqdata.encode('utf8'))
- self.response = data = self._socket.recv(4096)
- # Loop through & concatenate all of the data in the response body
- while len(data) > 0 and self.response.find(b'\r\n\r\n') < 0:
- data = self._socket.recv(4096)
- self.response += data
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d810ff7..751d33a 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -133,7 +133,7 @@
self.assertGreater(new_boot_time, boot_time,
'%s > %s' % (new_boot_time, boot_time))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
def test_reboot_server_hard(self):
# The server should be power cycled
@@ -145,6 +145,18 @@
# The server should be signaled to reboot gracefully
self._test_reboot_server('SOFT')
+ @decorators.idempotent_id('1d1c9104-1b0a-11e7-a3d4-fa163e65f5ce')
+ def test_remove_server_all_security_groups(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+
+ # Remove all Security group
+ self.client.remove_security_group(
+ server['id'], name=server['security_groups'][0]['name'])
+
+ # Verify all Security group
+ server = self.client.show_server(server['id'])['server']
+ self.assertNotIn('security_groups', server)
+
def _rebuild_server_and_check(self, image_ref):
rebuilt_server = (self.client.rebuild_server(self.server_id, image_ref)
['server'])
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index cf4ed85..92b1ff1 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -37,7 +37,7 @@
cls.server = cls.create_test_server(wait_until='ACTIVE')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('6eb718c0-02d9-4d5e-acd1-4e0c269cef39')
@test.services('network')
def test_list_server_addresses(self):
@@ -55,7 +55,7 @@
self.assertTrue(address['addr'])
self.assertTrue(address['version'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('87bbc374-5538-4f64-b673-2b0e4443cc30')
@test.services('network')
def test_list_server_addresses_by_network(self):
diff --git a/tempest/api/compute/servers/test_server_addresses_negative.py b/tempest/api/compute/servers/test_server_addresses_negative.py
index 1884e4f..76a102b 100644
--- a/tempest/api/compute/servers/test_server_addresses_negative.py
+++ b/tempest/api/compute/servers/test_server_addresses_negative.py
@@ -36,7 +36,7 @@
super(ServerAddressesNegativeTestJSON, cls).resource_setup()
cls.server = cls.create_test_server(wait_until='ACTIVE')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('02c3f645-2d2e-4417-8525-68c0407d001b')
@test.services('network')
def test_list_server_addresses_invalid_server_id(self):
@@ -44,7 +44,7 @@
self.assertRaises(lib_exc.NotFound, self.client.list_addresses,
'999')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a2ab5144-78c0-4942-a0ed-cc8edccfd9ba')
@test.services('network')
def test_list_server_addresses_by_network_neg(self):
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 22ce37d..482ba09 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ServerMetadataNegativeTestJSON(base.BaseV2ComputeTest):
@@ -33,7 +32,7 @@
cls.tenant_id = cls.client.tenant_id
cls.server = cls.create_test_server(metadata={}, wait_until='ACTIVE')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
def test_server_create_metadata_key_too_long(self):
# Attempt to start a server with a meta-data key that is > 255
@@ -49,7 +48,7 @@
# no teardown - all creates should fail
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('92431555-4d8b-467c-b95b-b17daa5e57ff')
def test_create_server_metadata_blank_key(self):
# Blank key should trigger an error.
@@ -58,7 +57,7 @@
self.create_test_server,
metadata=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4d9cd7a3-2010-4b41-b8fe-3bbf0b169466')
def test_server_metadata_non_existent_server(self):
# GET on a non-existent server should not succeed
@@ -68,7 +67,7 @@
non_existent_server_id,
'test2')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f408e78e-3066-4097-9299-3b0182da812e')
def test_list_server_metadata_non_existent_server(self):
# List metadata on a non-existent server should not succeed
@@ -77,7 +76,7 @@
self.client.list_server_metadata,
non_existent_server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0025fbd6-a4ba-4cde-b8c2-96805dcfdabc')
def test_wrong_key_passed_in_body(self):
# Raise BadRequest if key in uri does not match
@@ -87,7 +86,7 @@
self.client.set_server_metadata_item,
self.server['id'], 'key', meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
def test_set_metadata_non_existent_server(self):
# Set metadata on a non-existent server should not succeed
@@ -98,7 +97,7 @@
non_existent_server_id,
meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('904b13dc-0ef2-4e4c-91cd-3b4a0f2f49d8')
def test_update_metadata_non_existent_server(self):
# An update should not happen for a non-existent server
@@ -109,7 +108,7 @@
non_existent_server_id,
meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a452f38c-05c2-4b47-bd44-a4f0bf5a5e48')
def test_update_metadata_with_blank_key(self):
# Blank key should trigger an error
@@ -118,7 +117,7 @@
self.client.update_server_metadata,
self.server['id'], meta=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
def test_delete_metadata_non_existent_server(self):
# Should not be able to delete metadata item from a non-existent server
@@ -128,7 +127,7 @@
non_existent_server_id,
'd')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d8c0a210-a5c3-4664-be04-69d96746b547')
def test_metadata_items_limit(self):
# A 403 Forbidden or 413 Overlimit (old behaviour) exception
@@ -154,7 +153,7 @@
self.client.update_server_metadata,
self.server['id'], req_metadata)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
def test_set_server_metadata_blank_key(self):
# Raise a bad request error for blank key.
@@ -164,7 +163,7 @@
self.client.set_server_metadata,
self.server['id'], meta=meta)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
def test_set_server_metadata_missing_metadata(self):
# Raise a bad request error for a missing metadata field
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 565d76d..5fac433 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -74,7 +74,7 @@
@decorators.idempotent_id('cc3a883f-43c0-4fb6-a9bb-5579d64984ed')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_rescue_paused_instance(self):
# Rescue a paused server
self.servers_client.pause_server(self.server_id)
@@ -85,13 +85,13 @@
self.servers_client.rescue_server,
self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('db22b618-f157-4566-a317-1b6d467a8094')
def test_rescued_vm_reboot(self):
self.assertRaises(lib_exc.Conflict, self.servers_client.reboot_server,
self.rescue_id, type='HARD')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6dfc0a55-3a77-4564-a144-1587b7971dde')
def test_rescue_non_existent_server(self):
# Rescue a non-existing server
@@ -100,7 +100,7 @@
self.servers_client.rescue_server,
non_existent_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('70cdb8a1-89f8-437d-9448-8844fd82bf46')
def test_rescued_vm_rebuild(self):
self.assertRaises(lib_exc.Conflict,
@@ -110,7 +110,7 @@
@decorators.idempotent_id('d0ccac79-0091-4cf4-a1ce-26162d0cc55f')
@test.services('volume')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_rescued_vm_attach_volume(self):
volume = self.create_volume()
@@ -130,7 +130,7 @@
@decorators.idempotent_id('f56e465b-fe10-48bf-b75d-646cda3a8bc9')
@test.services('volume')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_rescued_vm_detach_volume(self):
volume = self.create_volume()
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index c6b3b40..a480a15 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -54,7 +54,7 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dbbfd247-c40c-449e-8f6c-d2aa7c7da7cf')
def test_server_name_blank(self):
# Create a server with name parameter empty
@@ -63,7 +63,7 @@
self.create_test_server,
name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b8a7235e-5246-4a8f-a08e-b34877c6586f')
@testtools.skipUnless(CONF.compute_feature_enabled.personality,
'Nova personality feature disabled')
@@ -78,7 +78,7 @@
self.create_test_server,
personality=person)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fcba1052-0a50-4cf3-b1ac-fae241edf02f')
def test_create_with_invalid_image(self):
# Create a server with an unknown image
@@ -87,7 +87,7 @@
self.create_test_server,
image_id=-1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('18f5227f-d155-4429-807c-ccb103887537')
def test_create_with_invalid_flavor(self):
# Create a server with an unknown flavor
@@ -96,7 +96,7 @@
self.create_test_server,
flavor=-1,)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7f70a4d1-608f-4794-9e56-cb182765972c')
def test_invalid_access_ip_v4_address(self):
# An access IPv4 address must match a valid address pattern
@@ -105,7 +105,7 @@
self.assertRaises(lib_exc.BadRequest,
self.create_test_server, accessIPv4=IPv4)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5226dd80-1e9c-4d8a-b5f9-b26ca4763fd0')
def test_invalid_ip_v6_address(self):
# An access IPv6 address must match a valid address pattern
@@ -118,7 +118,7 @@
@decorators.idempotent_id('7ea45b3e-e770-46fa-bfcc-9daaf6d987c0')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resize_nonexistent_server(self):
# Resize a non-existent server
nonexistent_server = data_utils.rand_uuid()
@@ -129,7 +129,7 @@
@decorators.idempotent_id('ced1a1d7-2ab6-45c9-b90f-b27d87b30efd')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resize_server_with_non_existent_flavor(self):
# Resize a server with non-existent flavor
nonexistent_flavor = data_utils.rand_uuid()
@@ -139,13 +139,13 @@
@decorators.idempotent_id('45436a7d-a388-4a35-a9d8-3adc5d0d940b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resize_server_with_null_flavor(self):
# Resize a server with null flavor
self.assertRaises(lib_exc.BadRequest, self.client.resize_server,
self.server_id, flavor_ref="")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d4c023a0-9c55-4747-9dd5-413b820143c7')
def test_reboot_non_existent_server(self):
# Reboot a non existent server
@@ -156,7 +156,7 @@
@decorators.idempotent_id('d1417e7f-a509-41b5-a102-d5eed8613369')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_pause_paused_server(self):
# Pause a paused server.
self.client.pause_server(self.server_id)
@@ -166,7 +166,7 @@
self.server_id)
self.client.unpause_server(self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('98fa0458-1485-440f-873b-fe7f0d714930')
def test_rebuild_deleted_server(self):
# Rebuild a deleted server
@@ -178,8 +178,8 @@
self.client.rebuild_server,
server['id'], self.image_ref)
- @test.related_bug('1660878', status_code=409)
- @test.attr(type=['negative'])
+ @decorators.related_bug('1660878', status_code=409)
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('581a397d-5eab-486f-9cf9-1014bbd4c984')
def test_reboot_deleted_server(self):
# Reboot a deleted server
@@ -190,7 +190,7 @@
self.assertRaises(lib_exc.NotFound, self.client.reboot_server,
server['id'], type='SOFT')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d86141a7-906e-4731-b187-d64a2ea61422')
def test_rebuild_non_existent_server(self):
# Rebuild a non existent server
@@ -200,7 +200,7 @@
nonexistent_server,
self.image_ref)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('fd57f159-68d6-4c2a-902b-03070828a87e')
def test_create_numeric_server_name(self):
server_name = 12345
@@ -208,7 +208,7 @@
self.create_test_server,
name=server_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c3e0fb12-07fc-4d76-a22e-37409887afe8')
def test_create_server_name_length_exceeds_256(self):
# Create a server with name length exceeding 255 characters
@@ -218,8 +218,9 @@
self.create_test_server,
name=server_name)
- @test.attr(type=['negative'])
- @test.related_bug('1651064', status_code=500)
+ @decorators.attr(type=['negative'])
+ @decorators.related_bug('1651064', status_code=500)
+ @test.services('volume')
@decorators.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
def test_create_server_invalid_bdm_in_2nd_dict(self):
volume = self.create_volume()
@@ -238,7 +239,7 @@
image_id=self.image_ref,
block_device_mapping_v2=bdm)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4e72dc2d-44c5-4336-9667-f7972e95c402')
def test_create_with_invalid_network_uuid(self):
# Pass invalid network uuid while creating a server
@@ -249,7 +250,7 @@
self.create_test_server,
networks=networks)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7a2efc39-530c-47de-b875-2dd01c8d39bd')
def test_create_with_non_existent_keypair(self):
# Pass a non-existent keypair while creating a server
@@ -259,7 +260,7 @@
self.create_test_server,
key_name=key_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7fc74810-0bd2-4cd7-8244-4f33a9db865a')
def test_create_server_metadata_exceeds_length_limit(self):
# Pass really long metadata while creating a server
@@ -269,7 +270,7 @@
self.create_test_server,
metadata=metadata)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('aa8eed43-e2cb-4ebf-930b-da14f6a21d81')
def test_update_name_of_non_existent_server(self):
# Update name of a non-existent server
@@ -281,7 +282,7 @@
self.assertRaises(lib_exc.NotFound, self.client.update_server,
nonexistent_server, name=new_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('38204696-17c6-44da-9590-40f87fb5a899')
def test_update_server_set_empty_name(self):
# Update name of the server to an empty string
@@ -291,7 +292,7 @@
self.assertRaises(lib_exc.BadRequest, self.client.update_server,
self.server_id, name=new_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5c8e244c-dada-4590-9944-749c455b431f')
def test_update_server_name_length_exceeds_256(self):
# Update name of server exceed the name length limit
@@ -302,7 +303,7 @@
self.server_id,
name=new_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1041b4e6-514b-4855-96a5-e974b60870a3')
def test_delete_non_existent_server(self):
# Delete a non existent server
@@ -311,14 +312,14 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_server,
nonexistent_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('75f79124-277c-45e6-a373-a1d6803f4cc4')
def test_delete_server_pass_negative_id(self):
# Pass an invalid string parameter to delete server
self.assertRaises(lib_exc.NotFound, self.client.delete_server, -1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f4d7279b-5fd2-4bf2-9ba4-ae35df0d18c5')
def test_delete_server_pass_id_exceeding_length_limit(self):
# Pass a server ID that exceeds length limit to delete server
@@ -326,7 +327,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_server,
sys.maxsize + 1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c5fa6041-80cd-483b-aa6d-4e45f19d093c')
def test_create_with_nonexistent_security_group(self):
# Create a server with a nonexistent security group
@@ -336,7 +337,7 @@
self.create_test_server,
security_groups=security_groups)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3436b02f-1b1e-4f03-881e-c6a602327439')
def test_get_non_existent_server(self):
# Get a non existent server details
@@ -344,7 +345,7 @@
self.assertRaises(lib_exc.NotFound, self.client.show_server,
nonexistent_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a31460a9-49e1-42aa-82ee-06e0bb7c2d03')
def test_stop_non_existent_server(self):
# Stop a non existent server
@@ -355,7 +356,7 @@
@decorators.idempotent_id('6a8dc0c6-6cd4-4c0a-9f32-413881828091')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_pause_non_existent_server(self):
# pause a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -365,7 +366,7 @@
@decorators.idempotent_id('705b8e3a-e8a7-477c-a19b-6868fc24ac75')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_unpause_non_existent_server(self):
# unpause a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -375,7 +376,7 @@
@decorators.idempotent_id('c8e639a7-ece8-42dd-a2e0-49615917ba4f')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_unpause_server_invalid_state(self):
# unpause an active server.
self.assertRaises(lib_exc.Conflict,
@@ -385,7 +386,7 @@
@decorators.idempotent_id('d1f032d5-7b6e-48aa-b252-d5f16dd994ca')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_suspend_non_existent_server(self):
# suspend a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -395,7 +396,7 @@
@decorators.idempotent_id('7f323206-05a9-4bf8-996b-dd5b2036501b')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_suspend_server_invalid_state(self):
# suspend a suspended server.
self.client.suspend_server(self.server_id)
@@ -409,7 +410,7 @@
@decorators.idempotent_id('221cd282-bddb-4837-a683-89c2487389b6')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resume_non_existent_server(self):
# resume a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -419,14 +420,14 @@
@decorators.idempotent_id('ccb6294d-c4c9-498f-8a43-554c098bfadb')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_resume_server_invalid_state(self):
# resume an active server.
self.assertRaises(lib_exc.Conflict,
self.client.resume_server,
self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7dd919e7-413f-4198-bebb-35e2a01b13e9')
def test_get_console_output_of_non_existent_server(self):
# get the console output for a non existent server
@@ -435,7 +436,7 @@
self.client.get_console_output,
nonexistent_server, length=10)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6f47992b-5144-4250-9f8b-f00aa33950f3')
def test_force_delete_nonexistent_server_id(self):
# force-delete a non existent server
@@ -444,7 +445,7 @@
self.client.force_delete_server,
nonexistent_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9c6d38cc-fcfb-437a-85b9-7b788af8bf01')
def test_restore_nonexistent_server_id(self):
# restore-delete a non existent server
@@ -453,7 +454,7 @@
self.client.restore_soft_deleted_server,
nonexistent_server)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7fcadfab-bd6a-4753-8db7-4a51e51aade9')
def test_restore_server_invalid_state(self):
# we can only restore-delete a server in 'soft-delete' state
@@ -464,7 +465,7 @@
@decorators.idempotent_id('abca56e2-a892-48ea-b5e5-e07e69774816')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_shelve_non_existent_server(self):
# shelve a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -474,7 +475,7 @@
@decorators.idempotent_id('443e4f9b-e6bf-4389-b601-3a710f15fddd')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_shelve_shelved_server(self):
# shelve a shelved server.
compute.shelve_server(self.client, self.server_id)
@@ -495,7 +496,7 @@
@decorators.idempotent_id('23d23b37-afaf-40d7-aa5d-5726f82d8821')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_unshelve_non_existent_server(self):
# unshelve a non existent server
nonexistent_server = data_utils.rand_uuid()
@@ -505,14 +506,14 @@
@decorators.idempotent_id('8f198ded-1cca-4228-9e65-c6b449c54880')
@testtools.skipUnless(CONF.compute_feature_enabled.shelve,
'Shelve is not available.')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_unshelve_server_invalid_state(self):
# unshelve an active server.
self.assertRaises(lib_exc.Conflict,
self.client.unshelve_server,
self.server_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('74085be3-a370-4ca2-bc51-2d0e10e0f573')
@test.services('volume', 'image')
def test_create_server_from_non_bootable_volume(self):
@@ -566,7 +567,7 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('543d84c1-dd2e-4c6d-8cb2-b9da0efaa384')
def test_update_server_of_another_tenant(self):
# Update name of a server that belongs to another tenant
@@ -576,7 +577,7 @@
self.alt_client.update_server, self.server_id,
name=new_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5c75009d-3eea-423e-bea3-61b09fd25f9c')
def test_delete_a_server_of_another_tenant(self):
# Delete a server that belongs to another tenant
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index b1374d2..173784a 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -33,7 +33,7 @@
super(VirtualInterfacesNegativeTestJSON, cls).setup_clients()
cls.client = cls.servers_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('64ebd03c-1089-4306-93fa-60f5eb5c803c')
@test.services('network')
def test_list_virtual_interfaces_invalid_server_id(self):
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index f87bf6d..3b41775 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -31,7 +31,7 @@
@decorators.idempotent_id('3bb27738-b759-4e0d-a5fa-37d7a6df07d1')
def test_list_extensions(self):
# List of all extensions
- if len(CONF.compute_feature_enabled.api_extensions) == 0:
+ if not CONF.compute_feature_enabled.api_extensions:
raise self.skipException('There are not any extensions configured')
extensions = self.extensions_client.list_extensions()['extensions']
ext = CONF.compute_feature_enabled.api_extensions[0]
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index 0f5ea57..ab63154 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -19,7 +19,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -37,7 +36,7 @@
server_id, host=dest_host, block_migration=bmflm,
disk_over_commit=False)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
def test_invalid_host_for_migration(self):
# Migrating to an invalid host should not change the status
diff --git a/tempest/api/compute/test_versions.py b/tempest/api/compute/test_versions.py
index dcab067..6141553 100644
--- a/tempest/api/compute/test_versions.py
+++ b/tempest/api/compute/test_versions.py
@@ -14,13 +14,12 @@
from tempest.api.compute import base
from tempest.lib import decorators
-from tempest import test
class TestVersions(base.BaseV2ComputeTest):
@decorators.idempotent_id('6c0a0990-43b6-4529-9b61-5fd8daf7c55c')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
def test_list_api_versions(self):
"""Test that a get of the unversioned url returns the choices doc.
@@ -39,7 +38,7 @@
"The first listed version should be v2.0")
@decorators.idempotent_id('b953a29e-929c-4a8e-81be-ec3a7e03cb76')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
def test_get_version_details(self):
"""Test individual version endpoints info works.
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 73c7614..b0a6622 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_log import log as logging
import testtools
from tempest.api.compute import base
@@ -25,8 +24,6 @@
CONF = config.CONF
-LOG = logging.getLogger(__name__)
-
class AttachVolumeTestJSON(base.BaseV2ComputeTest):
max_microversion = '2.19'
@@ -65,6 +62,20 @@
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
server = self._create_server()
+
+ # NOTE(andreaf) Create one remote client used throughout the test.
+ if CONF.validation.run_validation:
+ linux_client = remote_client.RemoteClient(
+ self.get_server_ip(server),
+ self.image_ssh_user,
+ self.image_ssh_password,
+ self.validation_resources['keypair']['private_key'],
+ server=server,
+ servers_client=self.servers_client)
+ # NOTE(andreaf) We need to ensure the ssh key has been
+ # injected in the guest before we power cycle
+ linux_client.validate_authentication()
+
volume = self.create_volume()
attachment = self.attach_volume(server, volume,
device=('/dev/%s' % self.device))
@@ -78,14 +89,6 @@
'ACTIVE')
if CONF.validation.run_validation:
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(server),
- self.image_ssh_user,
- self.image_ssh_password,
- self.validation_resources['keypair']['private_key'],
- server=server,
- servers_client=self.servers_client)
-
disks = linux_client.get_disks()
device_name_to_match = '\n' + self.device + ' '
self.assertIn(device_name_to_match, disks)
@@ -103,14 +106,6 @@
'ACTIVE')
if CONF.validation.run_validation:
- linux_client = remote_client.RemoteClient(
- self.get_server_ip(server),
- self.image_ssh_user,
- self.image_ssh_password,
- self.validation_resources['keypair']['private_key'],
- server=server,
- servers_client=self.servers_client)
-
disks = linux_client.get_disks()
self.assertNotIn(device_name_to_match, disks)
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index c017690..eabb907 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -16,7 +16,6 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -30,8 +29,8 @@
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
raise cls.skipException(skip_msg)
- @test.attr(type=['negative'])
- @test.related_bug('1630783', status_code=500)
+ @decorators.attr(type=['negative'])
+ @decorators.related_bug('1630783', status_code=500)
@decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
def test_delete_attached_volume(self):
server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 7676ee8..80db1be 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -37,7 +36,7 @@
super(VolumesNegativeTest, cls).setup_clients()
cls.client = cls.volumes_extensions_client
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c03ea686-905b-41a2-8748-9635154b7c57')
def test_volume_get_nonexistent_volume_id(self):
# Negative: Should not be able to get details of nonexistent volume
@@ -46,7 +45,7 @@
self.assertRaises(lib_exc.NotFound, self.client.show_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('54a34226-d910-4b00-9ef8-8683e6c55846')
def test_volume_delete_nonexistent_volume_id(self):
# Negative: Should not be able to delete nonexistent Volume
@@ -55,7 +54,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5125ae14-152b-40a7-b3c5-eae15e9022ef')
def test_create_volume_with_invalid_size(self):
# Negative: Should not be able to create volume with invalid size
@@ -65,7 +64,7 @@
self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
size='#$%', display_name=v_name, metadata=metadata)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
def test_create_volume_without_passing_size(self):
# Negative: Should not be able to create volume without passing size
@@ -75,7 +74,7 @@
self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
size='', display_name=v_name, metadata=metadata)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8cce995e-0a83-479a-b94d-e1e40b8a09d1')
def test_create_volume_with_size_zero(self):
# Negative: Should not be able to create volume with size zero
@@ -84,13 +83,13 @@
self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
size='0', display_name=v_name, metadata=metadata)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('62bab09a-4c03-4617-8cca-8572bc94af9b')
def test_get_volume_without_passing_volume_id(self):
# Negative: Should not be able to get volume when empty ID is passed
self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('62972737-124b-4513-b6cf-2f019f178494')
def test_delete_invalid_volume_id(self):
# Negative: Should not be able to delete volume when invalid ID is
@@ -99,7 +98,7 @@
self.client.delete_volume,
data_utils.rand_name('invalid'))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0d1417c5-4ae8-4c2c-adc5-5f0b864253e5')
def test_delete_volume_without_passing_volume_id(self):
# Negative: Should not be able to delete volume when empty ID is passed
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index f80721c..86d06e2 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class RolesNegativeTestJSON(base.BaseIdentityV2AdminTest):
@@ -28,14 +27,14 @@
role = self.setup_test_role()
return (user, tenant, role)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d5d5f1df-f8ca-4de0-b2ef-259c1cc67025')
def test_list_roles_by_unauthorized_user(self):
# Non-administrator user should not be able to list roles
self.assertRaises(lib_exc.Forbidden,
self.non_admin_roles_client.list_roles)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('11a3c7da-df6c-40c2-abc2-badd682edf9f')
def test_list_roles_request_without_token(self):
# Request to list roles without a valid token should fail
@@ -44,14 +43,14 @@
self.assertRaises(lib_exc.Unauthorized, self.roles_client.list_roles)
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c0b89e56-accc-4c73-85f8-9c0f866104c1')
def test_role_create_blank_name(self):
# Should not be able to create a role with a blank name
self.assertRaises(lib_exc.BadRequest, self.roles_client.create_role,
name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('585c8998-a8a4-4641-a5dd-abef7a8ced00')
def test_create_role_by_unauthorized_user(self):
# Non-administrator user should not be able to create role
@@ -60,7 +59,7 @@
self.non_admin_roles_client.create_role,
name=role_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a7edd17a-e34a-4aab-8bb7-fa6f498645b8')
def test_create_role_request_without_token(self):
# Request to create role without a valid token should fail
@@ -71,7 +70,7 @@
self.roles_client.create_role, name=role_name)
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c0cde2c8-81c1-4bb0-8fe2-cf615a3547a8')
def test_role_create_duplicate(self):
# Role names should be unique
@@ -82,7 +81,7 @@
self.assertRaises(lib_exc.Conflict, self.roles_client.create_role,
name=role_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('15347635-b5b1-4a87-a280-deb2bd6d865e')
def test_delete_role_by_unauthorized_user(self):
# Non-administrator user should not be able to delete role
@@ -93,7 +92,7 @@
self.assertRaises(lib_exc.Forbidden,
self.non_admin_roles_client.delete_role, role_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('44b60b20-70de-4dac-beaf-a3fc2650a16b')
def test_delete_role_request_without_token(self):
# Request to delete role without a valid token should fail
@@ -108,7 +107,7 @@
role_id)
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('38373691-8551-453a-b074-4260ad8298ef')
def test_delete_role_non_existent(self):
# Attempt to delete a non existent role should fail
@@ -116,7 +115,7 @@
self.assertRaises(lib_exc.NotFound, self.roles_client.delete_role,
non_existent_role)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('391df5cf-3ec3-46c9-bbe5-5cb58dd4dc41')
def test_assign_user_role_by_unauthorized_user(self):
# Non-administrator user should not be authorized to
@@ -127,7 +126,7 @@
self.non_admin_roles_client.create_user_role_on_project,
tenant['id'], user['id'], role['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f0d2683c-5603-4aee-95d7-21420e87cfd8')
def test_assign_user_role_request_without_token(self):
# Request to assign a role to a user without a valid token
@@ -140,7 +139,7 @@
user['id'], role['id'])
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('99b297f6-2b5d-47c7-97a9-8b6bb4f91042')
def test_assign_user_role_for_non_existent_role(self):
# Attempt to assign a non existent role to user should fail
@@ -150,7 +149,7 @@
self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], non_existent_role)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b2285aaa-9e76-4704-93a9-7a8acd0a6c8f')
def test_assign_user_role_for_non_existent_tenant(self):
# Attempt to assign a role on a non existent tenant should fail
@@ -160,7 +159,7 @@
self.roles_client.create_user_role_on_project,
non_existent_tenant, user['id'], role['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5c3132cd-c4c8-4402-b5ea-71eb44e97793')
def test_assign_duplicate_user_role(self):
# Duplicate user role should not get assigned
@@ -172,7 +171,7 @@
self.roles_client.create_user_role_on_project,
tenant['id'], user['id'], role['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d0537987-0977-448f-a435-904c15de7298')
def test_remove_user_role_by_unauthorized_user(self):
# Non-administrator user should not be authorized to
@@ -186,7 +185,7 @@
self.non_admin_roles_client.delete_role_from_user_on_project,
tenant['id'], user['id'], role['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('cac81cf4-c1d2-47dc-90d3-f2b7eb572286')
def test_remove_user_role_request_without_token(self):
# Request to remove a user's role without a valid token
@@ -201,7 +200,7 @@
tenant['id'], user['id'], role['id'])
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ab32d759-cd16-41f1-a86e-44405fa9f6d2')
def test_remove_user_role_non_existent_role(self):
# Attempt to delete a non existent role from a user should fail
@@ -214,7 +213,7 @@
self.roles_client.delete_role_from_user_on_project,
tenant['id'], user['id'], non_existent_role)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('67a679ec-03dd-4551-bbfc-d1c93284f023')
def test_remove_user_role_non_existent_tenant(self):
# Attempt to remove a role from a non existent tenant should fail
@@ -227,7 +226,7 @@
self.roles_client.delete_role_from_user_on_project,
non_existent_tenant, user['id'], role['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7391ab4c-06f3-477a-a64a-c8e55ce89837')
def test_list_user_roles_by_unauthorized_user(self):
# Non-administrator user should not be authorized to list
@@ -241,7 +240,7 @@
self.non_admin_roles_client.list_user_roles_on_project,
tenant['id'], user['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('682adfb2-fd5f-4b0a-a9ca-322e9bebb907')
def test_list_user_roles_request_without_token(self):
# Request to list user's roles without a valid token should fail
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index f6d4a24..634cf21 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ServicesTestJSON(base.BaseIdentityV2AdminTest):
@@ -78,7 +77,7 @@
self.assertIn('type', service)
self.assertEqual(s_type, service['type'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('34ea6489-012d-4a86-9038-1287cadd5eca')
def test_list_services(self):
# Create, List, Verify and Delete Services
diff --git a/tempest/api/identity/admin/v2/test_tenant_negative.py b/tempest/api/identity/admin/v2/test_tenant_negative.py
index 69f0920..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -17,19 +17,18 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class TenantsNegativeTestJSON(base.BaseIdentityV2AdminTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ca9bb202-63dd-4240-8a07-8ef9c19c04bb')
def test_list_tenants_by_unauthorized_user(self):
# Non-administrator user should not be able to list tenants
self.assertRaises(lib_exc.Forbidden,
self.non_admin_tenants_client.list_tenants)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('df33926c-1c96-4d8d-a762-79cc6b0c3cf4')
def test_list_tenant_request_without_token(self):
# Request to list tenants without a valid token should fail
@@ -39,24 +38,20 @@
self.tenants_client.list_tenants)
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@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'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@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,
@@ -64,26 +59,23 @@
tenant['id'])
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9c9a2aed-6e3c-467a-8f5c-89da9d1b516b')
def test_delete_non_existent_tenant(self):
# Attempt to delete a non existent tenant should fail
self.assertRaises(lib_exc.NotFound, self.tenants_client.delete_tenant,
data_utils.rand_uuid_hex())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('af16f44b-a849-46cb-9f13-a751c388f739')
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)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d26b278a-6389-4702-8d6e-5980d80137e0')
def test_create_tenant_by_unauthorized_user(self):
# Non-administrator user should not be authorized to create a tenant
@@ -92,7 +84,7 @@
self.non_admin_tenants_client.create_tenant,
name=tenant_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a3ee9d7e-6920-4dd5-9321-d4b2b7f0a638')
def test_create_tenant_request_without_token(self):
# Create tenant request without a token should not be authorized
@@ -104,7 +96,7 @@
name=tenant_name)
self.client.auth_provider.clear_auth()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5a2e4ca9-b0c0-486c-9c48-64a94fba2395')
def test_create_tenant_with_empty_name(self):
# Tenant name should not be empty
@@ -112,7 +104,7 @@
self.tenants_client.create_tenant,
name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('2ff18d1e-dfe3-4359-9dc3-abf582c196b9')
def test_create_tenants_name_length_over_64(self):
# Tenant name length should not be greater than 64 characters
@@ -121,31 +113,27 @@
self.tenants_client.create_tenant,
name=tenant_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bd20dc2a-9557-4db7-b755-f48d952ad706')
def test_update_non_existent_tenant(self):
# Attempt to update a non existent tenant should fail
self.assertRaises(lib_exc.NotFound, self.tenants_client.update_tenant,
data_utils.rand_uuid_hex())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@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'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@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 3428c07..b4c9389 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -26,17 +26,12 @@
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.users_client.create_user(name=user_name,
- password=user_password,
- tenantId=tenant['id'],
- email='')['user']
- # Delete the user at the end of the test
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(name=user_name,
+ password=user_password,
+ tenantId=tenant['id'],
+ email='')
# then get a token for the user
body = self.token_client.auth(user_name,
user_password,
@@ -65,32 +60,20 @@
user_name = data_utils.rand_name(name='user')
user_password = data_utils.rand_password()
tenant_id = None # No default tenant so will get unscoped token.
- email = ''
- user = self.users_client.create_user(name=user_name,
- password=user_password,
- tenantId=tenant_id,
- email=email)['user']
- # Delete the user at the end of the test
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(name=user_name,
+ password=user_password,
+ tenantId=tenant_id,
+ email='')
# 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_name = data_utils.rand_name(name='role')
- role = self.roles_client.create_role(name=role_name)['role']
- # Delete the role at the end of the test
- self.addCleanup(self.roles_client.delete_role, role['id'])
+ role = self.setup_test_role()
# Grant the user the role on the tenants.
self.roles_client.create_user_role_on_project(tenant1['id'],
diff --git a/tempest/api/identity/admin/v2/test_users.py b/tempest/api/identity/admin/v2/test_users.py
index df4ded8..2711a2d 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -19,9 +19,7 @@
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
-from tempest import test
class UsersTestJSON(base.BaseIdentityV2AdminTest):
@@ -30,20 +28,14 @@
def resource_setup(cls):
super(UsersTestJSON, cls).resource_setup()
cls.alt_user = data_utils.rand_name('test_user')
- cls.alt_password = data_utils.rand_password()
cls.alt_email = cls.alt_user + '@testmail.tm'
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2d55a71e-da1d-4b43-9c03-d269fd93d905')
def test_create_user(self):
# Create a user
tenant = self.setup_test_tenant()
- user = self.users_client.create_user(name=self.alt_user,
- password=self.alt_password,
- tenantId=tenant['id'],
- email=self.alt_email)['user']
- # Delete the User at the end of the test
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(name=self.alt_user, tenantId=tenant['id'])
self.assertEqual(self.alt_user, user['name'])
@decorators.idempotent_id('89d9fdb8-15c2-4304-a429-48715d0af33d')
@@ -51,13 +43,10 @@
# Create a user with enabled : False
tenant = self.setup_test_tenant()
name = data_utils.rand_name('test_user')
- user = self.users_client.create_user(name=name,
- password=self.alt_password,
- tenantId=tenant['id'],
- email=self.alt_email,
- enabled=False)['user']
- # Delete the User at the end of the test
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(name=name,
+ tenantId=tenant['id'],
+ email=self.alt_email,
+ enabled=False)
self.assertEqual(name, user['name'])
self.assertEqual(False, user['enabled'])
self.assertEqual(self.alt_email, user['email'])
@@ -65,14 +54,9 @@
@decorators.idempotent_id('39d05857-e8a5-4ed4-ba83-0b52d3ab97ee')
def test_update_user(self):
# Test case to check if updating of user attributes is successful.
- test_user = data_utils.rand_name('test_user')
tenant = self.setup_test_tenant()
- user = self.users_client.create_user(name=test_user,
- password=self.alt_password,
- tenantId=tenant['id'],
- email=self.alt_email)['user']
- # Delete the User at the end of this method
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(tenantId=tenant['id'])
+
# Updating user details with new values
u_name2 = data_utils.rand_name('user2')
u_email2 = u_name2 + '@testmail.tm'
@@ -92,15 +76,8 @@
@decorators.idempotent_id('29ed26f4-a74e-4425-9a85-fdb49fa269d2')
def test_delete_user(self):
# Delete a user
- test_user = data_utils.rand_name('test_user')
tenant = self.setup_test_tenant()
- user = self.users_client.create_user(name=test_user,
- password=self.alt_password,
- tenantId=tenant['id'],
- email=self.alt_email)['user']
- # Delete the User at the end of the test
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.users_client.delete_user, user['id'])
+ user = self.create_test_user(tenantId=tenant['id'])
self.users_client.delete_user(user['id'])
@decorators.idempotent_id('aca696c3-d645-4f45-b728-63646045beb1')
@@ -152,24 +129,10 @@
tenant = self.setup_test_tenant()
user_ids = list()
fetched_user_ids = list()
- password1 = data_utils.rand_password()
- alt_tenant_user1 = data_utils.rand_name('tenant_user1')
- user1 = self.users_client.create_user(name=alt_tenant_user1,
- password=password1,
- tenantId=tenant['id'],
- email='user1@123')['user']
+ user1 = self.create_test_user(tenantId=tenant['id'])
user_ids.append(user1['id'])
- # Delete the User at the end of the test
- self.addCleanup(self.users_client.delete_user, user1['id'])
- password2 = data_utils.rand_password()
- alt_tenant_user2 = data_utils.rand_name('tenant_user2')
- user2 = self.users_client.create_user(name=alt_tenant_user2,
- password=password2,
- tenantId=tenant['id'],
- email='user2@123')['user']
+ user2 = self.create_test_user(tenantId=tenant['id'])
user_ids.append(user2['id'])
- # Delete the User at the end of the test
- self.addCleanup(self.users_client.delete_user, user2['id'])
# List of users for the respective tenant ID
body = (self.tenants_client.list_tenant_users(tenant['id'])
['users'])
@@ -195,16 +158,8 @@
role = self.roles_client.create_user_role_on_project(
tenant['id'], user['id'], role['id'])['role']
- alt_user2 = data_utils.rand_name('second_user')
- alt_password2 = data_utils.rand_password()
- second_user = self.users_client.create_user(
- name=alt_user2,
- password=alt_password2,
- tenantId=tenant['id'],
- email='user2@123')['user']
+ second_user = self.create_test_user(tenantId=tenant['id'])
user_ids.append(second_user['id'])
- # Delete the User at the end of the test
- self.addCleanup(self.users_client.delete_user, second_user['id'])
role = self.roles_client.create_user_role_on_project(
tenant['id'], second_user['id'], role['id'])['role']
# List of users with roles for the respective tenant ID
diff --git a/tempest/api/identity/admin/v2/test_users_negative.py b/tempest/api/identity/admin/v2/test_users_negative.py
index 442c1a7..4f47e41 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class UsersNegativeTestJSON(base.BaseIdentityV2AdminTest):
@@ -29,7 +28,7 @@
cls.alt_password = data_utils.rand_password()
cls.alt_email = cls.alt_user + '@testmail.tm'
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('60a1f5fa-5744-4cdf-82bf-60b7de2d29a4')
def test_create_user_by_unauthorized_user(self):
# Non-administrator should not be authorized to create a user
@@ -40,7 +39,7 @@
tenantId=tenant['id'],
email=self.alt_email)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d80d0c2f-4514-4d1e-806d-0930dfc5a187')
def test_create_user_with_empty_name(self):
# User with an empty name should not be created
@@ -50,7 +49,7 @@
tenantId=tenant['id'],
email=self.alt_email)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7704b4f3-3b75-4b82-87cc-931d41c8f780')
def test_create_user_with_name_length_over_255(self):
# Length of user name filed should be restricted to 255 characters
@@ -60,7 +59,7 @@
tenantId=tenant['id'],
email=self.alt_email)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('57ae8558-120c-4723-9308-3751474e7ecf')
def test_create_user_with_duplicate_name(self):
# Duplicate user should not be created
@@ -73,7 +72,7 @@
tenantId=tenant['id'],
email=user['email'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0132cc22-7c4f-42e1-9e50-ac6aad31d59a')
def test_create_user_for_non_existent_tenant(self):
# Attempt to create a user in a non-existent tenant should fail
@@ -83,7 +82,7 @@
tenantId='49ffgg99999',
email=self.alt_email)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('55bbb103-d1ae-437b-989b-bcdf8175c1f4')
def test_create_user_request_without_a_token(self):
# Request to create a user without a valid token should fail
@@ -101,7 +100,7 @@
tenantId=tenant['id'],
email=self.alt_email)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('23a2f3da-4a1a-41da-abdd-632328a861ad')
def test_create_user_with_enabled_non_bool(self):
# Attempt to create a user with valid enabled para should fail
@@ -112,7 +111,7 @@
tenantId=tenant['id'],
email=self.alt_email, enabled=3)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3d07e294-27a0-4144-b780-a2a1bf6fee19')
def test_update_user_for_non_existent_user(self):
# Attempt to update a user non-existent user should fail
@@ -121,7 +120,7 @@
self.assertRaises(lib_exc.NotFound, self.users_client.update_user,
non_existent_id, name=user_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('3cc2a64b-83aa-4b02-88f0-d6ab737c4466')
def test_update_user_request_without_a_token(self):
# Request to update a user without a valid token should fail
@@ -137,7 +136,7 @@
self.assertRaises(lib_exc.Unauthorized, self.users_client.update_user,
self.alt_user)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('424868d5-18a7-43e1-8903-a64f95ee3aac')
def test_update_user_by_unauthorized_user(self):
# Non-administrator should not be authorized to update user
@@ -145,7 +144,7 @@
self.non_admin_users_client.update_user,
self.alt_user)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d45195d5-33ed-41b9-a452-7d0d6a00f6e9')
def test_delete_users_by_unauthorized_user(self):
# Non-administrator user should not be authorized to delete a user
@@ -154,14 +153,14 @@
self.non_admin_users_client.delete_user,
user['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7cc82f7e-9998-4f89-abae-23df36495867')
def test_delete_non_existent_user(self):
# Attempt to delete a non-existent user should fail
self.assertRaises(lib_exc.NotFound, self.users_client.delete_user,
'junk12345123')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('57fe1df8-0aa7-46c0-ae9f-c2e785c7504a')
def test_delete_user_request_without_a_token(self):
# Request to delete a user without a valid token should fail
@@ -177,7 +176,7 @@
self.assertRaises(lib_exc.Unauthorized, self.users_client.delete_user,
self.alt_user)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('593a4981-f6d4-460a-99a1-57a78bf20829')
def test_authentication_for_disabled_user(self):
# Disabled user's token should not get authenticated
@@ -190,7 +189,7 @@
password,
tenant['name'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('440a7a8d-9328-4b7b-83e0-d717010495e4')
def test_authentication_when_tenant_is_disabled(self):
# User's token for a disabled tenant should not be authenticated
@@ -203,7 +202,7 @@
password,
tenant['name'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('921f1ad6-7907-40b8-853f-637e7ee52178')
def test_authentication_with_invalid_tenant(self):
# User's token for an invalid tenant should not be authenticated
@@ -214,7 +213,7 @@
password,
'junktenant1234')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bde9aecd-3b1c-4079-858f-beb5deaa5b5e')
def test_authentication_with_invalid_username(self):
# Non-existent user's token should not get authenticated
@@ -224,7 +223,7 @@
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
'junkuser123', password, tenant['name'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d5308b33-3574-43c3-8d87-1c090c5e1eca')
def test_authentication_with_invalid_password(self):
# User's token with invalid password should not be authenticated
@@ -233,14 +232,14 @@
self.assertRaises(lib_exc.Unauthorized, self.token_client.auth,
user['name'], 'junkpass1234', tenant['name'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('284192ce-fb7c-4909-a63b-9a502e0ddd11')
def test_get_users_by_unauthorized_user(self):
# Non-administrator user should not be authorized to get user list
self.assertRaises(lib_exc.Forbidden,
self.non_admin_users_client.list_users)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a73591ec-1903-4ffe-be42-282b39fefc9d')
def test_get_users_request_without_token(self):
# Request to get list of users without a valid token should fail
@@ -252,7 +251,7 @@
self.assertRaises(lib_exc.Unauthorized, self.users_client.list_users)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f5d39046-fc5f-425c-b29e-bac2632da28e')
def test_list_users_with_invalid_tenant(self):
# Should not be able to return a list of all
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index c752532..75e877a 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -17,7 +17,6 @@
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class CredentialsTestJSON(base.BaseIdentityV3AdminTest):
@@ -52,7 +51,7 @@
def _delete_credential(self, cred_id):
self.creds_client.delete_credential(cred_id)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('7cd59bf9-bda4-4c72-9467-d21cab278355')
def test_credentials_create_get_update_delete(self):
blob = '{"access": "%s", "secret": "%s"}' % (
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index c2ab488..ac2faa9 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -42,13 +42,10 @@
self.addCleanup(self._delete_domain, dom_id)
# create a project in the domain
- proj_name = data_utils.rand_name('proj')
- proj_body = self.projects_client.create_project(
- proj_name, domain_id=dom_id)['project']
+ proj_body = self.setup_test_project(domain_id=dom_id)
proj_id = proj_body['id']
- self.addCleanup(self.projects_client.delete_project, proj_id)
self.assertEqual(proj_body['domain_id'], dom_id,
- "project " + proj_name +
+ "project " + proj_body['name'] +
"doesn't have domain id " + dom_id)
# create a user in the domain, with the previous project as his
diff --git a/tempest/api/identity/admin/v3/test_domain_configuration.py b/tempest/api/identity/admin/v3/test_domain_configuration.py
new file mode 100644
index 0000000..f731697
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_domain_configuration.py
@@ -0,0 +1,184 @@
+# Copyright 2017 AT&T Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.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
+from tempest.lib import exceptions as lib_exc
+
+
+class DomainConfigurationTestJSON(base.BaseIdentityV3AdminTest):
+
+ custom_config = {
+ "identity": {
+ "driver": "ldap"
+ },
+ "ldap": {
+ "url": "ldap://myldap.com:389/",
+ "user_tree_dn": "ou=Users,dc=my_new_root,dc=org"
+ }
+ }
+
+ @classmethod
+ def setup_clients(cls):
+ super(DomainConfigurationTestJSON, cls).setup_clients()
+ cls.client = cls.domain_config_client
+
+ @classmethod
+ def resource_setup(cls):
+ super(DomainConfigurationTestJSON, cls).resource_setup()
+ cls.group = cls.groups_client.create_group(
+ name=data_utils.rand_name('group'),
+ description=data_utils.rand_name('group-desc'))['group']
+
+ @classmethod
+ def resource_cleanup(cls):
+ cls.groups_client.delete_group(cls.group['id'])
+ super(DomainConfigurationTestJSON, cls).resource_cleanup()
+
+ def _create_domain_and_config(self, config):
+ domain = self.setup_test_domain()
+ config = self.client.create_domain_config(domain['id'], **config)[
+ 'config']
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.client.delete_domain_config, domain['id'])
+ return domain, config
+
+ @decorators.idempotent_id('11a02bf0-6f94-4380-b3b0-c8dc18fc0d22')
+ def test_show_default_group_config_and_options(self):
+ # The API supports only the identity and ldap groups. For the ldap
+ # group, a valid value is url or user_tree_dn. For the identity group,
+ # a valid value is driver.
+
+ # Check that the default config has the identity and ldap groups.
+ config = self.client.show_default_config_settings()['config']
+ self.assertIsInstance(config, dict)
+ self.assertIn('identity', config)
+ self.assertIn('ldap', config)
+
+ # Check that the identity group is correct.
+ identity_config = self.client.show_default_group_config('identity')[
+ 'config']
+
+ self.assertIsInstance(identity_config, dict)
+ self.assertIn('identity', identity_config)
+ self.assertIn('driver', identity_config['identity'])
+ self.assertIn('list_limit', identity_config['identity'])
+
+ # Show each option for the default domain and identity group.
+ for config_opt_name in ['driver', 'list_limit']:
+ retrieved_config_opt = self.client.show_default_group_option(
+ 'identity', config_opt_name)['config']
+ self.assertIn(config_opt_name, retrieved_config_opt)
+
+ # Check that the ldap group is correct.
+ ldap_config = self.client.show_default_group_config('ldap')['config']
+
+ self.assertIsInstance(ldap_config, dict)
+ self.assertIn('ldap', ldap_config)
+
+ # Several valid options exist for ldap group.
+ valid_options = ldap_config['ldap'].keys()
+
+ # Show each option for the default domain and ldap group.
+ for config_opt_name in valid_options:
+ retrieved_config_opt = self.client.show_default_group_option(
+ 'ldap', config_opt_name)['config']
+ self.assertIn(config_opt_name, retrieved_config_opt)
+
+ @decorators.idempotent_id('9e3ff13c-f597-4f01-9377-d6c06c2a1477')
+ def test_create_domain_config_and_show_config_groups_and_options(self):
+ domain, created_config = self._create_domain_and_config(
+ self.custom_config)
+
+ # Check that the entire configuration is correct.
+ self.assertEqual(self.custom_config, created_config)
+
+ # Check that each configuration group is correct.
+ for group_name in self.custom_config.keys():
+ group_cfg = self.client.show_domain_group_config(
+ domain['id'], group_name)['config']
+ self.assertIn(group_name, group_cfg)
+ self.assertEqual(self.custom_config[group_name],
+ group_cfg[group_name])
+
+ # Check that each configuration option is correct.
+ for opt_name in self.custom_config[group_name].keys():
+ group_opt = self.client.show_domain_group_option_config(
+ domain['id'], group_name, opt_name)['config']
+ self.assertIn(opt_name, group_opt)
+ self.assertEqual(self.custom_config[group_name][opt_name],
+ group_opt[opt_name])
+
+ @decorators.idempotent_id('7161023e-5dd0-4612-9da0-1bac6ac30b63')
+ def test_create_update_and_delete_domain_config(self):
+ domain, created_config = self._create_domain_and_config(
+ self.custom_config)
+
+ new_config = created_config
+ new_config['ldap']['url'] = data_utils.rand_url()
+
+ # Check that the altered configuration is reflected in updated_config.
+ updated_config = self.client.update_domain_config(
+ domain['id'], **new_config)['config']
+ self.assertEqual(new_config, updated_config)
+
+ # Check that showing the domain config shows the altered configuration.
+ retrieved_config = self.client.show_domain_config(domain['id'])[
+ 'config']
+ self.assertEqual(new_config, retrieved_config)
+
+ # Check that deleting a configuration works.
+ self.client.delete_domain_config(domain['id'])
+ self.assertRaises(lib_exc.NotFound, self.client.show_domain_config,
+ domain['id'])
+
+ @decorators.idempotent_id('c7510fa2-6661-4170-9c6b-4783a80651e9')
+ def test_create_update_and_delete_domain_config_groups_and_opts(self):
+ domain, _ = self._create_domain_and_config(self.custom_config)
+
+ # Check that updating configuration groups work.
+ new_driver = data_utils.rand_name('driver')
+ new_limit = data_utils.rand_int_id(0, 100)
+ new_group_config = {'identity': {'driver': new_driver,
+ 'list_limit': new_limit}}
+
+ updated_config = self.client.update_domain_group_config(
+ domain['id'], 'identity', **new_group_config)['config']
+
+ self.assertEqual(new_driver, updated_config['identity']['driver'])
+ self.assertEqual(new_limit, updated_config['identity']['list_limit'])
+
+ # Check that updating individual configuration group options work.
+ new_driver = data_utils.rand_name('driver')
+
+ updated_config = self.client.update_domain_group_option_config(
+ domain['id'], 'identity', 'driver', driver=new_driver)['config']
+
+ self.assertEqual(new_driver, updated_config['identity']['driver'])
+
+ # Check that deleting individual configuration group options work.
+ self.client.delete_domain_group_option_config(
+ domain['id'], 'identity', 'driver')
+ self.assertRaises(lib_exc.NotFound,
+ self.client.show_domain_group_option_config,
+ domain['id'], 'identity', 'driver')
+
+ # Check that deleting configuration groups work.
+ self.client.delete_domain_group_config(domain['id'], 'identity')
+ self.assertRaises(lib_exc.NotFound,
+ self.client.show_domain_group_config,
+ domain['id'], 'identity')
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index d04e774..86a5764 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
@@ -87,7 +86,7 @@
for domain in fetched_domains:
self.assertEqual(True, domain['enabled'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f2f5b44a-82e8-4dad-8084-0661ea3b18cf')
def test_create_update_delete_domain(self):
# Create domain
@@ -168,7 +167,7 @@
cls.domain_id = CONF.identity.default_domain_id
super(DefaultDomainTestJSON, cls).resource_setup()
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
def test_default_domain_exists(self):
domain = self.domains_client.show_domain(self.domain_id)['domain']
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 280a5a8..61b4fa2 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -17,13 +17,12 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
_interface = 'json'
- @test.attr(type=['negative', 'gate'])
+ @decorators.attr(type=['negative', 'gate'])
@decorators.idempotent_id('1f3fbff5-4e44-400d-9ca1-d953f05f609b')
def test_delete_active_domain(self):
d_name = data_utils.rand_name('domain')
@@ -39,14 +38,14 @@
self.assertRaises(lib_exc.Forbidden, self.domains_client.delete_domain,
domain_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9018461d-7d24-408d-b3fe-ae37e8cd5c9e')
def test_create_domain_with_empty_name(self):
# Domain name should not be empty
self.assertRaises(lib_exc.BadRequest,
self.domains_client.create_domain, name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('37b1bbf2-d664-4785-9a11-333438586eae')
def test_create_domain_with_name_length_over_64(self):
# Domain name length should not ne greater than 64 characters
@@ -55,14 +54,14 @@
self.domains_client.create_domain,
name=d_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
def test_delete_non_existent_domain(self):
# Attempt to delete a non existent domain should fail
self.assertRaises(lib_exc.NotFound, self.domains_client.delete_domain,
data_utils.rand_uuid_hex())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
def test_domain_create_duplicate(self):
domain_name = data_utils.rand_name('domain-dup')
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 0786d82..09f92e2 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -16,7 +16,6 @@
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class EndPointsTestJSON(base.BaseIdentityV3AdminTest):
@@ -109,7 +108,7 @@
fetched_endpoints_id = [e['id'] for e in fetched_endpoints]
self.assertNotIn(endpoint['id'], fetched_endpoints_id)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('37e8f15e-ee7c-4657-a1e7-f6b61e375eff')
def test_update_endpoint(self):
# Creating an endpoint so as to check update endpoint
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index d46e1f0..70dd7b5 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
@@ -48,7 +47,7 @@
cls.services_client.delete_service(s)
super(EndpointsNegativeTestJSON, cls).resource_cleanup()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ac6c137e-4d3d-448f-8c83-4f13d0942651')
def test_create_with_enabled_False(self):
# Enabled should be a boolean, not a string like 'False'
@@ -59,7 +58,7 @@
service_id=self.service_id, interface=interface,
url=url, region=region, enabled='False')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9c43181e-0627-484a-8c79-923e8a59598b')
def test_create_with_enabled_True(self):
# Enabled should be a boolean, not a string like 'True'
@@ -86,13 +85,13 @@
self.assertRaises(lib_exc.BadRequest, self.client.update_endpoint,
endpoint_for_update['id'], enabled=enabled)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('65e41f32-5eb7-498f-a92a-a6ccacf7439a')
def test_update_with_enabled_False(self):
# Enabled should be a boolean, not a string like 'False'
self._assert_update_raises_bad_request('False')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('faba3587-f066-4757-a48e-b4a3f01803bb')
def test_update_with_enabled_True(self):
# Enabled should be a boolean, not a string like 'True'
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index a423a12..2694402 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -16,7 +16,6 @@
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -72,7 +71,7 @@
# Verify that 'description' is not being updated or deleted.
self.assertEqual(old_description, updated_group['description'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('1598521a-2f36-4606-8df9-30772bd51339')
def test_group_users_add_list_delete(self):
name = data_utils.rand_name('Group')
@@ -82,12 +81,8 @@
# add user into group
users = []
for _ in range(3):
- name = data_utils.rand_name('User')
- password = data_utils.rand_password()
- user = self.users_client.create_user(name=name,
- password=password)['user']
+ user = self.create_test_user()
users.append(user)
- self.addCleanup(self.users_client.delete_user, user['id'])
self.groups_client.add_group_user(group['id'], user['id'])
# list users in group
@@ -105,10 +100,7 @@
@decorators.idempotent_id('64573281-d26a-4a52-b899-503cb0f4e4ec')
def test_list_user_groups(self):
# create a user
- user = self.users_client.create_user(
- name=data_utils.rand_name('User'),
- password=data_utils.rand_password())['user']
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user()
# create two groups, and add user into them
groups = []
for _ in range(2):
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 3fe591b..f630f74 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -65,9 +65,7 @@
@decorators.idempotent_id('4e6f0366-97c8-423c-b2be-41eae6ac91c8')
def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Assign role on domains user
self.inherited_roles_client.create_inherited_role_on_domains_user(
self.domain['id'], self.user['id'], src_role['id'])
@@ -91,9 +89,7 @@
@decorators.idempotent_id('c7a8dda2-be50-4fb4-9a9c-e830771078b1')
def test_inherit_assign_list_check_revoke_roles_on_domains_group(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Assign role on domains group
self.inherited_roles_client.create_inherited_role_on_domains_group(
self.domain['id'], self.group['id'], src_role['id'])
@@ -117,9 +113,7 @@
@decorators.idempotent_id('18b70e45-7687-4b72-8277-b8f1a47d7591')
def test_inherit_assign_check_revoke_roles_on_projects_user(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Assign role on projects user
self.inherited_roles_client.create_inherited_role_on_projects_user(
self.project['id'], self.user['id'], src_role['id'])
@@ -134,9 +128,7 @@
@decorators.idempotent_id('26021436-d5a4-4256-943c-ded01e0d4b45')
def test_inherit_assign_check_revoke_roles_on_projects_group(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Assign role on projects group
self.inherited_roles_client.create_inherited_role_on_projects_group(
self.project['id'], self.group['id'], src_role['id'])
@@ -152,17 +144,11 @@
@decorators.idempotent_id('3acf666e-5354-42ac-8e17-8b68893bcd36')
def test_inherit_assign_list_revoke_user_roles_on_domain(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Create a project hierarchy
- leaf_project_name = data_utils.rand_name('project')
- leaf_project = self.projects_client.create_project(
- leaf_project_name, domain_id=self.domain['id'],
- parent_id=self.project['id'])['project']
- self.addCleanup(
- self.projects_client.delete_project, leaf_project['id'])
+ leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+ parent_id=self.project['id'])
# Assign role on domain
self.inherited_roles_client.create_inherited_role_on_domains_user(
@@ -202,17 +188,11 @@
@decorators.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
def test_inherit_assign_list_revoke_user_roles_on_project_tree(self):
# Create role
- src_role = self.roles_client.create_role(
- name=data_utils.rand_name('Role'))['role']
- self.addCleanup(self.roles_client.delete_role, src_role['id'])
+ src_role = self.setup_test_role()
# Create a project hierarchy
- leaf_project_name = data_utils.rand_name('project')
- leaf_project = self.projects_client.create_project(
- leaf_project_name, domain_id=self.domain['id'],
- parent_id=self.project['id'])['project']
- self.addCleanup(
- self.projects_client.delete_project, leaf_project['id'])
+ leaf_project = self.setup_test_project(domain_id=self.domain['id'],
+ parent_id=self.project['id'])
# Assign role on parent project
self.inherited_roles_client.create_inherited_role_on_projects_user(
diff --git a/tempest/api/identity/admin/v3/test_oauth_consumers.py b/tempest/api/identity/admin/v3/test_oauth_consumers.py
new file mode 100644
index 0000000..f06fb8f
--- /dev/null
+++ b/tempest/api/identity/admin/v3/test_oauth_consumers.py
@@ -0,0 +1,91 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.api.identity import base
+from tempest.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as exceptions
+
+
+class OAUTHConsumersV3Test(base.BaseIdentityV3AdminTest):
+
+ def _create_consumer(self):
+ """Creates a consumer with a random description."""
+ description = data_utils.rand_name('test_create_consumer')
+ consumer = self.oauth_consumers_client.create_consumer(
+ description)['consumer']
+ # cleans up created consumers after tests
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.oauth_consumers_client.delete_consumer,
+ consumer['id'])
+ return consumer
+
+ @decorators.idempotent_id('c8307ea6-a86c-47fd-ae7b-5b3b2caca76d')
+ def test_create_and_show_consumer(self):
+ """Tests to make sure that a consumer with parameters is made"""
+ consumer = self._create_consumer()
+ # fetch created consumer from client
+ fetched_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ # assert that the fetched consumer matches the created one and
+ # has all parameters
+ for key in ['description', 'id', 'links']:
+ self.assertEqual(consumer[key], fetched_consumer[key])
+
+ @decorators.idempotent_id('fdfa1b7f-2a31-4354-b2c7-f6ae20554f93')
+ def test_delete_consumer(self):
+ """Tests the delete function."""
+ consumer = self._create_consumer()
+ # fetch consumer from client to confirm it exists
+ fetched_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ self.assertEqual(consumer['id'], fetched_consumer['id'])
+ # delete existing consumer
+ self.oauth_consumers_client.delete_consumer(consumer['id'])
+ # check that consumer no longer exists
+ self.assertRaises(exceptions.NotFound,
+ self.oauth_consumers_client.show_consumer,
+ consumer['id'])
+
+ @decorators.idempotent_id('080a9b1a-c009-47c0-9979-5305bf72e3dc')
+ def test_update_consumer(self):
+ """Tests the update functionality"""
+ # create a new consumer to update
+ consumer = self._create_consumer()
+ # create new description
+ new_description = data_utils.rand_name('test_update_consumer')
+ # update consumer
+ self.oauth_consumers_client.update_consumer(consumer['id'],
+ new_description)
+ # check that the same consumer now has the new description
+ updated_consumer = self.oauth_consumers_client.show_consumer(
+ consumer['id'])['consumer']
+ self.assertEqual(new_description, updated_consumer['description'])
+
+ @decorators.idempotent_id('09ca50de-78f2-4ffb-ac71-f2254036b2b8')
+ def test_list_consumers(self):
+ """Test for listing consumers"""
+ # create two consumers to populate list
+ new_consumer_one = self._create_consumer()
+ new_consumer_two = self._create_consumer()
+ # fetch the list of consumers
+ consumer_list = self.oauth_consumers_client \
+ .list_consumers()['consumers']
+ # add fetched consumer ids to a list
+ id_list = [consumer['id'] for consumer in consumer_list]
+ # check if created consumers are in the list
+ self.assertIn(new_consumer_one['id'], id_list)
+ self.assertIn(new_consumer_two['id'], id_list)
diff --git a/tempest/api/identity/admin/v3/test_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index f74cb1c..730d469 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -16,7 +16,6 @@
from tempest.api.identity import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class PoliciesTestJSON(base.BaseIdentityV3AdminTest):
@@ -44,7 +43,7 @@
missing_pols = [p for p in policy_ids if p not in fetched_ids]
self.assertEqual(0, len(missing_pols))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('e544703a-2f03-4cf2-9b0f-350782fdb0d3')
def test_create_update_delete_policy(self):
# Test to update policy
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 77a5c69..1b1d3f7 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.identity import base
from tempest import config
from tempest.lib.common.utils import data_utils
@@ -28,11 +26,8 @@
@decorators.idempotent_id('0ecf465c-0dc4-4532-ab53-91ffeb74d12d')
def test_project_create_with_description(self):
# Create project with a description
- project_name = data_utils.rand_name('project')
project_desc = data_utils.rand_name('desc')
- project = self.projects_client.create_project(
- project_name, description=project_desc)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(description=project_desc)
project_id = project['id']
desc1 = project['description']
self.assertEqual(desc1, project_desc, 'Description should have '
@@ -47,9 +42,8 @@
# Create project with a domain
domain = self.setup_test_domain()
project_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(
- project_name, domain_id=domain['id'])['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(
+ name=project_name, domain_id=domain['id'])
project_id = project['id']
self.assertEqual(project_name, project['name'])
self.assertEqual(domain['id'], project['domain_id'])
@@ -57,8 +51,6 @@
self.assertEqual(project_name, body['name'])
self.assertEqual(domain['id'], body['domain_id'])
- @testtools.skipUnless(CONF.identity_feature_enabled.reseller,
- 'Reseller not available.')
@decorators.idempotent_id('1854f9c0-70bc-4d11-a08a-1c789d339e3d')
def test_project_create_with_parent(self):
# Create root project without providing a parent_id
@@ -66,10 +58,8 @@
domain_id = domain['id']
root_project_name = data_utils.rand_name('root_project')
- root_project = self.projects_client.create_project(
- root_project_name, domain_id=domain_id)['project']
- self.addCleanup(
- self.projects_client.delete_project, root_project['id'])
+ root_project = self.setup_test_project(
+ name=root_project_name, domain_id=domain_id)
root_project_id = root_project['id']
parent_id = root_project['parent_id']
@@ -80,23 +70,16 @@
# Create a project using root_project_id as parent_id
project_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(
- project_name, domain_id=domain_id,
- parent_id=root_project_id)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(
+ name=project_name, domain_id=domain_id, parent_id=root_project_id)
parent_id = project['parent_id']
self.assertEqual(project_name, project['name'])
self.assertEqual(root_project_id, parent_id)
@decorators.idempotent_id('a7eb9416-6f9b-4dbb-b71b-7f73aaef59d5')
- @testtools.skipUnless(CONF.identity_feature_enabled.reseller,
- 'Reseller not available.')
def test_create_is_domain_project(self):
- project_name = data_utils.rand_name('is_domain_project')
- project = self.projects_client.create_project(
- project_name, domain_id=None, is_domain=True)['project']
+ project = self.setup_test_project(domain_id=None, is_domain=True)
# To delete a domain, we need to disable it first
- self.addCleanup(self.projects_client.delete_project, project['id'])
self.addCleanup(self.projects_client.update_project, project['id'],
enabled=False)
@@ -115,10 +98,7 @@
@decorators.idempotent_id('1f66dc76-50cc-4741-a200-af984509e480')
def test_project_create_enabled(self):
# Create a project that is enabled
- project_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(
- project_name, enabled=True)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(enabled=True)
project_id = project['id']
en1 = project['enabled']
self.assertTrue(en1, 'Enable should be True in response')
@@ -129,10 +109,7 @@
@decorators.idempotent_id('78f96a9c-e0e0-4ee6-a3ba-fbf6dfd03207')
def test_project_create_not_enabled(self):
# Create a project that is not enabled
- project_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(
- project_name, enabled=False)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(enabled=False)
en1 = project['enabled']
self.assertEqual('false', str(en1).lower(),
'Enable should be False in response')
@@ -145,8 +122,7 @@
def test_project_update_name(self):
# Update name attribute of a project
p_name1 = data_utils.rand_name('project')
- project = self.projects_client.create_project(p_name1)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(name=p_name1)
resp1_name = project['name']
@@ -166,11 +142,8 @@
@decorators.idempotent_id('f138b715-255e-4a7d-871d-351e1ef2e153')
def test_project_update_desc(self):
# Update description attribute of a project
- p_name = data_utils.rand_name('project')
p_desc = data_utils.rand_name('desc')
- project = self.projects_client.create_project(
- p_name, description=p_desc)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(description=p_desc)
resp1_desc = project['description']
p_desc2 = data_utils.rand_name('desc2')
@@ -189,11 +162,8 @@
@decorators.idempotent_id('b6b25683-c97f-474d-a595-55d410b68100')
def test_project_update_enable(self):
# Update the enabled attribute of a project
- p_name = data_utils.rand_name('project')
p_en = False
- project = self.projects_client.create_project(p_name,
- enabled=p_en)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project(enabled=p_en)
resp1_en = project['enabled']
@@ -214,9 +184,7 @@
def test_associate_user_to_project(self):
# Associate a user to a project
# Create a Project
- p_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(p_name)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project()
# Create a User
u_name = data_utils.rand_name('user')
diff --git a/tempest/api/identity/admin/v3/test_projects_negative.py b/tempest/api/identity/admin/v3/test_projects_negative.py
index 31e7302..33a9c8c 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -17,30 +17,28 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ProjectsNegativeTestJSON(base.BaseIdentityV3AdminTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('24c49279-45dd-4155-887a-cb738c2385aa')
def test_list_projects_by_unauthorized_user(self):
# Non-admin user should not be able to list projects
self.assertRaises(lib_exc.Forbidden,
self.non_admin_projects_client.list_projects)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('874c3e84-d174-4348-a16b-8c01f599561b')
def test_project_create_duplicate(self):
# Project names should be unique
project_name = data_utils.rand_name('project-dup')
- project = self.projects_client.create_project(project_name)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ self.setup_test_project(name=project_name)
self.assertRaises(lib_exc.Conflict,
self.projects_client.create_project, project_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8fba9de2-3e1f-4e77-812a-60cb68f8df13')
def test_create_project_by_unauthorized_user(self):
# Non-admin user should not be authorized to create a project
@@ -49,14 +47,14 @@
lib_exc.Forbidden, self.non_admin_projects_client.create_project,
project_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7828db17-95e5-475b-9432-9a51b4aa79a9')
def test_create_project_with_empty_name(self):
# Project name should not be empty
self.assertRaises(lib_exc.BadRequest,
self.projects_client.create_project, name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('502b6ceb-b0c8-4422-bf53-f08fdb21e2f0')
def test_create_projects_name_length_over_64(self):
# Project name length should not be greater than 64 characters
@@ -64,18 +62,16 @@
self.assertRaises(lib_exc.BadRequest,
self.projects_client.create_project, project_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8d68c012-89e0-4394-8d6b-ccd7196def97')
def test_project_delete_by_unauthorized_user(self):
# Non-admin user should not be able to delete a project
- project_name = data_utils.rand_name('project')
- project = self.projects_client.create_project(project_name)['project']
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project()
self.assertRaises(
lib_exc.Forbidden, self.non_admin_projects_client.delete_project,
project['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7965b581-60c1-43b7-8169-95d4ab7fc6fb')
def test_delete_non_existent_project(self):
# Attempt to delete a non existent project should fail
diff --git a/tempest/api/identity/admin/v3/test_regions.py b/tempest/api/identity/admin/v3/test_regions.py
index 56ee496..ac550a7 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
-from tempest import test
class RegionsTestJSON(base.BaseIdentityV3AdminTest):
@@ -79,7 +78,7 @@
regions_list = [r['id'] for r in body]
self.assertNotIn(region['id'], regions_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2c12c5b5-efcf-4aa5-90c5-bff1ab0cdbe2')
def test_create_region_with_specific_id(self):
# Create a region with a specific id
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index e07d525..ac56fc6 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -19,7 +19,6 @@
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -74,7 +73,7 @@
self.assertEqual(len(body), 1)
self.assertIn(role_id, fetched_role_ids)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('18afc6c0-46cf-4911-824e-9989cc056c3a')
def test_role_create_update_show_list(self):
r_name = data_utils.rand_name('Role')
@@ -381,3 +380,48 @@
role_assignments = self.role_assignments.list_role_assignments(
effective=True, **params)['role_assignments']
self.assertEmpty(role_assignments)
+
+ @decorators.idempotent_id('3748c316-c18f-4b08-997b-c60567bc6235')
+ def test_list_all_implied_roles(self):
+ # Create inference rule from "roles[0]" to "roles[1]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[1]['id'])
+
+ # Create inference rule from "roles[0]" to "roles[2]"
+ self._create_implied_role(
+ self.roles[0]['id'], self.roles[2]['id'])
+
+ # Create inference rule from "roles[2]" to "role"
+ self._create_implied_role(
+ self.roles[2]['id'], self.role['id'])
+
+ rules = self.roles_client.list_all_role_inference_rules()[
+ 'role_inferences']
+ # Sort the rules by the number of inferences, since there should be 1
+ # inference between "roles[2]" and "role" and 2 inferences for
+ # "roles[0]": between "roles[1]" and "roles[2]".
+ sorted_rules = sorted(rules, key=lambda r: len(r['implies']))
+
+ # Check that 2 sets of rules are returned.
+ self.assertEqual(2, len(sorted_rules))
+ # Check that only 1 inference rule exists between "roles[2]" and "role"
+ self.assertEqual(1, len(sorted_rules[0]['implies']))
+ # Check that 2 inference rules exist for "roles[0]": one between
+ # "roles[1]" and one between "roles[2]".
+ self.assertEqual(2, len(sorted_rules[1]['implies']))
+
+ # Check that "roles[2]" is the "prior_role" and that "role" is the
+ # "implies" role.
+ self.assertEqual(self.roles[2]['id'],
+ sorted_rules[0]['prior_role']['id'])
+ self.assertEqual(self.role['id'],
+ sorted_rules[0]['implies'][0]['id'])
+
+ # Check that "roles[0]" is the "prior_role" and that "roles[1]" and
+ # "roles[2]" are the "implies" roles.
+ self.assertEqual(self.roles[0]['id'],
+ sorted_rules[1]['prior_role']['id'])
+
+ implies_ids = [r['id'] for r in sorted_rules[1]['implies']]
+ self.assertIn(self.roles[1]['id'], implies_ids)
+ self.assertIn(self.roles[2]['id'], implies_ids)
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index 53e005d..ad4e549 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ServicesTestJSON(base.BaseIdentityV3AdminTest):
@@ -29,7 +28,7 @@
self.assertRaises(lib_exc.NotFound, self.services_client.show_service,
service_id)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('5193aad5-bcb7-411d-85b0-b3b61b96ef06')
def test_create_update_get_service(self):
# Creating a Service
diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py
index fabb91c..1a9502a 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -32,12 +32,9 @@
# Create a User
u_name = data_utils.rand_name('user')
u_desc = '%s-description' % u_name
- u_email = '%s@testmail.tm' % u_name
u_password = data_utils.rand_password()
- user = self.users_client.create_user(
- name=u_name, description=u_desc, password=u_password,
- email=u_email)['user']
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(
+ name=u_name, description=u_desc, password=u_password)
# Perform Authentication
resp = self.token.auth(user_id=user['id'],
password=u_password).response
@@ -63,27 +60,19 @@
"""
# Create a user.
- user_name = data_utils.rand_name(name='user')
user_password = data_utils.rand_password()
- user = self.users_client.create_user(name=user_name,
- password=user_password)['user']
- self.addCleanup(self.users_client.delete_user, user['id'])
+ user = self.create_test_user(password=user_password)
# Create a couple projects
project1_name = data_utils.rand_name(name='project')
- project1 = self.projects_client.create_project(
- project1_name)['project']
- self.addCleanup(self.projects_client.delete_project, project1['id'])
+ project1 = self.setup_test_project(name=project1_name)
project2_name = data_utils.rand_name(name='project')
- project2 = self.projects_client.create_project(
- project2_name)['project']
+ project2 = self.setup_test_project(name=project2_name)
self.addCleanup(self.projects_client.delete_project, project2['id'])
# Create a role
- role_name = data_utils.rand_name(name='role')
- role = self.roles_client.create_role(name=role_name)['role']
- self.addCleanup(self.roles_client.delete_role, role['id'])
+ role = self.setup_test_role()
# Grant the user the role on both projects.
self.roles_client.create_user_role_on_project(project1['id'],
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 7a569b7..339b4bb 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -22,7 +22,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -46,21 +45,21 @@
def create_trustor_and_roles(self):
# create a project that trusts will be granted on
- self.trustor_project_name = data_utils.rand_name(name='project')
+ trustor_project_name = data_utils.rand_name(name='project')
project = self.projects_client.create_project(
- self.trustor_project_name, domain_id='default')['project']
+ trustor_project_name, domain_id='default')['project']
self.trustor_project_id = project['id']
self.assertIsNotNone(self.trustor_project_id)
# Create a trustor User
- self.trustor_username = data_utils.rand_name('user')
- u_desc = self.trustor_username + 'description'
- u_email = self.trustor_username + '@testmail.xx'
- self.trustor_password = data_utils.rand_password()
+ trustor_username = data_utils.rand_name('user')
+ u_desc = trustor_username + 'description'
+ u_email = trustor_username + '@testmail.xx'
+ trustor_password = data_utils.rand_password()
user = self.users_client.create_user(
- name=self.trustor_username,
+ name=trustor_username,
description=u_desc,
- password=self.trustor_password,
+ password=trustor_password,
email=u_email,
project_id=self.trustor_project_id,
domain_id='default')['user']
@@ -95,10 +94,10 @@
# Initialize a new client with the trustor credentials
creds = common_creds.get_credentials(
identity_version='v3',
- username=self.trustor_username,
- password=self.trustor_password,
+ username=trustor_username,
+ password=trustor_password,
user_domain_id='default',
- tenant_name=self.trustor_project_name,
+ tenant_name=trustor_project_name,
project_domain_id='default',
domain_id='default')
os = clients.Manager(credentials=creds)
@@ -265,7 +264,7 @@
self.assertEqual(1, len(trusts_get))
self.validate_trust(trusts_get[0], summary=True)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
def test_get_trusts_all(self):
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 0d12ba9..4f271cb 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -42,11 +42,7 @@
# Delete the User at the end of this method
self.addCleanup(self.users_client.delete_user, user['id'])
# Creating second project for updation
- project = self.projects_client.create_project(
- data_utils.rand_name('project'),
- description=data_utils.rand_name('project-desc'))['project']
- # Delete the Project at the end of this method
- self.addCleanup(self.projects_client.delete_project, project['id'])
+ project = self.setup_test_project()
# Updating user details with new values
u_name2 = data_utils.rand_name('user2')
u_email2 = u_name2 + '@testmail.tm'
@@ -102,11 +98,7 @@
# List the projects that a user has access upon
assigned_project_ids = list()
fetched_project_ids = list()
- u_project = self.projects_client.create_project(
- data_utils.rand_name('project'),
- description=data_utils.rand_name('project-desc'))['project']
- # Delete the Project at the end of this method
- self.addCleanup(self.projects_client.delete_project, u_project['id'])
+ u_project = self.setup_test_project()
# Create a user.
u_name = data_utils.rand_name('user')
u_desc = u_name + 'description'
@@ -118,23 +110,15 @@
# Delete the User at the end of this method
self.addCleanup(self.users_client.delete_user, user_body['id'])
# Creating Role
- role_body = self.roles_client.create_role(
- name=data_utils.rand_name('role'))['role']
- # Delete the Role at the end of this method
- self.addCleanup(self.roles_client.delete_role, role_body['id'])
+ role_body = self.setup_test_role()
user = self.users_client.show_user(user_body['id'])['user']
role = self.roles_client.show_role(role_body['id'])['role']
for _ in range(2):
# Creating project so as to assign role
- project_body = self.projects_client.create_project(
- data_utils.rand_name('project'),
- description=data_utils.rand_name('project-desc'))['project']
+ project_body = self.setup_test_project()
project = self.projects_client.show_project(
project_body['id'])['project']
- # Delete the Project at the end of this method
- self.addCleanup(
- self.projects_client.delete_project, project_body['id'])
# Assigning roles to user on project
self.roles_client.create_user_role_on_project(project['id'],
user['id'],
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index 93241c5..09d1b9b 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -17,12 +17,11 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class UsersNegativeTest(base.BaseIdentityV3AdminTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e75f006c-89cc-477b-874d-588e4eab4b17')
def test_create_user_for_non_existent_domain(self):
# Attempt to create a user in a non-existent domain should fail
@@ -34,7 +33,7 @@
email=u_email,
domain_id=data_utils.rand_uuid_hex())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
def test_authentication_for_disabled_user(self):
# Attempt to authenticate for disabled user should fail
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 9339d3c..5dc88b8 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -47,7 +47,7 @@
else:
users = cls.users_client.list_users()['users']
user = [u for u in users if u['name'] == name]
- if len(user) > 0:
+ if user:
return user[0]
@classmethod
@@ -57,20 +57,24 @@
except AttributeError:
tenants = cls.projects_client.list_projects()['projects']
tenant = [t for t in tenants if t['name'] == name]
- if len(tenant) > 0:
+ if tenant:
return tenant[0]
@classmethod
def get_role_by_name(cls, name):
roles = cls.roles_client.list_roles()['roles']
role = [r for r in roles if r['name'] == name]
- if len(role) > 0:
+ if role:
return role[0]
- def _create_test_user(self, **kwargs):
+ def create_test_user(self, **kwargs):
if kwargs.get('password', None) is None:
- user_password = data_utils.rand_password()
- kwargs['password'] = user_password
+ kwargs['password'] = data_utils.rand_password()
+ if 'name' not in kwargs:
+ kwargs['name'] = data_utils.rand_name('test_user')
+ if 'email' not in kwargs:
+ kwargs['email'] = kwargs['name'] + '@testmail.tm'
+
user = self.users_client.create_user(**kwargs)['user']
# Delete the user at the end of the test
self.addCleanup(
@@ -78,9 +82,9 @@
self.users_client.delete_user, user['id'])
return user
- def setup_test_role(self, domain_id=None):
+ def setup_test_role(self, name=None, domain_id=None):
"""Set up a test role."""
- params = {'name': data_utils.rand_name('test_role')}
+ params = {'name': name or data_utils.rand_name('test_role')}
if domain_id:
params['domain_id'] = domain_id
@@ -123,6 +127,12 @@
force_tenant_isolation = True
@classmethod
+ def skip_checks(cls):
+ super(BaseIdentityV2AdminTest, cls).skip_checks()
+ if not CONF.identity_feature_enabled.api_v2_admin:
+ raise cls.skipException('Identity v2 admin not available')
+
+ @classmethod
def setup_clients(cls):
super(BaseIdentityV2AdminTest, cls).setup_clients()
cls.client = cls.os_adm.identity_client
@@ -145,17 +155,16 @@
def setup_test_user(self, password=None):
"""Set up a test user."""
tenant = self.setup_test_tenant()
- username = data_utils.rand_name('test_user')
- email = username + '@testmail.tm'
- user = self._create_test_user(name=username, email=email,
- tenantId=tenant['id'], password=password)
+ 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,
@@ -212,6 +221,8 @@
cls.groups_client = cls.os_adm.groups_client
cls.projects_client = cls.os_adm.projects_client
cls.role_assignments = cls.os_admin.role_assignments_client
+ cls.oauth_consumers_client = cls.os_adm.oauth_consumers_client
+ cls.domain_config_client = cls.os_adm.domain_config_client
if CONF.identity.admin_domain_scope:
# NOTE(andreaf) When keystone policy requires it, the identity
# admin clients for these tests shall use 'domain' scoped tokens.
@@ -241,18 +252,17 @@
def setup_test_user(self, password=None):
"""Set up a test user."""
project = self.setup_test_project()
- username = data_utils.rand_name('test_user')
- email = username + '@testmail.tm'
- user = self._create_test_user(name=username, email=email,
- project_id=project['id'],
- password=password)
+ user = self.create_test_user(project_id=project['id'],
+ password=password)
return user
- def setup_test_project(self):
+ def setup_test_project(self, **kwargs):
"""Set up a test project."""
- project = self.projects_client.create_project(
- name=data_utils.rand_name('test_project'),
- description=data_utils.rand_name('desc'))['project']
+ if 'name' not in kwargs:
+ kwargs['name'] = data_utils.rand_name('test_project')
+ if 'description' not in kwargs:
+ kwargs['description'] = data_utils.rand_name('test_description')
+ project = self.projects_client.create_project(**kwargs)['project']
# Delete the project at the end of the test
self.addCleanup(
test_utils.call_and_ignore_notfound_exc,
diff --git a/tempest/api/identity/v2/test_api_discovery.py b/tempest/api/identity/v2/test_api_discovery.py
index 02caef5..5b9d38c 100644
--- a/tempest/api/identity/v2/test_api_discovery.py
+++ b/tempest/api/identity/v2/test_api_discovery.py
@@ -15,13 +15,12 @@
from tempest.api.identity import base
from tempest.lib import decorators
-from tempest import test
class TestApiDiscovery(base.BaseIdentityV2Test):
"""Tests for API discovery features."""
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('ea889a68-a15f-4166-bfb1-c12456eae853')
def test_api_version_resources(self):
descr = self.non_admin_client.show_api_description()['version']
@@ -32,7 +31,7 @@
for res in expected_resources:
self.assertIn(res, keys)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('007a0be0-78fe-4fdb-bbee-e9216cc17bb2')
def test_api_media_types(self):
descr = self.non_admin_client.show_api_description()['version']
@@ -47,7 +46,7 @@
for s_type in supported_types:
self.assertIn(s_type, media_types)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('77fd6be0-8801-48e6-b9bf-38cdd2f253ec')
def test_api_version_statuses(self):
descr = self.non_admin_client.show_api_description()['version']
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index 177a49d..c04c21b 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -15,14 +15,13 @@
from tempest.api.identity import base
from tempest.lib import decorators
-from tempest import test
class TestApiDiscovery(base.BaseIdentityV3Test):
"""Tests for API discovery features."""
@decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
def test_list_api_versions(self):
# NOTE: Actually this API doesn't depend on v3 API at all, because
# the API operation is "GET /" without v3's endpoint. The reason of
@@ -35,7 +34,7 @@
for res in expected_resources:
self.assertIn(res, version)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
def test_api_version_resources(self):
descr = self.non_admin_client.show_api_description()['version']
@@ -46,7 +45,7 @@
for res in expected_resources:
self.assertIn(res, keys)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('657c1970-4722-4189-8831-7325f3bc4265')
def test_api_media_types(self):
descr = self.non_admin_client.show_api_description()['version']
@@ -61,7 +60,7 @@
for s_type in supported_types:
self.assertIn(s_type, media_types)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('8879a470-abfb-47bb-bb8d-5a7fd279ad1e')
def test_api_version_statuses(self):
descr = self.non_admin_client.show_api_description()['version']
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index f263258..e7998ee 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -34,8 +34,6 @@
super(IdentityV3UsersTest, cls).resource_setup()
cls.creds = cls.os.credentials
cls.user_id = cls.creds.user_id
- cls.username = cls.creds.username
- cls.password = cls.creds.password
def _update_password(self, original_password, password):
self.non_admin_users_client.update_user_password(
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/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index f075cab..2748bd5 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -16,12 +16,11 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ImageMembersNegativeTest(base.BaseV1ImageMembersTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('147a9536-18e3-45da-91ea-b037a028f364')
def test_add_member_with_non_existing_image(self):
# Add member with non existing image.
@@ -30,7 +29,7 @@
self.image_member_client.create_image_member,
non_exist_image, self.alt_tenant_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e1559f05-b667-4f1b-a7af-518b52dc0c0f')
def test_delete_member_with_non_existing_image(self):
# Delete member with non existing image.
@@ -39,7 +38,7 @@
self.image_member_client.delete_image_member,
non_exist_image, self.alt_tenant_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f5720333-dd69-4194-bb76-d2f048addd56')
def test_delete_member_with_non_existing_tenant(self):
# Delete member with non existing tenant.
@@ -49,7 +48,7 @@
self.image_member_client.delete_image_member,
image_id, non_exist_tenant)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f25f89e4-0b6c-453b-a853-1f80b9d7ef26')
def test_get_image_without_membership(self):
# Image is hidden from another tenants.
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index 42ff02f..690b8da 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -18,13 +18,12 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class CreateDeleteImagesNegativeTest(base.BaseV1ImageTest):
"""Here are negative tests for the deletion and creation of images."""
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('036ede36-6160-4463-8c01-c781eee6369d')
def test_register_with_invalid_container_format(self):
# Negative tests for invalid data supplied to POST /images
@@ -33,7 +32,7 @@
'x-image-meta-container_format': 'wrong',
'x-image-meta-disk_format': 'vhd'})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
def test_register_with_invalid_disk_format(self):
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
@@ -41,7 +40,7 @@
'x-image-meta-container_format': 'bare',
'x-image-meta-disk_format': 'wrong'})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ec652588-7e3c-4b67-a2f2-0fa96f57c8fc')
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
@@ -50,13 +49,13 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('04f72aa3-fcec-45a3-81a3-308ef7cc82bc')
def test_delete_image_blank_id(self):
# Return an error while trying to delete an image with blank Id
self.assertRaises(lib_exc.NotFound, self.client.delete_image, '')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('950e5054-a3c7-4dee-ada5-e576f1087abd')
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
@@ -64,13 +63,13 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
invalid_image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4ed757cd-450c-44b1-9fd1-c819748c650d')
def test_delete_image_negative_image_id(self):
# Return an error while trying to delete an image with negative id
self.assertRaises(lib_exc.NotFound, self.client.delete_image, -1)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a4a448ab-3db2-4d2d-b9b2-6a1271241dfe')
def test_delete_image_id_over_character_limit(self):
# Return an error while trying to delete image with id over limit
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 30f9ae2..a5d9773 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -18,12 +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 import test
+from tempest.lib import exceptions as lib_exc
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -32,7 +34,7 @@
class BasicOperationsImagesTest(base.BaseV2ImageTest):
"""Here we test the basic operations of images"""
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('139b765e-7f3d-4b3d-8b37-3ca3876ee318')
def test_register_upload_get_image_file(self):
"""Here we test these functionalities
@@ -74,7 +76,7 @@
body = self.client.show_image_file(image['id'])
self.assertEqual(file_content, body.data)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f848bb94-1c6e-45a4-8726-39e3a5b23535')
def test_delete_image(self):
# Deletes an image by image_id
@@ -96,7 +98,7 @@
images_id = [item['id'] for item in images]
self.assertNotIn(image['id'], images_id)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f66891a7-a35c-41a8-b590-a065c2a1caa6')
def test_update_image(self):
# Updates an image by image_id
@@ -126,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/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index 7a495e7..0208780 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -96,20 +96,3 @@
def test_get_image_members_schema(self):
body = self.schemas_client.show_schema("members")
self.assertEqual("members", body['name'])
-
- @decorators.idempotent_id('cb961424-3f68-4d21-8e36-30ad66fb6bfb')
- def test_get_private_image(self):
- image_id = self._create_image()
- member = self.image_member_client.create_image_member(
- image_id, member=self.alt_tenant_id)
- self.assertEqual(member['member_id'], self.alt_tenant_id)
- self.assertEqual(member['image_id'], image_id)
- self.assertEqual(member['status'], 'pending')
- self.assertNotIn(image_id, self._list_image_ids_as_alt())
- self.alt_image_member_client.update_image_member(image_id,
- self.alt_tenant_id,
- status='accepted')
- self.assertIn(image_id, self._list_image_ids_as_alt())
- self.image_member_client.delete_image_member(image_id,
- self.alt_tenant_id)
- self.assertNotIn(image_id, self._list_image_ids_as_alt())
diff --git a/tempest/api/image/v2/test_images_member_negative.py b/tempest/api/image/v2/test_images_member_negative.py
index ae9630a..caa90f9 100644
--- a/tempest/api/image/v2/test_images_member_negative.py
+++ b/tempest/api/image/v2/test_images_member_negative.py
@@ -13,12 +13,11 @@
from tempest.api.image import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ImagesMemberNegativeTest(base.BaseV2MemberImageTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b79efb37-820d-4cf0-b54c-308b00cf842c')
def test_image_share_invalid_status(self):
image_id = self._create_image()
@@ -30,7 +29,7 @@
image_id, self.alt_tenant_id,
status='notavalidstatus')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('27002f74-109e-4a37-acd0-f91cd4597967')
def test_image_share_owner_cannot_accept(self):
image_id = self._create_image()
diff --git a/tempest/api/image/v2/test_images_negative.py b/tempest/api/image/v2/test_images_negative.py
index 04c752a..b4baf05 100644
--- a/tempest/api/image/v2/test_images_negative.py
+++ b/tempest/api/image/v2/test_images_negative.py
@@ -18,7 +18,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ImagesNegativeTest(base.BaseV2ImageTest):
@@ -34,7 +33,7 @@
** delete the deleted image
"""
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('668743d5-08ad-4480-b2b8-15da34f81d9f')
def test_get_non_existent_image(self):
# get the non-existent image
@@ -42,14 +41,14 @@
self.assertRaises(lib_exc.NotFound, self.client.show_image,
non_existent_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ef45000d-0a72-4781-866d-4cb7bf2562ad')
def test_get_image_null_id(self):
# get image with image_id = NULL
image_id = ""
self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e57fc127-7ba0-4693-92d7-1d8a05ebcba9')
def test_get_delete_deleted_image(self):
# get and delete the deleted image
@@ -68,7 +67,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
image['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6fe40f1c-57bd-4918-89cc-8500f850f3de')
def test_delete_non_existing_image(self):
# delete non-existent image
@@ -76,7 +75,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('32248db1-ab88-4821-9604-c7c369f1f88c')
def test_delete_image_null_id(self):
# delete image with image_id=NULL
@@ -84,7 +83,7 @@
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
image_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('292bd310-369b-41c7-a7a3-10276ef76753')
def test_register_with_invalid_container_format(self):
# Negative tests for invalid data supplied to POST /images
@@ -92,9 +91,22 @@
name='test', container_format='wrong',
disk_format='vhd')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('70c6040c-5a97-4111-9e13-e73665264ce1')
def test_register_with_invalid_disk_format(self):
self.assertRaises(lib_exc.BadRequest, self.client.create_image,
name='test', container_format='bare',
disk_format='wrong')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('ab980a34-8410-40eb-872b-f264752f46e5')
+ def test_delete_protected_image(self):
+ # Create a protected image
+ image = self.create_image(protected=True)
+ self.addCleanup(self.client.update_image, image['id'],
+ [dict(replace="/protected", value=False)])
+
+ # Try deleting the protected image
+ self.assertRaises(lib_exc.Forbidden,
+ self.client.delete_image,
+ image['id'])
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 7032dd4..440fa36 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -16,12 +16,11 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class ImagesTagsNegativeTest(base.BaseV2ImageTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8cd30f82-6f9a-4c6e-8034-c1b51fba43d9')
def test_update_tags_for_non_existing_image(self):
# Update tag with non existing image.
@@ -30,7 +29,7 @@
self.assertRaises(lib_exc.NotFound, self.client.add_image_tag,
non_exist_image, tag)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('39c023a2-325a-433a-9eea-649bf1414b19')
def test_delete_non_existing_tag(self):
# Delete non existing tag.
diff --git a/tempest/api/image/v2/test_versions.py b/tempest/api/image/v2/test_versions.py
index 24f104c..84f1068 100644
--- a/tempest/api/image/v2/test_versions.py
+++ b/tempest/api/image/v2/test_versions.py
@@ -14,13 +14,12 @@
from tempest.api.image import base
from tempest.lib import decorators
-from tempest import test
class VersionsTest(base.BaseV2ImageTest):
@decorators.idempotent_id('659ea30a-a17c-4317-832c-0f68ed23c31d')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
def test_list_versions(self):
versions = self.versions_client.list_versions()['versions']
expected_resources = ('id', 'links', 'status')
diff --git a/tempest/api/network/admin/test_external_networks_negative.py b/tempest/api/network/admin/test_external_networks_negative.py
index 770d91f..0709d2a 100644
--- a/tempest/api/network/admin/test_external_networks_negative.py
+++ b/tempest/api/network/admin/test_external_networks_negative.py
@@ -19,14 +19,13 @@
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
class ExternalNetworksAdminNegativeTestJSON(base.BaseAdminNetworkTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d402ae6c-0be0-4d8e-833b-a738895d98d0')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
diff --git a/tempest/api/network/admin/test_l3_agent_scheduler.py b/tempest/api/network/admin/test_l3_agent_scheduler.py
index e1970b9..85b2472 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -27,12 +27,7 @@
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
- _agent_mode = 'legacy'
- is_dvr_router = False
-
- """
- 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.
@@ -53,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:
@@ -68,42 +59,6 @@
raise exceptions.InvalidConfiguration(msg)
cls.router = cls.create_router()
- if CONF.network.dvr_extra_resources:
- # NOTE(armax): If DVR is an available extension, and the created
- # router is indeed a distributed one, more resources need to be
- # provisioned in order to bind the router to the L3 agent in the
- # Liberty release or older, and are not required since the Mitaka
- # release.
- if test.is_extension_enabled('dvr', 'network'):
- cls.is_dvr_router = cls.admin_routers_client.show_router(
- cls.router['id'])['router'].get('distributed', False)
- if cls.is_dvr_router:
- cls.network = cls.create_network()
- cls.create_subnet(cls.network)
- cls.port = cls.create_port(cls.network)
- cls.routers_client.add_router_interface(
- cls.router['id'], port_id=cls.port['id'])
- # NOTE: Sometimes we have seen this test fail with dvr in,
- # multinode tests, since the dhcp port is not created
- # before the test gets executed and so the router is not
- # scheduled on the given agent. By adding the external
- # gateway info to the router, the router should be properly
- # scheduled in the dvr_snat node. This is a temporary work
- # around to prevent a race condition.
- external_gateway_info = {
- 'network_id': CONF.network.public_network_id,
- 'enable_snat': True}
- cls.admin_routers_client.update_router(
- cls.router['id'],
- external_gateway_info=external_gateway_info)
-
- @classmethod
- def resource_cleanup(cls):
- if cls.is_dvr_router:
- cls.routers_client.remove_router_interface(cls.router['id'],
- port_id=cls.port['id'])
- super(L3AgentSchedulerTestJSON, cls).resource_cleanup()
-
@decorators.idempotent_id('b7ce6e89-e837-4ded-9b78-9ed3c9c6a45a')
def test_list_routers_on_l3_agent(self):
self.admin_agents_client.list_routers_on_l3_agent(self.agent['id'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 2c639da..21688d2 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -39,7 +39,7 @@
msg = "quotas extension not enabled."
raise cls.skipException(msg)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
def test_network_quota_exceeding(self):
# Set the network quota to two
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index a724dc9..7ceeb50 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -106,7 +106,7 @@
# Clean up metering label rules
# Not all classes in the hierarchy have the client class variable
- if len(cls.metering_label_rules) > 0:
+ if cls.metering_label_rules:
label_rules_client = cls.admin_metering_label_rules_client
for metering_label_rule in cls.metering_label_rules:
test_utils.call_and_ignore_notfound_exc(
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index a7ae765..9d6d700 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -16,6 +16,7 @@
import random
import netaddr
+from oslo_utils import netutils
from tempest.api.network import base
from tempest.common.utils import net_info
@@ -92,8 +93,8 @@
port_mac = data_utils.rand_mac_address()
port = self.create_port(self.network, mac_address=port_mac)
real_ip = next(iter(port['fixed_ips']), None)['ip_address']
- eui_ip = data_utils.get_ipv6_addr_by_EUI64(subnet['cidr'],
- port_mac).format()
+ eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+ subnet['cidr'], port_mac))
return real_ip, eui_ip
@decorators.idempotent_id('e5517e62-6f16-430d-a672-f80875493d4c')
@@ -188,10 +189,8 @@
self.network, **kwargs_dhcp)
subnet_slaac = self.create_subnet(self.network, **kwargs)
port_mac = data_utils.rand_mac_address()
- eui_ip = data_utils.get_ipv6_addr_by_EUI64(
- subnet_slaac['cidr'],
- port_mac
- ).format()
+ eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+ subnet_slaac['cidr'], port_mac))
port = self.create_port(self.network, mac_address=port_mac)
real_ips = dict([(k['subnet_id'], k['ip_address'])
for k in port['fixed_ips']])
@@ -236,10 +235,8 @@
self.network, ip_version=4)
subnet_slaac = self.create_subnet(self.network, **kwargs)
port_mac = data_utils.rand_mac_address()
- eui_ip = data_utils.get_ipv6_addr_by_EUI64(
- subnet_slaac['cidr'],
- port_mac
- ).format()
+ eui_ip = str(netutils.get_ipv6_addr_by_EUI64(
+ subnet_slaac['cidr'], port_mac))
port = self.create_port(self.network, mac_address=port_mac)
real_ips = dict([(k['subnet_id'], k['ip_address'])
for k in port['fixed_ips']])
diff --git a/tempest/api/network/test_extensions.py b/tempest/api/network/test_extensions.py
index 2662e9c..014d064 100644
--- a/tempest/api/network/test_extensions.py
+++ b/tempest/api/network/test_extensions.py
@@ -29,7 +29,7 @@
etc/tempest.conf.
"""
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('ef28c7e6-e646-4979-9d67-deb207bc5564')
def test_list_show_extensions(self):
# List available extensions for the project
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index 1dc574b..f0f92ac 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -64,7 +64,7 @@
for i in range(2):
cls.create_port(cls.network)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e8718')
def test_create_list_show_update_delete_floating_ip(self):
# Creates a floating IP
@@ -171,7 +171,7 @@
port_other_router['id'])
self.assertIsNotNone(updated_floating_ip['fixed_ip_address'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('36de4bd0-f09c-43e3-a8e1-1decc1ffd3a5')
def test_create_floating_ip_specifying_a_fixed_ip_address(self):
body = self.floating_ips_client.create_floatingip(
diff --git a/tempest/api/network/test_floating_ips_negative.py b/tempest/api/network/test_floating_ips_negative.py
index cb29d3d..f5830ab 100644
--- a/tempest/api/network/test_floating_ips_negative.py
+++ b/tempest/api/network/test_floating_ips_negative.py
@@ -52,7 +52,7 @@
cls.create_router_interface(router['id'], subnet['id'])
cls.port = cls.create_port(cls.network)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('22996ea8-4a81-4b27-b6e1-fa5df92fa5e8')
def test_create_floatingip_with_port_ext_net_unreachable(self):
self.assertRaises(
@@ -61,7 +61,7 @@
fixed_ip_address=self.port['fixed_ips'][0]
['ip_address'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('50b9aeb4-9f0b-48ee-aa31-fa955a48ff54')
def test_create_floatingip_in_private_network(self):
self.assertRaises(lib_exc.BadRequest,
@@ -71,7 +71,7 @@
fixed_ip_address=self.port['fixed_ips'][0]
['ip_address'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6b3b8797-6d43-4191-985c-c48b773eb429')
def test_associate_floatingip_port_ext_net_unreachable(self):
# Create floating ip
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index fc9f465..269f2c2 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -174,7 +174,7 @@
project_network_v6_mask_bits is the equivalent for ipv6 subnets
"""
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('0e269138-0da6-4efc-a46d-578161e7b221')
def test_create_update_delete_network_subnet(self):
# Create a network
@@ -196,7 +196,7 @@
updated_subnet = body['subnet']
self.assertEqual(updated_subnet['name'], new_name)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2bf13842-c93f-4a69-83ed-717d2ec3b44e')
def test_show_network(self):
# Verify the details of a network
@@ -220,7 +220,7 @@
self.assertNotIn('tenant_id', network)
self.assertNotIn('project_id', network)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f7ffdeda-e200-4a7a-bcbe-05716e86bf43')
def test_list_networks(self):
# Verify the network exists in the list of all networks
@@ -241,7 +241,7 @@
for network in networks:
self.assertEqual(sorted(network.keys()), sorted(fields))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('bd635d81-6030-4dd1-b3b9-31ba0cfdf6cc')
def test_show_subnet(self):
# Verify the details of a subnet
@@ -263,7 +263,7 @@
for field_name in fields:
self.assertEqual(subnet[field_name], self.subnet[field_name])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('db68ba48-f4ea-49e9-81d1-e367f6d0b20a')
def test_list_subnets(self):
# Verify the subnet exists in the list of all subnets
@@ -368,7 +368,7 @@
enable_dhcp=True,
**self.subnet_dict(['gateway', 'host_routes', 'dns_nameservers']))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('af774677-42a9-4e4b-bb58-16fe6a5bc1ec')
@test.requires_ext(extension='external-net', service='network')
@testtools.skipUnless(CONF.network.public_network_id,
@@ -452,7 +452,7 @@
for n in created_ports:
self.assertNotIn(n['id'], ports_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('d4f9024d-1e28-4fc1-a6b1-25dbc6fa11e2')
def test_bulk_create_delete_network(self):
# Creates 2 networks in one request
@@ -468,7 +468,7 @@
self.assertIsNotNone(n['id'])
self.assertIn(n['id'], networks_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('8936533b-c0aa-4f29-8e53-6cc873aec489')
def test_bulk_create_delete_subnet(self):
networks = [self.create_network(), self.create_network()]
@@ -503,7 +503,7 @@
self.assertIsNotNone(n['id'])
self.assertIn(n['id'], subnets_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('48037ff2-e889-4c3b-b86a-8e3f34d2d060')
def test_bulk_create_delete_port(self):
networks = [self.create_network(), self.create_network()]
diff --git a/tempest/api/network/test_networks_negative.py b/tempest/api/network/test_networks_negative.py
index 5cb12fe..bc4f41f 100644
--- a/tempest/api/network/test_networks_negative.py
+++ b/tempest/api/network/test_networks_negative.py
@@ -18,33 +18,32 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class NetworksNegativeTestJSON(base.BaseNetworkTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9293e937-824d-42d2-8d5b-e985ea67002a')
def test_show_non_existent_network(self):
non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.networks_client.show_network,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('d746b40c-5e09-4043-99f7-cba1be8b70df')
def test_show_non_existent_subnet(self):
non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.subnets_client.show_subnet,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a954861d-cbfd-44e8-b0a9-7fab111f235d')
def test_show_non_existent_port(self):
non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.ports_client.show_port,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('98bfe4e3-574e-4012-8b17-b2647063de87')
def test_update_non_existent_network(self):
non_exist_id = data_utils.rand_uuid()
@@ -52,7 +51,7 @@
lib_exc.NotFound, self.networks_client.update_network,
non_exist_id, name="new_name")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('03795047-4a94-4120-a0a1-bd376e36fd4e')
def test_delete_non_existent_network(self):
non_exist_id = data_utils.rand_uuid()
@@ -60,21 +59,21 @@
self.networks_client.delete_network,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1cc47884-ac52-4415-a31c-e7ce5474a868')
def test_update_non_existent_subnet(self):
non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.subnets_client.update_subnet,
non_exist_id, name='new_name')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a176c859-99fb-42ec-a208-8a85b552a239')
def test_delete_non_existent_subnet(self):
non_exist_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound,
self.subnets_client.delete_subnet, non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('13d3b106-47e6-4b9b-8d53-dae947f092fe')
def test_create_port_on_non_existent_network(self):
non_exist_net_id = data_utils.rand_uuid()
@@ -82,14 +81,14 @@
self.ports_client.create_port,
network_id=non_exist_net_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('cf8eef21-4351-4f53-adcd-cc5cb1e76b92')
def test_update_non_existent_port(self):
non_exist_port_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.ports_client.update_port,
non_exist_port_id, name='new_name')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('49ec2bbd-ac2e-46fd-8054-798e679ff894')
def test_delete_non_existent_port(self):
non_exist_port_id = data_utils.rand_uuid()
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 376d4ae..69a1441 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -52,7 +52,7 @@
ports_list = body['ports']
self.assertFalse(port_id in [n['id'] for n in ports_list])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('c72c1c0c-2193-4aca-aaa4-b1442640f51c')
def test_create_update_delete_port(self):
# Verify port creation
@@ -100,7 +100,7 @@
return cidr
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('0435f278-40ae-48cb-a404-b8a087bc09b1')
def test_create_port_in_allowed_allocation_pools(self):
network = self.create_network()
@@ -125,7 +125,7 @@
ip_range = netaddr.IPRange(start_ip_address, end_ip_address)
self.assertIn(ip_address, ip_range)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('c9a685bd-e83f-499c-939f-9f7863ca259f')
def test_show_port(self):
# Verify the details of port
@@ -153,7 +153,7 @@
for field_name in fields:
self.assertEqual(port[field_name], self.port[field_name])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('cf95b358-3e92-4a29-a148-52445e1ac50e')
def test_list_ports(self):
# Verify the port exists in the list of all ports
@@ -342,7 +342,7 @@
self.assertEqual(free_mac_address,
show_port['mac_address'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('4179dcb9-1382-4ced-84fe-1b91c54f5735')
@testtools.skipUnless(
test.is_extension_enabled('security-group', 'network'),
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 742fe59..97ad4d4 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -41,7 +41,7 @@
if cls._ip_version == 4 else
CONF.network.project_network_v6_cidr)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@@ -122,7 +122,7 @@
self._verify_router_gateway(create_body['router']['id'],
exp_ext_gw_info=external_gateway_info)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('b42e6e39-2e37-49cc-a6f4-8467e940900a')
def test_add_remove_router_interface_with_subnet_id(self):
network = self.create_network()
@@ -141,7 +141,7 @@
self.assertEqual(show_port_body['port']['device_id'],
router['id'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2b7d2f37-6748-4d78-92e5-1d590234f0d5')
def test_add_remove_router_interface_with_port_id(self):
network = self.create_network()
@@ -243,6 +243,7 @@
@test.requires_ext(extension='ext-gw-mode', service='network')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
+ @decorators.skip_because(bug='1676207')
def test_create_router_set_gateway_with_fixed_ip(self):
# Don't know public_network_address, so at first create address
# from public_network and delete
@@ -375,7 +376,7 @@
show_body = self.routers_client.show_router(router['id'])
self.assertTrue(show_body['router']['admin_state_up'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('802c73c9-c937-4cef-824b-2191e24a6aab')
def test_add_multiple_router_interfaces(self):
network01 = self.create_network(
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 8d680e9..fdd8dd8 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -45,7 +45,7 @@
if cls._ip_version == 4 else
CONF.network.project_network_v6_cidr)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('37a94fc0-a834-45b9-bd23-9a81d2fd1e22')
def test_router_add_gateway_invalid_network_returns_404(self):
self.assertRaises(lib_exc.NotFound,
@@ -54,7 +54,7 @@
external_gateway_info={
'network_id': self.router['id']})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('11836a18-0b15-4327-a50b-f0d9dc66bddd')
def test_router_add_gateway_net_not_external_returns_400(self):
alt_network = self.create_network()
@@ -66,7 +66,7 @@
external_gateway_info={
'network_id': alt_network['id']})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('957751a3-3c68-4fa2-93b6-eb52ea10db6e')
def test_add_router_interfaces_on_overlapping_subnets_returns_400(self):
network01 = self.create_network(
@@ -82,7 +82,7 @@
self.router['id'],
subnet02['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7101cc02-058a-11e7-93e1-fa163e4fa634')
@test.requires_ext(extension='ext-gw-mode', service='network')
@testtools.skipUnless(CONF.network.public_network_id,
@@ -107,7 +107,7 @@
self.admin_routers_client.create_router,
external_gateway_info=external_gateway_info)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('04df80f9-224d-47f5-837a-bf23e33d1c20')
def test_router_remove_interface_in_use_returns_409(self):
self.routers_client.add_router_interface(self.router['id'],
@@ -116,21 +116,21 @@
self.routers_client.delete_router,
self.router['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c2a70d72-8826-43a7-8208-0209e6360c47')
def test_show_non_existent_router_returns_404(self):
router = data_utils.rand_name('non_exist_router')
self.assertRaises(lib_exc.NotFound, self.routers_client.show_router,
router)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b23d1569-8b0c-4169-8d4b-6abd34fad5c7')
def test_update_non_existent_router_returns_404(self):
router = data_utils.rand_name('non_exist_router')
self.assertRaises(lib_exc.NotFound, self.routers_client.update_router,
router, name="new_name")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c7edc5ad-d09d-41e6-a344-5c0c31e2e3e4')
def test_delete_non_existent_router_returns_404(self):
router = data_utils.rand_name('non_exist_router')
@@ -158,7 +158,7 @@
cls.network = cls.create_network()
cls.subnet = cls.create_subnet(cls.network)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
def test_router_create_tenant_distributed_returns_forbidden(self):
self.assertRaises(lib_exc.Forbidden, self.create_router,
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index d7e5149..0d5e230 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -67,7 +67,7 @@
"rule does not match with %s." %
(key, value))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('e30abd17-fef9-4739-8617-dc26da88e686')
def test_list_security_groups(self):
# Verify the security group belonging to project exist in list
@@ -80,7 +80,7 @@
msg = "Security-group list doesn't contain default security-group"
self.assertIsNotNone(found, msg)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('bfd128e5-3c92-44b6-9d66-7fe29d22c802')
def test_create_list_update_show_delete_security_group(self):
group_create_body, name = self._create_security_group()
@@ -109,7 +109,7 @@
self.assertEqual(show_body['security_group']['description'],
new_description)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('cfb99e0e-7410-4a3d-8a0c-959a63ee77e9')
def test_create_show_delete_security_group_rule(self):
group_create_body, _ = self._create_security_group()
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 218a79a..f51fb33 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -33,7 +33,7 @@
msg = "security-group extension not enabled."
raise cls.skipException(msg)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('424fd5c3-9ddc-486a-b45f-39bf0c820fc6')
def test_show_non_existent_security_group(self):
non_exist_id = data_utils.rand_uuid()
@@ -41,7 +41,7 @@
lib_exc.NotFound, self.security_groups_client.show_security_group,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4c094c09-000b-4e41-8100-9617600c02a6')
def test_show_non_existent_security_group_rule(self):
non_exist_id = data_utils.rand_uuid()
@@ -50,7 +50,7 @@
self.security_group_rules_client.show_security_group_rule,
non_exist_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1f1bb89d-5664-4956-9fcd-83ee0fa603df')
def test_delete_non_existent_security_group(self):
non_exist_id = data_utils.rand_uuid()
@@ -59,7 +59,7 @@
non_exist_id
)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('981bdc22-ce48-41ed-900a-73148b583958')
def test_create_security_group_rule_with_bad_protocol(self):
group_create_body, _ = self._create_security_group()
@@ -72,7 +72,7 @@
security_group_id=group_create_body['security_group']['id'],
protocol=pname, direction='ingress', ethertype=self.ethertype)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5f8daf69-3c5f-4aaa-88c9-db1d66f68679')
def test_create_security_group_rule_with_bad_remote_ip_prefix(self):
group_create_body, _ = self._create_security_group()
@@ -87,7 +87,7 @@
protocol='tcp', direction='ingress', ethertype=self.ethertype,
remote_ip_prefix=remote_ip_prefix)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('4bf786fd-2f02-443c-9716-5b98e159a49a')
def test_create_security_group_rule_with_non_existent_remote_groupid(self):
group_create_body, _ = self._create_security_group()
@@ -103,7 +103,7 @@
protocol='tcp', direction='ingress', ethertype=self.ethertype,
remote_group_id=remote_group_id)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b5c4b247-6b02-435b-b088-d10d45650881')
def test_create_security_group_rule_with_remote_ip_and_group(self):
sg1_body, _ = self._create_security_group()
@@ -119,7 +119,7 @@
ethertype=self.ethertype, remote_ip_prefix=prefix,
remote_group_id=sg2_body['security_group']['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5666968c-fff3-40d6-9efc-df1c8bd01abb')
def test_create_security_group_rule_with_bad_ethertype(self):
group_create_body, _ = self._create_security_group()
@@ -132,7 +132,7 @@
security_group_id=group_create_body['security_group']['id'],
protocol='udp', direction='ingress', ethertype=ethertype)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0d9c7791-f2ad-4e2f-ac73-abf2373b0d2d')
def test_create_security_group_rule_with_invalid_ports(self):
group_create_body, _ = self._create_security_group()
@@ -166,7 +166,7 @@
direction='ingress', ethertype=self.ethertype)
self.assertIn(msg, str(ex))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('2323061e-9fbf-4eb0-b547-7e8fafc90849')
def test_create_additional_default_security_group_fails(self):
# Create security group named 'default', it should be failed.
@@ -175,7 +175,7 @@
self.security_groups_client.create_security_group,
name=name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('966e2b96-023a-11e7-a9e4-fa163e4fa634')
def test_create_security_group_update_name_default(self):
# Update security group name to 'default', it should be failed.
@@ -185,7 +185,7 @@
group_create_body['security_group']['id'],
name="default")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8fde898f-ce88-493b-adc9-4e4692879fc5')
def test_create_duplicate_security_group_rule_fails(self):
# Create duplicate security group rule, it should fail.
@@ -211,7 +211,7 @@
protocol='tcp', direction='ingress', ethertype=self.ethertype,
port_range_min=min_port, port_range_max=max_port)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('be308db6-a7cf-4d5c-9baf-71bafd73f35e')
def test_create_security_group_rule_with_non_existent_security_group(self):
# Create security group rules with not existing security group.
@@ -227,7 +227,7 @@
_ip_version = 6
_project_network_cidr = CONF.network.project_network_v6_cidr
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7607439c-af73-499e-bf64-f687fd12a842')
def test_create_security_group_rule_wrong_ip_prefix_version(self):
group_create_body, _ = self._create_security_group()
diff --git a/tempest/api/network/test_subnetpools_extensions.py b/tempest/api/network/test_subnetpools_extensions.py
index be6bffc..01d7db2 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -46,7 +46,7 @@
msg = "subnet_allocation extension not enabled."
raise cls.skipException(msg)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('62595970-ab1c-4b7f-8fcc-fddfe55e9811')
def test_create_list_show_update_delete_subnetpools(self):
subnetpool_name = data_utils.rand_name('subnetpools')
diff --git a/tempest/api/network/test_versions.py b/tempest/api/network/test_versions.py
index 4f6d5ac..2f01e50 100644
--- a/tempest/api/network/test_versions.py
+++ b/tempest/api/network/test_versions.py
@@ -14,11 +14,10 @@
from tempest.api.network import base
from tempest.lib import decorators
-from tempest import test
class NetworksApiDiscovery(base.BaseNetworkTest):
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('cac8a836-c2e0-4304-b556-cd299c7281d1')
def test_api_version_resources(self):
"""Test that GET / returns expected resources.
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index e0216fd..c075b92 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -80,6 +80,7 @@
def setup_clients(cls):
super(BaseObjectTest, cls).setup_clients()
cls.object_client = cls.os.object_client
+ cls.bulk_client = cls.os.bulk_client
cls.container_client = cls.os.container_client
cls.account_client = cls.os.account_client
cls.capabilities_client = cls.os.capabilities_client
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index d882731..0a72d75 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -56,11 +56,10 @@
def _upload_archive(self, filepath):
# upload an archived file
- params = {'extract-archive': 'tar'}
with open(filepath) as fh:
mydata = fh.read()
- resp, body = self.account_client.create_account(data=mydata,
- params=params)
+ resp, body = self.bulk_client.upload_archive(
+ upload_path='', data=mydata, archive_file_format='tar')
return resp, body
def _check_contents_deleted(self, container_name):
@@ -113,9 +112,7 @@
self._upload_archive(filepath)
data = '%s/%s\n%s' % (container_name, object_name, container_name)
- params = {'bulk-delete': ''}
- resp, body = self.account_client.delete_account(data=data,
- params=params)
+ resp, body = self.bulk_client.delete_bulk_data(data=data)
# When deleting multiple files using the bulk operation, the response
# does not contain 'content-length' header. This is the special case,
@@ -140,10 +137,8 @@
self._upload_archive(filepath)
data = '%s/%s\n%s' % (container_name, object_name, container_name)
- params = {'bulk-delete': ''}
- resp, body = self.account_client.create_account_metadata(
- {}, data=data, params=params)
+ resp, body = self.bulk_client.delete_bulk_data_with_post(data=data)
# When deleting multiple files using the bulk operation, the response
# does not contain 'content-length' header. This is the special case,
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 84e4508..9077f88 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -76,7 +76,7 @@
cls.delete_containers()
super(AccountQuotasTest, cls).resource_cleanup()
- @test.attr(type="smoke")
+ @decorators.attr(type="smoke")
@decorators.idempotent_id('a22ef352-a342-4587-8f47-3bbdb5b039c4')
@test.requires_ext(extension='account_quotas', service='object')
def test_upload_valid_object(self):
@@ -87,7 +87,7 @@
self.assertHeaders(resp, 'Object', 'PUT')
- @test.attr(type=["smoke"])
+ @decorators.attr(type=["smoke"])
@decorators.idempotent_id('63f51f9f-5f1d-4fc6-b5be-d454d70949d6')
@test.requires_ext(extension='account_quotas', service='object')
def test_admin_modify_quota(self):
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 2e85a43..f955d34 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -75,18 +75,20 @@
cls.delete_containers()
super(AccountQuotasNegativeTest, cls).resource_cleanup()
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('d1dc5076-555e-4e6d-9697-28f1fe976324')
@test.requires_ext(extension='account_quotas', service='object')
def test_user_modify_quota(self):
"""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 de64dfe..e54b6e7 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -23,7 +23,6 @@
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
@@ -54,7 +53,7 @@
cls.delete_containers()
super(AccountTest, cls).resource_cleanup()
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('3499406a-ae53-4f8c-b43a-133d4dc6fe3f')
def test_list_containers(self):
# list of all containers should not be empty
@@ -265,7 +264,7 @@
self.assertEqual(sorted(orig_container_list, reverse=True),
container_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
def test_list_account_metadata(self):
# list all account metadata
@@ -273,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):
@@ -292,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()
@@ -300,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()
@@ -318,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()
@@ -329,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()
@@ -341,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()
@@ -354,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_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index d46534b..e98a4f5 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -16,7 +16,6 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -32,7 +31,7 @@
cls.os = cls.os_roles_operator
cls.os_operator = cls.os_roles_operator_alt
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
def test_list_containers_with_non_authorized_user(self):
# list containers using non-authorized user
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index c0f7b3a..9c9d821 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -17,7 +17,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
@@ -47,7 +46,7 @@
self.delete_containers([self.container_name])
super(ObjectACLsNegativeTest, self).tearDown()
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('af587587-0c24-4e15-9822-8352ce711013')
def test_write_object_without_using_creds(self):
# trying to create object with empty headers
@@ -61,7 +60,7 @@
self.object_client.create_object,
self.container_name, object_name, 'data', headers={})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('af85af0b-a025-4e72-a90e-121babf55720')
def test_delete_object_without_using_creds(self):
# create object
@@ -78,7 +77,7 @@
self.object_client.delete_object,
self.container_name, object_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('63d84e37-55a6-42e2-9e5f-276e60e26a00')
def test_write_object_with_non_authorized_user(self):
# attempt to upload another file using non-authorized user
@@ -93,7 +92,7 @@
self.object_client.create_object,
self.container_name, object_name, 'data', headers={})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('abf63359-be52-4feb-87dd-447689fc77fd')
def test_read_object_with_non_authorized_user(self):
# attempt to read object using non-authorized user
@@ -111,7 +110,7 @@
self.object_client.get_object,
self.container_name, object_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('7343ac3d-cfed-4198-9bb0-00149741a492')
def test_delete_object_with_non_authorized_user(self):
# attempt to delete object using non-authorized user
@@ -129,7 +128,7 @@
self.object_client.delete_object,
self.container_name, object_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9ed01334-01e9-41ea-87ea-e6f465582823')
def test_read_object_without_rights(self):
# attempt to read object using non-authorized user
@@ -153,7 +152,7 @@
self.object_client.get_object,
self.container_name, object_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a3a585a7-d8cf-4b65-a1a0-edc2b1204f85')
def test_write_object_without_rights(self):
# attempt to write object using non-authorized user
@@ -174,7 +173,7 @@
self.container_name,
object_name, 'data', headers={})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8ba512ad-aa6e-444e-b882-2906a0ea2052')
def test_write_object_without_write_rights(self):
# attempt to write object using non-authorized user
@@ -199,7 +198,7 @@
self.container_name,
object_name, 'data', headers={})
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b4e366f8-f185-47ab-b789-df4416f9ecdb')
def test_delete_object_without_write_rights(self):
# attempt to delete object using non-authorized user
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index 7ff337e..8266341 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -50,7 +50,7 @@
@decorators.idempotent_id('9a0fb034-86af-4df0-86fa-f8bd7db21ae0')
@test.requires_ext(extension='container_quotas', service='object')
- @test.attr(type="smoke")
+ @decorators.attr(type="smoke")
def test_upload_valid_object(self):
"""Attempts to uploads an object smaller than the bytes quota."""
object_name = data_utils.rand_name(name="TestObject")
@@ -67,7 +67,7 @@
@decorators.idempotent_id('22eeeb2b-3668-4160-baef-44790f65a5a0')
@test.requires_ext(extension='container_quotas', service='object')
- @test.attr(type="smoke")
+ @decorators.attr(type="smoke")
def test_upload_large_object(self):
"""Attempts to upload an object larger than the bytes quota."""
object_name = data_utils.rand_name(name="TestObject")
@@ -84,7 +84,7 @@
@decorators.idempotent_id('3a387039-697a-44fc-a9c0-935de31f426b')
@test.requires_ext(extension='container_quotas', service='object')
- @test.attr(type="smoke")
+ @decorators.attr(type="smoke")
def test_upload_too_many_objects(self):
"""Attempts to upload many objects that exceeds the count limit."""
for _ in range(QUOTA_COUNT):
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index a82d2cc..2b5692d 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -16,7 +16,6 @@
from tempest.api.object_storage import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class ContainerTest(base.BaseObjectTest):
@@ -24,7 +23,7 @@
self.delete_containers()
super(ContainerTest, self).tearDown()
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
def test_create_container(self):
container_name = data_utils.rand_name(name='TestContainer')
@@ -124,7 +123,7 @@
resp, _ = self.container_client.delete_container(container_name)
self.assertHeaders(resp, 'Container', 'DELETE')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
def test_list_container_contents(self):
# get container contents list
@@ -271,7 +270,7 @@
self.assertHeaders(resp, 'Container', 'GET')
self.assertEqual([object_name], object_list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
def test_list_container_metadata(self):
# List container metadata
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index be066ba..a8d70c5 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -20,7 +20,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
-from tempest import test
CONF = config.CONF
@@ -36,7 +35,7 @@
_, body = cls.capabilities_client.list_capabilities()
cls.constraints = body['swift']
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
@testtools.skipUnless(
CONF.object_storage_feature_enabled.discoverability,
@@ -52,7 +51,7 @@
self.assertIn('Container name length of ' + str(max_length + 1) +
' longer than ' + str(max_length), str(ex))
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
@testtools.skipUnless(
CONF.object_storage_feature_enabled.discoverability,
@@ -69,7 +68,7 @@
container_name, metadata=metadata)
self.assertIn('Metadata name too long', str(ex))
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
@testtools.skipUnless(
CONF.object_storage_feature_enabled.discoverability,
@@ -86,7 +85,7 @@
container_name, metadata=metadata)
self.assertIn('Metadata value longer than ' + str(max_length), str(ex))
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
@testtools.skipUnless(
CONF.object_storage_feature_enabled.discoverability,
@@ -106,7 +105,7 @@
self.assertIn('Too many metadata items; max ' + str(max_count),
str(ex))
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('1a95ab2e-b712-4a98-8a4d-8ce21b7557d6')
def test_get_metadata_headers_with_invalid_container_name(self):
# Attempts to retrieve metadata headers with an invalid
@@ -115,7 +114,7 @@
self.container_client.list_container_metadata,
'invalid_container_name')
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
def test_update_metadata_with_nonexistent_container_name(self):
# Attempts to update metadata using a nonexistent container name.
@@ -125,7 +124,7 @@
self.container_client.update_container_metadata,
'nonexistent_container_name', metadata)
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
def test_delete_with_nonexistent_container_name(self):
# Attempts to delete metadata using a nonexistent container name.
@@ -135,7 +134,7 @@
self.container_client.delete_container_metadata,
'nonexistent_container_name', metadata)
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
def test_list_all_container_objects_with_nonexistent_container(self):
# Attempts to get a listing of all objects on a container
@@ -145,7 +144,7 @@
self.container_client.list_container_contents,
'nonexistent_container_name', params)
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
def test_list_all_container_objects_on_deleted_container(self):
# Attempts to get a listing of all objects on a container
@@ -159,7 +158,7 @@
self.container_client.list_container_contents,
container_name, params)
- @test.attr(type=["negative"])
+ @decorators.attr(type=["negative"])
@decorators.idempotent_id('42da116e-1e8c-4c96-9e06-2f13884ed2b1')
def test_delete_non_empty_container(self):
# create a container and an object within it
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 1c45a98..3c11a51 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -22,7 +22,6 @@
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
@@ -128,7 +127,7 @@
resp, object_content = obj_client.get_object(cont, obj_name)
self.assertEqual(object_content, obj_name[::-1].encode())
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.skip_because(bug='1317133')
@decorators.idempotent_id('be008325-1bba-4925-b7dd-93b58f22ce9b')
@testtools.skipIf(
diff --git a/tempest/api/object_storage/test_container_sync_middleware.py b/tempest/api/object_storage/test_container_sync_middleware.py
index df738b3..9eae138 100644
--- a/tempest/api/object_storage/test_container_sync_middleware.py
+++ b/tempest/api/object_storage/test_container_sync_middleware.py
@@ -37,7 +37,7 @@
cls.key = 'sync_key'
cls.cluster_name = CONF.object_storage.cluster_name
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('ea4645a1-d147-4976-82f7-e5a7a3065f80')
@test.requires_ext(extension='container_sync', service='object')
def test_container_synchronization(self):
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 c4e6a3a..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()
@@ -108,7 +110,7 @@
@decorators.idempotent_id('d3fb3c4d-e627-48ce-9379-a1631f21336d')
@test.requires_ext(extension='formpost', service='object')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_post_object_using_form_expired(self):
body, content_type = self.get_multipart_form(expires=1)
time.sleep(2)
@@ -125,7 +127,7 @@
@decorators.idempotent_id('b277257f-113c-4499-b8d1-5fead79f7360')
@test.requires_ext(extension='formpost', service='object')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
def test_post_object_using_form_invalid_signature(self):
self.key = "Wrong"
body, content_type = self.get_multipart_form()
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index d463827..21ea6ae 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -24,7 +24,6 @@
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
@@ -75,7 +74,7 @@
for meta_key in not_in_meta:
self.assertNotIn('x-object-meta-' + meta_key, resp)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('5b4ce26f-3545-46c9-a2ba-5754358a4c62')
def test_create_object(self):
# create object
@@ -319,7 +318,7 @@
object_name)
self.assertHeaders(resp, 'Object', 'DELETE')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('7a94c25d-66e6-434c-9c38-97d4e2c29945')
def test_update_object_metadata(self):
# update object metadata
@@ -460,7 +459,7 @@
object_name)
self.assertNotIn('x-object-meta-test-meta', resp)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('9a447cf6-de06-48de-8226-a8c6ed31caf2')
def test_list_object_metadata(self):
# get object metadata
@@ -530,7 +529,7 @@
self.assertEqual(resp['x-object-manifest'],
'%s/%s' % (self.container_name, object_name))
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('02610ba7-86b7-4272-9ed8-aa8d417cb3cd')
def test_get_object(self):
# retrieve object's data (in response body)
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 9c1393f..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()
@@ -91,7 +92,7 @@
return url
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5a583aca-c804-41ba-9d9a-e7be132bdf0b')
@test.requires_ext(extension='tempurl', service='object')
def test_get_object_after_expiration_time(self):
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 4e17c3d..8d3d344 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -47,13 +47,8 @@
cls.servers_client = cls.os.servers_client
cls.keypairs_client = cls.os.keypairs_client
cls.networks_client = cls.os.networks_client
- cls.volumes_client = cls.os.volumes_client
cls.images_v2_client = cls.os.image_client_v2
-
- if CONF.volume_feature_enabled.api_v2:
- cls.volumes_client = cls.os.volumes_v2_client
- else:
- cls.volumes_client = cls.os.volumes_client
+ cls.volumes_client = cls.os.volumes_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/orchestration/stacks/test_resource_types.py b/tempest/api/orchestration/stacks/test_resource_types.py
index 63376d5..6d0707e 100644
--- a/tempest/api/orchestration/stacks/test_resource_types.py
+++ b/tempest/api/orchestration/stacks/test_resource_types.py
@@ -12,12 +12,11 @@
from tempest.api.orchestration import base
from tempest.lib import decorators
-from tempest import test
class ResourceTypesTest(base.BaseOrchestrationTest):
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('7123d082-3577-4a30-8f00-f805327c4ffd')
def test_resource_type_list(self):
"""Verify it is possible to list resource types."""
@@ -25,7 +24,7 @@
self.assertIsInstance(resource_types, list)
self.assertIn('OS::Nova::Server', resource_types)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('0e85a483-828b-4a28-a0e3-f0a21809192b')
def test_resource_type_show(self):
"""Verify it is possible to get schema about resource types."""
@@ -38,7 +37,7 @@
'attributes', 'resource_type')
self.assertEqual(resource_type, type_schema['resource_type'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('8401821d-65fe-4d43-9fa3-57d5ce3a35c7')
def test_resource_type_template(self):
"""Verify it is possible to get template about resource types."""
diff --git a/tempest/api/orchestration/stacks/test_soft_conf.py b/tempest/api/orchestration/stacks/test_soft_conf.py
index dae0bab..8192999 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -14,7 +14,6 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class TestSoftwareConfig(base.BaseOrchestrationTest):
@@ -75,7 +74,7 @@
self.assertRaises(
lib_exc.NotFound, self.client.show_software_config, config_id)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('136162ed-9445-4b9c-b7fc-306af8b5da99')
def test_get_software_config(self):
"""Testing software config get."""
@@ -83,7 +82,7 @@
api_config = self.client.show_software_config(conf['id'])
self._validate_config(conf, api_config)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('1275c835-c967-4a2c-8d5d-ad533447ed91')
def test_get_deployment_list(self):
"""Getting a list of all deployments"""
@@ -92,7 +91,7 @@
deploy_list['software_deployments']]
self.assertIn(self.deployment_id, deploy_ids)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('fe7cd9f9-54b1-429c-a3b7-7df8451db913')
def test_get_deployment_metadata(self):
"""Testing deployment metadata get"""
@@ -110,7 +109,7 @@
self.assertEqual(config_id,
deployment['software_deployment']['config_id'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f29d21f3-ed75-47cf-8cdc-ef1bdeb4c674')
def test_software_deployment_create_validate(self):
"""Testing software deployment was created as expected."""
@@ -123,7 +122,7 @@
self._validate_deployment(self.action, self.status,
self.status_reason, self.configs[0]['id'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('2ac43ab3-34f2-415d-be2e-eabb4d14ee32')
def test_software_deployment_update_no_metadata_change(self):
"""Testing software deployment update without metadata change."""
@@ -149,7 +148,7 @@
metadata['metadata'][0][key],
test_metadata['metadata'][0][key])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('92c48944-d79d-4595-a840-8e1a581c1a72')
def test_software_deployment_update_with_metadata_change(self):
"""Testing software deployment update with metadata change."""
diff --git a/tempest/api/orchestration/stacks/test_stacks.py b/tempest/api/orchestration/stacks/test_stacks.py
index 0be135b..dc1bb4f 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -13,19 +13,18 @@
from tempest.api.orchestration import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
class StacksTestJSON(base.BaseOrchestrationTest):
empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
def test_stack_list_responds(self):
stacks = self.client.list_stacks()['stacks']
self.assertIsInstance(stacks, list)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('10498bd5-a83e-4b62-a817-ce24afe938fe')
def test_stack_crud_no_resources(self):
stack_name = data_utils.rand_name('heat')
diff --git a/tempest/api/orchestration/stacks/test_templates_negative.py b/tempest/api/orchestration/stacks/test_templates_negative.py
index a90abe2..eb93d95 100644
--- a/tempest/api/orchestration/stacks/test_templates_negative.py
+++ b/tempest/api/orchestration/stacks/test_templates_negative.py
@@ -15,7 +15,6 @@
from tempest.api.orchestration import base
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
class TemplateYAMLNegativeTestJSON(base.BaseOrchestrationTest):
@@ -35,7 +34,7 @@
super(TemplateYAMLNegativeTestJSON, cls).resource_setup()
cls.parameters = {}
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5586cbca-ddc4-4152-9db8-fa1ce5fc1876')
def test_validate_template_url(self):
"""Validating template passing url to it."""
diff --git a/tempest/api/orchestration/stacks/test_volumes.py b/tempest/api/orchestration/stacks/test_volumes.py
index 86323e1..7ddf7af 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -35,18 +35,10 @@
self.assertEqual('available', volume.get('status'))
self.assertEqual(CONF.volume.volume_size, volume.get('size'))
- # Some volume properties have been renamed with Cinder v2
- if CONF.volume_feature_enabled.api_v2:
- description_field = 'description'
- name_field = 'name'
- else:
- description_field = 'display_description'
- name_field = 'display_name'
-
self.assertEqual(template['resources']['volume']['properties'][
- 'description'], volume.get(description_field))
+ 'description'], volume.get('description'))
self.assertEqual(template['resources']['volume']['properties'][
- 'name'], volume.get(name_field))
+ 'name'], volume.get('name'))
def _outputs_verify(self, stack_identifier, template):
self.assertEqual('available',
diff --git a/tempest/api/volume/admin/v2/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
similarity index 94%
rename from tempest/api/volume/admin/v2/test_backends_capabilities.py
rename to tempest/api/volume/admin/test_backends_capabilities.py
index 1060f2b..607fc43 100644
--- a/tempest/api/volume/admin/v2/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -19,7 +19,7 @@
from tempest.lib import decorators
-class BackendsCapabilitiesAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
CAPABILITIES = ('namespace',
'vendor_name',
@@ -34,7 +34,7 @@
@classmethod
def resource_setup(cls):
- super(BackendsCapabilitiesAdminV2TestsJSON, cls).resource_setup()
+ super(BackendsCapabilitiesAdminTestsJSON, cls).resource_setup()
# Get host list, formation: host@backend-name
cls.hosts = [
pool['name'] for pool in
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index eea918a..2db8010 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -20,18 +20,18 @@
CONF = config.CONF
-class VolumeMultiBackendV2Test(base.BaseVolumeAdminTest):
+class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumeMultiBackendV2Test, cls).skip_checks()
+ super(VolumeMultiBackendTest, cls).skip_checks()
if not CONF.volume_feature_enabled.multi_backend:
raise cls.skipException("Cinder multi-backend feature disabled")
@classmethod
def resource_setup(cls):
- super(VolumeMultiBackendV2Test, cls).resource_setup()
+ super(VolumeMultiBackendTest, cls).resource_setup()
# read backend name from a list .
backend_names = set(CONF.volume.backend_names)
@@ -89,7 +89,7 @@
cls.admin_volume_client.delete_volume(volume_id)
cls.admin_volume_client.wait_for_resource_deletion(volume_id)
- super(VolumeMultiBackendV2Test, cls).resource_cleanup()
+ super(VolumeMultiBackendTest, cls).resource_cleanup()
@decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
def test_backend_name_reporting(self):
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index aa6aa98..e31c0ef 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -18,16 +18,16 @@
from tempest.lib import decorators
-class QosSpecsV2TestJSON(base.BaseVolumeAdminTest):
+class QosSpecsTestJSON(base.BaseVolumeAdminTest):
"""Test the Cinder QoS-specs.
Tests for create, list, delete, show, associate,
- disassociate, set/unset key V2 APIs.
+ disassociate, set/unset key APIs.
"""
@classmethod
def resource_setup(cls):
- super(QosSpecsV2TestJSON, cls).resource_setup()
+ super(QosSpecsTestJSON, cls).resource_setup()
# Create admin qos client
# Create a test shared qos-specs for tests
cls.qos_name = utils.rand_name(cls.__name__ + '-QoS')
diff --git a/tempest/api/volume/admin/v2/test_snapshot_manage.py b/tempest/api/volume/admin/test_snapshot_manage.py
similarity index 97%
rename from tempest/api/volume/admin/v2/test_snapshot_manage.py
rename to tempest/api/volume/admin/test_snapshot_manage.py
index e8bd477..a2d5fb1 100644
--- a/tempest/api/volume/admin/v2/test_snapshot_manage.py
+++ b/tempest/api/volume/admin/test_snapshot_manage.py
@@ -23,7 +23,7 @@
CONF = config.CONF
-class SnapshotManageAdminV2Test(base.BaseVolumeAdminTest):
+class SnapshotManageAdminTest(base.BaseVolumeAdminTest):
"""Unmanage & manage snapshots
This feature provides the ability to import/export volume snapshot
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 4d8e5bf..471f39a 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -20,16 +20,16 @@
CONF = config.CONF
-class SnapshotsActionsV2Test(base.BaseVolumeAdminTest):
+class SnapshotsActionsTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(SnapshotsActionsV2Test, cls).skip_checks()
+ super(SnapshotsActionsTest, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
def resource_setup(cls):
- super(SnapshotsActionsV2Test, cls).resource_setup()
+ super(SnapshotsActionsTest, cls).resource_setup()
# Create a test shared volume for tests
cls.volume = cls.create_volume()
@@ -43,7 +43,7 @@
snapshot_id = self.snapshot['id']
self.admin_snapshots_client.reset_snapshot_status(snapshot_id,
status)
- super(SnapshotsActionsV2Test, self).tearDown()
+ super(SnapshotsActionsTest, self).tearDown()
def _create_reset_and_force_delete_temp_snapshot(self, status=None):
# Create snapshot, reset snapshot status,
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
similarity index 96%
rename from tempest/api/volume/admin/v3/test_user_messages.py
rename to tempest/api/volume/admin/test_user_messages.py
index 991397a..20c3538 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.v3 import base
+from tempest.api.volume import base
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -33,7 +33,8 @@
'links']
-class UserMessagesTest(base.VolumesV3AdminTest):
+class UserMessagesTest(base.BaseVolumeAdminTest):
+ _api_version = 3
min_microversion = '3.3'
max_microversion = 'latest'
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 1686582..04d6cf9 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -17,7 +17,7 @@
from tempest.lib import decorators
-class VolumeHostsAdminV2TestsJSON(base.BaseVolumeAdminTest):
+class VolumeHostsAdminTestsJSON(base.BaseVolumeAdminTest):
@decorators.idempotent_id('d5f3efa2-6684-4190-9ced-1c2f526352ad')
def test_list_hosts(self):
diff --git a/tempest/api/volume/admin/test_volume_manage.py b/tempest/api/volume/admin/test_volume_manage.py
new file mode 100644
index 0000000..a039085
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_manage.py
@@ -0,0 +1,81 @@
+# 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.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class VolumeManageAdminTest(base.BaseVolumeAdminTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumeManageAdminTest, cls).skip_checks()
+
+ if not CONF.volume_feature_enabled.manage_volume:
+ raise cls.skipException("Manage volume tests are disabled")
+
+ if len(CONF.volume.manage_volume_ref) != 2:
+ raise cls.skipException("Manage volume ref is not correctly "
+ "configured")
+
+ @decorators.idempotent_id('70076c71-0ce1-4208-a8ff-36a66e65cc1e')
+ def test_unmanage_manage_volume(self):
+ # Create original volume
+ org_vol_id = self.create_volume()['id']
+ org_vol_info = self.admin_volume_client.show_volume(
+ org_vol_id)['volume']
+
+ # Unmanage the original volume
+ self.admin_volume_client.unmanage_volume(org_vol_id)
+ self.admin_volume_client.wait_for_resource_deletion(org_vol_id)
+
+ # Verify the original volume does not exist in volume list
+ params = {'all_tenants': 1}
+ all_tenants_volumes = self.admin_volume_client.list_volumes(
+ detail=True, params=params)['volumes']
+ self.assertNotIn(org_vol_id, [v['id'] for v in all_tenants_volumes])
+
+ # Manage volume
+ new_vol_name = data_utils.rand_name(
+ self.__class__.__name__ + '-volume')
+ new_vol_ref = {
+ 'name': new_vol_name,
+ 'host': org_vol_info['os-vol-host-attr:host'],
+ 'ref': {CONF.volume.manage_volume_ref[0]:
+ CONF.volume.manage_volume_ref[1] % org_vol_id},
+ 'volume_type': org_vol_info['volume_type'],
+ 'availability_zone': org_vol_info['availability_zone']}
+ new_vol_id = self.admin_volume_manage_client.manage_volume(
+ **new_vol_ref)['volume']['id']
+ self.addCleanup(self.delete_volume,
+ self.admin_volume_client, new_vol_id)
+ waiters.wait_for_volume_resource_status(self.admin_volume_client,
+ new_vol_id, 'available')
+
+ # Compare the managed volume with the original
+ new_vol_info = self.admin_volume_client.show_volume(
+ new_vol_id)['volume']
+ self.assertNotIn(new_vol_id, [org_vol_id])
+ self.assertEqual(new_vol_info['name'], new_vol_name)
+ for key in ['size',
+ 'volume_type',
+ 'availability_zone',
+ 'os-vol-host-attr:host']:
+ self.assertEqual(new_vol_info[key], org_vol_info[key])
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/test_volume_pools.py
similarity index 61%
rename from tempest/api/volume/admin/v2/test_volume_pools.py
rename to tempest/api/volume/admin/test_volume_pools.py
index 91d092d..60a3bda 100644
--- a/tempest/api/volume/admin/v2/test_volume_pools.py
+++ b/tempest/api/volume/admin/test_volume_pools.py
@@ -14,29 +14,26 @@
# under the License.
from tempest.api.volume import base
+from tempest import config
from tempest.lib import decorators
+CONF = config.CONF
-class VolumePoolsAdminV2TestsJSON(base.BaseVolumeAdminTest):
- @classmethod
- def resource_setup(cls):
- super(VolumePoolsAdminV2TestsJSON, cls).resource_setup()
- # Create a test shared volume for tests
- cls.volume = cls.create_volume()
-
- def _assert_host_volume_in_pools(self, with_detail=False):
- volume_info = self.admin_volume_client.show_volume(
- self.volume['id'])['volume']
+class VolumePoolsAdminTestsJSON(base.BaseVolumeAdminTest):
+ def _assert_pools(self, with_detail=False):
cinder_pools = self.admin_volume_client.show_pools(
detail=with_detail)['pools']
- self.assertIn(volume_info['os-vol-host-attr:host'],
- [pool['name'] for pool in cinder_pools])
+ self.assertIn('name', cinder_pools[0])
+ if with_detail:
+ self.assertIn(CONF.volume.vendor_name,
+ [pool['capabilities']['vendor_name']
+ for pool in cinder_pools])
@decorators.idempotent_id('0248a46c-e226-4933-be10-ad6fca8227e7')
def test_get_pools_without_details(self):
- self._assert_host_volume_in_pools()
+ self._assert_pools()
@decorators.idempotent_id('d4bb61f7-762d-4437-b8a4-5785759a0ced')
def test_get_pools_with_details(self):
- self._assert_host_volume_in_pools(with_detail=True)
+ self._assert_pools(with_detail=True)
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 97f61c1..58ca92f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -21,17 +21,23 @@
QUOTA_USAGE_KEYS = ['reserved', 'limit', 'in_use']
-class BaseVolumeQuotasAdminV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_credentials(cls):
- super(BaseVolumeQuotasAdminV2TestJSON, cls).setup_credentials()
+ super(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
cls.alt_client = cls.os_alt.volumes_client
+ @classmethod
+ def setup_clients(cls):
+ super(BaseVolumeQuotasAdminTestJSON, cls).setup_clients()
+ cls.transfer_client = cls.os.volume_transfers_v2_client
+ cls.alt_transfer_client = cls.os_alt.volume_transfers_v2_client
+
@decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
def test_list_quotas(self):
quotas = (self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
@@ -136,13 +142,13 @@
self.alt_client.tenant_id, params={'usage': True})['quota_set']
# Creates a volume transfer
- transfer = self.volumes_client.create_volume_transfer(
+ transfer = self.transfer_client.create_volume_transfer(
volume_id=volume['id'])['transfer']
transfer_id = transfer['id']
auth_key = transfer['auth_key']
# Accepts a volume transfer
- self.alt_client.accept_volume_transfer(
+ self.alt_transfer_client.accept_volume_transfer(
transfer_id, auth_key=auth_key)['transfer']
# Verify volume transferred is available
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 1051445..63f61e4 100644
--- a/tempest/api/volume/admin/test_volume_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_quotas_negative.py
@@ -17,22 +17,21 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
-class BaseVolumeQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class BaseVolumeQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
@classmethod
def setup_credentials(cls):
- super(BaseVolumeQuotasNegativeV2TestJSON, cls).setup_credentials()
+ super(BaseVolumeQuotasNegativeTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
@classmethod
def resource_setup(cls):
- super(BaseVolumeQuotasNegativeV2TestJSON, cls).resource_setup()
+ super(BaseVolumeQuotasNegativeTestJSON, cls).resource_setup()
cls.shared_quota_set = {'gigabytes': 2 * CONF.volume.volume_size,
'volumes': 1}
@@ -46,14 +45,14 @@
# they are created using utility wrapper methods.
cls.volume = cls.create_volume()
- @test.attr(type='negative')
+ @decorators.attr(type='negative')
@decorators.idempotent_id('bf544854-d62a-47f2-a681-90f7a47d86b6')
def test_quota_volumes(self):
self.assertRaises(lib_exc.OverLimit,
self.volumes_client.create_volume,
size=CONF.volume.volume_size)
- @test.attr(type='negative')
+ @decorators.attr(type='negative')
@decorators.idempotent_id('2dc27eee-8659-4298-b900-169d71a91374')
def test_quota_volume_gigabytes(self):
# NOTE(gfidente): quota set needs to be changed for this test
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index 4d32fdd..94d5299 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -23,11 +23,11 @@
LOG = logging.getLogger(__name__)
-class VolumeRetypeWithMigrationV2Test(base.BaseVolumeAdminTest):
+class VolumeRetypeWithMigrationTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumeRetypeWithMigrationV2Test, cls).skip_checks()
+ super(VolumeRetypeWithMigrationTest, cls).skip_checks()
if not CONF.volume_feature_enabled.multi_backend:
raise cls.skipException("Cinder multi-backend feature disabled.")
@@ -38,7 +38,7 @@
@classmethod
def resource_setup(cls):
- super(VolumeRetypeWithMigrationV2Test, cls).resource_setup()
+ super(VolumeRetypeWithMigrationTest, cls).resource_setup()
# read backend name from a list.
backend_src = CONF.volume.backend_names[0]
backend_dst = CONF.volume.backend_names[1]
@@ -73,7 +73,7 @@
fetched_vol['id'])
break
- super(VolumeRetypeWithMigrationV2Test, cls).resource_cleanup()
+ super(VolumeRetypeWithMigrationTest, cls).resource_cleanup()
@decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
def test_available_volume_retype_with_migration(self):
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index aa5145d..4aab9c1 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -13,18 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
from tempest.api.volume import base
-from tempest import config
from tempest.lib import decorators
-CONF = config.CONF
-
-
def _get_host(host):
return host.split('@')[0]
-class VolumesServicesV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesServicesTestJSON(base.BaseVolumeAdminTest):
"""Tests Volume Services API.
volume service list requires admin privileges.
@@ -32,7 +28,7 @@
@classmethod
def resource_setup(cls):
- super(VolumesServicesV2TestJSON, cls).resource_setup()
+ super(VolumesServicesTestJSON, cls).resource_setup()
cls.services = (cls.admin_volume_services_client.list_services()
['services'])
# NOTE: Cinder service-list API returns the list contains
@@ -71,6 +67,20 @@
# on order.
self.assertEqual(sorted(s1), sorted(s2))
+ @decorators.idempotent_id('67ec6902-f91d-4dec-91fa-338523208bbc')
+ def test_get_service_by_volume_host_name(self):
+ volume_id = self.create_volume()['id']
+ volume = self.admin_volume_client.show_volume(volume_id)['volume']
+ hostname = _get_host(volume['os-vol-host-attr:host'])
+
+ services = (self.admin_volume_services_client.list_services(
+ host=hostname, binary='cinder-volume')['services'])
+
+ self.assertNotEqual(0, len(services),
+ 'cinder-volume not found on host %s' % hostname)
+ self.assertEqual(hostname, _get_host(services[0]['host']))
+ self.assertEqual('cinder-volume', services[0]['binary'])
+
@decorators.idempotent_id('ffa6167c-4497-4944-a464-226bbdb53908')
def test_get_service_by_service_and_host_name(self):
diff --git a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
index 6320fb1..fa8c7ab 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -17,28 +17,27 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
-class VolumeSnapshotQuotasNegativeV2TestJSON(base.BaseVolumeAdminTest):
+class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
force_tenant_isolation = True
@classmethod
def skip_checks(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).skip_checks()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException('Cinder volume snapshots are disabled')
@classmethod
def setup_credentials(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).setup_credentials()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).setup_credentials()
cls.demo_tenant_id = cls.os.credentials.tenant_id
@classmethod
def resource_setup(cls):
- super(VolumeSnapshotQuotasNegativeV2TestJSON, cls).resource_setup()
+ super(VolumeSnapshotQuotasNegativeTestJSON, cls).resource_setup()
cls.default_volume_size = CONF.volume.volume_size
cls.shared_quota_set = {'gigabytes': 3 * cls.default_volume_size,
'volumes': 1, 'snapshots': 1}
@@ -54,14 +53,14 @@
cls.volume = cls.create_volume()
cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
- @test.attr(type='negative')
+ @decorators.attr(type='negative')
@decorators.idempotent_id('02bbf63f-6c05-4357-9d98-2926a94064ff')
def test_quota_volume_snapshots(self):
self.assertRaises(lib_exc.OverLimit,
self.snapshots_client.create_snapshot,
volume_id=self.volume['id'])
- @test.attr(type='negative')
+ @decorators.attr(type='negative')
@decorators.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
def test_quota_volume_gigabytes_snapshots(self):
self.addCleanup(self.admin_quotas_client.update_quota_set,
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
similarity index 96%
rename from tempest/api/volume/admin/v2/test_volume_type_access.py
rename to tempest/api/volume/admin/test_volume_type_access.py
index b60b33b..297ab6e 100644
--- a/tempest/api/volume/admin/v2/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -23,13 +23,13 @@
CONF = config.CONF
-class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesAccessTest(base.BaseVolumeAdminTest):
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_clients(cls):
- super(VolumeTypesAccessV2Test, cls).setup_clients()
+ super(VolumeTypesAccessTest, cls).setup_clients()
cls.alt_client = cls.os_alt.volumes_client
@decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index fd333eb..ac717f8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -22,7 +22,7 @@
CONF = config.CONF
-class VolumeTypesV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesTest(base.BaseVolumeAdminTest):
@decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
@@ -122,43 +122,55 @@
fetched_volume_type['os-volume-type-access:is_public'])
@decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
- def test_volume_type_encryption_create_get_delete(self):
- # Create/get/delete encryption type.
- provider = "LuksEncryptor"
- control_location = "front-end"
- body = self.create_volume_type()
+ def test_volume_type_encryption_create_get_update_delete(self):
+ # Create/get/update/delete encryption type.
+ create_kwargs = {'provider': 'LuksEncryptor',
+ 'control_location': 'front-end'}
+ volume_type_id = self.create_volume_type()['id']
+
# Create encryption type
encryption_type = \
self.admin_encryption_types_client.create_encryption_type(
- body['id'], provider=provider,
- control_location=control_location)['encryption']
+ volume_type_id, **create_kwargs)['encryption']
self.assertIn('volume_type_id', encryption_type)
- self.assertEqual(provider, encryption_type['provider'],
- "The created encryption_type provider is not equal "
- "to the requested provider")
- self.assertEqual(control_location, encryption_type['control_location'],
- "The created encryption_type control_location is not "
- "equal to the requested control_location")
+ for key in create_kwargs:
+ self.assertEqual(create_kwargs[key], encryption_type[key],
+ 'The created encryption_type %s is different '
+ 'from the requested encryption_type' % key)
# Get encryption type
+ encrypt_type_id = encryption_type['volume_type_id']
fetched_encryption_type = (
self.admin_encryption_types_client.show_encryption_type(
- encryption_type['volume_type_id']))
- self.assertEqual(provider,
- fetched_encryption_type['provider'],
- 'The fetched encryption_type provider is different '
- 'from the created encryption_type')
- self.assertEqual(control_location,
- fetched_encryption_type['control_location'],
- 'The fetched encryption_type control_location is '
- 'different from the created encryption_type')
+ encrypt_type_id))
+ for key in create_kwargs:
+ self.assertEqual(create_kwargs[key], fetched_encryption_type[key],
+ 'The fetched encryption_type %s is different '
+ 'from the created encryption_type' % key)
+
+ # Update encryption type
+ update_kwargs = {'key_size': 128,
+ 'provider': 'SomeProvider',
+ 'cipher': 'aes-xts-plain64',
+ 'control_location': 'back-end'}
+ self.admin_encryption_types_client.update_encryption_type(
+ encrypt_type_id, **update_kwargs)
+ updated_encryption_type = (
+ self.admin_encryption_types_client.show_encryption_type(
+ encrypt_type_id))
+ for key in update_kwargs:
+ self.assertEqual(update_kwargs[key], updated_encryption_type[key],
+ 'The fetched encryption_type %s is different '
+ 'from the updated encryption_type' % key)
# Delete encryption type
- type_id = encryption_type['volume_type_id']
- self.admin_encryption_types_client.delete_encryption_type(type_id)
- self.admin_encryption_types_client.wait_for_resource_deletion(type_id)
+ self.admin_encryption_types_client.delete_encryption_type(
+ encrypt_type_id)
+ self.admin_encryption_types_client.wait_for_resource_deletion(
+ encrypt_type_id)
deleted_encryption_type = (
- self.admin_encryption_types_client.show_encryption_type(type_id))
+ self.admin_encryption_types_client.show_encryption_type(
+ encrypt_type_id))
self.assertEmpty(deleted_encryption_type)
@decorators.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 6a7ee3b..b5a2fb7 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -18,11 +18,11 @@
from tempest.lib import exceptions as lib_exc
-class VolumeTypesExtraSpecsV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesExtraSpecsTest(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
+ super(VolumeTypesExtraSpecsTest, cls).resource_setup()
cls.volume_type = cls.create_volume_type()
@decorators.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index 37422ca..4fa934e 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -17,18 +17,17 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
-class ExtraSpecsNegativeV2Test(base.BaseVolumeAdminTest):
+class ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(ExtraSpecsNegativeV2Test, cls).resource_setup()
+ super(ExtraSpecsNegativeTest, cls).resource_setup()
extra_specs = {"spec1": "val1"}
cls.volume_type = cls.create_volume_type(extra_specs=extra_specs)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
def test_update_no_body(self):
# Should not update volume type extra specs with no body
@@ -37,7 +36,7 @@
self.admin_volume_types_client.update_volume_type_extra_specs,
self.volume_type['id'], "spec1", None)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
def test_update_nonexistent_extra_spec_id(self):
# Should not update volume type extra specs with nonexistent id.
@@ -48,7 +47,7 @@
self.volume_type['id'], data_utils.rand_uuid(),
extra_spec)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
def test_update_none_extra_spec_id(self):
# Should not update volume type extra specs with none id.
@@ -58,7 +57,7 @@
self.admin_volume_types_client.update_volume_type_extra_specs,
self.volume_type['id'], None, extra_spec)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
def test_update_multiple_extra_spec(self):
# Should not update volume type extra specs with multiple specs as
@@ -70,7 +69,7 @@
self.volume_type['id'], list(extra_spec)[0],
extra_spec)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
def test_create_nonexistent_type_id(self):
# Should not create volume type extra spec for nonexistent volume
@@ -81,7 +80,7 @@
self.admin_volume_types_client.create_volume_type_extra_specs,
data_utils.rand_uuid(), extra_specs)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
def test_create_none_body(self):
# Should not create volume type extra spec for none POST body.
@@ -90,7 +89,7 @@
self.admin_volume_types_client.create_volume_type_extra_specs,
self.volume_type['id'], None)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
def test_create_invalid_body(self):
# Should not create volume type extra spec for invalid POST body.
@@ -99,7 +98,7 @@
self.admin_volume_types_client.create_volume_type_extra_specs,
self.volume_type['id'], extra_specs=['invalid'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
def test_delete_nonexistent_volume_type_id(self):
# Should not delete volume type extra spec for nonexistent
@@ -109,7 +108,7 @@
self.admin_volume_types_client.delete_volume_type_extra_specs,
data_utils.rand_uuid(), "spec1")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
def test_list_nonexistent_volume_type_id(self):
# Should not list volume type extra spec for nonexistent type id.
@@ -118,7 +117,7 @@
self.admin_volume_types_client.list_volume_types_extra_specs,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
def test_get_nonexistent_volume_type_id(self):
# Should not get volume type extra spec for nonexistent type id.
@@ -127,7 +126,7 @@
self.admin_volume_types_client.show_volume_type_extra_specs,
data_utils.rand_uuid(), "spec1")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
def test_get_nonexistent_extra_spec_id(self):
# Should not get volume type extra spec for nonexistent extra spec
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index 4017de6..4cad52a 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -17,12 +17,11 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
-class VolumeTypesNegativeV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesNegativeTest(base.BaseVolumeAdminTest):
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('b48c98f2-e662-4885-9b71-032256906314')
def test_create_with_nonexistent_volume_type(self):
# Should not be able to create volume with nonexistent volume_type.
@@ -31,7 +30,7 @@
self.assertRaises(lib_exc.NotFound,
self.volumes_client.create_volume, **params)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('878b4e57-faa2-4659-b0d1-ce740a06ae81')
def test_create_with_empty_name(self):
# Should not be able to create volume type with an empty name.
@@ -39,7 +38,7 @@
lib_exc.BadRequest,
self.admin_volume_types_client.create_volume_type, name='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('994610d6-0476-4018-a644-a2602ef5d4aa')
def test_get_nonexistent_type_id(self):
# Should not be able to get volume type with nonexistent type id.
@@ -47,7 +46,7 @@
self.admin_volume_types_client.show_volume_type,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('6b3926d2-7d73-4896-bc3d-e42dfd11a9f6')
def test_delete_nonexistent_type_id(self):
# Should not be able to delete volume type with nonexistent type id.
@@ -55,7 +54,7 @@
self.admin_volume_types_client.delete_volume_type,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8c09f849-f225-4d78-ba87-bffd9a5e0c6f')
def test_create_volume_with_private_volume_type(self):
# Should not be able to create volume with private volume type.
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 02b10f9..7f291e9 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -17,7 +17,7 @@
from tempest.lib import decorators
-class VolumesActionsV2Test(base.BaseVolumeAdminTest):
+class VolumesActionsTest(base.BaseVolumeAdminTest):
def _create_reset_and_force_delete_temp_volume(self, status=None):
# Create volume, reset volume status, and force delete temp volume
@@ -32,7 +32,9 @@
def test_volume_reset_status(self):
# test volume reset status : available->error->available
volume = self.create_volume()
- for status in ['error', 'available']:
+ self.addCleanup(self.admin_volume_client.reset_volume_status,
+ volume['id'], status='available')
+ for status in ['error', 'available', 'maintenance']:
self.admin_volume_client.reset_volume_status(
volume['id'], status=status)
volume_get = self.admin_volume_client.show_volume(
@@ -53,3 +55,8 @@
def test_volume_force_delete_when_volume_is_error(self):
# test force delete when status of volume is error
self._create_reset_and_force_delete_temp_volume('error')
+
+ @decorators.idempotent_id('b957cabd-1486-4e21-90cf-a9ed3c39dfb2')
+ def test_volume_force_delete_when_volume_is_maintenance(self):
+ # test force delete when status of volume is maintenance
+ self._create_reset_and_force_delete_temp_volume('maintenance')
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 7d2127f..a6f9246 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -25,11 +25,11 @@
CONF = config.CONF
-class VolumesBackupsAdminV2Test(base.BaseVolumeAdminTest):
+class VolumesBackupsAdminTest(base.BaseVolumeAdminTest):
@classmethod
def skip_checks(cls):
- super(VolumesBackupsAdminV2Test, cls).skip_checks()
+ super(VolumesBackupsAdminTest, cls).skip_checks()
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
diff --git a/tempest/api/volume/admin/v2/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
similarity index 95%
rename from tempest/api/volume/admin/v2/test_volumes_list.py
rename to tempest/api/volume/admin/test_volumes_list.py
index 6bab373..9d98b7a 100644
--- a/tempest/api/volume/admin/v2/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -23,11 +23,11 @@
CONF = config.CONF
-class VolumesListAdminV2TestJSON(base.BaseVolumeAdminTest):
+class VolumesListAdminTestJSON(base.BaseVolumeAdminTest):
@classmethod
def resource_setup(cls):
- super(VolumesListAdminV2TestJSON, cls).resource_setup()
+ super(VolumesListAdminTestJSON, cls).resource_setup()
# Create 3 test volumes
# NOTE(zhufl): When using pre-provisioned credentials, the project
# may have volumes other than those created below.
diff --git a/tempest/api/volume/admin/v2/__init__.py b/tempest/api/volume/admin/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/admin/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/admin/v3/__init__.py b/tempest/api/volume/admin/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/admin/v3/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 2362d30..52ec423 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -13,9 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
+from tempest.api.volume import api_microversion_fixture
from tempest.common import compute
from tempest.common import waiters
from tempest import config
+from tempest.lib.common import api_version_utils
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions
@@ -24,7 +26,8 @@
CONF = config.CONF
-class BaseVolumeTest(tempest.test.BaseTestCase):
+class BaseVolumeTest(api_version_utils.BaseMicroversionTest,
+ tempest.test.BaseTestCase):
"""Base test case class for all Cinder API tests."""
_api_version = 2
@@ -49,6 +52,10 @@
msg = ("Invalid Cinder API version (%s)" % cls._api_version)
raise exceptions.InvalidConfiguration(msg)
+ api_version_utils.check_skip_with_microversion(
+ cls.min_microversion, cls.max_microversion,
+ CONF.volume.min_microversion, CONF.volume.max_microversion)
+
@classmethod
def setup_credentials(cls):
cls.set_network_resources()
@@ -58,9 +65,11 @@
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
+ if CONF.service_available.glance:
+ cls.images_client = cls.os.image_client_v2
+
cls.snapshots_client = cls.os.snapshots_v2_client
cls.volumes_client = cls.os.volumes_v2_client
cls.backups_client = cls.os.backups_v2_client
@@ -68,10 +77,21 @@
cls.availability_zone_client = (
cls.os.volume_v2_availability_zone_client)
cls.volume_limits_client = cls.os.volume_v2_limits_client
+ cls.messages_client = cls.os.volume_v3_messages_client
+ cls.versions_client = cls.os.volume_v3_versions_client
+
+ def setUp(self):
+ super(BaseVolumeTest, self).setUp()
+ self.useFixture(api_microversion_fixture.APIMicroversionFixture(
+ self.request_microversion))
@classmethod
def resource_setup(cls):
super(BaseVolumeTest, cls).resource_setup()
+ cls.request_microversion = (
+ api_version_utils.select_request_microversion(
+ cls.min_microversion,
+ CONF.volume.min_microversion))
cls.snapshots = []
cls.volumes = []
@@ -130,6 +150,9 @@
"""Wrapper utility that returns a test backup."""
if backup_client is None:
backup_client = self.backups_client
+ if 'name' not in kwargs:
+ name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
+ kwargs['name'] = name
backup = backup_client.create_backup(
volume_id=volume_id, **kwargs)['backup']
@@ -229,6 +252,7 @@
cls.admin_volume_services_client = \
cls.os_adm.volume_services_v2_client
cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_volume_manage_client = cls.os_adm.volume_manage_v2_client
cls.admin_volume_client = cls.os_adm.volumes_v2_client
cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
cls.admin_snapshot_manage_client = \
@@ -243,6 +267,8 @@
cls.os_adm.volume_capabilities_v2_client
cls.admin_scheduler_stats_client = \
cls.os_adm.volume_scheduler_stats_v2_client
+ cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
@classmethod
def resource_setup(cls):
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index f0d653f..666efdf 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -17,12 +17,12 @@
from tempest.lib import decorators
-class AvailabilityZoneV2TestJSON(base.BaseVolumeTest):
- """Tests Availability Zone V2 API List"""
+class AvailabilityZoneTestJSON(base.BaseVolumeTest):
+ """Tests Availability Zone API List"""
@classmethod
def setup_clients(cls):
- super(AvailabilityZoneV2TestJSON, cls).setup_clients()
+ super(AvailabilityZoneTestJSON, cls).setup_clients()
cls.client = cls.availability_zone_client
@decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index ac3742c..39ce00c 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -25,14 +25,14 @@
LOG = logging.getLogger(__name__)
-class ExtensionsV2TestJSON(base.BaseVolumeTest):
+class ExtensionsTestJSON(base.BaseVolumeTest):
@decorators.idempotent_id('94607eb0-43a5-47ca-82aa-736b41bd2e2c')
def test_list_extensions(self):
# List of all extensions
extensions = (self.volumes_extension_client.list_extensions()
['extensions'])
- if len(CONF.volume_feature_enabled.api_extensions) == 0:
+ if not CONF.volume_feature_enabled.api_extensions:
raise self.skipException('There are not any extensions configured')
extension_list = [extension.get('alias') for extension in extensions]
LOG.debug("Cinder extensions: %s", ','.join(extension_list))
diff --git a/tempest/api/volume/v2/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
similarity index 87%
rename from tempest/api/volume/v2/test_image_metadata.py
rename to tempest/api/volume/test_image_metadata.py
index 9c082b3..77baf18 100644
--- a/tempest/api/volume/v2/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -23,11 +23,18 @@
CONF = config.CONF
-class VolumesV2ImageMetadata(base.BaseVolumeTest):
+class VolumesImageMetadata(base.BaseVolumeTest):
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesImageMetadata, cls).skip_checks()
+ if not CONF.service_available.glance:
+ skip_msg = ("%s skipped as Glance is not available" % cls.__name__)
+ raise cls.skipException(skip_msg)
@classmethod
def resource_setup(cls):
- super(VolumesV2ImageMetadata, cls).resource_setup()
+ super(VolumesImageMetadata, cls).resource_setup()
# Create a volume from image ID
cls.volume = cls.create_volume(imageRef=CONF.compute.image_ref)
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index c1d4e05..164ed37 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -22,16 +22,16 @@
CONF = config.CONF
-class SnapshotV2MetadataTestJSON(base.BaseVolumeTest):
+class SnapshotMetadataTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(SnapshotV2MetadataTestJSON, cls).skip_checks()
+ super(SnapshotMetadataTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder snapshot feature disabled")
@classmethod
def resource_setup(cls):
- super(SnapshotV2MetadataTestJSON, cls).resource_setup()
+ super(SnapshotMetadataTestJSON, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
# Create a snapshot
@@ -41,7 +41,7 @@
# Update the metadata to {}
self.snapshots_client.update_snapshot_metadata(
self.snapshot['id'], metadata={})
- super(SnapshotV2MetadataTestJSON, self).tearDown()
+ super(SnapshotMetadataTestJSON, self).tearDown()
@decorators.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
def test_crud_snapshot_metadata(self):
diff --git a/tempest/api/volume/v3/test_versions.py b/tempest/api/volume/test_versions.py
similarity index 87%
rename from tempest/api/volume/v3/test_versions.py
rename to tempest/api/volume/test_versions.py
index 20f1657..0083a3b 100644
--- a/tempest/api/volume/v3/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -12,15 +12,16 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.v3 import base
+from tempest.api.volume import base
from tempest.lib import decorators
-from tempest import test
-class VersionsTest(base.VolumesV3Test):
+class VersionsTest(base.BaseVolumeTest):
+
+ _api_version = 3
@decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
def test_list_versions(self):
# NOTE: The version data is checked on service client side
# with JSON-Schema validation. It is enough to just call
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 8e3c343..836e489 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -21,14 +21,14 @@
CONF = config.CONF
-class AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+class AbsoluteLimitsTests(base.BaseVolumeTest):
# avoid existing volumes of pre-defined tenant
force_tenant_isolation = True
@classmethod
def resource_setup(cls):
- super(AbsoluteLimitsV2Tests, cls).resource_setup()
+ super(AbsoluteLimitsTests, cls).resource_setup()
# Create a shared volume for tests
cls.volume = cls.create_volume()
diff --git a/tempest/api/volume/test_volume_delete_cascade.py b/tempest/api/volume/test_volume_delete_cascade.py
new file mode 100644
index 0000000..bb32c11
--- /dev/null
+++ b/tempest/api/volume/test_volume_delete_cascade.py
@@ -0,0 +1,101 @@
+# Copyright 2016 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 operator
+
+import testtools
+
+from tempest.api.volume import base
+from tempest import config
+from tempest.lib import decorators
+
+CONF = config.CONF
+
+
+class VolumesDeleteCascade(base.BaseVolumeTest):
+ """Delete a volume with associated snapshots.
+
+ Cinder provides the ability to delete a volume with its
+ associated snapshots.
+ It is allow a volume and its snapshots to be removed in one operation
+ both for usability and performance reasons.
+ """
+
+ @classmethod
+ def skip_checks(cls):
+ super(VolumesDeleteCascade, cls).skip_checks()
+ if not CONF.volume_feature_enabled.snapshot:
+ raise cls.skipException("Cinder snapshot feature disabled")
+
+ def _assert_cascade_delete(self, volume_id):
+ # Fetch volume ids
+ volume_list = [
+ vol['id'] for vol in
+ self.volumes_client.list_volumes()['volumes']
+ ]
+
+ # Verify the parent volume was deleted
+ self.assertNotIn(volume_id, volume_list)
+
+ # List snapshots
+ snapshot_list = self.snapshots_client.list_snapshots()['snapshots']
+
+ # Verify snapshots were deleted
+ self.assertNotIn(volume_id, map(operator.itemgetter('volume_id'),
+ snapshot_list))
+
+ @decorators.idempotent_id('994e2d40-de37-46e8-b328-a58fba7e4a95')
+ def test_volume_delete_cascade(self):
+ # The case validates the ability to delete a volume
+ # with associated snapshots.
+
+ # Create a volume
+ volume = self.create_volume()
+
+ for _ in range(2):
+ self.create_snapshot(volume['id'])
+
+ # Delete the parent volume with associated snapshots
+ self.volumes_client.delete_volume(volume['id'], cascade=True)
+ self.volumes_client.wait_for_resource_deletion(volume['id'])
+
+ # Verify volume parent was deleted with its associated snapshots
+ self._assert_cascade_delete(volume['id'])
+
+ @decorators.idempotent_id('59a77ede-609b-4ee8-9f68-fc3c6ffe97b5')
+ @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+ 'Skip because of Bug#1677525')
+ def test_volume_from_snapshot_cascade_delete(self):
+ # The case validates the ability to delete a volume with
+ # associated snapshot while there is another volume created
+ # from that snapshot.
+
+ # Create a volume
+ volume = self.create_volume()
+
+ snapshot = self.create_snapshot(volume['id'])
+
+ # Create volume from snapshot
+ volume_snap = self.create_volume(snapshot_id=snapshot['id'])
+ volume_details = self.volumes_client.show_volume(
+ volume_snap['id'])['volume']
+ self.assertEqual(snapshot['id'], volume_details['snapshot_id'])
+
+ # Delete the parent volume with associated snapshot
+ self.volumes_client.delete_volume(volume['id'], cascade=True)
+ self.volumes_client.wait_for_resource_deletion(volume['id'])
+
+ # Verify volume parent was deleted with its associated snapshot
+ self._assert_cascade_delete(volume['id'])
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index bbe1765..b4d22fe 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -19,18 +19,18 @@
from tempest.lib import decorators
-class VolumesV2MetadataTest(base.BaseVolumeTest):
+class VolumesMetadataTest(base.BaseVolumeTest):
@classmethod
def resource_setup(cls):
- super(VolumesV2MetadataTest, cls).resource_setup()
+ super(VolumesMetadataTest, cls).resource_setup()
# Create a volume
cls.volume = cls.create_volume()
def tearDown(self):
# Update the metadata to {}
self.volumes_client.update_volume_metadata(self.volume['id'], {})
- super(VolumesV2MetadataTest, self).tearDown()
+ super(VolumesMetadataTest, self).tearDown()
@decorators.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
def test_crud_volume_metadata(self):
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 22cbd85..75f2a73 100644
--- a/tempest/api/volume/test_volume_transfers.py
+++ b/tempest/api/volume/test_volume_transfers.py
@@ -20,23 +20,26 @@
from tempest.lib import decorators
-class VolumesV2TransfersTest(base.BaseVolumeTest):
+class VolumesTransfersTest(base.BaseVolumeTest):
credentials = ['primary', 'alt', 'admin']
@classmethod
def setup_clients(cls):
- super(VolumesV2TransfersTest, cls).setup_clients()
+ super(VolumesTransfersTest, cls).setup_clients()
- cls.client = cls.volumes_client
- cls.alt_client = cls.os_alt.volumes_client
- cls.adm_client = cls.os_adm.volumes_client
+ cls.client = cls.os.volume_transfers_v2_client
+ cls.alt_client = cls.os_alt.volume_transfers_v2_client
+ cls.alt_volumes_client = cls.os_alt.volumes_v2_client
+ cls.adm_volumes_client = cls.os_adm.volumes_v2_client
@decorators.idempotent_id('4d75b645-a478-48b1-97c8-503f64242f1a')
def test_create_get_list_accept_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
- self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
+ self.addCleanup(self.delete_volume,
+ self.adm_volumes_client,
+ volume['id'])
# Create a volume transfer
transfer = self.client.create_volume_transfer(
@@ -44,7 +47,7 @@
transfer_id = transfer['id']
auth_key = transfer['auth_key']
waiters.wait_for_volume_resource_status(
- self.client, volume['id'], 'awaiting-transfer')
+ self.volumes_client, volume['id'], 'awaiting-transfer')
# Get a volume transfer
body = self.client.show_volume_transfer(transfer_id)['transfer']
@@ -58,31 +61,34 @@
# Accept a volume transfer by alt_tenant
body = self.alt_client.accept_volume_transfer(
transfer_id, auth_key=auth_key)['transfer']
- waiters.wait_for_volume_resource_status(self.alt_client,
+ waiters.wait_for_volume_resource_status(self.alt_volumes_client,
volume['id'], 'available')
@decorators.idempotent_id('ab526943-b725-4c07-b875-8e8ef87a2c30')
def test_create_list_delete_volume_transfer(self):
# Create a volume first
volume = self.create_volume()
- self.addCleanup(self.delete_volume, self.adm_client, volume['id'])
+ self.addCleanup(self.delete_volume,
+ self.adm_volumes_client,
+ 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.client, volume['id'], 'awaiting-transfer')
+ 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)
waiters.wait_for_volume_resource_status(
- self.client, volume['id'], 'available')
+ self.volumes_client, volume['id'], 'available')
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 4fc1a69..1e05f22 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,12 +25,11 @@
CONF = config.CONF
-class VolumesV2ActionsTest(base.BaseVolumeTest):
+class VolumesActionsTest(base.BaseVolumeTest):
@classmethod
def setup_clients(cls):
- super(VolumesV2ActionsTest, cls).setup_clients()
- cls.client = cls.volumes_client
+ super(VolumesActionsTest, cls).setup_clients()
if CONF.service_available.glance:
# Check if glance v1 is available to determine which client to use.
if CONF.image_feature_enabled.api_v1:
@@ -44,35 +43,35 @@
@classmethod
def resource_setup(cls):
- super(VolumesV2ActionsTest, cls).resource_setup()
+ super(VolumesActionsTest, cls).resource_setup()
# Create a test shared volume for attach/detach tests
cls.volume = cls.create_volume()
@decorators.idempotent_id('fff42874-7db5-4487-a8e1-ddda5fb5288d')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@test.services('compute')
def test_attach_detach_volume_to_instance(self):
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Volume is attached and detached successfully from an instance
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'in-use')
- self.client.detach_volume(self.volume['id'])
- waiters.wait_for_volume_resource_status(self.client,
+ self.volumes_client.detach_volume(self.volume['id'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
def test_volume_bootable(self):
# Verify that a volume bootable flag is retrieved
for bool_bootable in [True, False]:
- self.client.set_bootable_volume(self.volume['id'],
- bootable=bool_bootable)
- fetched_volume = self.client.show_volume(
+ self.volumes_client.set_bootable_volume(self.volume['id'],
+ bootable=bool_bootable)
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# Get Volume information
# NOTE(masayukig): 'bootable' is "true" or "false" in the current
@@ -87,16 +86,18 @@
# Create a server
server = self.create_server(wait_until='ACTIVE')
# Verify that a volume's attachment information is retrieved
- self.client.attach_volume(self.volume['id'],
- instance_uuid=server['id'],
- mountpoint='/dev/%s' %
- CONF.compute.volume_device_name)
- waiters.wait_for_volume_resource_status(self.client, self.volume['id'],
+ self.volumes_client.attach_volume(self.volume['id'],
+ instance_uuid=server['id'],
+ mountpoint='/dev/%s' %
+ CONF.compute.volume_device_name)
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ self.volume['id'],
'in-use')
- self.addCleanup(waiters.wait_for_volume_resource_status, self.client,
+ self.addCleanup(waiters.wait_for_volume_resource_status,
+ self.volumes_client,
self.volume['id'], 'available')
- self.addCleanup(self.client.detach_volume, self.volume['id'])
- volume = self.client.show_volume(self.volume['id'])['volume']
+ self.addCleanup(self.volumes_client.detach_volume, self.volume['id'])
+ volume = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attachments', volume)
attachment = volume['attachments'][0]
@@ -115,7 +116,7 @@
# there is no way to delete it from Cinder, so we delete it from Glance
# using the Glance image_client and from Cinder via tearDownClass.
image_name = data_utils.rand_name(self.__class__.__name__ + '-Image')
- body = self.client.upload_volume(
+ body = self.volumes_client.upload_volume(
self.volume['id'], image_name=image_name,
disk_format=CONF.volume.disk_format)['os-volume_upload_image']
image_id = body["image_id"]
@@ -123,30 +124,30 @@
self.image_client.delete_image,
image_id)
waiters.wait_for_image_status(self.image_client, image_id, 'active')
- waiters.wait_for_volume_resource_status(self.client,
+ waiters.wait_for_volume_resource_status(self.volumes_client,
self.volume['id'], 'available')
@decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
def test_reserve_unreserve_volume(self):
# Mark volume as reserved.
- body = self.client.reserve_volume(self.volume['id'])
+ body = self.volumes_client.reserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('attaching', body['status'])
# Unmark volume as reserved.
- body = self.client.unreserve_volume(self.volume['id'])
+ body = self.volumes_client.unreserve_volume(self.volume['id'])
# To get the volume info
- body = self.client.show_volume(self.volume['id'])['volume']
+ body = self.volumes_client.show_volume(self.volume['id'])['volume']
self.assertIn('available', body['status'])
@decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
def test_volume_readonly_update(self):
for readonly in [True, False]:
# Update volume readonly
- self.client.update_volume_readonly(self.volume['id'],
- readonly=readonly)
+ self.volumes_client.update_volume_readonly(self.volume['id'],
+ readonly=readonly)
# Get Volume information
- fetched_volume = self.client.show_volume(
+ fetched_volume = self.volumes_client.show_volume(
self.volume['id'])['volume']
# NOTE(masayukig): 'readonly' is "True" or "False" in the current
# cinder implementation. So we need to cast boolean values to str
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index e0b54b5..5ad209c 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
from testtools import matchers
from tempest.api.volume import base
@@ -25,11 +26,11 @@
CONF = config.CONF
-class VolumesBackupsV2Test(base.BaseVolumeTest):
+class VolumesBackupsTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesBackupsV2Test, cls).skip_checks()
+ super(VolumesBackupsTest, cls).skip_checks()
if not CONF.volume_feature_enabled.backup:
raise cls.skipException("Cinder backup feature disabled")
@@ -49,6 +50,8 @@
'available')
return restored_volume
+ @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+ 'ceph does not support arbitrary container names')
@decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
def test_volume_backup_create_get_detailed_list_restore_delete(self):
# Create a volume with metadata
@@ -65,7 +68,8 @@
description = data_utils.rand_name("volume-backup-description")
backup = self.create_backup(volume_id=volume['id'],
name=backup_name,
- description=description)
+ description=description,
+ container='container')
self.assertEqual(backup_name, backup['name'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
@@ -74,6 +78,7 @@
backup = self.backups_client.show_backup(backup['id'])['backup']
self.assertEqual(backup_name, backup['name'])
self.assertEqual(description, backup['description'])
+ self.assertEqual('container', backup['container'])
# Get all backups with detail
backups = self.backups_client.list_backups(
@@ -113,6 +118,8 @@
name=backup_name, force=True)
self.assertEqual(backup_name, backup['name'])
+ @testtools.skipUnless(CONF.service_available.glance,
+ "Glance is not available")
@decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
def test_bootable_volume_backup_and_restore(self):
# Create volume from image
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 620f3d4..a6bbb0a 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
+
from tempest.api.volume import base
from tempest import config
from tempest.lib import decorators
@@ -21,11 +23,11 @@
CONF = config.CONF
-class VolumesV2CloneTest(base.BaseVolumeTest):
+class VolumesCloneTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2CloneTest, cls).skip_checks()
+ super(VolumesCloneTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
@@ -45,6 +47,8 @@
self.assertEqual(volume['source_volid'], src_vol['id'])
self.assertEqual(volume['size'], src_size + 1)
+ @testtools.skipUnless(CONF.service_available.glance,
+ "Glance is not available")
@decorators.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
def test_create_from_bootable_volume(self):
# Create volume from image
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index 28327f4..bba7a0b 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -17,20 +17,19 @@
from tempest import config
from tempest.lib import decorators
from tempest.lib import exceptions
-from tempest import test
CONF = config.CONF
-class VolumesV2CloneNegativeTest(base.BaseVolumeTest):
+class VolumesCloneNegativeTest(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2CloneNegativeTest, cls).skip_checks()
+ super(VolumesCloneNegativeTest, cls).skip_checks()
if not CONF.volume_feature_enabled.clone:
raise cls.skipException("Cinder volume clones are disabled")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.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/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 3b09b60..84ecb22 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -18,7 +18,7 @@
from tempest.lib import decorators
-class VolumesV2ExtendTest(base.BaseVolumeTest):
+class VolumesExtendTest(base.BaseVolumeTest):
@decorators.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
def test_volume_extend(self):
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 9eff97a..1d9b846 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -26,7 +26,7 @@
CONF = config.CONF
-class VolumesV2GetTest(base.BaseVolumeTest):
+class VolumesGetTest(base.BaseVolumeTest):
def _volume_create_get_update_delete(self, **kwargs):
# Create a volume, Get it's details and Delete the volume
@@ -115,12 +115,12 @@
else:
self.assertEqual('false', updated_volume['bootable'])
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('27fb0e9f-fb64-41dd-8bdb-1ffa762f0d51')
def test_volume_create_get_update_delete(self):
self._volume_create_get_update_delete(size=CONF.volume.volume_size)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('54a01030-c7fc-447c-86ee-c1182beae638')
@test.services('image')
def test_volume_create_get_update_delete_from_image(self):
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index a852cea..9787160 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -13,17 +13,19 @@
# 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
+import operator
+import random
+
+from six.moves.urllib import parse
from testtools import matchers
from tempest.api.volume import base
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
+class VolumesListTestJSON(base.BaseVolumeTest):
# NOTE: This test creates a number of 1G volumes. To run successfully,
# ensure that the backing file for the volume group that Nova uses
# has space for at least 3 1G volumes!
@@ -44,7 +46,7 @@
fetched_list = [fieldsgetter(item) for item in fetched_list]
missing_vols = [v for v in expected_list if v not in fetched_list]
- if len(missing_vols) == 0:
+ if not missing_vols:
return
def str_vol(vol):
@@ -57,8 +59,12 @@
@classmethod
def resource_setup(cls):
- super(VolumesV2ListTestJSON, cls).resource_setup()
+ super(VolumesListTestJSON, cls).resource_setup()
cls.name = cls.VOLUME_FIELDS[1]
+
+ existing_volumes = cls.volumes_client.list_volumes()['volumes']
+ cls.volume_id_list = [vol['id'] for vol in existing_volumes]
+
# Create 3 test volumes
cls.volume_list = []
cls.metadata = {'Type': 'work'}
@@ -66,6 +72,7 @@
volume = cls.create_volume(metadata=cls.metadata)
volume = cls.volumes_client.show_volume(volume['id'])['volume']
cls.volume_list.append(volume)
+ cls.volume_id_list.append(volume['id'])
def _list_by_param_value_and_assert(self, params, with_detail=False):
"""list or list_details with given params and validates result"""
@@ -78,11 +85,7 @@
params=params)['volumes']
# Validating params of fetched volumes
- # In v2, only list detail view includes items in params.
- # In v1, list view and list detail view are same. So the
- # following check should be run when 'with_detail' is True
- # or v1 tests.
- if with_detail or self._api_version == 1:
+ if with_detail:
for volume in fetched_vol_list:
for key in params:
msg = "Failed to list volumes %s by %s" % \
@@ -95,7 +98,7 @@
else:
self.assertEqual(params[key], volume[key], msg)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('0b6ddd39-b948-471f-8038-4787978747c4')
def test_volume_list(self):
# Get a list of Volumes
@@ -221,3 +224,157 @@
params = {self.name: volume[self.name],
'status': 'available'}
self._list_by_param_value_and_assert(params, with_detail=True)
+
+ @decorators.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
+ def test_volume_list_details_with_multiple_params(self):
+ # List volumes detail using combined condition
+ def _list_details_with_multiple_params(limit=2,
+ status='available',
+ sort_dir='asc',
+ sort_key='id'):
+ params = {'limit': limit,
+ 'status': status,
+ 'sort_dir': sort_dir,
+ 'sort_key': sort_key
+ }
+ fetched_volume = self.volumes_client.list_volumes(
+ detail=True, params=params)['volumes']
+ self.assertEqual(limit, len(fetched_volume),
+ "The count of volumes is %s, expected:%s " %
+ (len(fetched_volume), limit))
+ self.assertEqual(status, fetched_volume[0]['status'])
+ self.assertEqual(status, fetched_volume[1]['status'])
+ val0 = fetched_volume[0][sort_key]
+ val1 = fetched_volume[1][sort_key]
+ if sort_dir == 'asc':
+ self.assertLess(val0, val1,
+ "list is not in asc order with sort_key: %s."
+ " %s" % (sort_key, fetched_volume))
+ elif sort_dir == 'desc':
+ self.assertGreater(val0, val1,
+ "list is not in desc order with sort_key: "
+ "%s. %s" % (sort_key, fetched_volume))
+
+ _list_details_with_multiple_params()
+ _list_details_with_multiple_params(sort_dir='desc')
+
+ def _test_pagination(self, resource, ids=None, limit=1, **kwargs):
+ """Check list pagination functionality for a resource.
+
+ This method requests the list of resources and follows pagination
+ links.
+
+ If an iterable is supplied in ids it will check that all ids are
+ retrieved and that only those are listed, that we will get a next
+ link for an empty page if the number of items is divisible by used
+ limit (this is expected behavior).
+
+ We can specify number of items per request using limit argument.
+ """
+
+ # Get list method for the type of resource from the client
+ client = getattr(self, resource + '_client')
+ method = getattr(client, 'list_' + resource)
+
+ # Include limit in params for list request
+ params = kwargs.pop('params', {})
+ params['limit'] = limit
+
+ # Store remaining items we are expecting from list
+ if ids is not None:
+ remaining = list(ids)
+ else:
+ remaining = None
+
+ # Mark that the current iteration is not from a 'next' link
+ next = None
+
+ while True:
+ # Get a list page
+ response = method(params=params, **kwargs)
+
+ # If we have to check ids
+ if remaining is not None:
+ # Confirm we receive expected number of elements
+ num_expected = min(len(remaining), limit)
+ self.assertEqual(num_expected, len(response[resource]),
+ 'Requested %(#expect)d but got %(#received)d '
+ % {'#expect': num_expected,
+ '#received': len(response[resource])})
+
+ # For each received element
+ for element in response[resource]:
+ element_id = element['id']
+ # Check it's one of expected ids
+ self.assertIn(element_id,
+ ids,
+ 'Id %(id)s is not in expected ids %(ids)s' %
+ {'id': element_id, 'ids': ids})
+ # If not in remaining, we have received it twice
+ self.assertIn(element_id,
+ remaining,
+ 'Id %s was received twice' % element_id)
+ # We no longer expect it
+ remaining.remove(element_id)
+
+ # If the current iteration is from a 'next' link, check that the
+ # absolute url is the same as the one used for this request
+ if next:
+ self.assertEqual(next, response.response['content-location'])
+
+ # Get next from response
+ next = None
+ for link in response.get(resource + '_links', ()):
+ if link['rel'] == 'next':
+ next = link['href']
+ break
+
+ # Check if we have next and we shouldn't or the other way around
+ if remaining is not None:
+ if remaining or (num_expected and len(ids) % limit == 0):
+ self.assertIsNotNone(next, 'Missing link to next page')
+ else:
+ self.assertIsNone(next, 'Unexpected link to next page')
+
+ # If we can follow to the next page, get params from url to make
+ # request in the form of a relative URL
+ if next:
+ params = parse.urlparse(next).query
+
+ # If cannot follow make sure it's because we have finished
+ else:
+ self.assertEqual([], remaining or [],
+ 'No more pages reported, but still '
+ 'missing ids %s' % remaining)
+ break
+
+ @decorators.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
+ def test_volume_list_details_pagination(self):
+ self._test_pagination('volumes', ids=self.volume_id_list, detail=True)
+
+ @decorators.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
+ def test_volume_list_pagination(self):
+ self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
+
+ @decorators.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
+ def test_volume_list_with_detail_param_marker(self):
+ # Choosing a random volume from a list of volumes for 'marker'
+ # parameter
+ marker = random.choice(self.volume_id_list)
+
+ # Though Cinder volumes are returned sorted by ID by default
+ # this is implicit. Let make this explicit in case Cinder
+ # folks change their minds.
+ params = {'marker': marker, 'sort': 'id:asc'}
+
+ # Running volume list using marker parameter
+ vol_with_marker = self.volumes_client.list_volumes(
+ detail=True, params=params)['volumes']
+
+ expected_volumes_id = {
+ id for id in self.volume_id_list if id > marker
+ }
+
+ self.assertEqual(
+ expected_volumes_id, {v['id'] for v in vol_with_marker}
+ )
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 8990a15..0516c69 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -13,163 +13,141 @@
# License for the specific language governing permissions and limitations
# under the License.
+import six
+
from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
from tempest import test
+CONF = config.CONF
-class VolumesV2NegativeTest(base.BaseVolumeTest):
+
+class VolumesNegativeTest(base.BaseVolumeTest):
@classmethod
def resource_setup(cls):
- super(VolumesV2NegativeTest, cls).resource_setup()
+ super(VolumesNegativeTest, cls).resource_setup()
# Create a test shared instance and volume for attach/detach tests
cls.volume = cls.create_volume()
cls.mountpoint = "/dev/vdc"
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
def test_volume_get_nonexistent_volume_id(self):
# Should not be able to get a non-existent volume
self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
def test_volume_delete_nonexistent_volume_id(self):
# Should not be able to delete a non-existent Volume
self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
def test_create_volume_with_invalid_size(self):
- # Should not be able to create volume with invalid size
- # in request
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
+ # Should not be able to create volume with invalid size in request
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='#$%', params=params)
+ self.volumes_client.create_volume, size='#$%')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
def test_create_volume_without_passing_size(self):
# Should not be able to create volume without passing size
# in request
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='', params=params)
+ self.volumes_client.create_volume, size='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
def test_create_volume_with_size_zero(self):
# Should not be able to create volume with size zero
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='0', params=params)
+ self.volumes_client.create_volume, size='0')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
def test_create_volume_with_size_negative(self):
# Should not be able to create volume with size negative
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.BadRequest,
- self.volumes_client.create_volume,
- size='-1', params=params)
+ self.volumes_client.create_volume, size='-1')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
def test_create_volume_with_nonexistent_volume_type(self):
# Should not be able to create volume with non-existent volume type
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', volume_type=data_utils.rand_uuid(),
- params=params)
+ size='1', volume_type=data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
def test_create_volume_with_nonexistent_snapshot_id(self):
# Should not be able to create volume with non-existent snapshot
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', snapshot_id=data_utils.rand_uuid(),
- params=params)
+ size='1', snapshot_id=data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
def test_create_volume_with_nonexistent_source_volid(self):
# Should not be able to create volume with non-existent source volume
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
- size='1', source_volid=data_utils.rand_uuid(),
- params=params)
+ size='1', source_volid=data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
def test_update_volume_with_nonexistent_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id=data_utils.rand_uuid(), params=params)
+ volume_id=data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
def test_update_volume_with_invalid_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id=data_utils.rand_name('invalid'),
- params=params)
+ volume_id=data_utils.rand_name('invalid'))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
def test_update_volume_with_empty_volume_id(self):
- v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
- params = {'name': v_name}
self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
- volume_id='', params=params)
+ volume_id='')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
def test_get_invalid_volume_id(self):
# Should not be able to get volume with invalid id
self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
data_utils.rand_name('invalid'))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
def test_get_volume_without_passing_volume_id(self):
# Should not be able to get volume when empty ID is passed
self.assertRaises(lib_exc.NotFound,
self.volumes_client.show_volume, '')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
def test_delete_invalid_volume_id(self):
# Should not be able to delete volume when invalid ID is passed
self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
data_utils.rand_name('invalid'))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
def test_delete_volume_without_passing_volume_id(self):
# Should not be able to delete volume when empty ID is passed
self.assertRaises(lib_exc.NotFound,
self.volumes_client.delete_volume, '')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
@test.services('compute')
def test_attach_volumes_with_nonexistent_volume_id(self):
@@ -181,14 +159,14 @@
instance_uuid=server['id'],
mountpoint=self.mountpoint)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
def test_detach_volumes_with_invalid_volume_id(self):
self.assertRaises(lib_exc.NotFound,
self.volumes_client.detach_volume,
'xxx')
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e0c75c74-ee34-41a9-9288-2a2051452854')
def test_volume_extend_with_size_smaller_than_original_size(self):
# Extend volume with smaller size than original size.
@@ -197,7 +175,7 @@
self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('5d0b480d-e833-439f-8a5a-96ad2ed6f22f')
def test_volume_extend_with_non_number_size(self):
# Extend volume when size is non number.
@@ -206,7 +184,7 @@
self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('355218f1-8991-400a-a6bb-971239287d92')
def test_volume_extend_with_None_size(self):
# Extend volume with None size.
@@ -215,7 +193,7 @@
self.volumes_client.extend_volume,
self.volume['id'], new_size=extend_size)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8f05a943-013c-4063-ac71-7baf561e82eb')
def test_volume_extend_with_nonexistent_volume_id(self):
# Extend volume size when volume is nonexistent.
@@ -223,7 +201,7 @@
self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
data_utils.rand_uuid(), new_size=extend_size)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
def test_volume_extend_without_passing_volume_id(self):
# Extend volume size when passing volume id is None.
@@ -231,21 +209,21 @@
self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
None, new_size=extend_size)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
def test_reserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
self.volumes_client.reserve_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
def test_unreserve_volume_with_nonexistent_volume_id(self):
self.assertRaises(lib_exc.NotFound,
self.volumes_client.unreserve_volume,
data_utils.rand_uuid())
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
def test_reserve_volume_with_negative_volume_status(self):
# Mark volume as reserved.
@@ -257,7 +235,7 @@
# Unmark volume as reserved.
self.volumes_client.unreserve_volume(self.volume['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
def test_list_volumes_with_nonexistent_name(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
@@ -266,7 +244,7 @@
params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('9ca17820-a0e7-4cbd-a7fa-f4468735e359')
def test_list_volumes_detail_with_nonexistent_name(self):
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
@@ -276,7 +254,7 @@
detail=True, params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
def test_list_volumes_with_invalid_status(self):
params = {'status': 'null'}
@@ -284,7 +262,7 @@
params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('ba94b27b-be3f-496c-a00e-0283b373fa75')
def test_list_volumes_detail_with_invalid_status(self):
params = {'status': 'null'}
@@ -292,3 +270,32 @@
self.volumes_client.list_volumes(detail=True,
params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('5b810c91-0ad1-47ce-aee8-615f789be78f')
+ @test.services('image')
+ def test_create_volume_from_image_with_decreasing_size(self):
+ # Create image
+ image_name = data_utils.rand_name(self.__class__.__name__ + "-image")
+ image = self.images_client.create_image(
+ name=image_name,
+ container_format=CONF.image.container_formats[0],
+ disk_format=CONF.image.disk_formats[0],
+ visibility='private',
+ min_disk=CONF.volume.volume_size + 1)
+ self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+ self.images_client.delete_image, image['id'])
+
+ # Upload image with 1KB data
+ image_file = six.BytesIO(data_utils.random_bytes())
+ self.images_client.store_image_file(image['id'], image_file)
+ waiters.wait_for_image_status(self.images_client,
+ image['id'], 'active')
+
+ # Note(jeremyZ): To shorten the test time (uploading a big size image
+ # is time-consuming), here just consider the scenario that volume size
+ # is smaller than the min_disk of image.
+ self.assertRaises(lib_exc.BadRequest,
+ self.volumes_client.create_volume,
+ size=CONF.volume.volume_size,
+ imageRef=image['id'])
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 82f456b..8ffc99d 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -21,17 +21,17 @@
CONF = config.CONF
-class VolumesV2SnapshotTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotTestJSON, cls).skip_checks()
+ super(VolumesSnapshotTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
def resource_setup(cls):
- super(VolumesV2SnapshotTestJSON, cls).resource_setup()
+ super(VolumesSnapshotTestJSON, cls).resource_setup()
cls.volume_origin = cls.create_volume()
@decorators.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
@@ -102,6 +102,7 @@
self.assertEqual(self.volume_origin['id'],
snap_get['volume_id'],
"Referred volume origin mismatch")
+ self.assertEqual(self.volume_origin['size'], snap_get['size'])
# Verify snapshot metadata
self.assertThat(snap_get['metadata'].items(),
@@ -135,7 +136,8 @@
@decorators.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot(self):
- # Creates a volume a snapshot passing a size different from the source
+ # Creates a volume from a snapshot passing a size
+ # different from the source
src_size = CONF.volume.volume_size
src_vol = self.create_volume(size=src_size)
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index 0ea8ec7..507df1f 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -17,21 +17,25 @@
CONF = config.CONF
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
+ super(VolumesSnapshotListTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
@classmethod
def resource_setup(cls):
- super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
+ super(VolumesSnapshotListTestJSON, cls).resource_setup()
+ cls.snapshot_id_list = []
volume_origin = cls.create_volume()
+
# Create snapshots with params
- for _ in range(2):
- cls.snapshot = cls.create_snapshot(volume_origin['id'])
+ for _ in range(3):
+ snapshot = cls.create_snapshot(volume_origin['id'])
+ cls.snapshot_id_list.append(snapshot['id'])
+ cls.snapshot = snapshot
def _list_by_param_values_and_assert(self, with_detail=False, **params):
"""list or list_details with given params and validates result."""
@@ -97,8 +101,60 @@
self._list_snapshots_by_param_limit(limit=100000,
expected_elements=len(snap_list))
- @decorators.skip_because(bug='1540893')
@decorators.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
def test_snapshot_list_param_limit_equals_zero(self):
# List returns zero elements
self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
+
+ def _list_snapshots_param_sort(self, sort_key, sort_dir):
+ """list snapshots by sort param"""
+ snap_list = self.snapshots_client.list_snapshots(
+ sort_key=sort_key, sort_dir=sort_dir)['snapshots']
+ self.assertNotEmpty(snap_list)
+ if sort_key is 'display_name':
+ sort_key = 'name'
+ # Note: On Cinder API, 'display_name' works as a sort key
+ # on a request, a volume name appears as 'name' on the response.
+ # So Tempest needs to change the key name here for this inconsistent
+ # API behavior.
+ sorted_list = [snapshot[sort_key] for snapshot in snap_list]
+ msg = 'The list of snapshots was not sorted correctly.'
+ self.assertEqual(sorted(sorted_list, reverse=(sort_dir == 'desc')),
+ sorted_list, msg)
+
+ @decorators.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
+ def test_snapshot_list_param_sort_id_asc(self):
+ self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
+
+ @decorators.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
+ def test_snapshot_list_param_sort_id_desc(self):
+ self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
+
+ @decorators.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
+ def test_snapshot_list_param_sort_created_at_asc(self):
+ self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
+
+ @decorators.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
+ def test_snapshot_list_param_sort_created_at_desc(self):
+ self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
+
+ @decorators.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
+ def test_snapshot_list_param_sort_name_asc(self):
+ self._list_snapshots_param_sort(sort_key='display_name',
+ sort_dir='asc')
+
+ @decorators.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
+ def test_snapshot_list_param_sort_name_desc(self):
+ self._list_snapshots_param_sort(sort_key='display_name',
+ sort_dir='desc')
+
+ @decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
+ def test_snapshot_list_param_marker(self):
+ # The list of snapshots should end before the provided marker
+ params = {'marker': self.snapshot_id_list[1]}
+ snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
+ fetched_list_id = [snap['id'] for snap in snap_list]
+ # Verify the list of snapshots ends before the provided
+ # marker(second snapshot), therefore only the first snapshot
+ # should displayed.
+ self.assertEqual(self.snapshot_id_list[:1], fetched_list_id)
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 285d3f5..ea5f036 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -15,20 +15,19 @@
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
-from tempest import test
CONF = config.CONF
-class VolumesV2SnapshotNegativeTestJSON(base.BaseVolumeTest):
+class VolumesSnapshotNegativeTestJSON(base.BaseVolumeTest):
@classmethod
def skip_checks(cls):
- super(VolumesV2SnapshotNegativeTestJSON, cls).skip_checks()
+ super(VolumesSnapshotNegativeTestJSON, cls).skip_checks()
if not CONF.volume_feature_enabled.snapshot:
raise cls.skipException("Cinder volume snapshots are disabled")
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('e3e466af-70ab-4f4b-a967-ab04e3532ea7')
def test_create_snapshot_with_nonexistent_volume_id(self):
# Create a snapshot with nonexistent volume id
@@ -38,7 +37,7 @@
volume_id=data_utils.rand_uuid(),
display_name=s_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
def test_create_snapshot_without_passing_volume_id(self):
# Create a snapshot without passing volume id
@@ -47,7 +46,7 @@
self.snapshots_client.create_snapshot,
volume_id=None, display_name=s_name)
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
def test_volume_from_snapshot_decreasing_size(self):
# Creates a volume a snapshot passing a size different from the source
@@ -62,9 +61,23 @@
size=src_size - 1,
snapshot_id=src_snap['id'])
- @test.attr(type=['negative'])
+ @decorators.attr(type=['negative'])
@decorators.idempotent_id('8fd92339-e22f-4591-86b4-1e2215372a40')
def test_list_snapshot_invalid_param_limit(self):
self.assertRaises(lib_exc.BadRequest,
self.snapshots_client.list_snapshots,
limit='invalid')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
+ def test_list_snapshots_invalid_param_sort(self):
+ self.assertRaises(lib_exc.BadRequest,
+ self.snapshots_client.list_snapshots,
+ sort_key='invalid')
+
+ @decorators.attr(type=['negative'])
+ @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
+ def test_list_snapshots_invalid_param_marker(self):
+ self.assertRaises(lib_exc.NotFound,
+ self.snapshots_client.list_snapshots,
+ marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v2/__init__.py b/tempest/api/volume/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v2/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
deleted file mode 100644
index d2328c8..0000000
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# Copyright 2013 IBM Corp.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import random
-
-from six.moves.urllib import parse
-
-from tempest.api.volume import base
-from tempest.lib import decorators
-
-
-class VolumesV2ListTestJSON(base.BaseVolumeTest):
- """volumes v2 specific tests.
-
- This test creates a number of 1G volumes. To run successfully,
- ensure that the backing file for the volume group that Nova uses
- has space for at least 3 1G volumes!
- If you are running a Devstack environment, ensure that the
- VOLUME_BACKING_FILE_SIZE is at least 4G in your localrc
- """
-
- @classmethod
- def resource_setup(cls):
- super(VolumesV2ListTestJSON, cls).resource_setup()
-
- # Create 3 test volumes
- # NOTE(zhufl): When using pre-provisioned credentials, the project
- # may have volumes other than those created below.
- existing_volumes = cls.volumes_client.list_volumes()['volumes']
- cls.volume_id_list = [vol['id'] for vol in existing_volumes]
- for _ in range(3):
- volume = cls.create_volume()
- cls.volume_id_list.append(volume['id'])
-
- @decorators.idempotent_id('2a7064eb-b9c3-429b-b888-33928fc5edd3')
- def test_volume_list_details_with_multiple_params(self):
- # List volumes detail using combined condition
- def _list_details_with_multiple_params(limit=2,
- status='available',
- sort_dir='asc',
- sort_key='id'):
- params = {'limit': limit,
- 'status': status,
- 'sort_dir': sort_dir,
- 'sort_key': sort_key
- }
- fetched_volume = self.volumes_client.list_volumes(
- detail=True, params=params)['volumes']
- self.assertEqual(limit, len(fetched_volume),
- "The count of volumes is %s, expected:%s " %
- (len(fetched_volume), limit))
- self.assertEqual(status, fetched_volume[0]['status'])
- self.assertEqual(status, fetched_volume[1]['status'])
- val0 = fetched_volume[0][sort_key]
- val1 = fetched_volume[1][sort_key]
- if sort_dir == 'asc':
- self.assertLess(val0, val1,
- "list is not in asc order with sort_key: %s."
- " %s" % (sort_key, fetched_volume))
- elif sort_dir == 'desc':
- self.assertGreater(val0, val1,
- "list is not in desc order with sort_key: "
- "%s. %s" % (sort_key, fetched_volume))
-
- _list_details_with_multiple_params()
- _list_details_with_multiple_params(sort_dir='desc')
-
- def _test_pagination(self, resource, ids=None, limit=1, **kwargs):
- """Check list pagination functionality for a resource.
-
- This method requests the list of resources and follows pagination
- links.
-
- If an iterable is supplied in ids it will check that all ids are
- retrieved and that only those are listed, that we will get a next
- link for an empty page if the number of items is divisible by used
- limit (this is expected behavior).
-
- We can specify number of items per request using limit argument.
- """
-
- # Get list method for the type of resource from the client
- client = getattr(self, resource + '_client')
- method = getattr(client, 'list_' + resource)
-
- # Include limit in params for list request
- params = kwargs.pop('params', {})
- params['limit'] = limit
-
- # Store remaining items we are expecting from list
- if ids is not None:
- remaining = list(ids)
- else:
- remaining = None
-
- # Mark that the current iteration is not from a 'next' link
- next = None
-
- while True:
- # Get a list page
- response = method(params=params, **kwargs)
-
- # If we have to check ids
- if remaining is not None:
- # Confirm we receive expected number of elements
- num_expected = min(len(remaining), limit)
- self.assertEqual(num_expected, len(response[resource]),
- 'Requested %(#expect)d but got %(#received)d '
- % {'#expect': num_expected,
- '#received': len(response[resource])})
-
- # For each received element
- for element in response[resource]:
- element_id = element['id']
- # Check it's one of expected ids
- self.assertIn(element_id,
- ids,
- 'Id %(id)s is not in expected ids %(ids)s' %
- {'id': element_id, 'ids': ids})
- # If not in remaining, we have received it twice
- self.assertIn(element_id,
- remaining,
- 'Id %s was received twice' % element_id)
- # We no longer expect it
- remaining.remove(element_id)
-
- # If the current iteration is from a 'next' link, check that the
- # absolute url is the same as the one used for this request
- if next:
- self.assertEqual(next, response.response['content-location'])
-
- # Get next from response
- next = None
- for link in response.get(resource + '_links', ()):
- if link['rel'] == 'next':
- next = link['href']
- break
-
- # Check if we have next and we shouldn't or the other way around
- if remaining is not None:
- if remaining or (num_expected and len(ids) % limit == 0):
- self.assertIsNotNone(next, 'Missing link to next page')
- else:
- self.assertIsNone(next, 'Unexpected link to next page')
-
- # If we can follow to the next page, get params from url to make
- # request in the form of a relative URL
- if next:
- params = parse.urlparse(next).query
-
- # If cannot follow make sure it's because we have finished
- else:
- self.assertEqual([], remaining or [],
- 'No more pages reported, but still '
- 'missing ids %s' % remaining)
- break
-
- @decorators.idempotent_id('e9138a2c-f67b-4796-8efa-635c196d01de')
- def test_volume_list_details_pagination(self):
- self._test_pagination('volumes', ids=self.volume_id_list, detail=True)
-
- @decorators.idempotent_id('af55e775-8e4b-4feb-8719-215c43b0238c')
- def test_volume_list_pagination(self):
- self._test_pagination('volumes', ids=self.volume_id_list, detail=False)
-
- @decorators.idempotent_id('46eff077-100b-427f-914e-3db2abcdb7e2')
- @decorators.skip_because(bug='1572765')
- def test_volume_list_with_detail_param_marker(self):
- # Choosing a random volume from a list of volumes for 'marker'
- # parameter
- random_volume = random.choice(self.volume_id_list)
-
- params = {'marker': random_volume}
-
- # Running volume list using marker parameter
- vol_with_marker = self.volumes_client.list_volumes(
- detail=True, params=params)['volumes']
-
- # Fetching the index of the random volume from volume_id_list
- index_marker = self.volume_id_list.index(random_volume)
-
- # The expected list with marker parameter
- verify_volume_list = self.volume_id_list[:index_marker]
-
- failed_msg = "Failed to list volume details by marker"
-
- # Validating the expected list is the same like the observed list
- self.assertEqual(verify_volume_list,
- map(lambda x: x['id'],
- vol_with_marker[::-1]), failed_msg)
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_list.py b/tempest/api/volume/v2/test_volumes_snapshots_list.py
deleted file mode 100644
index d385f65..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_list.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright 2016 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.
-
-from tempest.api.volume import base
-from tempest import config
-from tempest.lib import decorators
-
-CONF = config.CONF
-
-
-class VolumesV2SnapshotListTestJSON(base.BaseVolumeTest):
-
- @classmethod
- def skip_checks(cls):
- super(VolumesV2SnapshotListTestJSON, cls).skip_checks()
- if not CONF.volume_feature_enabled.snapshot:
- raise cls.skipException("Cinder volume snapshots are disabled")
-
- @classmethod
- def resource_setup(cls):
- super(VolumesV2SnapshotListTestJSON, cls).resource_setup()
- cls.snapshot_id_list = []
- # Create a volume
- volume_origin = cls.create_volume()
- # Create 3 snapshots
- for _ in range(3):
- snapshot = cls.create_snapshot(volume_origin['id'])
- cls.snapshot_id_list.append(snapshot['id'])
-
- def _list_snapshots_param_sort(self, sort_key, sort_dir):
- """list snapshots by sort param"""
- snap_list = self.snapshots_client.list_snapshots(
- sort_key=sort_key, sort_dir=sort_dir)['snapshots']
- self.assertNotEmpty(snap_list)
- if sort_key is 'display_name':
- sort_key = 'name'
- # Note: On Cinder V2 API, 'display_name' works as a sort key
- # on a request, a volume name appears as 'name' on the response.
- # So Tempest needs to change the key name here for this inconsistent
- # API behavior.
- sorted_list = [snapshot[sort_key] for snapshot in snap_list]
- msg = 'The list of snapshots was not sorted correctly.'
- self.assertEqual(sorted(sorted_list, reverse=(sort_dir == 'desc')),
- sorted_list, msg)
-
- @decorators.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
- def test_snapshot_list_param_sort_id_asc(self):
- self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
-
- @decorators.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
- def test_snapshot_list_param_sort_id_desc(self):
- self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
-
- @decorators.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
- def test_snapshot_list_param_sort_created_at_asc(self):
- self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
-
- @decorators.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
- def test_snapshot_list_param_sort_created_at_desc(self):
- self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
-
- @decorators.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
- def test_snapshot_list_param_sort_name_asc(self):
- self._list_snapshots_param_sort(sort_key='display_name',
- sort_dir='asc')
-
- @decorators.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
- def test_snapshot_list_param_sort_name_desc(self):
- self._list_snapshots_param_sort(sort_key='display_name',
- sort_dir='desc')
-
- @decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
- def test_snapshot_list_param_marker(self):
- # The list of snapshots should end before the provided marker
- params = {'marker': self.snapshot_id_list[1]}
- snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
- fetched_list_id = [snap['id'] for snap in snap_list]
- # Verify the list of snapshots ends before the provided
- # marker(second snapshot), therefore only the first snapshot
- # should displayed.
- self.assertEqual(self.snapshot_id_list[:1], fetched_list_id)
diff --git a/tempest/api/volume/v2/test_volumes_snapshots_negative.py b/tempest/api/volume/v2/test_volumes_snapshots_negative.py
deleted file mode 100644
index 4608b07..0000000
--- a/tempest/api/volume/v2/test_volumes_snapshots_negative.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2017 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.
-
-from tempest.api.volume 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
-from tempest import test
-
-CONF = config.CONF
-
-
-class VolumesV2SnapshotNegativeTest(base.BaseVolumeTest):
-
- @classmethod
- def skip_checks(cls):
- super(VolumesV2SnapshotNegativeTest, cls).skip_checks()
- if not CONF.volume_feature_enabled.snapshot:
- raise cls.skipException("Cinder volume snapshots are disabled")
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
- def test_list_snapshots_invalid_param_sort(self):
- self.assertRaises(lib_exc.BadRequest,
- self.snapshots_client.list_snapshots,
- sort_key='invalid')
-
- @test.attr(type=['negative'])
- @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
- def test_list_snapshots_invalid_param_marker(self):
- self.assertRaises(lib_exc.NotFound,
- self.snapshots_client.list_snapshots,
- marker=data_utils.rand_uuid())
diff --git a/tempest/api/volume/v3/__init__.py b/tempest/api/volume/v3/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/volume/v3/__init__.py
+++ /dev/null
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
deleted file mode 100644
index 7f76e6f..0000000
--- a/tempest/api/volume/v3/base.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2016 Andrew Kerr
-# 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.volume import api_microversion_fixture
-from tempest.api.volume import base
-from tempest import config
-from tempest.lib.common import api_version_utils
-
-CONF = config.CONF
-
-
-class VolumesV3Test(api_version_utils.BaseMicroversionTest,
- base.BaseVolumeTest):
- """Base test case class for all v3 Cinder API tests."""
-
- _api_version = 3
-
- @classmethod
- def skip_checks(cls):
- super(VolumesV3Test, cls).skip_checks()
- api_version_utils.check_skip_with_microversion(
- cls.min_microversion, cls.max_microversion,
- CONF.volume.min_microversion, CONF.volume.max_microversion)
-
- @classmethod
- def resource_setup(cls):
- super(VolumesV3Test, cls).resource_setup()
- cls.request_microversion = (
- api_version_utils.select_request_microversion(
- cls.min_microversion,
- CONF.volume.min_microversion))
-
- @classmethod
- def setup_clients(cls):
- super(VolumesV3Test, cls).setup_clients()
- cls.messages_client = cls.os.volume_v3_messages_client
- cls.versions_client = cls.os.volume_v3_versions_client
-
- def setUp(self):
- super(VolumesV3Test, self).setUp()
- self.useFixture(api_microversion_fixture.APIMicroversionFixture(
- self.request_microversion))
-
-
-class VolumesV3AdminTest(VolumesV3Test,
- base.BaseVolumeAdminTest):
- """Base test case class for all v3 Volume Admin API tests."""
-
- credentials = ['primary', 'admin']
-
- @classmethod
- def setup_clients(cls):
- super(VolumesV3AdminTest, cls).setup_clients()
- cls.admin_messages_client = cls.os_adm.volume_v3_messages_client
- cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
diff --git a/tempest/clients.py b/tempest/clients.py
index 0654110..4ef6872 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -159,7 +159,6 @@
self.aggregates_client = self.compute.AggregatesClient()
self.services_client = self.compute.ServicesClient()
self.tenant_usages_client = self.compute.TenantUsagesClient()
- self.baremetal_nodes_client = self.compute.BaremetalNodesClient()
self.hosts_client = self.compute.HostsClient()
self.hypervisor_client = self.compute.HypervisorClient()
self.instance_usages_audit_log_client = (
@@ -168,11 +167,10 @@
# NOTE: The following client needs special timeout values because
# the API is a proxy for the other component.
- params_volume = {}
- for _key in ('build_interval', 'build_timeout'):
- _value = self.parameters['volume'].get(_key)
- if _value:
- params_volume[_key] = _value
+ params_volume = {
+ 'build_interval': CONF.volume.build_interval,
+ 'build_timeout': CONF.volume.build_timeout
+ }
self.volumes_extensions_client = self.compute.VolumesClient(
**params_volume)
self.compute_versions_client = self.compute.VersionsClient(
@@ -229,6 +227,10 @@
self.groups_client = self.identity_v3.GroupsClient(**params_v3)
self.identity_versions_v3_client = self.identity_v3.VersionsClient(
**params_v3)
+ self.oauth_consumers_client = self.identity_v3.OAUTHConsumerClient(
+ **params_v3)
+ self.domain_config_client = self.identity_v3.DomainConfigurationClient(
+ **params_v3)
# Token clients do not use the catalog. They only need default_params.
# They read auth_url, so they should only be set if the corresponding
@@ -262,6 +264,7 @@
self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
self.snapshots_client = self.volume_v1.SnapshotsClient()
self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
+ self.volume_manage_v2_client = self.volume_v2.VolumeManageClient()
self.volumes_client = self.volume_v1.VolumesClient()
self.volumes_v2_client = self.volume_v2.VolumesClient()
self.volume_v3_messages_client = self.volume_v3.MessagesClient()
@@ -284,6 +287,8 @@
self.volume_v2.CapabilitiesClient()
self.volume_scheduler_stats_v2_client = \
self.volume_v2.SchedulerStatsClient()
+ self.volume_transfers_v2_client = \
+ self.volume_v2.TransfersClient()
def _set_object_storage_clients(self):
# Mandatory parameters (always defined)
@@ -291,6 +296,8 @@
self.account_client = object_storage.AccountClient(self.auth_provider,
**params)
+ self.bulk_client = object_storage.BulkMiddlewareClient(
+ self.auth_provider, **params)
self.capabilities_client = object_storage.CapabilitiesClient(
self.auth_provider, **params)
self.container_client = object_storage.ContainerClient(
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index a632726..f1c0a3e 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -105,7 +105,7 @@
def _filter_by_tenant_id(self, item_list):
if (item_list is None
- or len(item_list) == 0
+ or not item_list
or not hasattr(self, 'tenant_id')
or self.tenant_id is None
or 'tenant_id' not in item_list[0]):
diff --git a/tempest/cmd/config-generator.tempest.conf b/tempest/cmd/config-generator.tempest.conf
index d718f93..b8f16d9 100644
--- a/tempest/cmd/config-generator.tempest.conf
+++ b/tempest/cmd/config-generator.tempest.conf
@@ -2,7 +2,4 @@
output_file = etc/tempest.conf.sample
namespace = tempest.config
namespace = oslo.concurrency
-namespace = oslo.i18n
namespace = oslo.log
-namespace = oslo.serialization
-namespace = oslo.utils
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index b2667e5..9587ffe 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -14,8 +14,13 @@
# limitations under the License.
import base64
+import socket
+import struct
import textwrap
+import six
+from six.moves.urllib import parse as urlparse
+
from oslo_log import log as logging
from oslo_utils import excutils
@@ -25,6 +30,11 @@
from tempest.lib.common import rest_client
from tempest.lib.common.utils import data_utils
+if six.PY2:
+ ord_func = ord
+else:
+ ord_func = int
+
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -132,11 +142,7 @@
if volume_backed:
volume_name = data_utils.rand_name(__name__ + '-volume')
volumes_client = clients.volumes_v2_client
- name_field = 'name'
- if not CONF.volume_feature_enabled.api_v2:
- volumes_client = clients.volumes_client
- name_field = 'display_name'
- params = {name_field: volume_name,
+ params = {'name': volume_name,
'imageRef': image_id,
'size': CONF.volume.volume_size}
volume = volumes_client.create_volume(**params)
@@ -226,3 +232,87 @@
servers_client.shelve_offload_server(server_id)
waiters.wait_for_server_status(servers_client, server_id,
'SHELVED_OFFLOADED')
+
+
+def create_websocket(url):
+ url = urlparse.urlparse(url)
+ client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ client_socket.connect((url.hostname, url.port))
+ # Turn the Socket into a WebSocket to do the communication
+ return _WebSocket(client_socket, url)
+
+
+class _WebSocket(object):
+ def __init__(self, client_socket, url):
+ """Contructor for the WebSocket wrapper to the socket."""
+ self._socket = client_socket
+ # Upgrade the HTTP connection to a WebSocket
+ self._upgrade(url)
+
+ def receive_frame(self):
+ """Wrapper for receiving data to parse the WebSocket frame format"""
+ # We need to loop until we either get some bytes back in the frame
+ # or no data was received (meaning the socket was closed). This is
+ # done to handle the case where we get back some empty frames
+ while True:
+ header = self._socket.recv(2)
+ # If we didn't receive any data, just return None
+ if not header:
+ return None
+ # We will make the assumption that we are only dealing with
+ # frames less than 125 bytes here (for the negotiation) and
+ # that only the 2nd byte contains the length, and since the
+ # server doesn't do masking, we can just read the data length
+ if ord_func(header[1]) & 127 > 0:
+ return self._socket.recv(ord_func(header[1]) & 127)
+
+ def send_frame(self, data):
+ """Wrapper for sending data to add in the WebSocket frame format."""
+ frame_bytes = list()
+ # For the first byte, want to say we are sending binary data (130)
+ frame_bytes.append(130)
+ # Only sending negotiation data so don't need to worry about > 125
+ # We do need to add the bit that says we are masking the data
+ frame_bytes.append(len(data) | 128)
+ # We don't really care about providing a random mask for security
+ # So we will just hard-code a value since a test program
+ mask = [7, 2, 1, 9]
+ for i in range(len(mask)):
+ frame_bytes.append(mask[i])
+ # Mask each of the actual data bytes that we are going to send
+ for i in range(len(data)):
+ frame_bytes.append(ord_func(data[i]) ^ mask[i % 4])
+ # Convert our integer list to a binary array of bytes
+ frame_bytes = struct.pack('!%iB' % len(frame_bytes), * frame_bytes)
+ self._socket.sendall(frame_bytes)
+
+ def close(self):
+ """Helper method to close the connection."""
+ # Close down the real socket connection and exit the test program
+ if self._socket is not None:
+ self._socket.shutdown(1)
+ self._socket.close()
+ self._socket = None
+
+ def _upgrade(self, url):
+ """Upgrade the HTTP connection to a WebSocket and verify."""
+ # The real request goes to the /websockify URI always
+ reqdata = 'GET /websockify HTTP/1.1\r\n'
+ reqdata += 'Host: %s:%s\r\n' % (url.hostname, url.port)
+ # Tell the HTTP Server to Upgrade the connection to a WebSocket
+ reqdata += 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
+ # The token=xxx is sent as a Cookie not in the URI
+ reqdata += 'Cookie: %s\r\n' % url.query
+ # Use a hard-coded WebSocket key since a test program
+ reqdata += 'Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n'
+ reqdata += 'Sec-WebSocket-Version: 13\r\n'
+ # We are choosing to use binary even though browser may do Base64
+ reqdata += 'Sec-WebSocket-Protocol: binary\r\n\r\n'
+ # Send the HTTP GET request and get the response back
+ self._socket.sendall(reqdata.encode('utf8'))
+ self.response = data = self._socket.recv(4096)
+ # Loop through & concatenate all of the data in the response body
+ while data and self.response.find(b'\r\n\r\n') < 0:
+ data = self._socket.recv(4096)
+ self.response += data
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index a92d16a..8053cac 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -241,7 +241,7 @@
def _get_creds(self, roles=None):
useable_hashes = self._get_match_hash_list(roles)
- if len(useable_hashes) == 0:
+ if not useable_hashes:
msg = 'No users configured for type/roles %s' % roles
raise lib_exc.InvalidCredentials(msg)
free_hash = self._get_free_hash(useable_hashes)
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 414c016..9319d2a 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -81,12 +81,6 @@
cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
return self.exec_command(cmd)
- def set_mac_address(self, nic, address):
- self.set_nic_state(nic=nic, state="down")
- cmd = "sudo ip link set dev {0} address {1}".format(nic, address)
- self.exec_command(cmd)
- self.set_nic_state(nic=nic, state="up")
-
def get_mac_address(self, nic=""):
show_nic = "show {nic} ".format(nic=nic) if nic else ""
cmd = "ip addr %s| awk '/ether/ {print $2}'" % show_nic
@@ -102,15 +96,6 @@
nic = self.exec_command(cmd)
return nic.strip().strip(":").lower()
- def assign_static_ip(self, nic, addr, network_mask_bits=28):
- cmd = "sudo ip addr add {ip}/{mask} dev {nic}".format(
- ip=addr, mask=network_mask_bits, nic=nic)
- return self.exec_command(cmd)
-
- def set_nic_state(self, nic, state="up"):
- cmd = "sudo ip link set {nic} {state}".format(nic=nic, state=state)
- return self.exec_command(cmd)
-
def get_dns_servers(self):
cmd = 'cat /etc/resolv.conf'
resolve_file = self.exec_command(cmd).strip().split('\n')
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 3e5600c..9c83c99 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -261,16 +261,16 @@
time.sleep(client.build_interval)
-def wait_for_interface_status(client, server, port_id, status):
+def wait_for_interface_status(client, server_id, port_id, status):
"""Waits for an interface to reach a given status."""
- body = (client.show_interface(server, port_id)
+ body = (client.show_interface(server_id, port_id)
['interfaceAttachment'])
interface_status = body['port_state']
start = int(time.time())
while(interface_status != status):
time.sleep(client.build_interval)
- body = (client.show_interface(server, port_id)
+ body = (client.show_interface(server_id, port_id)
['interfaceAttachment'])
interface_status = body['port_state']
diff --git a/tempest/config.py b/tempest/config.py
index a8d0355..b8a91b7 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -208,6 +208,10 @@
cfg.BoolOpt('api_v2',
default=True,
help='Is the v2 identity API enabled'),
+ cfg.BoolOpt('api_v2_admin',
+ default=True,
+ help="Is the v2 identity admin API available? This setting "
+ "only applies if api_v2 is set to True."),
cfg.BoolOpt('api_v3',
default=True,
help='Is the v3 identity API enabled'),
@@ -217,21 +221,17 @@
"entry all which indicates every extension is enabled. "
"Empty list indicates all extensions are disabled. "
"To get the list of extensions run: 'keystone discover'"),
- # TODO(rodrigods): Remove the reseller flag when Kilo and Liberty is end
- # of life.
- cfg.BoolOpt('reseller',
- default=True,
- help='Does the environment support reseller?',
- deprecated_for_removal=True,
- deprecated_reason="All supported versions of OpenStack now "
- "support the 'reseller' feature"),
# TODO(rodrigods): This is a feature flag for bug 1590578 which is fixed
# in Newton and Ocata. This option can be removed after Mitaka is end of
# life.
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 '
@@ -291,7 +291,9 @@
cfg.StrOpt('volume_device_name',
default='vdb',
help="Expected device name when a volume is attached to "
- "an instance"),
+ "an instance. Not all hypervisors guarantee that they "
+ "will respect the user defined device name, tests may "
+ "fail if inappropriate device name is set."),
cfg.IntOpt('shelved_offload_time',
default=0,
help='Time in seconds before a shelved instance is eligible '
@@ -334,15 +336,6 @@
title="Enabled Compute Service Features")
ComputeFeaturesGroup = [
- # NOTE(mriedem): This is a feature toggle for bug 1175464 which is fixed in
- # mitaka and newton. This option can be removed after liberty-eol.
- cfg.BoolOpt('allow_port_security_disabled',
- default=True,
- help='Does the test environment support creating ports in a '
- 'network where port security is disabled?',
- deprecated_for_removal=True,
- deprecated_reason='This config switch was added for Liberty '
- 'which is not supported anymore.'),
cfg.BoolOpt('disk_config',
default=True,
help="If false, skip disk config tests"),
@@ -384,6 +377,11 @@
cfg.BoolOpt('live_migration',
default=True,
help="Does the test environment support live migration?"),
+ cfg.BoolOpt('live_migrate_back_and_forth',
+ default=False,
+ help="Does the test environment support live migrating "
+ "VM back and forth between different versions of "
+ "nova-compute?"),
cfg.BoolOpt('metadata_service',
default=True,
help="Does the test environment support metadata service? "
@@ -503,7 +501,7 @@
"users can specify."),
cfg.ListOpt('disk_formats',
default=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2',
- 'vdi', 'iso'],
+ 'vdi', 'iso', 'vhdx'],
help="A list of image's disk formats "
"users can specify.")
]
@@ -611,18 +609,6 @@
default=False,
help="The environment does not support network separation "
"between tenants."),
- # TODO(ylobankov): Delete this option once the Liberty release is EOL.
- cfg.BoolOpt('dvr_extra_resources',
- default=True,
- help="Whether or not to create internal network, subnet, "
- "port and add network interface to distributed router "
- "in L3 agent scheduler test. Extra resources need to be "
- "provisioned in order to bind router to L3 agent in the "
- "Liberty release or older, and are not required since "
- "the Mitaka release.",
- deprecated_for_removal=True,
- deprecated_reason='This config switch was added for Liberty '
- 'which is not supported anymore.')
]
network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -684,9 +670,7 @@
help='Default IP version for ssh connections.'),
cfg.IntOpt('ping_timeout',
default=120,
- help='Timeout in seconds to wait for ping to succeed.',
- deprecated_opts=[cfg.DeprecatedOpt('ping_timeout',
- group='compute')]),
+ help='Timeout in seconds to wait for ping to succeed.'),
cfg.IntOpt('connect_timeout',
default=60,
help='Timeout in seconds to wait for the TCP connection to be '
@@ -696,13 +680,7 @@
help='Timeout in seconds to wait for the ssh banner.'),
cfg.StrOpt('image_ssh_user',
default="root",
- help="User name used to authenticate to an instance.",
- deprecated_opts=[cfg.DeprecatedOpt('image_ssh_user',
- group='compute'),
- cfg.DeprecatedOpt('ssh_user',
- group='compute'),
- cfg.DeprecatedOpt('ssh_user',
- group='scenario')]),
+ help="User name used to authenticate to an instance."),
cfg.StrOpt('image_ssh_password',
default="password",
help="Password used to authenticate to an instance."),
@@ -727,9 +705,7 @@
cfg.StrOpt('network_for_ssh',
default='public',
help="Network used for SSH connections. Ignored if "
- "connect_method=floating.",
- deprecated_opts=[cfg.DeprecatedOpt('network_for_ssh',
- group='compute')]),
+ "connect_method=floating."),
]
volume_group = cfg.OptGroup(name='volume',
@@ -773,6 +749,12 @@
cfg.IntOpt('volume_size',
default=1,
help='Default size in GB for volumes created by volumes tests'),
+ cfg.ListOpt('manage_volume_ref',
+ default=['source-name', 'volume-%s'],
+ help="A reference to existing volume for volume manage. "
+ "It contains two elements, the first is ref type "
+ "(like 'source-name', 'source-id', etc), the second is "
+ "volume name template used in storage backend"),
cfg.StrOpt('min_microversion',
default=None,
help="Lower version of the test target microversion range. "
@@ -812,6 +794,9 @@
cfg.BoolOpt('manage_snapshot',
default=False,
help='Runs Cinder manage snapshot tests'),
+ cfg.BoolOpt('manage_volume',
+ default=False,
+ help='Runs Cinder manage volume tests'),
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled volume extensions with a special '
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index f48d7ac..a437761 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -17,10 +17,6 @@
from tempest.lib import exceptions
-class InvalidServiceTag(exceptions.TempestException):
- message = "Invalid service tag"
-
-
class BuildErrorException(exceptions.TempestException):
message = "Server %(server_id)s failed to build and is in ERROR status"
diff --git a/tempest/lib/api_schema/response/compute/v2_1/servers.py b/tempest/lib/api_schema/response/compute/v2_1/servers.py
index 4c4b5eb..33a7757 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/servers.py
@@ -100,8 +100,10 @@
'id': {'type': 'string'},
'links': parameter_types.links
},
- 'additionalProperties': False,
- 'required': ['id', 'links']
+ # NOTE(gmann): This will be empty object if there is no
+ # flavor info present in DB. This can happen when flavor info is
+ # deleted after server creation.
+ 'additionalProperties': False
},
'fault': {
'type': 'object',
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index 83aa405..ab4308f 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -539,18 +539,18 @@
# Select entries with matching service type
service_catalog = [ep for ep in catalog if ep['type'] == service]
- if len(service_catalog) > 0:
+ if service_catalog:
if name is not None:
service_catalog = (
[ep for ep in service_catalog if ep['name'] == name])
- if len(service_catalog) > 0:
+ if service_catalog:
service_catalog = service_catalog[0]['endpoints']
else:
raise exceptions.EndpointNotFound(name)
else:
service_catalog = service_catalog[0]['endpoints']
else:
- if len(catalog) == 0 and service == 'identity':
+ if not catalog and service == 'identity':
# NOTE(andreaf) If there's no catalog at all and the service
# is identity, it's a valid use case. Having a non-empty
# catalog with no identity in it is not valid instead.
@@ -571,13 +571,13 @@
# Filter by endpoint type (interface)
filtered_catalog = [ep for ep in service_catalog if
ep['interface'] == endpoint_type]
- if len(filtered_catalog) == 0:
+ if not filtered_catalog:
# No matching type, keep all and try matching by region at least
filtered_catalog = service_catalog
# Filter by region
filtered_catalog = [ep for ep in filtered_catalog if
ep['region'] == region]
- if len(filtered_catalog) == 0:
+ if not filtered_catalog:
# No matching region (or name), take the first endpoint
filtered_catalog = [service_catalog[0]]
# There should be only one match. If not take the first.
diff --git a/tempest/lib/cli/output_parser.py b/tempest/lib/cli/output_parser.py
index 716f374..2edd5c1 100644
--- a/tempest/lib/cli/output_parser.py
+++ b/tempest/lib/cli/output_parser.py
@@ -114,7 +114,7 @@
label = line
else:
LOG.warning('Invalid line between tables: %s', line)
- if len(table_) > 0:
+ if table_:
LOG.warning('Missing end of table')
return tables_
diff --git a/tempest/lib/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index eafde44..101d692 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -29,7 +29,7 @@
DECORATOR_MODULE = 'decorators'
DECORATOR_NAME = 'idempotent_id'
DECORATOR_IMPORT = 'tempest.%s' % DECORATOR_MODULE
-IMPORT_LINE = 'from tempest import %s' % DECORATOR_MODULE
+IMPORT_LINE = 'from tempest.lib import %s' % DECORATOR_MODULE
DECORATOR_TEMPLATE = "@%s.%s('%%s')" % (DECORATOR_MODULE,
DECORATOR_NAME)
UNIT_TESTS_EXCLUDE = 'tempest.tests'
@@ -355,7 +355,7 @@
if errors:
sys.exit("@decorators.idempotent_id existence and uniqueness checks "
"failed\n"
- "Run 'tox -v -euuidgen' to automatically fix tests with\n"
+ "Run 'tox -v -e uuidgen' to automatically fix tests with\n"
"missing @decorators.idempotent_id decorators.")
if __name__ == '__main__':
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index 07b811d..87806b7 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -34,10 +34,14 @@
LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
LOG = logging.getLogger(__name__)
+BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))
+TESTDIR = os.path.join(BASEDIR, 'tempest')
+
def parse_args():
parser = argparse.ArgumentParser()
- parser.add_argument('test_path', help='Path of test dir')
+ parser.add_argument('test_path', nargs='?', default=TESTDIR,
+ help='Path of test dir')
return parser.parse_args()
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 92f9698..f82f707 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -16,9 +16,12 @@
import uuid
import debtcollector.removals
+from oslo_log import log as logging
import six
import testtools
+LOG = logging.getLogger(__name__)
+
def skip_because(*args, **kwargs):
"""A decorator useful to skip tests hitting known bugs
@@ -45,6 +48,28 @@
return decorator
+def related_bug(bug, status_code=None):
+ """A decorator useful to know solutions from launchpad bug reports
+
+ @param bug: The launchpad bug number causing the test
+ @param status_code: The status code related to the bug report
+ """
+ def decorator(f):
+ @functools.wraps(f)
+ def wrapper(self, *func_args, **func_kwargs):
+ try:
+ return f(self, *func_args, **func_kwargs)
+ except Exception as exc:
+ exc_status_code = getattr(exc, 'status_code', None)
+ if status_code is None or status_code == exc_status_code:
+ LOG.error('Hints: This test was made for the bug %s. '
+ 'The failure could be related to '
+ 'https://launchpad.net/bugs/%s', bug, bug)
+ raise exc
+ return wrapper
+ return decorator
+
+
def idempotent_id(id):
"""Stub for metadata decorator"""
if not isinstance(id, six.string_types):
@@ -79,3 +104,21 @@
raise testtools.TestCase.skipException(self.message)
func(*args, **kw)
return _skipper
+
+
+def attr(**kwargs):
+ """A decorator which applies the testtools attr decorator
+
+ This decorator applies the testtools.testcase.attr if it is in the list of
+ attributes to testtools we want to apply.
+ """
+
+ def decorator(f):
+ if 'type' in kwargs and isinstance(kwargs['type'], str):
+ f = testtools.testcase.attr(kwargs['type'])(f)
+ elif 'type' in kwargs and isinstance(kwargs['type'], list):
+ for attr in kwargs['type']:
+ f = testtools.testcase.attr(attr)(f)
+ return f
+
+ return decorator
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index dea3289..68ce57a 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -32,7 +32,7 @@
except Exception:
# at least get the core message out if something happened
self._error_string = self.message
- if len(args) > 0:
+ if args:
# If there is a non-kwarg parameter, assume it's the error
# message or reason description and tack it on to the end
# of the exception message
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index eefac66..cd3bab0 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -17,7 +17,9 @@
import copy
import importlib
import inspect
+import warnings
+from debtcollector import removals
from oslo_log import log as logging
from tempest.lib import auth
@@ -29,7 +31,7 @@
from tempest.lib.services import network
from tempest.lib.services import volume
-
+warnings.simplefilter("once")
LOG = logging.getLogger(__name__)
@@ -70,15 +72,15 @@
:raise PluginRegistrationException: if a plugin exposes a service_version
already defined by Tempest or another plugin.
- Examples:
+ Examples::
- >>> from tempest import config
- >>> params = {}
- >>> for service_version in available_modules():
- >>> service = service_version.split('.')[0]
- >>> params[service] = config.service_client_config(service)
- >>> service_clients = ServiceClients(creds, identity_uri,
- >>> client_parameters=params)
+ from tempest import config
+ params = {}
+ for service_version in available_modules():
+ service = service_version.split('.')[0]
+ params[service] = config.service_client_config(service)
+ service_clients = ServiceClients(creds, identity_uri,
+ client_parameters=params)
"""
extra_service_versions = set([])
_tempest_modules = set(tempest_modules())
@@ -161,17 +163,17 @@
parameters cannot be deleted.
:raise ImportError if the specified module_path cannot be imported
- Example:
+ Example::
- >>> # Get credentials and an auth_provider
- >>> clients = ClientsFactory(
- >>> module_path='my_service.my_service_clients',
- >>> client_names=['ServiceClient1', 'ServiceClient2'],
- >>> auth_provider=auth_provider,
- >>> service='my_service',
- >>> region='region1')
- >>> my_api_client = clients.MyApiClient()
- >>> my_api_client_region2 = clients.MyApiClient(region='region2')
+ # Get credentials and an auth_provider
+ clients = ClientsFactory(
+ module_path='my_service.my_service_clients',
+ client_names=['ServiceClient1', 'ServiceClient2'],
+ auth_provider=auth_provider,
+ service='my_service',
+ region='region1')
+ my_api_client = clients.MyApiClient()
+ my_api_client_region2 = clients.MyApiClient(region='region2')
"""
# Import the module. If it's not importable, the raised exception
@@ -242,13 +244,19 @@
It hides some of the complexity from the authorization and configuration
layers.
- Examples:
+ Examples::
- >>> from tempest.lib.services import clients
- >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
- >>> johndoe_clients = clients.ServiceClients(johndoe,
- >>> identity_uri)
- >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+ # johndoe is a tempest.lib.auth.Credentials type instance
+ johndoe_clients = clients.ServiceClients(johndoe, identity_uri)
+
+ # List servers in default region
+ johndoe_servers_client = johndoe_clients.compute.ServersClient()
+ johndoe_servers = johndoe_servers_client.list_servers()
+
+ # List servers in Region B
+ johndoe_servers_client_B = johndoe_clients.compute.ServersClient(
+ region='B')
+ johndoe_servers = johndoe_servers_client_B.list_servers()
"""
# NOTE(andreaf) This class does not depend on tempest configuration
@@ -257,6 +265,7 @@
# initialises this class using values from tempest CONF object. The wrapper
# class should only be used by tests hosted in Tempest.
+ @removals.removed_kwarg('client_parameters')
def __init__(self, credentials, identity_uri, region=None, scope='project',
disable_ssl_certificate_validation=True, ca_certs=None,
trace_requests='', client_parameters=None):
@@ -272,7 +281,12 @@
Parameters dscv, ca_certs and trace_requests all apply to the auth
provider as well as any service clients provided by this manager.
- Any other client parameter must be set via client_parameters.
+ Any other client parameter should be set via ClientsRegistry.
+
+ Client parameter used to be set via client_parameters, but this is
+ deprecated, and it is actually already not honoured
+ anymore: https://launchpad.net/bugs/1680915.
+
The list of available parameters is defined in the service clients
interfaces. For reference, most clients will accept 'region',
'service', 'endpoint_type', 'build_timeout' and 'build_interval', which
@@ -287,14 +301,18 @@
- Volume client for 'volume' accepts 'default_volume_size'
- Servers client from 'compute' accepts 'enable_instance_password'
- Examples:
+ If Tempest configuration is used, parameters will be loaded in the
+ Registry automatically for all service client (Tempest stable ones
+ and plugins).
- >>> identity_params = config.service_client_config('identity')
- >>> params = {
- >>> 'identity': identity_params,
- >>> 'compute': {'region': 'region2'}}
- >>> manager = lib_manager.Manager(
- >>> my_creds, identity_uri, client_parameters=params)
+ Examples::
+
+ identity_params = config.service_client_config('identity')
+ params = {
+ 'identity': identity_params,
+ 'compute': {'region': 'region2'}}
+ manager = lib_manager.Manager(
+ my_creds, identity_uri, client_parameters=params)
:param credentials: An instance of `auth.Credentials`
:param identity_uri: URI of the identity API. This should be a
@@ -310,15 +328,6 @@
name, as declared in `service_clients.available_modules()` except
for the version. Values are dictionaries of parameters that are
going to be passed to all clients in the service client module.
-
- Examples:
-
- >>> params_service_x = {'param_name': 'param_value'}
- >>> client_parameters = { 'service_x': params_service_x }
-
- >>> params_service_y = config.service_client_config('service_y')
- >>> client_parameters['service_y'] = params_service_y
-
"""
self._registered_services = set([])
self.credentials = credentials
diff --git a/tempest/lib/services/compute/quota_classes_client.py b/tempest/lib/services/compute/quota_classes_client.py
index 523a306..0fe9868 100644
--- a/tempest/lib/services/compute/quota_classes_client.py
+++ b/tempest/lib/services/compute/quota_classes_client.py
@@ -35,9 +35,8 @@
def update_quota_class_set(self, quota_class_id, **kwargs):
"""Update the quota class's limits for one or more resources.
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref-compute-v2.1.html#updatequota
+ # NOTE: Current api-site doesn't contain this API description.
+ # LP: https://bugs.launchpad.net/nova/+bug/1602400
"""
post_body = json.dumps({'quota_class_set': kwargs})
diff --git a/tempest/lib/services/compute/tenant_usages_client.py b/tempest/lib/services/compute/tenant_usages_client.py
index d0eb1c9..ade60e5 100644
--- a/tempest/lib/services/compute/tenant_usages_client.py
+++ b/tempest/lib/services/compute/tenant_usages_client.py
@@ -28,7 +28,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/compute/#list-tenant-usage-for-all-tenants
+ https://developer.openstack.org/api-ref/compute/#list-tenant-usage-statistics-for-all-tenants
"""
url = 'os-simple-tenant-usage'
if params:
@@ -44,7 +44,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/compute/#show-usage-details-for-tenant
+ https://developer.openstack.org/api-ref/compute/#show-usage-statistics-for-tenant
"""
url = 'os-simple-tenant-usage/%s' % tenant_id
if params:
diff --git a/tempest/lib/services/compute/versions_client.py b/tempest/lib/services/compute/versions_client.py
index 75984ec..8fbb136 100644
--- a/tempest/lib/services/compute/versions_client.py
+++ b/tempest/lib/services/compute/versions_client.py
@@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import time
+
from oslo_serialization import jsonutils as json
from tempest.lib.api_schema.response.compute.v2_1 import versions as schema
@@ -23,7 +25,13 @@
def list_versions(self):
version_url = self._get_base_version_url()
+
+ start = time.time()
resp, body = self.raw_request(version_url, 'GET')
+ end = time.time()
+ self._log_request('GET', version_url, resp, secs=(end - start),
+ resp_body=body)
+
self._error_checker(resp, body)
body = json.loads(body)
self.validate_response(schema.list_versions, resp, body)
diff --git a/tempest/lib/services/identity/v2/endpoints_client.py b/tempest/lib/services/identity/v2/endpoints_client.py
index 770e8ae..db8d7cc 100644
--- a/tempest/lib/services/identity/v2/endpoints_client.py
+++ b/tempest/lib/services/identity/v2/endpoints_client.py
@@ -25,7 +25,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref-identity-v2-ext.html#createEndpoint
+ https://developer.openstack.org/api-ref/identity/v2-admin/index.html#create-endpoint-template
"""
post_body = json.dumps({'endpoint': kwargs})
diff --git a/tempest/lib/services/identity/v2/users_client.py b/tempest/lib/services/identity/v2/users_client.py
index cfd97bb..44bb5fd 100644
--- a/tempest/lib/services/identity/v2/users_client.py
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -88,7 +88,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref-identity-v2-ext.html#enableUser
+ https://developer.openstack.org/api-ref/identity/v2-ext/index.html#enable-disable-user
"""
# NOTE: The URL (users/<id>/enabled) is different from the api-site
# one (users/<id>/OS-KSADM/enabled) , but they are the same API
diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py
index 88801e7..f2f3391 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -14,12 +14,16 @@
from tempest.lib.services.identity.v3.credentials_client import \
CredentialsClient
+from tempest.lib.services.identity.v3.domain_configuration_client \
+ import DomainConfigurationClient
from tempest.lib.services.identity.v3.domains_client import DomainsClient
from tempest.lib.services.identity.v3.endpoints_client import EndPointsClient
from tempest.lib.services.identity.v3.groups_client import GroupsClient
from tempest.lib.services.identity.v3.identity_client import IdentityClient
from tempest.lib.services.identity.v3.inherited_roles_client import \
InheritedRolesClient
+from tempest.lib.services.identity.v3.oauth_consumers_client import \
+ OAUTHConsumerClient
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
@@ -32,8 +36,9 @@
from tempest.lib.services.identity.v3.users_client import UsersClient
from tempest.lib.services.identity.v3.versions_client import VersionsClient
-__all__ = ['CredentialsClient', 'DomainsClient', 'EndPointsClient',
- 'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
- 'PoliciesClient', 'ProjectsClient', 'RegionsClient',
- 'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
- 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
+__all__ = ['CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
+ 'EndPointsClient', 'GroupsClient', 'IdentityClient',
+ 'InheritedRolesClient', 'OAUTHConsumerClient', 'PoliciesClient',
+ 'ProjectsClient', 'RegionsClient', 'RoleAssignmentsClient',
+ 'RolesClient', 'ServicesClient', 'V3TokenClient', 'TrustsClient',
+ 'UsersClient', 'VersionsClient']
diff --git a/tempest/lib/services/identity/v3/domain_configuration_client.py b/tempest/lib/services/identity/v3/domain_configuration_client.py
new file mode 100644
index 0000000..d57f2d4
--- /dev/null
+++ b/tempest/lib/services/identity/v3/domain_configuration_client.py
@@ -0,0 +1,188 @@
+# Copyright 2017 AT&T Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class DomainConfigurationClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def show_default_config_settings(self):
+ """Show default configuration settings.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-configuration-settings
+ """
+ url = 'domains/config/default'
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_default_group_config(self, group):
+ """Show default configuration for a group.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-configuration-for-a-group
+ """
+ url = 'domains/config/%s/default' % group
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_default_group_option(self, group, option):
+ """Show default option for a group.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-default-option-for-a-group
+ """
+ url = 'domains/config/%s/%s/default' % (group, option)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_domain_group_option_config(self, domain_id, group, option):
+ """Show domain group option configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-group-option-configuration
+ """
+ url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_domain_group_option_config(self, domain_id, group, option,
+ **kwargs):
+ """Update domain group option configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-group-option-configuration
+ """
+ url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+ resp, body = self.patch(url, json.dumps({'config': kwargs}))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_domain_group_option_config(self, domain_id, group, option):
+ """Delete domain group option configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-group-option-configuration
+ """
+ url = 'domains/%s/config/%s/%s' % (domain_id, group, option)
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_domain_group_config(self, domain_id, group):
+ """Shows details for a domain group configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-group-configuration
+ """
+ url = 'domains/%s/config/%s' % (domain_id, group)
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_domain_group_config(self, domain_id, group, **kwargs):
+ """Update domain group configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-group-configuration
+ """
+ url = 'domains/%s/config/%s' % (domain_id, group)
+ resp, body = self.patch(url, json.dumps({'config': kwargs}))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_domain_group_config(self, domain_id, group):
+ """Delete domain group configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-group-configuration
+ """
+ url = 'domains/%s/config/%s' % (domain_id, group)
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def create_domain_config(self, domain_id, **kwargs):
+ """Create domain configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain-configuration
+ """
+ url = 'domains/%s/config' % domain_id
+ resp, body = self.put(url, json.dumps({'config': kwargs}))
+ self.expected_success([200, 201], resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_domain_config(self, domain_id):
+ """Show domain configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-configuration
+ """
+ url = 'domains/%s/config' % domain_id
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_domain_config(self, domain_id, **kwargs):
+ """Update domain configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain-configuration
+ """
+ url = 'domains/%s/config' % domain_id
+ resp, body = self.patch(url, json.dumps({'config': kwargs}))
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_domain_config(self, domain_id):
+ """Delete domain configuration.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain-configuration
+ """
+ url = 'domains/%s/config' % domain_id
+ resp, body = self.delete(url)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/oauth_consumers_client.py b/tempest/lib/services/identity/v3/oauth_consumers_client.py
new file mode 100644
index 0000000..97fb141
--- /dev/null
+++ b/tempest/lib/services/identity/v3/oauth_consumers_client.py
@@ -0,0 +1,95 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class OAUTHConsumerClient(rest_client.RestClient):
+ api_version = "v3"
+
+ def create_consumer(self, description=None):
+ """Creates a consumer.
+
+ :param str description: Optional field to add notes about the consumer
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#create-consumer
+ """
+ post_body = {"description": description}
+ post_body = json.dumps({'consumer': post_body})
+ resp, body = self.post('OS-OAUTH1/consumers', post_body)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_consumer(self, consumer_id):
+ """Deletes a consumer.
+
+ :param str consumer_id: The ID of the consumer that will be deleted
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#delete-consumer
+ """
+ resp, body = self.delete('OS-OAUTH1/consumers/%s' % consumer_id)
+ self.expected_success(204, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_consumer(self, consumer_id, description=None):
+ """Updates a consumer.
+
+ :param str consumer_id: The ID of the consumer that will be updated
+ :param str description: Optional field to add notes about the consumer
+
+ For more information, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/identity/v3-ext/#update-consumer
+ """
+ post_body = {"description": description}
+ post_body = json.dumps({'consumer': post_body})
+ resp, body = self.patch('OS-OAUTH1/consumers/%s' % consumer_id,
+ post_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_consumer(self, consumer_id):
+ """Show consumer details.
+
+ :param str consumer_id: The ID of the consumer that will be shown
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#show-consumer-details
+ """
+ resp, body = self.get('OS-OAUTH1/consumers/%s' % consumer_id)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def list_consumers(self):
+ """List all consumers.
+
+ For more information, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3-ext/#list-consumers
+ """
+ resp, body = self.get('OS-OAUTH1/consumers')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 0df23ce..43e3c01 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -214,6 +214,18 @@
body = json.loads(body)
return rest_client.ResponseBody(resp, body)
+ def list_all_role_inference_rules(self):
+ """Lists all role inference rules.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref/identity/v3/index.html#list-all-role-inference-rules
+ """
+ resp, body = self.get('role_inferences')
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
def check_role_inference_rule(self, prior_role, implies_role):
"""Check a role inference rule."""
resp, body = self.head('roles/%s/implies/%s' %
diff --git a/tempest/lib/services/image/v1/images_client.py b/tempest/lib/services/image/v1/images_client.py
index faafb4a..42f8cf2 100644
--- a/tempest/lib/services/image/v1/images_client.py
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -118,7 +118,7 @@
if 'changes_since' in kwargs:
kwargs['changes-since'] = kwargs.pop('changes_since')
- if len(kwargs) > 0:
+ if kwargs:
url += '?%s' % urllib.urlencode(kwargs)
resp, body = self.get(url)
diff --git a/tempest/lib/services/image/v2/namespace_tags_client.py b/tempest/lib/services/image/v2/namespace_tags_client.py
index a7f8c39..61cc33d 100644
--- a/tempest/lib/services/image/v2/namespace_tags_client.py
+++ b/tempest/lib/services/image/v2/namespace_tags_client.py
@@ -41,7 +41,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get_tag_definition
+ https://developer.openstack.org/api-ref/image/v2/metadefs-index.html#get-tag-definition
"""
url = 'metadefs/namespaces/%s/tags/%s' % (namespace,
tag_name)
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 8acad0f..9434896 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -30,11 +30,14 @@
from tempest.lib.services.volume.v2.snapshot_manage_client import \
SnapshotManageClient
from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
+from tempest.lib.services.volume.v2.transfers_client import TransfersClient
from tempest.lib.services.volume.v2.types_client import TypesClient
+from tempest.lib.services.volume.v2.volume_manage_client import \
+ VolumeManageClient
from tempest.lib.services.volume.v2.volumes_client import VolumesClient
__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
- 'SnapshotManageClient']
+ 'SnapshotManageClient', 'VolumeManageClient', 'TransfersClient']
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
index 40cb8bf..240be13 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -24,9 +24,9 @@
def show_backend_capabilities(self, host):
"""Shows capabilities for a storage back end.
- Output params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html
- #showBackendCapabilities
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/index.html#show-back-end-capabilities
"""
url = 'capabilities/%s' % host
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/encryption_types_client.py b/tempest/lib/services/volume/v2/encryption_types_client.py
index 8b01f11..eeff537 100755
--- a/tempest/lib/services/volume/v2/encryption_types_client.py
+++ b/tempest/lib/services/volume/v2/encryption_types_client.py
@@ -67,3 +67,17 @@
"/types/%s/encryption/provider" % volume_type_id)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def update_encryption_type(self, volume_type_id, **kwargs):
+ """Update an encryption type for an existing volume type.
+
+ TODO: Current api-site doesn't contain this API description.
+ After fixing the api-site, we need to fix here also for putting
+ the link to api-site.
+ """
+ url = "/types/%s/encryption/provider" % volume_type_id
+ put_body = json.dumps({'encryption': kwargs})
+ resp, body = self.put(url, put_body)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
index 3f56f82..0d04f85 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -24,8 +24,9 @@
def list_pools(self, detail=False):
"""List all the volumes pools (hosts).
- Output params: see http://developer.openstack.org/
- api-ref-blockstorage-v2.html#listPools
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/index.html#list-back-end-storage-pools
"""
url = 'scheduler-stats/get_pools'
if detail:
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 2bdf1b1..983ed89 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -27,7 +27,8 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-with-details
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots
"""
url = 'snapshots'
if detail:
@@ -45,7 +46,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-details
"""
url = "snapshots/%s" % snapshot_id
resp, body = self.get(url)
@@ -71,7 +72,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot
"""
put_body = json.dumps({'snapshot': kwargs})
resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -84,7 +85,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot
"""
resp, body = self.delete("snapshots/%s" % snapshot_id)
self.expected_success(202, resp.status)
@@ -136,7 +137,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata
"""
url = "snapshots/%s/metadata" % snapshot_id
resp, body = self.get(url)
@@ -149,7 +150,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata
"""
put_body = json.dumps(kwargs)
url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v2/transfers_client.py b/tempest/lib/services/volume/v2/transfers_client.py
new file mode 100644
index 0000000..2dfbe7b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -0,0 +1,83 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class TransfersClient(rest_client.RestClient):
+ """Client class to send CRUD Volume Transfer V2 API requests"""
+ api_version = "v2"
+
+ def create_volume_transfer(self, **kwargs):
+ """Create a volume transfer.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer
+ """
+ post_body = json.dumps({'transfer': kwargs})
+ resp, body = self.post('os-volume-transfer', post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def show_volume_transfer(self, transfer_id):
+ """Returns the details of a volume transfer."""
+ url = "os-volume-transfer/%s" % transfer_id
+ resp, body = self.get(url)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ 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)
+ body = json.loads(body)
+ self.expected_success(200, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def delete_volume_transfer(self, transfer_id):
+ """Delete a volume transfer."""
+ resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
+
+ def accept_volume_transfer(self, transfer_id, **kwargs):
+ """Accept a volume transfer.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer
+ """
+ url = 'os-volume-transfer/%s/accept' % transfer_id
+ post_body = json.dumps({'accept': kwargs})
+ resp, body = self.post(url, post_body)
+ body = json.loads(body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index 31597d7..5d30615 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -41,7 +41,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types
"""
url = 'types'
if params:
@@ -57,7 +57,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-details
"""
url = "types/%s" % volume_type_id
resp, body = self.get(url)
@@ -70,7 +70,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type
"""
post_body = json.dumps({'volume_type': kwargs})
resp, body = self.post('types', post_body)
@@ -83,7 +83,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type
"""
resp, body = self.delete("types/%s" % volume_type_id)
self.expected_success(202, resp.status)
@@ -138,7 +138,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type
"""
put_body = json.dumps({'volume_type': kwargs})
resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -156,7 +156,7 @@
updated value.
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-extra-specs-for-a-volume-type
"""
url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
put_body = json.dumps(extra_specs)
@@ -170,7 +170,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#add-private-volume-type-access
"""
post_body = json.dumps({'addProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -183,7 +183,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#remove-private-volume-type-access
"""
post_body = json.dumps({'removeProjectAccess': kwargs})
url = 'types/%s/action' % volume_type_id
@@ -196,7 +196,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#list-private-volume-type-access-details
"""
url = 'types/%s/os-volume-type-access' % volume_type_id
resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volume_manage_client.py b/tempest/lib/services/volume/v2/volume_manage_client.py
new file mode 100644
index 0000000..12f4240
--- /dev/null
+++ b/tempest/lib/services/volume/v2/volume_manage_client.py
@@ -0,0 +1,37 @@
+# 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VolumeManageClient(rest_client.RestClient):
+ """Volume manage V2 client."""
+
+ api_version = "v2"
+
+ def manage_volume(self, **kwargs):
+ """Manage existing volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#manage-existing-volume
+ """
+ post_body = json.dumps({'volume': kwargs})
+ resp, body = self.post('os-volume-manage', post_body)
+ self.expected_success(202, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 8b8e249..c67ddfb 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+from debtcollector import moves
from debtcollector import removals
from oslo_serialization import jsonutils as json
import six
@@ -20,12 +21,43 @@
from tempest.lib.common import rest_client
from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.volume.v2 import transfers_client
class VolumesClient(rest_client.RestClient):
"""Client class to send CRUD Volume V2 API requests"""
api_version = "v2"
+ create_volume_transfer = moves.moved_function(
+ transfers_client.TransfersClient.create_volume_transfer,
+ 'VolumesClient.create_volume_transfer', __name__,
+ message='Use create_volume_transfer from new location.',
+ version='Pike', removal_version='Queens')
+
+ show_volume_transfer = moves.moved_function(
+ transfers_client.TransfersClient.show_volume_transfer,
+ 'VolumesClient.show_volume_transfer', __name__,
+ message='Use show_volume_transfer from new location.',
+ version='Pike', removal_version='Queens')
+
+ list_volume_transfers = moves.moved_function(
+ transfers_client.TransfersClient.list_volume_transfers,
+ 'VolumesClient.list_volume_transfers', __name__,
+ message='Use list_volume_transfer from new location.',
+ version='Pike', removal_version='Queens')
+
+ delete_volume_transfer = moves.moved_function(
+ transfers_client.TransfersClient.delete_volume_transfer,
+ 'VolumesClient.delete_volume_transfer', __name__,
+ message='Use delete_volume_transfer from new location.',
+ version='Pike', removal_version='Queens')
+
+ accept_volume_transfer = moves.moved_function(
+ transfers_client.TransfersClient.accept_volume_transfer,
+ 'VolumesClient.accept_volume_transfer', __name__,
+ message='Use accept_volume_transfer from new location.',
+ version='Pike', removal_version='Queens')
+
def _prepare_params(self, params):
"""Prepares params for use in get or _ext_get methods.
@@ -65,7 +97,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#create-volume
"""
post_body = json.dumps({'volume': kwargs})
resp, body = self.post('volumes', post_body)
@@ -78,7 +110,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#update-volume
"""
put_body = json.dumps({'volume': kwargs})
resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -86,9 +118,12 @@
self.expected_success(200, resp.status)
return rest_client.ResponseBody(resp, body)
- def delete_volume(self, volume_id):
+ def delete_volume(self, volume_id, cascade=False):
"""Deletes the Specified Volume."""
- resp, body = self.delete("volumes/%s" % volume_id)
+ url = 'volumes/%s' % volume_id
+ if cascade:
+ url += '?cascade=True'
+ resp, body = self.delete(url)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
@@ -106,7 +141,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-to-server
"""
post_body = json.dumps({'os-attach': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -163,7 +198,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-size
"""
post_body = json.dumps({'os-extend': kwargs})
url = 'volumes/%s/action' % (volume_id)
@@ -176,69 +211,13 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-statuses
"""
post_body = json.dumps({'os-reset_status': kwargs})
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.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
- """
- post_body = json.dumps({'transfer': kwargs})
- resp, body = self.post('os-volume-transfer', post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def show_volume_transfer(self, transfer_id):
- """Returns the details of a volume transfer."""
- url = "os-volume-transfer/%s" % transfer_id
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def list_volume_transfers(self, **params):
- """List all the volume transfers created.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
- """
- url = 'os-volume-transfer'
- if params:
- url += '?%s' % urllib.urlencode(params)
- resp, body = self.get(url)
- body = json.loads(body)
- self.expected_success(200, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def delete_volume_transfer(self, transfer_id):
- """Delete a volume transfer."""
- resp, body = self.delete("os-volume-transfer/%s" % transfer_id)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
- def accept_volume_transfer(self, transfer_id, **kwargs):
- """Accept a volume transfer.
-
- For a full list of available parameters, please refer to the official
- API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
- """
- url = 'os-volume-transfer/%s/accept' % transfer_id
- post_body = json.dumps({'accept': kwargs})
- resp, body = self.post(url, post_body)
- body = json.loads(body)
- self.expected_success(202, resp.status)
- return rest_client.ResponseBody(resp, body)
-
def update_volume_readonly(self, volume_id, **kwargs):
"""Update the Specified Volume readonly."""
post_body = json.dumps({'os-update_readonly_flag': kwargs})
@@ -307,7 +286,7 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#set-image-metadata-for-volume
"""
post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
url = "volumes/%s/action" % (volume_id)
@@ -344,10 +323,22 @@
For a full list of available parameters, please refer to the official
API reference:
- http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
+ http://developer.openstack.org/api-ref/block-storage/v2/#show-back-end-capabilities
"""
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 unmanage_volume(self, volume_id):
+ """Unmanage volume.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v2/#unmanage-volume
+ """
+ post_body = json.dumps({'os-unmanage': {}})
+ resp, body = self.post('volumes/%s/action' % volume_id, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index d4f0e13..3418a2f 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -411,16 +411,17 @@
return image
- def _log_console_output(self, servers=None):
+ def _log_console_output(self, servers=None, client=None):
if not CONF.compute_feature_enabled.console_output:
LOG.debug('Console output not supported, cannot log')
return
+ client = client or self.servers_client
if not servers:
- servers = self.servers_client.list_servers()
+ servers = client.list_servers()
servers = servers['servers']
for server in servers:
try:
- console_output = self.servers_client.get_console_output(
+ console_output = client.get_console_output(
server['id'])['output']
LOG.debug('Console output for %s\nbody=\n%s',
server['id'], console_output)
@@ -878,16 +879,16 @@
show_floatingip(floatingip_id)['floatingip'])
return status == result['status']
- test_utils.call_until_true(refresh,
- CONF.network.build_timeout,
- CONF.network.build_interval)
- floating_ip = self.floating_ips_client.show_floatingip(
- floatingip_id)['floatingip']
- self.assertEqual(status, floating_ip['status'],
- message="FloatingIP: {fp} is at status: {cst}. "
- "failed to reach status: {st}"
- .format(fp=floating_ip, cst=floating_ip['status'],
- st=status))
+ if not test_utils.call_until_true(refresh,
+ CONF.network.build_timeout,
+ CONF.network.build_interval):
+ floating_ip = self.floating_ips_client.show_floatingip(
+ floatingip_id)['floatingip']
+ self.assertEqual(status, floating_ip['status'],
+ message="FloatingIP: {fp} is at status: {cst}. "
+ "failed to reach status: {st}"
+ .format(fp=floating_ip, cst=floating_ip['status'],
+ st=status))
LOG.info("FloatingIP: {fp} is at status: {st}"
.format(fp=floating_ip, st=status))
@@ -1241,14 +1242,9 @@
@classmethod
def setup_clients(cls):
super(EncryptionScenarioTest, cls).setup_clients()
- if CONF.volume_feature_enabled.api_v2:
- cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
- cls.admin_encryption_types_client =\
- cls.os_adm.encryption_types_v2_client
- else:
- cls.admin_volume_types_client = cls.os_adm.volume_types_client
- cls.admin_encryption_types_client =\
- cls.os_adm.encryption_types_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_encryption_types_client =\
+ cls.os_adm.encryption_types_v2_client
def create_encryption_type(self, client=None, type_id=None, provider=None,
key_size=None, cipher=None,
diff --git a/tempest/scenario/test_aggregates_basic_ops.py b/tempest/scenario/test_aggregates_basic_ops.py
index cefa119..8408a1e 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -96,7 +96,7 @@
return aggregate
@decorators.idempotent_id('cb2b4c4f-0c7c-4164-bdde-6285b302a081')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute')
def test_aggregate_basic_ops(self):
self.useFixture(fixtures.LockFixture('availability_zone'))
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index a05b1b1..d7b86f6 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -62,7 +62,7 @@
self.nova_volume_detach(server, attached_volume)
@decorators.idempotent_id('79165fb4-5534-4b9d-8429-97ccffb8f86e')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_luks(self):
server = self.launch_instance()
@@ -72,7 +72,7 @@
self.attach_detach_volume(server, volume)
@decorators.idempotent_id('cbc752ed-b716-4717-910f-956cce965722')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'volume', 'image')
def test_encrypted_cinder_volumes_cryptsetup(self):
server = self.launch_instance()
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index 6665fa7..ae2ba2f 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -104,7 +104,7 @@
return body['OS-EXT-SRV-ATTR:host']
@decorators.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_stop_start(self):
keypair = self.create_keypair()
@@ -130,7 +130,7 @@
server, keypair, floating_ip)
@decorators.idempotent_id('88a529c2-1daa-4c85-9aec-d541ba3eb699')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_rebuild(self):
keypair = self.create_keypair()
@@ -145,7 +145,7 @@
@decorators.idempotent_id('2b2642db-6568-4b35-b812-eceed3fa20ce')
@testtools.skipUnless(CONF.compute_feature_enabled.pause,
'Pause is not available.')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_pause_unpause(self):
keypair = self.create_keypair()
@@ -163,7 +163,7 @@
@decorators.idempotent_id('5cdf9499-541d-4923-804e-b9a60620a7f0')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_suspend_resume(self):
keypair = self.create_keypair()
@@ -181,7 +181,7 @@
@decorators.idempotent_id('719eb59d-2f42-4b66-b8b1-bb1254473967')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_resize(self):
resize_flavor = CONF.compute.flavor_ref_alt
@@ -205,7 +205,7 @@
@testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
'Less than 2 compute nodes, skipping multinode '
'tests.')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_cold_migration(self):
keypair = self.create_keypair()
@@ -231,7 +231,7 @@
@testtools.skipUnless(CONF.compute.min_compute_nodes > 1,
'Less than 2 compute nodes, skipping multinode '
'tests.')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_server_connectivity_cold_migration_revert(self):
keypair = self.create_keypair()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 15a0a70..756ca4d 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -286,10 +286,11 @@
% CONF.network.build_timeout)
num, new_nic = self.diff_list[0]
- ssh_client.assign_static_ip(
- nic=new_nic, addr=new_port['fixed_ips'][0]['ip_address'],
- network_mask_bits=CONF.network.project_network_mask_bits)
- ssh_client.set_nic_state(nic=new_nic)
+ ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
+ new_port['fixed_ips'][0]['ip_address'],
+ CONF.network.project_network_mask_bits,
+ new_nic))
+ ssh_client.exec_command("sudo ip link set %s up" % new_nic)
def _get_server_nics(self, ssh_client):
reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+):')
@@ -351,7 +352,7 @@
self.check_remote_connectivity(ssh_source, remote_ip,
should_connect)
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
@test.services('compute', 'network')
def test_network_basic_ops(self):
@@ -407,7 +408,7 @@
@decorators.idempotent_id('b158ea55-472e-4086-8fa9-c64ac0c6c1d0')
@testtools.skipUnless(test.is_extension_enabled('net-mtu', 'network'),
'No way to calculate MTU for networks')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_mtu_sized_frames(self):
"""Validate that network MTU sized frames fit through."""
@@ -420,7 +421,7 @@
'Connectivity can only be tested when in a '
'multitenant network environment')
@decorators.skip_because(bug="1610994")
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_connectivity_between_vms_on_different_networks(self):
"""Test connectivity between VMs on different networks
@@ -496,7 +497,7 @@
@testtools.skipIf(CONF.network.shared_physical_network,
'Router state can be altered only with multitenant '
'networks capabilities')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_update_router_admin_state(self):
"""Test to update admin state up of router
@@ -530,7 +531,7 @@
'network isolation not available')
@testtools.skipUnless(CONF.scenario.dhcp_client,
"DHCP client is not available.")
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_subnet_details(self):
"""Tests that subnet's extra configuration details are affecting VMs.
@@ -614,7 +615,7 @@
@testtools.skipUnless(CONF.network_feature_enabled.port_admin_state_change,
"Changing a port's admin state is not supported "
"by the test environment")
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_update_instance_port_admin_state(self):
"""Test to update admin_state_up attribute of instance port
@@ -660,7 +661,7 @@
should_succeed=True)
@decorators.idempotent_id('759462e1-8535-46b0-ab3a-33aa45c55aaa')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_preserve_preexisting_port(self):
"""Test preserve pre-existing port
@@ -712,7 +713,7 @@
@test.requires_ext(service='network', extension='l3_agent_scheduler')
@decorators.idempotent_id('2e788c46-fb3f-4ac9-8f82-0561555bea73')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_router_rescheduling(self):
"""Tests that router can be removed from agent and add to a new agent.
@@ -792,7 +793,7 @@
@testtools.skipUnless(CONF.compute_feature_enabled.interface_attach,
'NIC hotplug not available')
@decorators.idempotent_id('7c0bb1a2-d053-49a4-98f9-ca1a1d849f63')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_port_security_macspoofing_port(self):
"""Tests port_security extension enforces mac spoofing
@@ -833,7 +834,13 @@
peer_address = peer['addresses'][self.new_net['name']][0]['addr']
self.check_remote_connectivity(ssh_client, dest=peer_address,
nic=spoof_nic, should_succeed=True)
- ssh_client.set_mac_address(spoof_nic, spoof_mac)
+ # Set a mac address by making nic down temporary
+ cmd = ("sudo ip link set {nic} down;"
+ "sudo ip link set dev {nic} address {mac};"
+ "sudo ip link set {nic} up").format(nic=spoof_nic,
+ mac=spoof_mac)
+ ssh_client.exec_command(cmd)
+
new_mac = ssh_client.get_mac_address(nic=spoof_nic)
self.assertEqual(spoof_mac, new_mac)
self.check_remote_connectivity(ssh_client, dest=peer_address,
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index cfd83d0..daf4d13 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -153,7 +153,8 @@
"ports: %s")
% (network_id, ports))
mac6 = ports[0]
- ssh.set_nic_state(ssh.get_nic_name_by_mac(mac6))
+ nic = ssh.get_nic_name_by_mac(mac6)
+ ssh.exec_command("sudo ip link set %s up" % nic)
def _prepare_and_test(self, address6_mode, n_subnets6=1, dualnet=False):
net_list = self.prepare_network(address6_mode=address6_mode,
@@ -205,43 +206,43 @@
self.check_remote_connectivity(sshv4_2,
self.subnets_v6[i]['gateway_ip'])
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('2c92df61-29f0-4eaa-bee3-7c65bef62a43')
@test.services('compute', 'network')
def test_slaac_from_os(self):
self._prepare_and_test(address6_mode='slaac')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('d7e1f858-187c-45a6-89c9-bdafde619a9f')
@test.services('compute', 'network')
def test_dhcp6_stateless_from_os(self):
self._prepare_and_test(address6_mode='dhcpv6-stateless')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('7ab23f41-833b-4a16-a7c9-5b42fe6d4123')
@test.services('compute', 'network')
def test_multi_prefix_dhcpv6_stateless(self):
self._prepare_and_test(address6_mode='dhcpv6-stateless', n_subnets6=2)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('dec222b1-180c-4098-b8c5-cc1b8342d611')
@test.services('compute', 'network')
def test_multi_prefix_slaac(self):
self._prepare_and_test(address6_mode='slaac', n_subnets6=2)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('b6399d76-4438-4658-bcf5-0d6c8584fde2')
@test.services('compute', 'network')
def test_dualnet_slaac_from_os(self):
self._prepare_and_test(address6_mode='slaac', dualnet=True)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('76f26acd-9688-42b4-bc3e-cd134c4cb09e')
@test.services('compute', 'network')
def test_dualnet_dhcp6_stateless_from_os(self):
self._prepare_and_test(address6_mode='dhcpv6-stateless', dualnet=True)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('cf1c4425-766b-45b8-be35-e2959728eb00')
@test.services('compute', 'network')
def test_dualnet_multi_prefix_dhcpv6_stateless(self):
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index 7fd8c91..25e9f5c 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -46,7 +46,7 @@
self.delete_container(container_name)
@decorators.idempotent_id('916c7111-cb1f-44b2-816d-8f760e4ea910')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('object_storage')
def test_swift_acl_anonymous_download(self):
"""This test will cover below steps:
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 72b61c8..2116fe8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -122,6 +122,7 @@
self.router = None
self.security_groups = {}
self.servers = list()
+ self.access_point = None
def set_network(self, network, subnet, router):
self.network = network
@@ -458,6 +459,14 @@
subnet_id = tenant.subnet['id']
self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
+ def _log_console_output_for_all_tenants(self):
+ for tenant in self.tenants.values():
+ client = tenant.manager.servers_client
+ self._log_console_output(servers=tenant.servers, client=client)
+ if tenant.access_point is not None:
+ self._log_console_output(
+ servers=[tenant.access_point], client=client)
+
@decorators.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@test.services('compute', 'network')
def test_cross_tenant_traffic(self):
@@ -475,8 +484,7 @@
self._test_cross_tenant_block(source_tenant, dest_tenant)
self._test_cross_tenant_allow(source_tenant, dest_tenant)
except Exception:
- for tenant in self.tenants.values():
- self._log_console_output(servers=tenant.servers)
+ self._log_console_output_for_all_tenants()
raise
@decorators.idempotent_id('63163892-bbf6-4249-aa12-d5ea1f8f421b')
@@ -489,12 +497,11 @@
self._test_in_tenant_block(self.primary_tenant)
self._test_in_tenant_allow(self.primary_tenant)
except Exception:
- for tenant in self.tenants.values():
- self._log_console_output(servers=tenant.servers)
+ self._log_console_output_for_all_tenants()
raise
@decorators.idempotent_id('f4d556d7-1526-42ad-bafb-6bebf48568f6')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_port_update_new_security_group(self):
"""Verifies the traffic after updating the vm port
@@ -544,12 +551,11 @@
source=access_point_ssh,
dest=self._get_server_ip(server))
except Exception:
- for tenant in self.tenants.values():
- self._log_console_output(servers=tenant.servers)
+ self._log_console_output_for_all_tenants()
raise
@decorators.idempotent_id('d2f77418-fcc4-439d-b935-72eca704e293')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'network')
def test_multiple_security_groups(self):
"""Verify multiple security groups and checks that rules
@@ -581,7 +587,7 @@
private_key=private_key,
should_connect=True)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.requires_ext(service='network', extension='port-security')
@decorators.idempotent_id('7c811dcc-263b-49a3-92d2-1b4d8405f50c')
@test.services('compute', 'network')
@@ -618,16 +624,12 @@
source=access_point_ssh,
dest=self._get_server_ip(server))
except Exception:
- for tenant in self.tenants.values():
- self._log_console_output(servers=tenant.servers)
+ self._log_console_output_for_all_tenants()
raise
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.requires_ext(service='network', extension='port-security')
@decorators.idempotent_id('13ccf253-e5ad-424b-9c4a-97b88a026699')
- @testtools.skipUnless(
- CONF.compute_feature_enabled.allow_port_security_disabled,
- 'Port security must be enabled.')
# TODO(mriedem): We shouldn't actually need to check this since neutron
# disables the port_security extension by default, but the problem is nova
# assumes port_security_enabled=True if it's not set on the network
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index cc3687f..6005e28 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -41,7 +41,7 @@
cls.set_network_resources()
super(TestServerAdvancedOps, cls).setup_credentials()
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@testtools.skipUnless(CONF.compute_feature_enabled.resize,
'Resize is not available.')
@@ -68,7 +68,7 @@
waiters.wait_for_server_status(self.servers_client, instance_id,
'ACTIVE')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('949da7d5-72c8-4808-8802-e3d70df98e2c')
@testtools.skipUnless(CONF.compute_feature_enabled.suspend,
'Suspend is not available.')
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index ddbaf5a..2be9e06 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -124,7 +124,7 @@
# instance info and do direct comparison.
@decorators.idempotent_id('7fff3fb3-91d8-4fd0-bd7d-0204f1f180ba')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@test.services('compute', 'network')
def test_server_basic_ops(self):
keypair = self.create_keypair()
diff --git a/tempest/scenario/test_server_multinode.py b/tempest/scenario/test_server_multinode.py
index db91a21..bf2858b 100644
--- a/tempest/scenario/test_server_multinode.py
+++ b/tempest/scenario/test_server_multinode.py
@@ -45,7 +45,7 @@
cls.servers_client = cls.admin_manager.servers_client
@decorators.idempotent_id('9cecbe35-b9d4-48da-a37e-7ce70aa43d30')
- @test.attr(type='smoke')
+ @decorators.attr(type='smoke')
@test.services('compute', 'network')
def test_schedule_to_all_nodes(self):
available_zone = \
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index 9e763f8..fc04b44 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -74,7 +74,7 @@
private_key=keypair['private_key'])
self.assertEqual(timestamp, timestamp2)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@@ -82,7 +82,7 @@
def test_shelve_instance(self):
self._create_server_then_shelve_and_unshelve()
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('c1b6318c-b9da-490b-9c67-9339b627271f')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index a699de2..52767dc 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -41,7 +41,7 @@
raise cls.skipException("Snapshotting is not available.")
@decorators.idempotent_id('608e604b-1d63-4a82-8e3e-91bc665c90b4')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@test.services('compute', 'network', 'image')
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 5f5d701..debd664 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -85,7 +85,7 @@
CONF.compute.build_interval):
raise lib_exc.TimeoutException
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.skip_because(bug="1664793")
@decorators.idempotent_id('10fd234a-515c-41e5-b092-8323060598c5')
@testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 888bff2..3dfbf18 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -170,7 +170,7 @@
self.assertEqual(timestamp, timestamp3)
@decorators.idempotent_id('05795fb2-b2a7-4c9f-8fac-ff25aedb1489')
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@test.services('compute', 'image', 'volume')
def test_create_server_from_volume_snapshot(self):
# Create a volume from an image
diff --git a/tempest/scenario/test_volume_migrate_attached.py b/tempest/scenario/test_volume_migrate_attached.py
index f580ea6..a3cb3eb 100644
--- a/tempest/scenario/test_volume_migrate_attached.py
+++ b/tempest/scenario/test_volume_migrate_attached.py
@@ -40,10 +40,7 @@
@classmethod
def setup_clients(cls):
super(TestVolumeMigrateRetypeAttached, cls).setup_clients()
- if CONF.volume_feature_enabled.api_v1:
- cls.admin_volume_types_client = cls.os_adm.volume_types_client
- else:
- cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
+ cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
@classmethod
def skip_checks(cls):
@@ -91,7 +88,7 @@
waiters.wait_for_volume_retype(self.volumes_client,
volume_id, new_volume_type)
- @test.attr(type='slow')
+ @decorators.attr(type='slow')
@decorators.idempotent_id('deadd2c2-beef-4dce-98be-f86765ff311b')
@test.services('compute', 'volume')
def test_volume_migrate_attached(self):
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index d1a61d6..1738566 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -13,10 +13,12 @@
# the License.
from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.bulk_middleware_client import \
+ BulkMiddlewareClient
from tempest.services.object_storage.capabilities_client import \
CapabilitiesClient
from tempest.services.object_storage.container_client import ContainerClient
from tempest.services.object_storage.object_client import ObjectClient
-__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
- 'ObjectClient']
+__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
+ 'ContainerClient', 'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 4859e75..5a1737e 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -23,39 +23,30 @@
class AccountClient(rest_client.RestClient):
- def create_account(self, data=None,
- params=None,
- metadata=None,
- remove_metadata=None,
- metadata_prefix='X-Account-Meta-',
- remove_metadata_prefix='X-Remove-Account-Meta-'):
- """Create an account."""
- if metadata is None:
- metadata = {}
- if remove_metadata is None:
- remove_metadata = {}
- url = ''
- if params:
- url += '?%s' % urllib.urlencode(params)
+ 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 = {}
- for key in metadata:
- headers[metadata_prefix + key] = metadata[key]
- for key in remove_metadata:
- headers[remove_metadata_prefix + key] = remove_metadata[key]
+ 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.put(url, data, headers)
- self.expected_success(200, resp.status)
- return resp, body
-
- def delete_account(self, data=None, params=None):
- """Delete an account."""
- url = ''
- if params:
- url = '?%s%s' % (url, urllib.urlencode(params))
-
- resp, body = self.delete(url, headers={}, body=data)
- self.expected_success(200, resp.status)
+ resp, body = self.post('', headers=headers, body=None)
+ self.expected_success([200, 204], resp.status)
return resp, body
def list_account_metadata(self):
@@ -67,51 +58,6 @@
self.expected_success(204, resp.status)
return resp, body
- def create_account_metadata(self, metadata,
- metadata_prefix='X-Account-Meta-',
- data=None, params=None):
- """Creates an account metadata entry."""
- headers = {}
- if metadata:
- for key in metadata:
- headers[metadata_prefix + key] = metadata[key]
-
- url = ''
- if params:
- url = '?%s%s' % (url, urllib.urlencode(params))
-
- resp, body = self.post(url, headers=headers, body=data)
- 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/services/object_storage/bulk_middleware_client.py b/tempest/services/object_storage/bulk_middleware_client.py
new file mode 100644
index 0000000..c194ea9
--- /dev/null
+++ b/tempest/services/object_storage/bulk_middleware_client.py
@@ -0,0 +1,62 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.common import rest_client
+
+
+class BulkMiddlewareClient(rest_client.RestClient):
+
+ def upload_archive(self, upload_path, data,
+ archive_file_format='tar', headers=None):
+ """Expand tar files into a Swift cluster.
+
+ To extract containers and objects on Swift cluster from
+ uploaded archived file. For More information please check:
+ http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ """
+ url = '%s?extract-archive=%s' % (upload_path, archive_file_format)
+ if headers is None:
+ headers = {}
+ resp, body = self.put(url, data, headers)
+ self.expected_success(200, resp.status)
+ return resp, body
+
+ def delete_bulk_data(self, data=None, headers=None):
+ """Delete multiple objects or containers from their account.
+
+ For More information please check:
+ http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ """
+ url = '?bulk-delete'
+
+ if headers is None:
+ headers = {}
+ resp, body = self.delete(url, headers=headers, body=data)
+ self.expected_success(200, resp.status)
+ return resp, body
+
+ def delete_bulk_data_with_post(self, data=None, headers=None):
+ """Delete multiple objects or containers with POST request.
+
+ For More information please check:
+ http://docs.openstack.org/developer/swift/middleware.html#module-swift.common.middleware.bulk
+ """
+ url = '?bulk-delete'
+
+ if headers is None:
+ headers = {}
+ resp, body = self.post(url, headers=headers, body=data)
+ self.expected_success([200, 204], resp.status)
+ return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 52994ac..e63e08f 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -29,7 +29,6 @@
from tempest.common import fixed_network
import tempest.common.validation_resources as vresources
from tempest import config
-from tempest import exceptions
from tempest.lib.common import cred_client
from tempest.lib import decorators
from tempest.lib import exceptions as lib_exc
@@ -45,22 +44,18 @@
version='Mitaka', removal_version='?')
-def attr(**kwargs):
- """A decorator which applies the testtools attr decorator
+related_bug = debtcollector.moves.moved_function(
+ decorators.related_bug, 'related_bug', __name__,
+ version='Pike', removal_version='?')
- This decorator applies the testtools.testcase.attr if it is in the list of
- attributes to testtools we want to apply.
- """
- def decorator(f):
- if 'type' in kwargs and isinstance(kwargs['type'], str):
- f = testtools.testcase.attr(kwargs['type'])(f)
- elif 'type' in kwargs and isinstance(kwargs['type'], list):
- for attr in kwargs['type']:
- f = testtools.testcase.attr(attr)(f)
- return f
+attr = debtcollector.moves.moved_function(
+ decorators.attr, 'attr', __name__,
+ version='Pike', removal_version='?')
- return decorator
+
+class InvalidServiceTag(lib_exc.TempestException):
+ message = "Invalid service tag"
def get_service_list():
@@ -82,13 +77,12 @@
exercised by a test case.
"""
def decorator(f):
- services = ['compute', 'image', 'baremetal', 'volume',
- 'network', 'identity', 'object_storage']
+ known_services = get_service_list()
+
for service in args:
- if service not in services:
- raise exceptions.InvalidServiceTag('%s is not a valid '
- 'service' % service)
- attr(type=list(args))(f)
+ if service not in known_services:
+ raise InvalidServiceTag('%s is not a valid service' % service)
+ decorators.attr(type=list(args))(f)
@functools.wraps(f)
def wrapper(self, *func_args, **func_kwargs):
@@ -134,7 +128,7 @@
'object': CONF.object_storage_feature_enabled.discoverable_apis,
'identity': CONF.identity_feature_enabled.api_extensions
}
- if len(config_dict[service]) == 0:
+ if not config_dict[service]:
return False
if config_dict[service][0] == 'all':
return True
@@ -143,28 +137,6 @@
return False
-def related_bug(bug, status_code=None):
- """A decorator useful to know solutions from launchpad bug reports
-
- @param bug: The launchpad bug number causing the test
- @param status_code: The status code related to the bug report
- """
- def decorator(f):
- @functools.wraps(f)
- def wrapper(self, *func_args, **func_kwargs):
- try:
- return f(self, *func_args, **func_kwargs)
- except Exception as exc:
- exc_status_code = getattr(exc, 'status_code', None)
- if status_code is None or status_code == exc_status_code:
- LOG.error('Hints: This test was made for the bug %s. '
- 'The failure could be related to '
- 'https://launchpad.net/bugs/%s', bug, bug)
- raise exc
- return wrapper
- return decorator
-
-
def is_scheduler_filter_enabled(filter_name):
"""Check the list of enabled compute scheduler filters from config.
@@ -177,7 +149,7 @@
"""
filters = CONF.compute_feature_enabled.scheduler_available_filters
- if len(filters) == 0:
+ if not filters:
return False
if 'all' in filters:
return True
@@ -483,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__):
@@ -492,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
@@ -642,12 +615,24 @@
return fixed_network.get_tenant_network(
cred_provider, networks_client, CONF.compute.fixed_network_name)
- def assertEmpty(self, list, msg=None):
- if msg is None:
- msg = "list is not empty: %s" % list
- self.assertEqual(0, len(list), msg)
+ def assertEmpty(self, items, msg=None):
+ """Asserts whether a sequence or collection is empty
- def assertNotEmpty(self, list, msg=None):
+ :param items: sequence or collection to be tested
+ :param msg: message to be passed to the AssertionError
+ :raises AssertionError: when items is not empty
+ """
if msg is None:
- msg = "list is empty."
- self.assertGreater(len(list), 0, msg)
+ msg = "sequence or collection is not empty: %s" % items
+ self.assertFalse(items, msg)
+
+ def assertNotEmpty(self, items, msg=None):
+ """Asserts whether a sequence or collection is not empty
+
+ :param items: sequence or collection to be tested
+ :param msg: message to be passed to the AssertionError
+ :raises AssertionError: when items is empty
+ """
+ if msg is None:
+ msg = "sequence or collection is empty."
+ self.assertTrue(items, msg)
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index 613ab92..1206e3f 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -51,36 +51,37 @@
:param ConfigOpts conf: The conf object that can be used to register
additional options on.
- Example:
- >>> # Config options are defined in a config.py module
- >>> service_option = cfg.BoolOpt(
- >>> "my_service",
- >>> default=True,
- >>> help="Whether or not my service is available")
- >>>
- >>> # Note: as long as the group is listed in get_opt_lists,
- >>> # it will be possible to access its optins in the plugin code
- >>> # via ("-" in the group name are replaces with "_"):
- >>> # CONF.my_service.<option_name>
- >>> my_service_group = cfg.OptGroup(name="my-service",
- >>> title="My service options")
- >>>
- >>> MyServiceGroup = [<list of options>]
- >>> # (...) More groups and options...
- >>>
- >>> # Plugin is implemented in a plugin.py module
- >>> from my_plugin import config as my_config
- >>>
- >>> def register_opts(self, conf):
- >>> conf.register_opt(my_config.service_option,
- >>> group='service_available')
- >>> conf.register_group(my_config.my_service_group)
- >>> conf.register_opts(my_config.MyService +
- >>> my_config.my_service_group)
- >>>
- >>> conf.register_group(my_config.my_service_feature_group)
- >>> conf.register_opts(my_config.MyServiceFeaturesGroup,
- >>> my_config.my_service_feature_group)
+ Example::
+
+ # Config options are defined in a config.py module
+ service_option = cfg.BoolOpt(
+ "my_service",
+ default=True,
+ help="Whether or not my service is available")
+
+ # Note: as long as the group is listed in get_opt_lists,
+ # it will be possible to access its optins in the plugin code
+ # via ("-" in the group name are replaces with "_"):
+ # CONF.my_service.<option_name>
+ my_service_group = cfg.OptGroup(name="my-service",
+ title="My service options")
+
+ MyServiceGroup = [<list of options>]
+ # (...) More groups and options...
+
+ # Plugin is implemented in a plugin.py module
+ from my_plugin import config as my_config
+
+ def register_opts(self, conf):
+ conf.register_opt(my_config.service_option,
+ group='service_available')
+ conf.register_group(my_config.my_service_group)
+ conf.register_opts(my_config.MyService +
+ my_config.my_service_group)
+
+ conf.register_group(my_config.my_service_feature_group)
+ conf.register_opts(my_config.MyServiceFeaturesGroup,
+ my_config.my_service_feature_group)
"""
return
@@ -91,6 +92,31 @@
:return option_list: A list of tuples with the group name and options
in that group.
:rtype: list
+
+ Example::
+
+ # Config options are defined in a config.py module
+ service_option = cfg.BoolOpt(
+ "my_service", default=True,
+ help="Whether or not my service is available")
+
+ my_service_group = cfg.OptGroup(name="my-service",
+ title="My service options")
+ my_service_features_group = cfg.OptGroup(
+ name="my-service-features",
+ title="My service available features")
+
+ MyServiceGroup = [<list of options>]
+ MyServiceFeaturesGroup = [<list of options>]
+
+ # Plugin is implemented in a plugin.py module
+ from my_plugin import config as my_config
+
+ def get_opt_lists(self, conf):
+ return [
+ (my_service_group.name, MyServiceGroup),
+ (my_service_features_group.name, MyServiceFeaturesGroup)
+ ]
"""
return []
@@ -107,37 +133,41 @@
of `service_clients.ServiceClients.register_service_client_module`.
:rtype: list of dictionaries
- Example:
+ Example implementation with one service client::
- >>> # Example implementation with one service client
- >>> myservice_config = config.service_client_config('myservice')
- >>> params = {
- >>> 'name': 'myservice',
- >>> 'service_version': 'myservice',
- >>> 'module_path': 'myservice_tempest_tests.services',
- >>> 'client_names': ['API1Client', 'API2Client'],
- >>> }
- >>> params.update(myservice_config)
- >>> return [params]
+ def get_service_clients(self):
+ # Example implementation with one service client
+ myservice_config = config.service_client_config('myservice')
+ params = {
+ 'name': 'myservice',
+ 'service_version': 'myservice',
+ 'module_path': 'myservice_tempest_tests.services',
+ 'client_names': ['API1Client', 'API2Client'],
+ }
+ params.update(myservice_config)
+ return [params]
- >>> # Example implementation with two service clients
- >>> foo1_config = config.service_client_config('foo')
- >>> params_foo1 = {
- >>> 'name': 'foo_v1',
- >>> 'service_version': 'foo.v1',
- >>> 'module_path': 'bar_tempest_tests.services.foo.v1',
- >>> 'client_names': ['API1Client', 'API2Client'],
- >>> }
- >>> params_foo1.update(foo_config)
- >>> foo2_config = config.service_client_config('foo')
- >>> params_foo2 = {
- >>> 'name': 'foo_v2',
- >>> 'service_version': 'foo.v2',
- >>> 'module_path': 'bar_tempest_tests.services.foo.v2',
- >>> 'client_names': ['API1Client', 'API2Client'],
- >>> }
- >>> params_foo2.update(foo2_config)
- >>> return [params_foo1, params_foo2]
+ Example implementation with two service clients::
+
+ def get_service_clients(self):
+ # Example implementation with two service clients
+ foo1_config = config.service_client_config('foo')
+ params_foo1 = {
+ 'name': 'foo_v1',
+ 'service_version': 'foo.v1',
+ 'module_path': 'bar_tempest_tests.services.foo.v1',
+ 'client_names': ['API1Client', 'API2Client'],
+ }
+ params_foo1.update(foo_config)
+ foo2_config = config.service_client_config('foo')
+ params_foo2 = {
+ 'name': 'foo_v2',
+ 'service_version': 'foo.v2',
+ 'module_path': 'bar_tempest_tests.services.foo.v2',
+ 'client_names': ['API1Client', 'API2Client'],
+ }
+ params_foo2.update(foo2_config)
+ return [params_foo1, params_foo2]
"""
return []
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 48cb86b..ecb8e64 100644
--- a/tempest/tests/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -139,23 +139,6 @@
self._assert_exec_called_with(
"ip addr | awk '/ether/ {print $2}'")
- def test_assign_static_ip(self):
- self.ssh_mock.mock.exec_command.return_value = ''
- ip = '10.0.0.2'
- nic = 'eth0'
- self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
- self._assert_exec_called_with(
- "sudo ip addr add %s/%s dev %s" % (ip, '28', nic))
-
- def test_set_nic_state(self):
- nic = 'eth0'
- self.conn.set_nic_state(nic)
- self._assert_exec_called_with(
- 'sudo ip link set %s up' % nic)
- self.conn.set_nic_state(nic, "down")
- self._assert_exec_called_with(
- 'sudo ip link set %s down' % nic)
-
class TestRemoteClientWithServer(base.TestCase):
diff --git a/tempest/tests/fake_config.py b/tempest/tests/fake_config.py
index 71a4c81..ee63684 100644
--- a/tempest/tests/fake_config.py
+++ b/tempest/tests/fake_config.py
@@ -93,12 +93,6 @@
self.conf.set_default('ca_certificates_file', '/fake/certificates',
group='identity')
self.conf.set_default('region', 'fake_region', 'identity')
- # Identity endpoints
- self.conf.set_default('v3_endpoint_type', 'fake_v3_uri', 'identity')
- self.conf.set_default('v2_public_endpoint_type', 'fake_v2_public_uri',
- 'identity')
- self.conf.set_default('v2_admin_endpoint_type', 'fake_v2_admin_uri',
- 'identity')
# Compute default values
self.conf.set_default('build_interval', 88, group='compute')
self.conf.set_default('build_timeout', 8, group='compute')
@@ -109,5 +103,4 @@
self._set_attrs()
self.fake_service1 = cfg.CONF['fake-service1']
self.fake_service2 = cfg.CONF['fake-service2']
- print('Services registered')
self.lock_path = cfg.CONF.oslo_concurrency.lock_path
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
index 1cb3103..3dff16f 100644
--- a/tempest/tests/lib/common/test_cred_client.py
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -73,6 +73,5 @@
def test_delete_project(self):
self.creds_client.delete_project('fake_id')
- print(self.projects_client.calls)
self.projects_client.delete_project.assert_called_once_with(
'fake_id')
diff --git a/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py b/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py
new file mode 100644
index 0000000..72e5bd2
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_domain_configuration_client.py
@@ -0,0 +1,217 @@
+# 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 domain_configuration_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestDomainConfigurationClient(base.BaseServiceTest):
+
+ FAKE_CONFIG_SETTINGS = {
+ "config": {
+ "identity": {
+ "driver": "ldap"
+ },
+ "ldap": {
+ "url": "ldap://localhost",
+ "user": "",
+ "suffix": "cn=example,cn=com",
+ }
+ }
+ }
+
+ FAKE_DOMAIN_ID = '07ef7d04-2941-4bee-8551-f79f08a021de'
+
+ def setUp(self):
+ super(TestDomainConfigurationClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = domain_configuration_client.DomainConfigurationClient(
+ fake_auth, 'identity', 'regionOne')
+
+ def _test_show_default_config_settings(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_default_config_settings,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONFIG_SETTINGS,
+ bytes_body)
+
+ def _test_show_default_group_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_default_group_config,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+ bytes_body,
+ group='ldap')
+
+ def _test_show_default_group_option(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_default_group_option,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ {'driver': 'ldap'},
+ bytes_body,
+ group='identity',
+ option='driver')
+
+ def _test_show_domain_group_option_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_domain_group_option_config,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ {'driver': 'ldap'},
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='identity',
+ option='driver')
+
+ def _test_update_domain_group_option_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_domain_group_option_config,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CONFIG_SETTINGS,
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='identity',
+ option='driver',
+ url='http://myldap/my_other_root')
+
+ def _test_show_domain_group_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_domain_group_config,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='ldap')
+
+ def _test_update_domain_group_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_domain_group_config,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CONFIG_SETTINGS['config']['ldap'],
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='ldap',
+ **self.FAKE_CONFIG_SETTINGS['config'])
+
+ def _test_create_domain_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_domain_config,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ self.FAKE_CONFIG_SETTINGS,
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID,
+ status=201)
+
+ def _test_show_domain_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_domain_config,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONFIG_SETTINGS,
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID)
+
+ def _test_update_domain_config(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_domain_config,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CONFIG_SETTINGS,
+ bytes_body,
+ domain_id=self.FAKE_DOMAIN_ID)
+
+ def test_show_default_config_settings_with_str_body(self):
+ self._test_show_default_config_settings()
+
+ def test_show_default_config_settings_with_bytes_body(self):
+ self._test_show_default_config_settings(bytes_body=True)
+
+ def test_show_default_group_config_with_str_body(self):
+ self._test_show_default_group_config()
+
+ def test_show_default_group_config_with_bytes_body(self):
+ self._test_show_default_group_config(bytes_body=True)
+
+ def test_show_default_group_option_with_str_body(self):
+ self._test_show_default_group_option()
+
+ def test_show_default_group_option_with_bytes_body(self):
+ self._test_show_default_group_option(bytes_body=True)
+
+ def test_show_domain_group_option_config_with_str_body(self):
+ self._test_show_domain_group_option_config()
+
+ def test_show_domain_group_option_config_with_bytes_body(self):
+ self._test_show_domain_group_option_config(bytes_body=True)
+
+ def test_update_domain_group_option_config_with_str_body(self):
+ self._test_update_domain_group_option_config()
+
+ def test_update_domain_group_option_config_with_bytes_body(self):
+ self._test_update_domain_group_option_config(bytes_body=True)
+
+ def test_delete_domain_group_option_config(self):
+ self.check_service_client_function(
+ self.client.delete_domain_group_option_config,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ status=204,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='identity',
+ option='driver')
+
+ def test_show_domain_group_config_with_str_body(self):
+ self._test_show_domain_group_config()
+
+ def test_show_domain_group_config_with_bytes_body(self):
+ self._test_show_domain_group_config(bytes_body=True)
+
+ def test_test_update_domain_group_config_with_str_body(self):
+ self._test_update_domain_group_config()
+
+ def test_update_domain_group_config_with_bytes_body(self):
+ self._test_update_domain_group_config(bytes_body=True)
+
+ def test_delete_domain_group_config(self):
+ self.check_service_client_function(
+ self.client.delete_domain_group_config,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ status=204,
+ domain_id=self.FAKE_DOMAIN_ID,
+ group='identity')
+
+ def test_create_domain_config_with_str_body(self):
+ self._test_create_domain_config()
+
+ def test_create_domain_config_with_bytes_body(self):
+ self._test_create_domain_config(bytes_body=True)
+
+ def test_show_domain_config_with_str_body(self):
+ self._test_show_domain_config()
+
+ def test_show_domain_config_with_bytes_body(self):
+ self._test_show_domain_config(bytes_body=True)
+
+ def test_update_domain_config_with_str_body(self):
+ self._test_update_domain_config()
+
+ def test_update_domain_config_with_bytes_body(self):
+ self._test_update_domain_config(bytes_body=True)
+
+ def test_delete_domain_config(self):
+ self.check_service_client_function(
+ self.client.delete_domain_config,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ status=204,
+ domain_id=self.FAKE_DOMAIN_ID)
diff --git a/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
new file mode 100644
index 0000000..8d53792
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_oauth_consumers_client.py
@@ -0,0 +1,160 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v3 import oauth_consumers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestOAUTHConsumerClient(base.BaseServiceTest):
+ FAKE_CREATE_CONSUMER = {
+ "consumer": {
+ 'description': 'A fake description 1'
+ }
+
+ }
+
+ FAKE_CONSUMER_INFO = {
+ "consumer": {
+ 'id': '6392c7d3b7a2062e09a07aa377',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/g6f2l9'
+ },
+ 'description': 'A description that is fake'
+ }
+
+ }
+
+ FAKE_LIST_CONSUMERS = {
+ 'links': {
+ 'self': 'http://example.com/identity/v3/OS-OAUTH1/consumers/',
+ 'next': None,
+ 'previous': None
+ },
+ 'consumers': [
+ {
+ 'id': '6392c7d3b7a2062e09a07aa377',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/6b9f2g5'
+ },
+ 'description': 'A description that is fake'
+ },
+ {
+ 'id': '677a855c9e3eb3a3954b36aca6',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/6a9f2366'
+ },
+ 'description': 'A very fake description 2'
+ },
+ {
+ 'id': '9d3ac57b08d65e07826b5e506',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/626b5e506'
+ },
+ 'description': 'A very fake description 3'
+ },
+ {
+ 'id': 'b522d163b1a18e928aca9y426',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/g7ca9426'
+ },
+ 'description': 'A very fake description 4'
+ },
+ {
+ 'id': 'b7e47321b5ef9051f93c2049e',
+ 'links': {
+ 'self': 'http://example.com/identity/v3/' +
+ 'OS-OAUTH1/consumers/23d82049e'
+ },
+ 'description': 'A very fake description 5'
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestOAUTHConsumerClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = oauth_consumers_client.OAUTHConsumerClient(fake_auth,
+ 'identity',
+ 'regionOne')
+
+ def _test_create_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.create_consumer,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_CREATE_CONSUMER,
+ bytes_body,
+ description=self.FAKE_CREATE_CONSUMER["consumer"]["description"],
+ status=201)
+
+ def _test_show_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.show_consumer,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_CONSUMER_INFO,
+ bytes_body,
+ consumer_id="6392c7d3b7a2062e09a07aa377")
+
+ def _test_list_consumers(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_consumers,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_CONSUMERS,
+ bytes_body)
+
+ def _test_update_consumer(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.update_consumer,
+ 'tempest.lib.common.rest_client.RestClient.patch',
+ self.FAKE_CONSUMER_INFO,
+ bytes_body,
+ consumer_id="6392c7d3b7a2062e09a07aa377")
+
+ def test_create_consumer_with_str_body(self):
+ self._test_create_consumer()
+
+ def test_create_consumer_with_bytes_body(self):
+ self._test_create_consumer(bytes_body=True)
+
+ def test_show_consumer_with_str_body(self):
+ self._test_show_consumer()
+
+ def test_show_consumer_with_bytes_body(self):
+ self._test_show_consumer(bytes_body=True)
+
+ def test_list_consumers_with_str_body(self):
+ self._test_list_consumers()
+
+ def test_list_consumers_with_bytes_body(self):
+ self._test_list_consumers(bytes_body=True)
+
+ def test_update_consumer_with_str_body(self):
+ self._test_update_consumer()
+
+ def test_update_consumer_with_bytes_body(self):
+ self._test_update_consumer(bytes_body=True)
+
+ def test_delete_consumer(self):
+ self.check_service_client_function(
+ self.client.delete_consumer,
+ 'tempest.lib.common.rest_client.RestClient.delete',
+ {},
+ consumer_id="6392c7d3b7a2062e09a07aa377",
+ status=204)
diff --git a/tempest/tests/lib/services/identity/v3/test_roles_client.py b/tempest/tests/lib/services/identity/v3/test_roles_client.py
index 41cea85..8d6bb42 100644
--- a/tempest/tests/lib/services/identity/v3/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_roles_client.py
@@ -26,6 +26,18 @@
FAKE_ROLE_ID_2 = "2"
FAKE_ROLE_NAME_2 = "test2"
+ FAKE_ROLE_ID_3 = "3"
+ FAKE_ROLE_NAME_3 = "test3"
+
+ FAKE_ROLE_ID_4 = "4"
+ FAKE_ROLE_NAME_4 = "test4"
+
+ FAKE_ROLE_ID_5 = "5"
+ FAKE_ROLE_NAME_5 = "test5"
+
+ FAKE_ROLE_ID_6 = "6"
+ FAKE_ROLE_NAME_6 = "test6"
+
FAKE_ROLE_INFO = {
"role": {
"domain_id": FAKE_DOMAIN_ID,
@@ -77,8 +89,8 @@
}
}
- FAKE_LIST_ROLE_INFERENCES_RULES = {
- "role_inference": {
+ COMMON_FAKE_LIST_ROLE_INFERENCE_RULES = [
+ {
"prior_role": {
"id": FAKE_ROLE_ID,
"name": FAKE_ROLE_NAME,
@@ -97,20 +109,60 @@
}
},
{
- "id": "3",
- "name": "test3",
+ "id": FAKE_ROLE_ID_3,
+ "name": FAKE_ROLE_NAME_3,
"links": {
- "self": "http://example.com/identity/v3/roles/3"
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_3)
}
}
]
},
+ {
+ "prior_role": {
+ "id": FAKE_ROLE_ID_4,
+ "name": FAKE_ROLE_NAME_4,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_4)
+ }
+ },
+ "implies": [
+ {
+ "id": FAKE_ROLE_ID_5,
+ "name": FAKE_ROLE_NAME_5,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_5)
+ }
+ },
+ {
+ "id": FAKE_ROLE_ID_6,
+ "name": FAKE_ROLE_NAME_6,
+ "links": {
+ "self": "http://example.com/identity/v3/roles/%s" % (
+ FAKE_ROLE_ID_6)
+ }
+ }
+ ]
+ }
+ ]
+
+ FAKE_LIST_ROLE_INFERENCE_RULES = {
+ "role_inference": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES[0],
"links": {
"self": "http://example.com/identity/v3/roles/"
"%s/implies" % FAKE_ROLE_ID
}
}
+ FAKE_LIST_ALL_ROLE_INFERENCE_RULES = {
+ "role_inferences": COMMON_FAKE_LIST_ROLE_INFERENCE_RULES,
+ "links": {
+ "self": "http://example.com/identity/v3/role_inferences"
+ }
+ }
+
def setUp(self):
super(TestRolesClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -254,10 +306,17 @@
self.check_service_client_function(
self.client.list_role_inferences_rules,
'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_LIST_ROLE_INFERENCES_RULES,
+ self.FAKE_LIST_ROLE_INFERENCE_RULES,
bytes_body,
prior_role=self.FAKE_ROLE_ID)
+ def _test_list_all_role_inference_rules(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_all_role_inference_rules,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_LIST_ALL_ROLE_INFERENCE_RULES,
+ bytes_body)
+
def test_create_role_with_str_body(self):
self._test_create_role()
@@ -441,3 +500,9 @@
status=204,
prior_role=self.FAKE_ROLE_ID,
implies_role=self.FAKE_ROLE_ID_2)
+
+ def test_list_all_role_inference_rules_with_str_body(self):
+ self._test_list_all_role_inference_rules()
+
+ def test_list_all_role_inference_rules_with_bytes_body(self):
+ self._test_list_all_role_inference_rules(bytes_body=True)
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index a3b390e..a837199 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -100,9 +100,8 @@
def test___init___no_module(self):
auth_provider = fake_auth_provider.FakeAuthProvider()
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
- with testtools.ExpectedException(ImportError, '.*fake_module.*'):
- clients.ClientsFactory('fake_module', class_names,
- auth_provider)
+ self.assertRaises(ImportError, clients.ClientsFactory,
+ 'fake_module', class_names, auth_provider)
def test___init___not_a_class(self):
class_names = ['FakeServiceClient1', 'FakeServiceClient2']
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)
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index f3a4e9c..bbebcd3 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import mock
import testtools
from tempest.lib import base as test
@@ -21,6 +22,34 @@
from tempest.tests import base
+class TestAttrDecorator(base.TestCase):
+ def _test_attr_helper(self, expected_attrs, **decorator_args):
+ @decorators.attr(**decorator_args)
+ def foo():
+ pass
+
+ # By our decorators.attr decorator the attribute __testtools_attrs
+ # will be set only for 'type' argument, so we test it first.
+ if 'type' in decorator_args:
+ # this is what testtools sets
+ self.assertEqual(getattr(foo, '__testtools_attrs'),
+ set(expected_attrs))
+
+ def test_attr_without_type(self):
+ self._test_attr_helper(expected_attrs='baz', bar='baz')
+
+ def test_attr_decorator_with_list_type(self):
+ # if type is 'smoke' we'll get the original list of types
+ self._test_attr_helper(expected_attrs=['smoke', 'foo'],
+ type=['smoke', 'foo'])
+
+ def test_attr_decorator_with_unknown_type(self):
+ self._test_attr_helper(expected_attrs=['foo'], type='foo')
+
+ def test_attr_decorator_with_duplicated_type(self):
+ self._test_attr_helper(expected_attrs=['foo'], type=['foo', 'foo'])
+
+
class TestSkipBecauseDecorator(base.TestCase):
def _test_skip_because_helper(self, expected_to_skip=True,
**decorator_args):
@@ -123,3 +152,33 @@
def test_no_skip_for_attr_exist_and_true(self):
self._test_skip_unless_attr('expected_attr', expected_to_skip=False)
+
+
+class TestRelatedBugDecorator(base.TestCase):
+ def test_relatedbug_when_no_exception(self):
+ f = mock.Mock()
+ sentinel = object()
+
+ @decorators.related_bug(bug="1234", status_code=500)
+ def test_foo(self):
+ f(self)
+
+ test_foo(sentinel)
+ f.assert_called_once_with(sentinel)
+
+ def test_relatedbug_when_exception(self):
+ class MyException(Exception):
+ def __init__(self, status_code):
+ self.status_code = status_code
+
+ def f(self):
+ raise MyException(status_code=500)
+
+ @decorators.related_bug(bug="1234", status_code=500)
+ def test_foo(self):
+ f(self)
+
+ with mock.patch.object(decorators.LOG, 'error') as m_error:
+ self.assertRaises(MyException, test_foo, object())
+
+ m_error.assert_called_once_with(mock.ANY, '1234', '1234')
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index ae2f2a3..8b6472b 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -17,7 +17,6 @@
import testtools
from tempest import config
-from tempest import exceptions
from tempest.lib.common.utils import data_utils
from tempest import test
from tempest.tests import base
@@ -32,34 +31,6 @@
fake_config.FakePrivate)
-class TestAttrDecorator(BaseDecoratorsTest):
- def _test_attr_helper(self, expected_attrs, **decorator_args):
- @test.attr(**decorator_args)
- def foo():
- pass
-
- # By our test.attr decorator the attribute __testtools_attrs will be
- # set only for 'type' argument, so we test it first.
- if 'type' in decorator_args:
- # this is what testtools sets
- self.assertEqual(getattr(foo, '__testtools_attrs'),
- set(expected_attrs))
-
- def test_attr_without_type(self):
- self._test_attr_helper(expected_attrs='baz', bar='baz')
-
- def test_attr_decorator_with_list_type(self):
- # if type is 'smoke' we'll get the original list of types
- self._test_attr_helper(expected_attrs=['smoke', 'foo'],
- type=['smoke', 'foo'])
-
- def test_attr_decorator_with_unknown_type(self):
- self._test_attr_helper(expected_attrs=['foo'], type='foo')
-
- def test_attr_decorator_with_duplicated_type(self):
- self._test_attr_helper(expected_attrs=['foo'], type=['foo', 'foo'])
-
-
class TestIdempotentIdDecorator(BaseDecoratorsTest):
def _test_helper(self, _id, **decorator_args):
@@ -119,7 +90,7 @@
self._test_services_helper('compute', 'compute')
def test_services_decorator_with_invalid_service(self):
- self.assertRaises(exceptions.InvalidServiceTag,
+ self.assertRaises(test.InvalidServiceTag,
self._test_services_helper, 'compute',
'bad_service')
@@ -135,7 +106,7 @@
for service in service_list:
try:
self._test_services_helper(service)
- except exceptions.InvalidServiceTag:
+ except test.InvalidServiceTag:
self.fail('%s is not listed in the valid service tag list'
% service)
except KeyError:
diff --git a/tools/generate-tempest-plugins-list.sh b/tools/generate-tempest-plugins-list.sh
index ecff508..e6aad86 100755
--- a/tools/generate-tempest-plugins-list.sh
+++ b/tools/generate-tempest-plugins-list.sh
@@ -1,4 +1,4 @@
-#!/bin/bash -ex
+#!/usr/bin/env bash
# Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
#
@@ -38,6 +38,8 @@
# current working directory, it will be prepended or appended to
# the generated reStructuredText plugins table respectively.
+set -ex
+
(
declare -A plugins
diff --git a/tools/skip_tracker.py b/tools/skip_tracker.py
index 9735f77..44f5874 100755
--- a/tools/skip_tracker.py
+++ b/tools/skip_tracker.py
@@ -20,127 +20,10 @@
is fixed but a skip is still in the Tempest test code
"""
-import os
-import re
-
-from launchpadlib import launchpad
-from oslo_log import log as logging
-
-BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
-TESTDIR = os.path.join(BASEDIR, 'tempest')
-LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
-
-LOG = logging.getLogger(__name__)
-
-
-def info(msg, *args, **kwargs):
- LOG.info(msg, *args, **kwargs)
-
-
-def debug(msg, *args, **kwargs):
- LOG.debug(msg, *args, **kwargs)
-
-
-def find_skips(start=TESTDIR):
- """Find skipped tests
-
- Returns a list of tuples (method, bug) that represent
- test methods that have been decorated to skip because of
- a particular bug.
- """
- results = {}
- debug("Searching in %s", start)
- for root, _dirs, files in os.walk(start):
- for name in files:
- if name.startswith('test_') and name.endswith('py'):
- path = os.path.join(root, name)
- debug("Searching in %s", path)
- temp_result = find_skips_in_file(path)
- for method_name, bug_no in temp_result:
- if results.get(bug_no):
- result_dict = results.get(bug_no)
- if result_dict.get(name):
- result_dict[name].append(method_name)
- else:
- result_dict[name] = [method_name]
- results[bug_no] = result_dict
- else:
- results[bug_no] = {name: [method_name]}
- return results
-
-
-def find_skips_in_file(path):
- """Return the skip tuples in a test file"""
- BUG_RE = re.compile(r'\s*@.*skip_because\(bug=[\'"](\d+)[\'"]')
- DEF_RE = re.compile(r'\s*def (\w+)\(')
- bug_found = False
- results = []
- with open(path, 'rb') as content:
- lines = content.readlines()
- for x, line in enumerate(lines):
- if not bug_found:
- res = BUG_RE.match(line)
- if res:
- bug_no = int(res.group(1))
- debug("Found bug skip %s on line %d", bug_no, x + 1)
- bug_found = True
- else:
- res = DEF_RE.match(line)
- if res:
- method = res.group(1)
- debug("Found test method %s skips for bug %d",
- method, bug_no)
- results.append((method, bug_no))
- bug_found = False
- return results
-
-
-def get_results(result_dict):
- results = []
- for bug_no in result_dict:
- for method in result_dict[bug_no]:
- results.append((method, bug_no))
- return results
+from tempest.lib.cmd import skip_tracker
if __name__ == '__main__':
- results = find_skips()
- unique_bugs = sorted(set([bug for (method, bug) in get_results(results)]))
- unskips = []
- duplicates = []
- info("Total bug skips found: %d", len(results))
- info("Total unique bugs causing skips: %d", len(unique_bugs))
- lp = launchpad.Launchpad.login_anonymously('grabbing bugs',
- 'production',
- LPCACHEDIR)
- for bug_no in unique_bugs:
- bug = lp.bugs[bug_no]
- duplicate = bug.duplicate_of_link
- if duplicate is not None:
- dup_id = duplicate.split('/')[-1]
- duplicates.append((bug_no, dup_id))
- for task in bug.bug_tasks:
- info("Bug #%7s (%12s - %12s)", bug_no,
- task.importance, task.status)
- if task.status in ('Fix Released', 'Fix Committed'):
- unskips.append(bug_no)
-
- for bug_id, dup_id in duplicates:
- if bug_id not in unskips:
- dup_bug = lp.bugs[dup_id]
- for task in dup_bug.bug_tasks:
- info("Bug #%7s is a duplicate of Bug#%7s (%12s - %12s)",
- bug_id, dup_id, task.importance, task.status)
- if task.status in ('Fix Released', 'Fix Committed'):
- unskips.append(bug_id)
-
- unskips = sorted(set(unskips))
- if unskips:
- print("The following bugs have been fixed and the corresponding skips")
- print("should be removed from the test cases:")
- print()
- for bug in unskips:
- message = " %7s in " % bug
- locations = ["%s" % x for x in results[bug].keys()]
- message += " and ".join(locations)
- print(message)
+ print("DEPRECATED: `skip_tracker.py` is already deprecated, "
+ "use `skip-tracker` command instead.")
+ skip_tracker.main()
diff --git a/tools/with_venv.sh b/tools/with_venv.sh
index 165c883..408b5f1 100755
--- a/tools/with_venv.sh
+++ b/tools/with_venv.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)/../}
VENV_PATH=${VENV_PATH:-${TOOLS_PATH}}
VENV_DIR=${VENV_DIR:-/.venv}
diff --git a/tox.ini b/tox.ini
index e2a9b3f..892f834 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = pep8,py35,py34,py27,pip-check-reqs
+envlist = pep8,py35,py27,pip-check-reqs
minversion = 2.3.1
skipsdist = True
@@ -36,7 +36,6 @@
commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf
[testenv:cover]
-setenv = OS_TEST_PATH=./tempest/tests
commands = python setup.py testr --coverage --testr-arg='tempest\.tests {posargs}'
[testenv:all]