Merge "Improvement in API Microversion testing doc"
diff --git a/HACKING.rst b/HACKING.rst
index 910a977..c0a857c 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -170,7 +170,7 @@
All negative tests should be based on `API-WG guideline`_ . Such negative
tests can block any changes from accurate failure code to invalid one.
-.. _API-WG guideline: https://github.com/openstack/api-wg/blob/master/guidelines/http.rst#failure-code-clarifications
+.. _API-WG guideline: http://specs.openstack.org/openstack/api-wg/guidelines/http.html#failure-code-clarifications
If facing some gray area which is not clarified on the above guideline, propose
a new guideline to the API-WG. With a proposal to the API-WG we will be able to
@@ -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 3d7c804..ac93992 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,6 @@
.. image:: http://governance.openstack.org/badges/tempest.svg
:target: http://governance.openstack.org/reference/tags/index.html
- :remote:
.. Change things from this point on
@@ -105,7 +104,7 @@
$ tempest run
from the Tempest workspace directory. Or you can use the ``--workspace``
- argument to run in the workspace you created regarless of your current
+ argument to run in the workspace you created regardless of your current
working directory. For example::
$ tempest run --workspace cloud-01
@@ -173,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.
@@ -259,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 04ddfdf..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.
#
@@ -140,9 +153,8 @@
git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local",
"-n1"]
try:
- html_last_updated_fmt = subprocess.Popen(git_cmd,
- stdout=subprocess.PIPE).\
- communicate()[0]
+ html_last_updated_fmt = str(
+ subprocess.Popen(git_cmd, stdout=subprocess.PIPE).communicate()[0])
except Exception:
warnings.warn('Cannot get last updated time from git repository. '
'Not setting "html_last_updated_fmt".')
@@ -184,123 +196,5 @@
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Tempestdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'Tempest.tex', u'Tempest Documentation',
- u'OpenStack QA Team', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'tempest', u'Tempest Documentation',
- [u'OpenStack QA Team'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'Tempest', u'Tempest Documentation',
- u'OpenStack QA Team', 'Tempest', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-
-# -- Options for Epub output ---------------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = u'Tempest'
-epub_author = u'Sean Dague'
-epub_publisher = u'OpenStack QA Team'
-epub_copyright = u'2013, OpenStack QA Team'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-#epub_exclude_files = []
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
+# A list of warning types to suppress arbitrary warning messages.
+suppress_warnings = ['image.nonlocal_uri']
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/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-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/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml b/releasenotes/notes/add-OAUTH-Consumer-Client-tempest-tests-db1df7aae4a9fd4e.yaml
new file mode 100644
index 0000000..6b45666
--- /dev/null
+++ b/releasenotes/notes/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/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml b/releasenotes/notes/add-additional-methods-to-roles-client-library-178d4a6000dec72d.yaml
new file mode 100644
index 0000000..01136c6
--- /dev/null
+++ b/releasenotes/notes/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/add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml b/releasenotes/notes/add-cascade-parameter-to-volumes-client-ff4f7f12795003a4.yaml
new file mode 100644
index 0000000..6801858
--- /dev/null
+++ b/releasenotes/notes/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/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
new file mode 100644
index 0000000..848a21b
--- /dev/null
+++ b/releasenotes/notes/add-compute-server-evaculate-client-as-a-library-ed76baf25f02c3ca.yaml
@@ -0,0 +1,3 @@
+---
+features:
+ - Define the compute server evacuate client method in the servers_client library.
diff --git a/releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml b/releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml
new file mode 100644
index 0000000..0075a36
--- /dev/null
+++ b/releasenotes/notes/add-content-type-without-spaces-b2c9b91b257814f3.yaml
@@ -0,0 +1,9 @@
+---
+upgrade:
+ - The ``JSON_ENC`` and ``TXT_ENC`` option in the ``_error_checker``
+ section have been added with additional content-type which are
+ defined in RFC7231 but missing in the currnt rest_client.py file.
+ The lack of these additional content-type will cause defcore test
+ to fail for OpenStack public cloud which uses tomcat module in the
+ api gateway. The additions are ``application/json;charset=utf-8``,
+ ``text/html;charset=utf-8``,``text/plain;charset=utf-8``
\ No newline at end of file
diff --git a/releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml b/releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml
new file mode 100644
index 0000000..471f8f0
--- /dev/null
+++ b/releasenotes/notes/add-list-auth-project-client-5905076d914a3943.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add the list auth projects API to the identity client library. This feature
+ enables the possibility to list projects that are available to be scoped
+ to based on the X-Auth-Token provided in the request.
diff --git a/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml b/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
new file mode 100644
index 0000000..acc7a41
--- /dev/null
+++ b/releasenotes/notes/add-list-glance-api-versions-ec5fc8081fc8a0ae.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add versions_client module for image service.
+ This new module provides list_versions() method which shows API versions
+ from Image service.
diff --git a/releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml b/releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
new file mode 100644
index 0000000..233cc28
--- /dev/null
+++ b/releasenotes/notes/add-list-version-to-volume-client-4769dd1bd4ab9c5e.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add versions_client module for volume service.
+ This new module provides list_versions() method which shows API versions
+ from Volume service.
diff --git a/releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml b/releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
new file mode 100644
index 0000000..06f4fcd
--- /dev/null
+++ b/releasenotes/notes/add-quota-sets-detail-kwarg-74b72183295b3ce7.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Interface show_quota_set of compute quotas_client has been extended to include the
+ argument "detail", which allows for detailed quota set information for a project to be
+ retrieved, if set to True.
diff --git a/releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml b/releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
new file mode 100644
index 0000000..1b8cda2
--- /dev/null
+++ b/releasenotes/notes/add-tempest-lib-remote-client-adbeb3f42a36910b.yaml
@@ -0,0 +1,11 @@
+---
+features:
+ - |
+ Add remote_client under tempest.lib.
+ This remote_client under tempest.lib is defined as stable
+ interface, and now this module provides the following
+ essential methods.
+
+ - exec_command
+ - validate_authentication
+ - ping_host
diff --git a/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml b/releasenotes/notes/add-update-encryption-type-to-encryption-types-client-f3093532a0bcf9a1.yaml
new file mode 100644
index 0000000..c95e77c
--- /dev/null
+++ b/releasenotes/notes/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/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml b/releasenotes/notes/add-volume-manage-client-as-library-78ab198a1dc1bd41.yaml
new file mode 100644
index 0000000..3453050
--- /dev/null
+++ b/releasenotes/notes/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/deprecate-deactivate_image-config-7a282c471937bbcb.yaml b/releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml
new file mode 100644
index 0000000..69c6bb6
--- /dev/null
+++ b/releasenotes/notes/deprecate-deactivate_image-config-7a282c471937bbcb.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+ - |
+ The ``deactivate_image`` configuration switch from the ``config`` module
+ is deprecated. It was added to support the older-than-kilo releases
+ which we don't support anymore.
diff --git a/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml b/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
new file mode 100644
index 0000000..c3e43ee
--- /dev/null
+++ b/releasenotes/notes/deprecate-dvr_extra_resources-config-8c319d6dab7f7e5c.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+ - |
+ The ``dvr_extra_resources`` configuration switch from the ``config`` module
+ is deprecated. It was added to support the Liberty Release which we don't
+ support anymore.
+
diff --git a/releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml b/releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
new file mode 100644
index 0000000..788bc95
--- /dev/null
+++ b/releasenotes/notes/deprecate-glance-api-version-config-options-8370b63aea8e14cf.yaml
@@ -0,0 +1,10 @@
+---
+deprecations:
+ - |
+ Glance v1 APIs are deprecated and v2 are current.
+ Tempest should tests only v2 APIs.
+ Below API version selection config options
+ for glance have been deprecated and will be removed in future.
+
+ * CONF.image_feature_enabled.api_v2
+ * CONF.image_feature_enabled.api_v1
diff --git a/releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml b/releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
new file mode 100644
index 0000000..0660d9c
--- /dev/null
+++ b/releasenotes/notes/deprecated-cinder-api-v1-option-df7d5a54d93db5cf.yaml
@@ -0,0 +1,12 @@
+---
+deprecations:
+ - |
+ Volume v1 API is deprecated and the v3 are CURRENT.
+ Tempest doesn't need to test the v1 API as the default.
+ The volume config option 'api_v1' has been marked as
+ deprecated.
+upgrade:
+ - |
+ The volume config option 'api_v1' default is changed to
+ ``False`` because the volume v1 API has been deprecated
+ since Juno release.
diff --git a/releasenotes/notes/dreprecate_client_parameters-cb8d069e62957f7e.yaml b/releasenotes/notes/dreprecate_client_parameters-cb8d069e62957f7e.yaml
new file mode 100644
index 0000000..4081f6a
--- /dev/null
+++ b/releasenotes/notes/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/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
new file mode 100644
index 0000000..6d31576
--- /dev/null
+++ b/releasenotes/notes/fix-volume-v2-service-clients-bugfix-1667354-73d2c3c8fedc08bf.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+ - |
+ Fix below volume v2 service clients to make v2 API call: Bug#1667354
+
+ - SchedulerStatsClient
+ - CapabilitiesClient
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/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml b/releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
new file mode 100644
index 0000000..710ad9e
--- /dev/null
+++ b/releasenotes/notes/remove-cinder-v1-api-tests-71e266b8d55d475f.yaml
@@ -0,0 +1,5 @@
+upgrade:
+ - Remove Cinder v1 API tests.
+ Cinder v1 API has been deprecated since Juno release, and Juno is
+ not supported by current Tempest. Then Cinder v1 API tests are
+ removed from Tempest.
diff --git a/releasenotes/notes/remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml b/releasenotes/notes/remove-deprecated-allow_port_security_disabled-option-d0ffaeb2e7817707.yaml
new file mode 100644
index 0000000..9d7102f
--- /dev/null
+++ b/releasenotes/notes/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/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml b/releasenotes/notes/remove-deprecated-compute-validation-config-options-part-2-5cd17b6e0e6cb8a3.yaml
new file mode 100644
index 0000000..b4e4dd1
--- /dev/null
+++ b/releasenotes/notes/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/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml b/releasenotes/notes/remove-deprecated-dvr_extra_resources-option-e8c441c38eab7ddd.yaml
new file mode 100644
index 0000000..889e862
--- /dev/null
+++ b/releasenotes/notes/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/remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml b/releasenotes/notes/remove-deprecated-identity-reseller-option-4411c7e3951f1094.yaml
new file mode 100644
index 0000000..8085694
--- /dev/null
+++ b/releasenotes/notes/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/remove-sahara-service-available-44a642aa9c634ab4.yaml
new file mode 100644
index 0000000..c0dc7d7
--- /dev/null
+++ b/releasenotes/notes/remove-sahara-service-available-44a642aa9c634ab4.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+ - |
+ The 'sahara' config option in the 'service-available' group has been moved to the sahara plugin
+ (openstack/sahara-tests) along with tests and service client during the Ocata timeframe.
+ A 'sahara' config option was left over on Tempest side, and it's removed now.
+ As long as the sahara plugin is installed, this change as no impact on users of sahara tests.
diff --git a/releasenotes/notes/volume-transfers-client-e5ed3f5464c0cdc0.yaml b/releasenotes/notes/volume-transfers-client-e5ed3f5464c0cdc0.yaml
new file mode 100644
index 0000000..e5e479b
--- /dev/null
+++ b/releasenotes/notes/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/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 6962e3e..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
@@ -9,8 +9,8 @@
netaddr!=0.7.16,>=0.7.13 # BSD
testrepository>=0.0.18 # Apache-2.0/BSD
oslo.concurrency>=3.8.0 # Apache-2.0
-oslo.config!=3.18.0,>=3.14.0 # Apache-2.0
-oslo.log>=3.11.0 # Apache-2.0
+oslo.config>=3.22.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/setup.cfg b/setup.cfg
index 96313fd..b2035bc 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -45,12 +45,10 @@
tempest.config = tempest.config:list_opts
[build_sphinx]
-all_files = 1
+all-files = 1
+warning-is-error = 1
build-dir = doc/build
source-dir = doc/source
-[pbr]
-warnerrors = True
-
[wheel]
universal = 1
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 118ad60..4ae4372 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 36ce975..2f5382e 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -17,8 +17,8 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
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
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 00107cd..69689a7 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -15,10 +15,9 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+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.py b/tempest/api/compute/admin/test_flavors.py
index c3c88a5..3821895 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -16,7 +16,7 @@
import uuid
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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
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.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index ee1e3a0..4d7abb6 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
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 dab83e5..f39feb9 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -15,7 +15,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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
@@ -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 1a37312..2addce9 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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_keypairs_v210.py b/tempest/api/compute/admin/test_keypairs_v210.py
index 82d3bae..b6c2c3d 100644
--- a/tempest/api/compute/admin/test_keypairs_v210.py
+++ b/tempest/api/compute/admin/test_keypairs_v210.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
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_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 18655cb..df8b175 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,9 +15,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 3e9dc79..c9d7722 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -18,7 +18,7 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
LOG = logging.getLogger(__name__)
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index ca8382f..cc4654d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
@@ -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_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index 2efe755..b4d0f2a 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 9e1c0e9..aff61bf 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -15,10 +15,9 @@
from tempest.api.compute import base
from tempest.common import compute
from tempest.common import fixed_network
-from tempest.common.utils import data_utils
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 adb49a5..677972e 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -16,12 +16,11 @@
from tempest.api.compute import base
from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -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 45472df..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
@@ -30,6 +33,9 @@
5. Swap volume from "volume1" to "volume2" as admin.
6. Check the swap volume is successful and "volume2"
is attached to "instance1" and "volume1" is in available state.
+ 7. Swap volume from "volume2" to "volume1" as admin.
+ 8. Check the swap volume is successful and "volume1"
+ is attached to "instance1" and "volume2" is in available state.
"""
@classmethod
@@ -38,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):
@@ -58,13 +99,25 @@
volume1['id'], 'available')
waiters.wait_for_volume_resource_status(self.volumes_client,
volume2['id'], 'in-use')
- self.addCleanup(self.servers_client.detach_volume,
- server['id'], volume2['id'])
+ 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']
self.assertEqual(1, len(vol_attachments))
self.assertIn(volume2['id'], vol_attachments[0]['volumeId'])
- # TODO(mriedem): Test swapping back from volume2 to volume1 after
- # nova bug 1490236 is fixed.
+ # Swap volume from "volume2" to "volume1"
+ self.admin_servers_client.update_attached_volume(
+ server['id'], volume2['id'], volumeId=volume1['id'])
+ waiters.wait_for_volume_resource_status(self.volumes_client,
+ 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']
+ self.assertEqual(1, len(vol_attachments))
+ self.assertIn(volume1['id'], vol_attachments[0]['volumeId'])
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 905bc3d..4a7f36f 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -13,11 +13,10 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
@@ -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 55cc293..1736463 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -19,11 +19,11 @@
from tempest.api.compute import api_microversion_fixture
from tempest.common import compute
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
from tempest import exceptions
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 as lib_exc
import tempest.test
@@ -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):
@@ -295,20 +292,22 @@
@classmethod
def create_image_from_server(cls, server_id, **kwargs):
"""Wrapper utility that returns an image created from the server."""
- name = data_utils.rand_name(cls.__name__ + "-image")
- if 'name' in kwargs:
- name = kwargs.pop('name')
+ name = kwargs.pop('name',
+ data_utils.rand_name(cls.__name__ + "-image"))
+ wait_until = kwargs.pop('wait_until', None)
+ wait_for_server = kwargs.pop('wait_for_server', True)
- image = cls.compute_images_client.create_image(server_id, name=name)
+ image = cls.compute_images_client.create_image(server_id, name=name,
+ **kwargs)
image_id = data_utils.parse_image_id(image.response['location'])
cls.images.append(image_id)
- if 'wait_until' in kwargs:
+ if wait_until is not None:
try:
waiters.wait_for_image_status(cls.compute_images_client,
- image_id, kwargs['wait_until'])
+ image_id, wait_until)
except lib_exc.NotFound:
- if kwargs['wait_until'].upper() == 'ACTIVE':
+ if wait_until.upper() == 'ACTIVE':
# If the image is not found after create_image returned
# that means the snapshot failed in nova-compute and nova
# deleted the image. There should be a compute fault
@@ -326,8 +325,8 @@
raise
image = cls.compute_images_client.show_image(image_id)['image']
- if kwargs['wait_until'] == 'ACTIVE':
- if kwargs.get('wait_for_server', True):
+ if wait_until.upper() == 'ACTIVE':
+ if wait_for_server:
waiters.wait_for_server_status(cls.servers_client,
server_id, 'ACTIVE')
return image
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 b313f44..b2c33e2 100644
--- a/tempest/api/compute/flavors/test_flavors_negative.py
+++ b/tempest/api/compute/flavors/test_flavors_negative.py
@@ -19,8 +19,8 @@
from tempest.api.compute import base
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
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
@@ -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 5e47d18..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
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.compute.floating_ips import base
-from tempest.common.utils import data_utils
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
@@ -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 00a4075..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
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
@@ -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.py b/tempest/api/compute/images/test_image_metadata.py
index f131007..dcc44d8 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -17,9 +17,9 @@
from tempest.api.compute import base
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 0f5b9d8..03d0789 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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.py b/tempest/api/compute/images/test_images.py
index a0c860a..29bd6da 100644
--- a/tempest/api/compute/images/test_images.py
+++ b/tempest/api/compute/images/test_images.py
@@ -13,10 +13,11 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
+import testtools
CONF = config.CONF
@@ -64,3 +65,39 @@
wait_for_server=False)
self.addCleanup(self.client.delete_image, image['id'])
self.assertEqual(snapshot_name, image['name'])
+
+ @decorators.idempotent_id('71bcb732-0261-11e7-9086-fa163e4fa634')
+ @testtools.skipUnless(CONF.compute_feature_enabled.pause,
+ 'Pause is not available.')
+ def test_create_image_from_paused_server(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ self.servers_client.pause_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'PAUSED')
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+
+ snapshot_name = data_utils.rand_name('test-snap')
+ image = self.create_image_from_server(server['id'],
+ name=snapshot_name,
+ wait_until='ACTIVE',
+ wait_for_server=False)
+ self.addCleanup(self.client.delete_image, image['id'])
+ self.assertEqual(snapshot_name, image['name'])
+
+ @decorators.idempotent_id('8ca07fec-0262-11e7-907e-fa163e4fa634')
+ @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
+ 'Suspend is not available.')
+ def test_create_image_from_suspended_server(self):
+ server = self.create_test_server(wait_until='ACTIVE')
+ self.servers_client.suspend_server(server['id'])
+ waiters.wait_for_server_status(self.servers_client,
+ server['id'], 'SUSPENDED')
+ self.addCleanup(self.servers_client.delete_server, server['id'])
+
+ snapshot_name = data_utils.rand_name('test-snap')
+ image = self.create_image_from_server(server['id'],
+ name=snapshot_name,
+ wait_until='ACTIVE',
+ wait_for_server=False)
+ self.addCleanup(self.client.delete_image, image['id'])
+ self.assertEqual(snapshot_name, image['name'])
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 10f3c70..e292389 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -13,12 +13,11 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -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
@@ -54,18 +53,18 @@
meta = {'image_type': 'test'}
self.assertRaises(lib_exc.NotFound,
self.create_image_from_server,
- server['id'], meta=meta)
+ 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
# Create a new image with invalid server id
meta = {'image_type': 'test'}
self.assertRaises(lib_exc.NotFound, self.create_image_from_server,
- data_utils.rand_name('invalid'), meta=meta)
+ 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.py b/tempest/api/compute/images/test_images_oneserver.py
index 7fff555..db24174 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -14,10 +14,8 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
-from tempest.common import waiters
from tempest import config
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
@@ -53,15 +51,11 @@
# Create a new image
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
- body = self.client.create_image(server_id, name=name,
- metadata=meta)
- image_id = data_utils.parse_image_id(body.response['location'])
- self.addCleanup(test_utils.call_and_ignore_notfound_exc,
- self.client.delete_image, image_id)
- waiters.wait_for_image_status(self.client, image_id, 'ACTIVE')
+ image = self.create_image_from_server(server_id, name=name,
+ metadata=meta,
+ wait_until='ACTIVE')
# Verify the image was created correctly
- image = self.client.show_image(image_id)['image']
self.assertEqual(name, image['name'])
self.assertEqual('test', image['metadata']['image_type'])
@@ -76,8 +70,9 @@
(str(original_image['minDisk']), str(flavor_disk_size)))
# Verify the image was deleted correctly
- self.client.delete_image(image_id)
- self.client.wait_for_resource_deletion(image_id)
+ self.client.delete_image(image['id'])
+ self.images.remove(image['id'])
+ self.client.wait_for_resource_deletion(image['id'])
@decorators.idempotent_id('3b7c6fe4-dfe7-477c-9243-b06359db51e6')
def test_create_image_specify_multibyte_character_image_name(self):
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 09bbfbe..cf32ba3 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -17,12 +17,11 @@
from oslo_log import log as logging
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -33,9 +32,6 @@
def tearDown(self):
"""Terminate test instances created after a test is executed."""
- for image_id in self.image_ids:
- self.client.delete_image(image_id)
- self.image_ids.remove(image_id)
self.server_check_teardown()
super(ImagesOneServerNegativeTestJSON, self).tearDown()
@@ -80,44 +76,40 @@
server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
- cls.image_ids = []
-
- @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
- snapshot_name = data_utils.rand_name('test-snap')
meta = {'': ''}
- self.assertRaises(lib_exc.BadRequest, self.client.create_image,
- self.server_id, name=snapshot_name, metadata=meta)
+ 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
- snapshot_name = data_utils.rand_name('test-snap')
meta = {'a' * 256: 'b' * 256}
- self.assertRaises(lib_exc.BadRequest, self.client.create_image,
- self.server_id, name=snapshot_name, metadata=meta)
+ 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
# Create first snapshot
- snapshot_name = data_utils.rand_name('test-snap')
- body = self.client.create_image(self.server_id, name=snapshot_name)
- image_id = data_utils.parse_image_id(body.response['location'])
- self.image_ids.append(image_id)
+ image = self.create_image_from_server(self.server_id)
self.addCleanup(self._reset_server)
# Create second snapshot
- alt_snapshot_name = data_utils.rand_name('test-snap')
- self.assertRaises(lib_exc.Conflict, self.client.create_image,
- self.server_id, name=alt_snapshot_name)
+ self.assertRaises(lib_exc.Conflict, self.create_image_from_server,
+ self.server_id)
- @test.attr(type=['negative'])
+ image_id = data_utils.parse_image_id(image.response['location'])
+ self.client.delete_image(image_id)
+ self.images.remove(image_id)
+
+ @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
@@ -126,19 +118,18 @@
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
- snapshot_name = data_utils.rand_name('test-snap')
- body = self.client.create_image(self.server_id, name=snapshot_name)
- image_id = data_utils.parse_image_id(body.response['location'])
- self.image_ids.append(image_id)
+ image = self.create_image_from_server(self.server_id)
+ image_id = data_utils.parse_image_id(image.response['location'])
+
self.addCleanup(self._reset_server)
# Do not wait, attempt to delete the image, ensure it's successful
self.client.delete_image(image_id)
- self.image_ids.remove(image_id)
-
- self.assertRaises(lib_exc.NotFound, self.client.show_image, image_id)
+ self.images.remove(image_id)
+ self.assertRaises(lib_exc.NotFound,
+ self.client.show_image, image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 9c9b8a1..7168a8c 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -20,9 +20,9 @@
from tempest.api.compute import base
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
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 e04a57a..d37f8fc 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -13,11 +13,10 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
@@ -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/base.py b/tempest/api/compute/keypairs/base.py
index 21f504b..0051810 100644
--- a/tempest/api/compute/keypairs/base.py
+++ b/tempest/api/compute/keypairs/base.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
class BaseKeypairTest(base.BaseV2ComputeTest):
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 11e84e8..0b7a967 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 863ce0d..205076c 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -15,14 +15,13 @@
# under the License.
from tempest.api.compute.keypairs import base
-from tempest.common.utils import data_utils
+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/keypairs/test_keypairs_v22.py b/tempest/api/compute/keypairs/test_keypairs_v22.py
index c893a4f..f39bb12 100644
--- a/tempest/api/compute/keypairs/test_keypairs_v22.py
+++ b/tempest/api/compute/keypairs/test_keypairs_v22.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.api.compute.keypairs import test_keypairs
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
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/base.py b/tempest/api/compute/security_groups/base.py
index cb18775..6148e16 100644
--- a/tempest/api/compute/security_groups/base.py
+++ b/tempest/api/compute/security_groups/base.py
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest import test
CONF = config.CONF
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 0312736..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
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
+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
@@ -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 e90a1fc..930a58e 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
+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
@@ -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 48bb1b6..207778a 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -16,8 +16,8 @@
import testtools
from tempest.api.compute.security_groups import base
-from tempest.common.utils import data_utils
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
@@ -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 38dbb50..d54c3ec 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -17,9 +17,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 5bcbdac..57aa72e 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -17,9 +17,9 @@
from oslo_log import log as logging
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
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
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 85e3281..1d3a790 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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 7b782de..1ad153a 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -16,9 +16,9 @@
from tempest.api.compute import base
from tempest.common import fixed_network
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -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.py b/tempest/api/compute/servers/test_multiple_create.py
index 7e55b12..7cbb513 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -14,6 +14,7 @@
# under the License.
from tempest.api.compute import base
+from tempest.common import compute
from tempest.lib import decorators
@@ -21,13 +22,19 @@
@decorators.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
def test_multiple_create(self):
- body = self.create_test_server(wait_until='ACTIVE',
- min_count=1,
- max_count=2)
+ tenant_network = self.get_tenant_network()
+ body, servers = compute.create_test_server(
+ self.os,
+ wait_until='ACTIVE',
+ min_count=2,
+ tenant_network=tenant_network)
+ for server in servers:
+ self.addCleanup(self.servers_client.delete_server, server['id'])
# NOTE(maurosr): do status response check and also make sure that
# reservation_id is not in the response body when the request send
# contains return_reservation_id=False
self.assertNotIn('reservation_id', body)
+ self.assertEqual(2, len(servers))
@decorators.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
def test_multiple_create_with_reservation_return(self):
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 3f6abab..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,33 +82,52 @@
"""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
- # and greater than or equal to 3.3
+ # and belong to the known supported RFB versions.
version = float("%d.%d" % (int(data[4:7], base=10),
int(data[8:11], base=10)))
- self.assertTrue(version >= 3.3, 'Bad RFB Version: ' + str(version))
- # Send our RFB version to the server, which we will just go with 3.3
+ # Add the max RFB versions supported
+ supported_versions = [3.3, 3.8]
+ self.assertIn(version, supported_versions,
+ 'Bad RFB Version: ' + str(version))
+ # Send our RFB version to the server
self._websocket.send_frame(data)
# Get the sever authentication type and make sure None is supported
data = self._websocket.receive_frame()
self.assertIsNotNone(data, 'Expected authentication type None.')
- self.assertGreaterEqual(
- len(data), 2, 'Expected authentication type None.')
- self.assertIn(
- 1, [ord_func(data[i + 1]) for i in range(ord_func(data[0]))],
- 'Expected authentication type None.')
- # Send to the server that we only support authentication type None
- self._websocket.send_frame(six.int2byte(1))
- # The server should send 4 bytes of 0's if security handshake succeeded
- data = self._websocket.receive_frame()
- self.assertEqual(
- len(data), 4, 'Server did not think security was successful.')
- self.assertEqual(
- [ord_func(i) for i in data], [0, 0, 0, 0],
- 'Server did not think security was successful.')
+ data_length = len(data)
+ if version == 3.3:
+ # For RFB 3.3: in the security handshake, rather than a two-way
+ # negotiation, the server decides the security type and sends a
+ # single word(4 bytes).
+ self.assertEqual(
+ data_length, 4, 'Expected authentication type None.')
+ self.assertIn(1, [ord_func(data[i]) for i in (0, 3)],
+ 'Expected authentication type None.')
+ else:
+ self.assertGreaterEqual(
+ len(data), 2, 'Expected authentication type None.')
+ self.assertIn(
+ 1,
+ [ord_func(data[i + 1]) for i in range(ord_func(data[0]))],
+ 'Expected authentication type None.')
+ # Send to the server that we only support authentication
+ # type None
+ self._websocket.send_frame(six.int2byte(1))
+
+ # The server should send 4 bytes of 0's if security
+ # handshake succeeded
+ data = self._websocket.receive_frame()
+ self.assertEqual(
+ len(data), 4,
+ 'Server did not think security was successful.')
+ self.assertEqual(
+ [ord_func(i) for i in data], [0, 0, 0, 0],
+ 'Server did not think security was successful.')
+
# Say to leave the desktop as shared as part of client initialization
self._websocket.send_frame(six.int2byte(1))
# Get the server initialization packet back and make sure it is the
@@ -139,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'],
@@ -155,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
@@ -168,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 b915739..751d33a 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -19,10 +19,10 @@
from tempest.api.compute import base
from tempest.common import compute
-from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest.common import waiters
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
@@ -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_group.py b/tempest/api/compute/servers/test_server_group.py
index 6679b58..69d7897 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 92ffa86..482ba09 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+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.py b/tempest/api/compute/servers/test_server_rescue.py
index 75ba15c..8760af6 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -16,9 +16,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index c3a8c36..5fac433 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -16,9 +16,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -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_server_tags.py b/tempest/api/compute/servers/test_server_tags.py
index 20e2cee..0370215 100644
--- a/tempest/api/compute/servers/test_server_tags.py
+++ b/tempest/api/compute/servers/test_server_tags.py
@@ -16,7 +16,7 @@
import six
from tempest.api.compute import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index b463726..11f236b 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -16,9 +16,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 1418b3f..a480a15 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -19,9 +19,9 @@
from tempest.api.compute import base
from tempest.common import compute
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -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 01fd9ef..ab63154 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -14,12 +14,11 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
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
@@ -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_quotas.py b/tempest/api/compute/test_quotas.py
index 0ad2df8..9d83ee1 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -62,8 +62,8 @@
self.assertIn(quota, quota_set.keys())
# get the quota set using user id
- quota_set = self.client.show_quota_set(self.tenant_id,
- self.user_id)['quota_set']
+ quota_set = self.client.show_quota_set(
+ self.tenant_id, user_id=self.user_id)['quota_set']
self.assertEqual(quota_set['id'], self.tenant_id)
for quota in expected_quota_set:
self.assertIn(quota, quota_set.keys())
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_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 4b06867..2f3a06e 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -16,9 +16,9 @@
import testtools
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 0eaa359..43c837a 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -16,9 +16,9 @@
from testtools import matchers
from tempest.api.compute import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 2ad8631..80db1be 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -14,11 +14,10 @@
# under the License.
from tempest.api.compute import base
-from tempest.common.utils import data_utils
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
@@ -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_endpoints.py b/tempest/api/identity/admin/v2/test_endpoints.py
index 0ea2eb3..db32f5a 100644
--- a/tempest/api/identity/admin/v2/test_endpoints.py
+++ b/tempest/api/identity/admin/v2/test_endpoints.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/identity/admin/v2/test_roles.py b/tempest/api/identity/admin/v2/test_roles.py
index 799b653..479663c 100644
--- a/tempest/api/identity/admin/v2/test_roles.py
+++ b/tempest/api/identity/admin/v2/test_roles.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
diff --git a/tempest/api/identity/admin/v2/test_roles_negative.py b/tempest/api/identity/admin/v2/test_roles_negative.py
index b017b44..86d06e2 100644
--- a/tempest/api/identity/admin/v2/test_roles_negative.py
+++ b/tempest/api/identity/admin/v2/test_roles_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 3b0ddbb..634cf21 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 e8c32b9..49bb949 100644
--- a/tempest/api/identity/admin/v2/test_tenant_negative.py
+++ b/tempest/api/identity/admin/v2/test_tenant_negative.py
@@ -14,22 +14,21 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 eb51b5a..ad9b983 100644
--- a/tempest/api/identity/admin/v2/test_tenants.py
+++ b/tempest/api/identity/admin/v2/test_tenants.py
@@ -14,8 +14,7 @@
# 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.common.utils import data_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 7b5e01e..b4c9389 100644
--- a/tempest/api/identity/admin/v2/test_tokens.py
+++ b/tempest/api/identity/admin/v2/test_tokens.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -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 5889813..2711a2d 100644
--- a/tempest/api/identity/admin/v2/test_users.py
+++ b/tempest/api/identity/admin/v2/test_users.py
@@ -18,10 +18,8 @@
from testtools import matchers
from tempest.api.identity import base
-from tempest.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils import data_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 49fda4e..4f47e41 100644
--- a/tempest/api/identity/admin/v2/test_users_negative.py
+++ b/tempest/api/identity/admin/v2/test_users_negative.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 e5a218d..75e877a 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -15,9 +15,8 @@
from oslo_serialization import jsonutils as json
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 110695d..ac2faa9 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -11,9 +11,9 @@
# under the License.
from tempest.api.identity import base
from tempest import clients
-from tempest.common.utils import data_utils
from tempest import config
from tempest.lib import auth
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
@@ -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_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 4cf9f66..86a5764 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -14,11 +14,10 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
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 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 4555a6a..61b4fa2 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -14,16 +14,15 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 9a0b3e4..09f92e2 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -14,9 +14,8 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 8e00193..70dd7b5 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -15,10 +15,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 5ce0709..2694402 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -14,9 +14,8 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 8523396..f630f74 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 2d046bb..7e70c14 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/identity/admin/v3/test_list_users.py b/tempest/api/identity/admin/v3/test_list_users.py
index 0a0d65a..bcbf6b6 100644
--- a/tempest/api/identity/admin/v3/test_list_users.py
+++ b/tempest/api/identity/admin/v3/test_list_users.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
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 eb1c69e..730d469 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -14,9 +14,8 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 b37d33d..1b1d3f7 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -13,11 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-import testtools
-
from tempest.api.identity import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
@@ -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 87f8684..33a9c8c 100644
--- a/tempest/api/identity/admin/v3/test_projects_negative.py
+++ b/tempest/api/identity/admin/v3/test_projects_negative.py
@@ -14,33 +14,31 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 f57a07c..ac550a7 100644
--- a/tempest/api/identity/admin/v3/test_regions.py
+++ b/tempest/api/identity/admin/v3/test_regions.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 9bee24a..ac56fc6 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -14,12 +14,11 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
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
@@ -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 35e1814..ad4e549 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -14,10 +14,9 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 a9508d1..1a9502a 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -16,10 +16,13 @@
import six
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 TokensV3TestJSON(base.BaseIdentityV3AdminTest):
@@ -29,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
@@ -60,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'],
@@ -150,3 +142,34 @@
token_auth['token']['project']['id'])
self.assertEqual(project2['name'],
token_auth['token']['project']['name'])
+
+ @decorators.idempotent_id('08ed85ce-2ba8-4864-b442-bcc61f16ae89')
+ def test_get_available_project_scopes(self):
+ manager_project_id = self.manager.credentials.project_id
+ admin_user_id = self.os_adm.credentials.user_id
+ admin_role_id = self.get_role_by_name(CONF.identity.admin_role)['id']
+
+ # Grant the user the role on both projects.
+ self.roles_client.create_user_role_on_project(
+ manager_project_id, admin_user_id, admin_role_id)
+ self.addCleanup(
+ self.roles_client.delete_role_from_user_on_project,
+ manager_project_id, admin_user_id, admin_role_id)
+
+ assigned_project_ids = [self.os_adm.credentials.project_id,
+ manager_project_id]
+
+ # Get available project scopes
+ available_projects =\
+ self.client.list_auth_projects()['projects']
+
+ # create list to save fetched project's id
+ fetched_project_ids = [i['id'] for i in available_projects]
+
+ # verifying the project ids in list
+ missing_project_ids = \
+ [p for p in assigned_project_ids
+ if p not in fetched_project_ids]
+ self.assertEmpty(missing_project_ids,
+ "Failed to find project_id %s in fetched list" %
+ ', '.join(missing_project_ids))
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 1a466a0..339b4bb 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -18,11 +18,10 @@
from tempest.api.identity import base
from tempest import clients
from tempest.common import credentials_factory as common_creds
-from tempest.common.utils import data_utils
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
@@ -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 e0ec883..4f271cb 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -18,8 +18,8 @@
import testtools
from tempest.api.identity import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -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 9626108..09d1b9b 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -14,15 +14,14 @@
# under the License.
from tempest.api.identity import base
-from tempest.common.utils import data_utils
+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 3c15d8c..aff3238 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -13,8 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
import tempest.test
@@ -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
@@ -145,17 +149,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 +215,7 @@
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
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 +245,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/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
index 11b595a..fc5ed79 100644
--- a/tempest/api/image/admin/v2/test_images.py
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -17,8 +17,8 @@
import testtools
from tempest.api.image import base
-from tempest.common.utils import data_utils
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
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index cd4f820..c586960 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -15,8 +15,8 @@
import six
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
import tempest.test
@@ -145,6 +145,7 @@
cls.namespace_objects_client = cls.os.namespace_objects_client
cls.namespace_tags_client = cls.os.namespace_tags_client
cls.schemas_client = cls.os.schemas_client
+ cls.versions_client = cls.os.image_versions_client
def create_namespace(cls, namespace_name=None, visibility='public',
description='Tempest', protected=False,
diff --git a/tempest/api/image/v1/test_image_members_negative.py b/tempest/api/image/v1/test_image_members_negative.py
index d1aa801..2748bd5 100644
--- a/tempest/api/image/v1/test_image_members_negative.py
+++ b/tempest/api/image/v1/test_image_members_negative.py
@@ -13,15 +13,14 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+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.py b/tempest/api/image/v1/test_images.py
index 756c78c..b341ab7 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -17,9 +17,9 @@
from tempest.api.image import base
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index abec82a..690b8da 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -15,16 +15,15 @@
from tempest.api.image import base
-from tempest.common.utils import data_utils
+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 2812c68..8310001 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -20,10 +20,9 @@
from oslo_log import log as logging
from tempest.api.image import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
LOG = logging.getLogger(__name__)
@@ -32,7 +31,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 +73,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 +95,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
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_metadefs_namespace_objects.py b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
index 38e56d4..80f8112 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_objects.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
index ead70f2..ed91726 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
index 608d9fc..e2a3617 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_tags.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
diff --git a/tempest/api/image/v2/test_images_metadefs_namespaces.py b/tempest/api/image/v2/test_images_metadefs_namespaces.py
index 9fda937..f71b16c 100644
--- a/tempest/api/image/v2/test_images_metadefs_namespaces.py
+++ b/tempest/api/image/v2/test_images_metadefs_namespaces.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+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
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.py b/tempest/api/image/v2/test_images_tags.py
index fd9591f..601826e 100644
--- a/tempest/api/image/v2/test_images_tags.py
+++ b/tempest/api/image/v2/test_images_tags.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/image/v2/test_images_tags_negative.py b/tempest/api/image/v2/test_images_tags_negative.py
index 2a08f3a..440fa36 100644
--- a/tempest/api/image/v2/test_images_tags_negative.py
+++ b/tempest/api/image/v2/test_images_tags_negative.py
@@ -13,15 +13,14 @@
# under the License.
from tempest.api.image import base
-from tempest.common.utils import data_utils
+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
new file mode 100644
index 0000000..84f1068
--- /dev/null
+++ b/tempest/api/image/v2/test_versions.py
@@ -0,0 +1,29 @@
+# Copyright 2017 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.image import base
+from tempest.lib import decorators
+
+
+class VersionsTest(base.BaseV2ImageTest):
+
+ @decorators.idempotent_id('659ea30a-a17c-4317-832c-0f68ed23c31d')
+ @decorators.attr(type='smoke')
+ def test_list_versions(self):
+ versions = self.versions_client.list_versions()['versions']
+ expected_resources = ('id', 'links', 'status')
+
+ for version in versions:
+ for res in expected_resources:
+ self.assertIn(res, version)
diff --git a/tempest/api/network/admin/test_external_network_extension.py b/tempest/api/network/admin/test_external_network_extension.py
index dc440d3..c83dd7f 100644
--- a/tempest/api/network/admin/test_external_network_extension.py
+++ b/tempest/api/network/admin/test_external_network_extension.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import decorators
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..e7460af 100644
--- a/tempest/api/network/admin/test_l3_agent_scheduler.py
+++ b/tempest/api/network/admin/test_l3_agent_scheduler.py
@@ -28,7 +28,6 @@
class L3AgentSchedulerTestJSON(base.BaseAdminNetworkTest):
_agent_mode = 'legacy'
- is_dvr_router = False
"""
Tests the following operations in the Neutron API using the REST client for
@@ -68,42 +67,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/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 7b6ebef..aa8b2dc 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+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
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 78ca494..4ccad30 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -16,7 +16,7 @@
import testtools
from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 132e23e..7ceeb50 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -15,9 +15,9 @@
import netaddr
-from tempest.common.utils import data_utils
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -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/base_security_groups.py b/tempest/api/network/base_security_groups.py
index 3ea3aea..b8d677a 100644
--- a/tempest/api/network/base_security_groups.py
+++ b/tempest/api/network/base_security_groups.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
class BaseSecGroupTest(base.BaseNetworkTest):
diff --git a/tempest/api/network/test_dhcp_ipv6.py b/tempest/api/network/test_dhcp_ipv6.py
index 136f9e6..9d6d700 100644
--- a/tempest/api/network/test_dhcp_ipv6.py
+++ b/tempest/api/network/test_dhcp_ipv6.py
@@ -16,11 +16,12 @@
import random
import netaddr
+from oslo_utils import netutils
from tempest.api.network import base
-from tempest.common.utils import data_utils
from tempest.common.utils import net_info
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
@@ -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_extra_dhcp_options.py b/tempest/api/network/test_extra_dhcp_options.py
index 1156275..dc9042e 100644
--- a/tempest/api/network/test_extra_dhcp_options.py
+++ b/tempest/api/network/test_extra_dhcp_options.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
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_metering_extensions.py b/tempest/api/network/test_metering_extensions.py
index 42fa9e4..18dccc3 100644
--- a/tempest/api/network/test_metering_extensions.py
+++ b/tempest/api/network/test_metering_extensions.py
@@ -13,7 +13,7 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index 69d4ebe..269f2c2 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -18,8 +18,8 @@
from tempest.api.network import base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
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
@@ -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 3cc6fb7..bc4f41f 100644
--- a/tempest/api/network/test_networks_negative.py
+++ b/tempest/api/network/test_networks_negative.py
@@ -15,36 +15,35 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
+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 3908aae..69a1441 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -21,8 +21,8 @@
from tempest.api.network import base
from tempest.api.network import base_security_groups as sec_base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
from tempest import test
@@ -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 694b86b..97ad4d4 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -17,8 +17,8 @@
import testtools
from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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()
@@ -239,6 +239,37 @@
'enable_snat': False})
self._verify_gateway_port(router['id'])
+ @decorators.idempotent_id('cbe42f84-04c2-11e7-8adb-fa163e4fa634')
+ @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
+ port = self.admin_ports_client.create_port(
+ network_id=CONF.network.public_network_id)['port']
+ self.admin_ports_client.delete_port(port_id=port['id'])
+
+ fixed_ip = {
+ 'subnet_id': port['fixed_ips'][0]['subnet_id'],
+ 'ip_address': port['fixed_ips'][0]['ip_address']
+ }
+ external_gateway_info = {
+ 'network_id': CONF.network.public_network_id,
+ 'external_fixed_ips': [fixed_ip]
+ }
+
+ # Create a router and set gateway to fixed_ip
+ router = self.admin_routers_client.create_router(
+ external_gateway_info=external_gateway_info)['router']
+ self.addCleanup(self.admin_routers_client.delete_router,
+ router_id=router['id'])
+ # Examine router's gateway is equal to fixed_ip
+ self.assertEqual(router['external_gateway_info'][
+ 'external_fixed_ips'][0]['ip_address'],
+ fixed_ip['ip_address'])
+
@decorators.idempotent_id('ad81b7ee-4f81-407b-a19c-17e623f763e8')
@testtools.skipUnless(CONF.network.public_network_id,
'The public_network_id option must be specified.')
@@ -345,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 2bda431..fdd8dd8 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -14,10 +14,11 @@
# under the License.
import netaddr
+import testtools
from tempest.api.network import base_routers as base
-from tempest.common.utils import data_utils
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
@@ -44,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,
@@ -53,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()
@@ -65,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(
@@ -81,7 +82,32 @@
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,
+ 'The public_network_id option must be specified.')
+ def test_router_set_gateway_used_ip_returns_409(self):
+ # At first create a address from public_network_id
+ port = self.admin_ports_client.create_port(
+ network_id=CONF.network.public_network_id)['port']
+ self.addCleanup(self.admin_ports_client.delete_port,
+ port_id=port['id'])
+ # Add used ip and subnet_id in external_fixed_ips
+ fixed_ip = {
+ 'subnet_id': port['fixed_ips'][0]['subnet_id'],
+ 'ip_address': port['fixed_ips'][0]['ip_address']
+ }
+ external_gateway_info = {
+ 'network_id': CONF.network.public_network_id,
+ 'external_fixed_ips': [fixed_ip]
+ }
+ # Create a router and set gateway to used ip
+ self.assertRaises(lib_exc.Conflict,
+ self.admin_routers_client.create_router,
+ external_gateway_info=external_gateway_info)
+
+ @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'],
@@ -90,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')
@@ -132,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 607baf0..0d5e230 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.network import base_security_groups as base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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 f46b873..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,17 @@
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.
+ group_create_body, _ = self._create_security_group()
+ self.assertRaises(lib_exc.Conflict,
+ self.security_groups_client.update_security_group,
+ group_create_body['security_group']['id'],
+ name="default")
+
+ @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.
@@ -201,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.
@@ -217,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 d9599c4..01d7db2 100644
--- a/tempest/api/network/test_subnetpools_extensions.py
+++ b/tempest/api/network/test_subnetpools_extensions.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.network import base
-from tempest.common.utils import data_utils
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
@@ -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 642c1ac..c075b92 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -16,8 +16,8 @@
import time
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -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 cbf0d4b..9077f88 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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 cf7dbe8..e54b6e7 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -20,10 +20,9 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
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.py b/tempest/api/object_storage/test_container_acl.py
index aa4e92c..d4e5ec5 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -14,8 +14,8 @@
# under the License.
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index d5d5ea7..9c9d821 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -13,11 +13,10 @@
# under the License.
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
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
@@ -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 d3b456a..8266341 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+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
@@ -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 2e617f3..2b5692d 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -14,9 +14,8 @@
# under the License.
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+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 ebbb84e..a8d70c5 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -16,11 +16,10 @@
import testtools
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest import config
+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_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index a280248..9e01c26 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -14,7 +14,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+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
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 63fb93d..3c11a51 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -18,12 +18,10 @@
from six.moves.urllib import parse as urlparse
import testtools
-
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
-from tempest import test
CONF = config.CONF
@@ -129,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 2829ac7..3a2233a 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -19,7 +19,7 @@
from six.moves.urllib import parse as urlparse
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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 52b3978..c56d91a 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -19,7 +19,7 @@
from six.moves.urllib import parse as urlparse
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+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
@@ -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 58fd822..21ea6ae 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -21,10 +21,9 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
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_slo.py b/tempest/api/object_storage/test_object_slo.py
index 8ed9bf8..085ad13 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -18,7 +18,7 @@
from tempest.api.object_storage import base
from tempest.common import custom_matchers
-from tempest.common.utils import data_utils
+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
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 5e3c9f7..4b506f8 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -19,7 +19,7 @@
from six.moves.urllib import parse as urlparse
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
@@ -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 d0e0935..f4d63fd 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -19,7 +19,7 @@
from six.moves.urllib import parse as urlparse
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
+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
@@ -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/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 1b12dac..dc0d179 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -16,8 +16,8 @@
import testtools
from tempest.api.object_storage import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
diff --git a/tempest/api/orchestration/base.py b/tempest/api/orchestration/base.py
index 3701b55..8d3d344 100644
--- a/tempest/api/orchestration/base.py
+++ b/tempest/api/orchestration/base.py
@@ -14,8 +14,8 @@
import yaml
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
import tempest.test
@@ -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_environment.py b/tempest/api/orchestration/stacks/test_environment.py
index 0a9b3e5..c8a9aa7 100644
--- a/tempest/api/orchestration/stacks/test_environment.py
+++ b/tempest/api/orchestration/stacks/test_environment.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
diff --git a/tempest/api/orchestration/stacks/test_limits.py b/tempest/api/orchestration/stacks/test_limits.py
index b079435..9c9d4f9 100644
--- a/tempest/api/orchestration/stacks/test_limits.py
+++ b/tempest/api/orchestration/stacks/test_limits.py
@@ -11,8 +11,8 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
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
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index 0154658..6faca71 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -14,8 +14,8 @@
from oslo_log import log as logging
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.lib import exceptions
from tempest import test
diff --git a/tempest/api/orchestration/stacks/test_non_empty_stack.py b/tempest/api/orchestration/stacks/test_non_empty_stack.py
index f106349..6b9c88b 100644
--- a/tempest/api/orchestration/stacks/test_non_empty_stack.py
+++ b/tempest/api/orchestration/stacks/test_non_empty_stack.py
@@ -11,8 +11,8 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index 4d1db6d..a51648f 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
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 89b10cc..8192999 100644
--- a/tempest/api/orchestration/stacks/test_soft_conf.py
+++ b/tempest/api/orchestration/stacks/test_soft_conf.py
@@ -11,10 +11,9 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
+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 7b5f161..dc1bb4f 100644
--- a/tempest/api/orchestration/stacks/test_stacks.py
+++ b/tempest/api/orchestration/stacks/test_stacks.py
@@ -11,21 +11,20 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
+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_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index 505abe7..97fdac4 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -13,8 +13,8 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
diff --git a/tempest/api/orchestration/stacks/test_templates.py b/tempest/api/orchestration/stacks/test_templates.py
index 21548a0..4ff951d 100644
--- a/tempest/api/orchestration/stacks/test_templates.py
+++ b/tempest/api/orchestration/stacks/test_templates.py
@@ -11,7 +11,7 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
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 d34eb89..7ddf7af 100644
--- a/tempest/api/orchestration/stacks/test_volumes.py
+++ b/tempest/api/orchestration/stacks/test_volumes.py
@@ -11,8 +11,8 @@
# under the License.
from tempest.api.orchestration import base
-from tempest.common.utils import data_utils
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
@@ -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 c3e904a..2db8010 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -12,31 +12,30 @@
import six
from tempest.api.volume import base
-from tempest.common.utils import data_utils
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 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)
- cls.name_field = cls.special_fields['name_field']
cls.volume_id_list_with_prefix = []
cls.volume_id_list_without_prefix = []
@@ -65,7 +64,7 @@
cls.create_volume_type(name=type_name,
extra_specs=extra_specs)
- params = {cls.name_field: vol_name, 'volume_type': type_name,
+ params = {'name': vol_name, 'volume_type': type_name,
'size': CONF.volume.volume_size}
cls.volume = cls.admin_volume_client.create_volume(
**params)['volume']
@@ -90,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):
@@ -140,7 +139,3 @@
msg = ("volumes %s were created in the same backend" % ", "
.join(volume_hosts))
six.assertCountEqual(self, volume_hosts, set(volume_hosts), msg)
-
-
-class VolumeMultiBackendV1Test(VolumeMultiBackendV2Test):
- _api_version = 1
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index e1cfb30..e31c0ef 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -13,21 +13,21 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils as utils
from tempest.common import waiters
+from tempest.lib.common.utils import data_utils as utils
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')
@@ -157,7 +157,3 @@
operation = 'disassociate-all'
waiters.wait_for_qos_operations(self.admin_volume_qos_client,
self.created_qos['id'], operation)
-
-
-class QosSpecsV1TestJSON(QosSpecsV2TestJSON):
- _api_version = 1
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 e1c9492..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,
@@ -107,7 +107,3 @@
def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
# test force delete when status of snapshot is error_deleting
self._create_reset_and_force_delete_temp_snapshot('error_deleting')
-
-
-class SnapshotsActionsV1Test(SnapshotsActionsV2Test):
- _api_version = 1
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/test_user_messages.py
similarity index 95%
rename from tempest/api/volume/admin/v3/test_user_messages.py
rename to tempest/api/volume/admin/test_user_messages.py
index ed85d4d..20c3538 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/test_user_messages.py
@@ -13,9 +13,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.api.volume.v3 import base
-from tempest.common.utils import data_utils
+from tempest.api.volume import base
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
CONF = config.CONF
@@ -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 73c927a..04d6cf9 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -17,14 +17,10 @@
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):
hosts = self.admin_hosts_client.list_hosts()['hosts']
self.assertGreaterEqual(len(hosts), 2, "No. of hosts are < 2,"
"response of list hosts is: % s" % hosts)
-
-
-class VolumeHostsAdminV1TestsJSON(VolumeHostsAdminV2TestsJSON):
- _api_version = 1
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 83fca45..58ca92f 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,25 +13,31 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups']
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
@@ -169,7 +175,3 @@
self.assertEqual(primary_quota['gigabytes']['in_use'] -
volume['size'],
new_primary_quota['gigabytes']['in_use'])
-
-
-class VolumeQuotasAdminV1TestJSON(BaseVolumeQuotasAdminV2TestJSON):
- _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_quotas_negative.py b/tempest/api/volume/admin/test_volume_quotas_negative.py
index 8170626..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
@@ -70,7 +69,3 @@
self.assertRaises(lib_exc.OverLimit,
self.volumes_client.create_volume,
size=CONF.volume.volume_size)
-
-
-class VolumeQuotasNegativeV1TestJSON(BaseVolumeQuotasNegativeV2TestJSON):
- _api_version = 1
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 5dfdfce..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):
@@ -80,7 +90,3 @@
self.assertNotEqual(0, len(services))
self.assertEqual(self.host_name, _get_host(services[0]['host']))
self.assertEqual(self.binary_name, services[0]['binary'])
-
-
-class VolumesServicesV1TestJSON(VolumesServicesV2TestJSON):
- _api_version = 1
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 90a0022..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,
@@ -75,7 +74,3 @@
self.assertRaises(lib_exc.OverLimit,
self.snapshots_client.create_snapshot,
volume_id=self.volume['id'])
-
-
-class VolumeSnapshotNegativeV1TestJSON(VolumeSnapshotQuotasNegativeV2TestJSON):
- _api_version = 1
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 5d08416..ac717f8 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -14,15 +14,15 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
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 VolumeTypesV2Test(base.BaseVolumeAdminTest):
+class VolumeTypesTest(base.BaseVolumeAdminTest):
@decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
def test_volume_type_list(self):
@@ -36,7 +36,6 @@
# Create/update/get/delete volume with volume_type and extra spec.
volume_types = list()
vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
- name_field = self.special_fields['name_field']
proto = CONF.volume.storage_protocol
vendor = CONF.volume.vendor_name
extra_specs = {"storage_protocol": proto,
@@ -46,14 +45,14 @@
vol_type = self.create_volume_type(
extra_specs=extra_specs)
volume_types.append(vol_type)
- params = {name_field: vol_name,
+ params = {'name': vol_name,
'volume_type': volume_types[0]['id'],
'size': CONF.volume.volume_size}
# Create volume
volume = self.create_volume(**params)
self.assertEqual(volume_types[0]['name'], volume["volume_type"])
- self.assertEqual(volume[name_field], vol_name,
+ self.assertEqual(volume['name'], vol_name,
"The created volume name is not equal "
"to the requested name")
self.assertIsNotNone(volume['id'],
@@ -74,7 +73,7 @@
fetched_volume['volume_type'],
'The fetched Volume type is different '
'from updated volume type')
- self.assertEqual(vol_name, fetched_volume[name_field],
+ self.assertEqual(vol_name, fetched_volume['name'],
'The fetched Volume is different '
'from the created Volume')
self.assertEqual(volume['id'], fetched_volume['id'],
@@ -123,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')
@@ -183,7 +194,3 @@
self.assertEqual(name, updated_vol_type['name'])
self.assertEqual(description, updated_vol_type['description'])
self.assertEqual(is_public, updated_vol_type['is_public'])
-
-
-class VolumeTypesV1Test(VolumeTypesV2Test):
- _api_version = 1
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 1729d06..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')
@@ -77,7 +77,3 @@
lib_exc.NotFound,
self.admin_volume_types_client.show_volume_type_extra_specs,
self.volume_type['id'], spec_key)
-
-
-class VolumeTypesExtraSpecsV1Test(VolumeTypesExtraSpecsV2Test):
- _api_version = 1
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 5f590bc..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
@@ -14,21 +14,20 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
+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
@@ -136,7 +135,3 @@
lib_exc.NotFound,
self.admin_volume_types_client.show_volume_type_extra_specs,
self.volume_type['id'], data_utils.rand_uuid())
-
-
-class ExtraSpecsNegativeV1Test(ExtraSpecsNegativeV2Test):
- _api_version = 1
diff --git a/tempest/api/volume/admin/test_volume_types_negative.py b/tempest/api/volume/admin/test_volume_types_negative.py
index 69e9cc0..4cad52a 100644
--- a/tempest/api/volume/admin/test_volume_types_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_negative.py
@@ -17,22 +17,20 @@
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.
- name_field = self.special_fields['name_field']
- params = {name_field: data_utils.rand_uuid(),
+ params = {'name': data_utils.rand_uuid(),
'volume_type': data_utils.rand_uuid()}
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.
@@ -40,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.
@@ -48,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.
@@ -56,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.
@@ -64,7 +62,3 @@
volume_type = self.create_volume_type(**params)
self.assertRaises(lib_exc.NotFound,
self.create_volume, volume_type=volume_type['id'])
-
-
-class VolumeTypesNegativeV1Test(VolumeTypesNegativeV2Test):
- _api_version = 1
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 693ffe7..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(
@@ -54,6 +56,7 @@
# test force delete when status of volume is error
self._create_reset_and_force_delete_temp_volume('error')
-
-class VolumesActionsV1Test(VolumesActionsV2Test):
- _api_version = 1
+ @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 13b7384..afc3281 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -17,19 +17,19 @@
from oslo_serialization import jsonutils as json
from tempest.api.volume import base
-from tempest.common.utils import data_utils
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 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")
@@ -136,7 +136,3 @@
status="error")
waiters.wait_for_volume_resource_status(self.admin_backups_client,
backup['id'], 'error')
-
-
-class VolumesBackupsAdminV1Test(VolumesBackupsAdminV2Test):
- _api_version = 1
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/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 fd10fb3..767b3c7 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -13,10 +13,12 @@
# 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.utils import data_utils
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
import tempest.test
@@ -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
@@ -37,11 +40,7 @@
if not CONF.service_available.cinder:
skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
raise cls.skipException(skip_msg)
- if cls._api_version == 1:
- if not CONF.volume_feature_enabled.api_v1:
- msg = "Volume API v1 is disabled"
- raise cls.skipException(msg)
- elif cls._api_version == 2:
+ if cls._api_version == 2:
if not CONF.volume_feature_enabled.api_v2:
msg = "Volume API v2 is disabled"
raise cls.skipException(msg)
@@ -53,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()
@@ -62,30 +65,30 @@
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 cls._api_version == 1:
- cls.snapshots_client = cls.os.snapshots_client
- cls.volumes_client = cls.os.volumes_client
- cls.backups_client = cls.os.backups_client
- cls.volume_services_client = cls.os.volume_services_client
- cls.volumes_extension_client = cls.os.volumes_extension_client
- cls.availability_zone_client = (
- cls.os.volume_availability_zone_client)
- cls.volume_limits_client = cls.os.volume_limits_client
- else:
- cls.snapshots_client = cls.os.snapshots_v2_client
- cls.volumes_client = cls.os.volumes_v2_client
- cls.backups_client = cls.os.backups_v2_client
- cls.volumes_extension_client = cls.os.volumes_v2_extension_client
- cls.availability_zone_client = (
- cls.os.volume_v2_availability_zone_client)
- cls.volume_limits_client = cls.os.volume_v2_limits_client
+ cls.snapshots_client = cls.os.snapshots_v2_client
+ cls.volumes_client = cls.os.volumes_v2_client
+ cls.backups_client = cls.os.backups_v2_client
+ cls.volumes_extension_client = cls.os.volumes_v2_extension_client
+ 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 = []
@@ -94,15 +97,6 @@
cls.build_interval = CONF.volume.build_interval
cls.build_timeout = CONF.volume.build_timeout
- if cls._api_version == 1:
- # Special fields and resp code for cinder v1
- cls.special_fields = {'name_field': 'display_name',
- 'descrip_field': 'display_description'}
- else:
- # Special fields and resp code for cinder v2
- cls.special_fields = {'name_field': 'name',
- 'descrip_field': 'description'}
-
@classmethod
def resource_cleanup(cls):
cls.clear_snapshots()
@@ -124,10 +118,9 @@
min_disk = image.get('minDisk')
kwargs['size'] = max(kwargs['size'], min_disk)
- name_field = cls.special_fields['name_field']
- if name_field not in kwargs:
+ if 'name' not in kwargs:
name = data_utils.rand_name(cls.__name__ + '-Volume')
- kwargs[name_field] = name
+ kwargs['name'] = name
volume = cls.volumes_client.create_volume(**kwargs)['volume']
cls.volumes.append(volume)
@@ -138,10 +131,9 @@
@classmethod
def create_snapshot(cls, volume_id=1, **kwargs):
"""Wrapper utility that returns a test snapshot."""
- name_field = cls.special_fields['name_field']
- if name_field not in kwargs:
+ if 'name' not in kwargs:
name = data_utils.rand_name(cls.__name__ + '-Snapshot')
- kwargs[name_field] = name
+ kwargs['name'] = name
snapshot = cls.snapshots_client.create_snapshot(
volume_id=volume_id, **kwargs)['snapshot']
@@ -154,6 +146,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']
@@ -246,38 +241,27 @@
def setup_clients(cls):
super(BaseVolumeAdminTest, cls).setup_clients()
- if cls._api_version == 1:
- cls.admin_volume_qos_client = cls.os_adm.volume_qos_client
- cls.admin_volume_services_client = \
- cls.os_adm.volume_services_client
- cls.admin_volume_types_client = cls.os_adm.volume_types_client
- cls.admin_volume_client = cls.os_adm.volumes_client
- cls.admin_hosts_client = cls.os_adm.volume_hosts_client
- cls.admin_snapshots_client = cls.os_adm.snapshots_client
- cls.admin_backups_client = cls.os_adm.backups_client
- cls.admin_encryption_types_client = \
- cls.os_adm.encryption_types_client
- cls.admin_quotas_client = cls.os_adm.volume_quotas_client
- cls.admin_volume_limits_client = cls.os_adm.volume_limits_client
- elif cls._api_version == 2:
- cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
- 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_client = cls.os_adm.volumes_v2_client
- cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
- cls.admin_snapshot_manage_client = \
- cls.os_adm.snapshot_manage_v2_client
- cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
- cls.admin_backups_client = cls.os_adm.backups_v2_client
- cls.admin_encryption_types_client = \
- cls.os_adm.encryption_types_v2_client
- cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
- cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
- cls.admin_capabilities_client = \
- cls.os_adm.volume_capabilities_v2_client
- cls.admin_scheduler_stats_client = \
- cls.os_adm.volume_scheduler_stats_v2_client
+ cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
+ 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 = \
+ cls.os_adm.snapshot_manage_v2_client
+ cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
+ cls.admin_backups_client = cls.os_adm.backups_v2_client
+ cls.admin_encryption_types_client = \
+ cls.os_adm.encryption_types_v2_client
+ cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
+ cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
+ cls.admin_capabilities_client = \
+ 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 25fe5ad..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')
@@ -31,7 +31,3 @@
availability_zone = (self.client.list_availability_zones()
['availabilityZoneInfo'])
self.assertGreater(len(availability_zone), 0)
-
-
-class AvailabilityZoneV1TestJSON(AvailabilityZoneV2TestJSON):
- _api_version = 1
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index ca86748..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))
@@ -43,7 +43,3 @@
self.assertIn(ext, map(lambda x: x['alias'], extensions))
else:
raise self.skipException('There are not any extensions configured')
-
-
-class ExtensionsV1TestJSON(ExtensionsV2TestJSON):
- _api_version = 1
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 d5ad8d2..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):
@@ -102,7 +102,3 @@
body = self.snapshots_client.show_snapshot_metadata(
self.snapshot['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
-
-
-class SnapshotV1MetadataTestJSON(SnapshotV2MetadataTestJSON):
- _api_version = 1
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
new file mode 100644
index 0000000..0083a3b
--- /dev/null
+++ b/tempest/api/volume/test_versions.py
@@ -0,0 +1,29 @@
+# Copyright 2017 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.volume import base
+from tempest.lib import decorators
+
+
+class VersionsTest(base.BaseVolumeTest):
+
+ _api_version = 3
+
+ @decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
+ @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
+ # the API here.
+ self.versions_client.list_versions()['versions']
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 36445b9..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()
@@ -46,7 +46,3 @@
self.assertEqual(absolute_limits['totalSnapshotsUsed'], 0)
self.assertEqual(absolute_limits['totalBackupsUsed'], 0)
self.assertEqual(absolute_limits['totalBackupGigabytesUsed'], 0)
-
-
-class AbsoluteLimitsV1Tests(AbsoluteLimitsV2Tests):
- _api_version = 1
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 9e7b0a7..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):
@@ -89,7 +89,3 @@
body = self.volumes_client.show_volume_metadata(
self.volume['id'])['metadata']
self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
-
-
-class VolumesV1MetadataTest(VolumesV2MetadataTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volume_transfers.py b/tempest/api/volume/test_volume_transfers.py
index 9f63b14..afcffc2 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,21 +61,23 @@
# 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']
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']
@@ -85,8 +90,4 @@
# Delete a volume transfer
self.client.delete_volume_transfer(transfer_id)
waiters.wait_for_volume_resource_status(
- self.client, volume['id'], 'available')
-
-
-class VolumesV1TransfersTest(VolumesV2TransfersTest):
- _api_version = 1
+ 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 0a6901c..1e05f22 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -14,9 +14,9 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
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
@@ -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,37 +124,33 @@
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
# to compare here.
self.assertEqual(str(readonly),
fetched_volume['metadata']['readonly'])
-
-
-class VolumesV1ActionsTest(VolumesV2ActionsTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index e24a142..5ad209c 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -13,23 +13,24 @@
# License for the specific language governing permissions and limitations
# under the License.
+import testtools
from testtools import matchers
from tempest.api.volume import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
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
@@ -134,7 +141,3 @@
restored_volume_id)['volume']
self.assertEqual('true', restored_volume_info['bootable'])
-
-
-class VolumesBackupsV1Test(VolumesBackupsV2Test):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index d653808..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
@@ -60,7 +64,3 @@
self.assertEqual('true', cloned_vol_details['bootable'])
self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
self.assertEqual(src_vol['size'], cloned_vol_details['size'])
-
-
-class VolumesV1CloneTest(VolumesV2CloneTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index 5331243..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
@@ -43,7 +42,3 @@
self.volumes_client.create_volume,
size=src_size - 1,
source_volid=src_vol['id'])
-
-
-class VolumesV1CloneNegativeTest(VolumesV2CloneNegativeTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 3df9b00..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):
@@ -31,7 +31,3 @@
volume['id'], 'available')
volume = self.volumes_client.show_volume(volume['id'])['volume']
self.assertEqual(volume['size'], extend_size)
-
-
-class VolumesV1ExtendTest(VolumesV2ExtendTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index a3e46a8..1d9b846 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -17,34 +17,31 @@
from testtools import matchers
from tempest.api.volume import base
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
CONF = config.CONF
-class VolumesV2GetTest(base.BaseVolumeTest):
+class VolumesGetTest(base.BaseVolumeTest):
def _volume_create_get_update_delete(self, **kwargs):
- name_field = self.special_fields['name_field']
- descrip_field = self.special_fields['descrip_field']
-
# Create a volume, Get it's details and Delete the volume
v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
metadata = {'Type': 'Test'}
# Create a volume
- kwargs[name_field] = v_name
+ kwargs['name'] = v_name
kwargs['metadata'] = metadata
volume = self.volumes_client.create_volume(**kwargs)['volume']
self.assertIn('id', volume)
self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
- self.assertIn(name_field, volume)
- self.assertEqual(volume[name_field], v_name,
+ self.assertIn('name', volume)
+ self.assertEqual(volume['name'], v_name,
"The created volume name is not equal "
"to the requested name")
@@ -52,7 +49,7 @@
fetched_volume = self.volumes_client.show_volume(
volume['id'])['volume']
self.assertEqual(v_name,
- fetched_volume[name_field],
+ fetched_volume['name'],
'The fetched Volume name is different '
'from the created Volume')
self.assertEqual(volume['id'],
@@ -71,25 +68,25 @@
# Update Volume
# Test volume update when display_name is same with original value
- params = {name_field: v_name}
+ params = {'name': v_name}
self.volumes_client.update_volume(volume['id'], **params)
# Test volume update when display_name is new
new_v_name = data_utils.rand_name(
self.__class__.__name__ + '-new-Volume')
new_desc = 'This is the new description of volume'
- params = {name_field: new_v_name,
- descrip_field: new_desc}
+ params = {'name': new_v_name,
+ 'description': new_desc}
update_volume = self.volumes_client.update_volume(
volume['id'], **params)['volume']
# Assert response body for update_volume method
- self.assertEqual(new_v_name, update_volume[name_field])
- self.assertEqual(new_desc, update_volume[descrip_field])
+ self.assertEqual(new_v_name, update_volume['name'])
+ self.assertEqual(new_desc, update_volume['description'])
# Assert response body for show_volume method
updated_volume = self.volumes_client.show_volume(
volume['id'])['volume']
self.assertEqual(volume['id'], updated_volume['id'])
- self.assertEqual(new_v_name, updated_volume[name_field])
- self.assertEqual(new_desc, updated_volume[descrip_field])
+ self.assertEqual(new_v_name, updated_volume['name'])
+ self.assertEqual(new_desc, updated_volume['description'])
self.assertThat(updated_volume['metadata'].items(),
matchers.ContainsAll(metadata.items()),
'The fetched Volume metadata misses data '
@@ -99,7 +96,7 @@
# contains specific characters,
# then test volume update if display_name is duplicated
new_v_desc = data_utils.rand_name('@#$%^* description')
- params = {descrip_field: new_v_desc,
+ params = {'description': new_v_desc,
'availability_zone': volume['availability_zone'],
'size': CONF.volume.volume_size}
new_volume = self.volumes_client.create_volume(**params)['volume']
@@ -109,8 +106,8 @@
waiters.wait_for_volume_resource_status(self.volumes_client,
new_volume['id'], 'available')
- params = {name_field: volume[name_field],
- descrip_field: volume[descrip_field]}
+ params = {'name': volume['name'],
+ 'description': volume['description']}
self.volumes_client.update_volume(new_volume['id'], **params)
if 'imageRef' in kwargs:
@@ -118,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):
@@ -141,7 +138,3 @@
origin = self.create_volume()
self._volume_create_get_update_delete(source_volid=origin['id'],
size=CONF.volume.volume_size)
-
-
-class VolumesV1GetTest(VolumesV2GetTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index d1da95d..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.common.utils import data_utils
+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
@@ -222,7 +225,156 @@
'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))
-class VolumesV1ListTestJSON(VolumesV2ListTestJSON):
- _api_version = 1
- VOLUME_FIELDS = ('id', 'display_name')
+ _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 28e65ed..5ba72c3 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -14,164 +14,133 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
+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 VolumesV2NegativeTest(base.BaseVolumeTest):
+class VolumesNegativeTest(base.BaseVolumeTest):
@classmethod
def resource_setup(cls):
- super(VolumesV2NegativeTest, cls).resource_setup()
-
- cls.name_field = cls.special_fields['name_field']
+ 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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 = {self.name_field: 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):
@@ -183,14 +152,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.
@@ -199,7 +168,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.
@@ -208,7 +177,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.
@@ -217,7 +186,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.
@@ -225,7 +194,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.
@@ -233,21 +202,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.
@@ -259,26 +228,26 @@
# 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')
- params = {self.name_field: v_name}
+ params = {'name': v_name}
fetched_volume = self.volumes_client.list_volumes(
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')
- params = {self.name_field: v_name}
+ params = {'name': v_name}
fetched_volume = \
self.volumes_client.list_volumes(
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'}
@@ -286,7 +255,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'}
@@ -294,7 +263,3 @@
self.volumes_client.list_volumes(detail=True,
params=params)['volumes']
self.assertEqual(0, len(fetched_volume))
-
-
-class VolumesV1NegativeTest(VolumesV2NegativeTest):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 5abda5e..8ffc99d 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -13,28 +13,26 @@
from testtools import matchers
from tempest.api.volume import base
-from tempest.common.utils import data_utils
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest import test
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()
- cls.name_field = cls.special_fields['name_field']
- cls.descrip_field = cls.special_fields['descrip_field']
@decorators.idempotent_id('b467b54c-07a4-446d-a1cf-651dedcc3ff1')
@test.services('compute')
@@ -104,40 +102,42 @@
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(),
matchers.ContainsAll(metadata.items()))
# Compare also with the output from the list action
- tracking_data = (snapshot['id'], snapshot[self.name_field])
+ tracking_data = (snapshot['id'], snapshot['name'])
snaps_list = self.snapshots_client.list_snapshots()['snapshots']
- snaps_data = [(f['id'], f[self.name_field]) for f in snaps_list]
+ snaps_data = [(f['id'], f['name']) for f in snaps_list]
self.assertIn(tracking_data, snaps_data)
# Updates snapshot with new values
new_s_name = data_utils.rand_name(
self.__class__.__name__ + '-new-snap')
new_desc = 'This is the new description of snapshot.'
- params = {self.name_field: new_s_name,
- self.descrip_field: new_desc}
+ params = {'name': new_s_name,
+ 'description': new_desc}
update_snapshot = self.snapshots_client.update_snapshot(
snapshot['id'], **params)['snapshot']
# Assert response body for update_snapshot method
- self.assertEqual(new_s_name, update_snapshot[self.name_field])
- self.assertEqual(new_desc, update_snapshot[self.descrip_field])
+ self.assertEqual(new_s_name, update_snapshot['name'])
+ self.assertEqual(new_desc, update_snapshot['description'])
# Assert response body for show_snapshot method
updated_snapshot = self.snapshots_client.show_snapshot(
snapshot['id'])['snapshot']
- self.assertEqual(new_s_name, updated_snapshot[self.name_field])
- self.assertEqual(new_desc, updated_snapshot[self.descrip_field])
+ self.assertEqual(new_s_name, updated_snapshot['name'])
+ self.assertEqual(new_desc, updated_snapshot['description'])
# Delete the snapshot
self.delete_snapshot(snapshot['id'])
@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)
@@ -158,7 +158,3 @@
# Should allow
self.assertEqual(volume['snapshot_id'], src_snap['id'])
self.assertEqual(volume['size'], src_size + 1)
-
-
-class VolumesV1SnapshotTestJSON(VolumesV2SnapshotTestJSON):
- _api_version = 1
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index a0eaa00..507df1f 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -17,22 +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()
- cls.name_field = cls.special_fields['name_field']
+
# 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."""
@@ -59,7 +62,7 @@
def test_snapshots_list_with_params(self):
"""list snapshots with params."""
# Verify list snapshots by display_name filter
- params = {self.name_field: self.snapshot[self.name_field]}
+ params = {'name': self.snapshot['name']}
self._list_by_param_values_and_assert(**params)
# Verify list snapshots by status filter
@@ -68,21 +71,21 @@
# Verify list snapshots by status and display name filter
params = {'status': 'available',
- self.name_field: self.snapshot[self.name_field]}
+ 'name': self.snapshot['name']}
self._list_by_param_values_and_assert(**params)
@decorators.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
def test_snapshots_list_details_with_params(self):
"""list snapshot details with params."""
# Verify list snapshot details by display_name filter
- params = {self.name_field: self.snapshot[self.name_field]}
+ params = {'name': self.snapshot['name']}
self._list_by_param_values_and_assert(with_detail=True, **params)
# Verify list snapshot details by status filter
params = {'status': 'available'}
self._list_by_param_values_and_assert(with_detail=True, **params)
# Verify list snapshot details by status and display name filter
params = {'status': 'available',
- self.name_field: self.snapshot[self.name_field]}
+ 'name': self.snapshot['name']}
self._list_by_param_values_and_assert(with_detail=True, **params)
@decorators.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
@@ -98,12 +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)
-class VolumesV1SnapshotLimitTestJSON(VolumesV2SnapshotListTestJSON):
- _api_version = 1
+ @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 9e44379..ea5f036 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -11,24 +11,23 @@
# under the License.
from tempest.api.volume import base
-from tempest.common.utils import data_utils
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 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,13 +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')
-class VolumesV1SnapshotNegativeTestJSON(VolumesV2SnapshotNegativeTestJSON):
- _api_version = 1
+ @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 e5581b9..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.common.utils import data_utils
-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 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 31fc1eb..0000000
--- a/tempest/api/volume/v3/base.py
+++ /dev/null
@@ -1,65 +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
-
- 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 9cb918a..dbaacda 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -125,6 +125,8 @@
self.image_v2.NamespacePropertiesClient()
self.namespace_tags_client = \
self.image_v2.NamespaceTagsClient()
+ self.image_versions_client = \
+ self.image_v2.VersionsClient()
def _set_compute_clients(self):
self.agents_client = self.compute.AgentsClient()
@@ -157,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 = (
@@ -166,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(
@@ -227,6 +227,8 @@
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)
# 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
@@ -260,9 +262,11 @@
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()
+ self.volume_v3_versions_client = self.volume_v3.VersionsClient()
self.volume_types_client = self.volume_v1.TypesClient()
self.volume_types_v2_client = self.volume_v2.TypesClient()
self.volume_hosts_client = self.volume_v1.HostsClient()
@@ -281,6 +285,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)
@@ -288,6 +294,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 99da983..9587ffe 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -13,14 +13,27 @@
# See the License for the specific language governing permissions and
# 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
from tempest.common import fixed_network
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
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
@@ -113,14 +126,23 @@
if wait_until is None:
wait_until = 'ACTIVE'
+ if 'user_data' not in kwargs:
+ # If nothing overrides the default user data script then run
+ # a simple script on the host to print networking info. This is
+ # to aid in debugging ssh failures.
+ script = '''
+ #!/bin/sh
+ echo "Printing {user} user authorized keys"
+ cat ~{user}/.ssh/authorized_keys || true
+ '''.format(user=CONF.validation.image_ssh_user)
+ script_clean = textwrap.dedent(script).lstrip().encode('utf8')
+ script_b64 = base64.b64encode(script_clean)
+ kwargs['user_data'] = script_b64
+
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)
@@ -210,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 b7c776b..9319d2a 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -11,17 +11,12 @@
# under the License.
import re
-import sys
import time
-import netaddr
-import six
-
from oslo_log import log as logging
from tempest import config
-from tempest.lib.common import ssh
-from tempest.lib.common.utils import test_utils
+from tempest.lib.common.utils.linux import remote_client
import tempest.lib.exceptions
CONF = config.CONF
@@ -29,39 +24,11 @@
LOG = logging.getLogger(__name__)
-def debug_ssh(function):
- """Decorator to generate extra debug info in case off SSH failure"""
- def wrapper(self, *args, **kwargs):
- try:
- return function(self, *args, **kwargs)
- except tempest.lib.exceptions.SSHTimeout:
- try:
- original_exception = sys.exc_info()
- caller = test_utils.find_test_caller() or "not found"
- if self.server:
- msg = 'Caller: %s. Timeout trying to ssh to server %s'
- LOG.debug(msg, caller, self.server)
- if self.log_console and self.servers_client:
- try:
- msg = 'Console log for server %s: %s'
- console_log = (
- self.servers_client.get_console_output(
- self.server['id'])['output'])
- LOG.debug(msg, self.server['id'], console_log)
- except Exception:
- msg = 'Could not get console_log for server %s'
- LOG.debug(msg, self.server['id'])
- # re-raise the original ssh timeout exception
- six.reraise(*original_exception)
- finally:
- # Delete the traceback to avoid circular references
- _, _, trace = original_exception
- del trace
- return wrapper
+class RemoteClient(remote_client.RemoteClient):
-
-class RemoteClient(object):
-
+ # TODO(oomichi): Make this class deprecated after migrating
+ # necessary methods to tempest.lib and cleaning
+ # unnecessary methods up from this class.
def __init__(self, ip_address, username, password=None, pkey=None,
server=None, servers_client=None):
"""Executes commands in a VM over ssh
@@ -73,36 +40,18 @@
:param server: server dict, used for debugging purposes
:param servers_client: servers client, used for debugging purposes
"""
- self.server = server
- self.servers_client = servers_client
+ super(RemoteClient, self).__init__(
+ ip_address, username, password=password, pkey=pkey,
+ server=server, servers_client=servers_client,
+ ssh_timeout=CONF.validation.ssh_timeout,
+ connect_timeout=CONF.validation.connect_timeout,
+ console_output_enabled=CONF.compute_feature_enabled.console_output,
+ ssh_shell_prologue=CONF.validation.ssh_shell_prologue,
+ ping_count=CONF.validation.ping_count,
+ ping_size=CONF.validation.ping_size)
- ssh_timeout = CONF.validation.ssh_timeout
- connect_timeout = CONF.validation.connect_timeout
- self.log_console = CONF.compute_feature_enabled.console_output
- self.ssh_shell_prologue = CONF.validation.ssh_shell_prologue
- self.ping_count = CONF.validation.ping_count
- self.ping_size = CONF.validation.ping_size
-
- self.ssh_client = ssh.Client(ip_address, username, password,
- ssh_timeout, pkey=pkey,
- channel_timeout=connect_timeout)
-
- @debug_ssh
- def exec_command(self, cmd):
- # Shell options below add more clearness on failures,
- # path is extended for some non-cirros guest oses (centos7)
- cmd = self.ssh_shell_prologue + " " + cmd
- LOG.debug("Remote command: %s", cmd)
- return self.ssh_client.exec_command(cmd)
-
- @debug_ssh
- def validate_authentication(self):
- """Validate ssh connection and authentication
-
- This method raises an Exception when the validation fails.
- """
- self.ssh_client.test_connection_auth()
-
+ # Note that this method will not work on SLES11 guests, as they do
+ # not support the TYPE column on lsblk
def get_disks(self):
# Select root disk devices as shown by lsblk
command = 'lsblk -lb --nodeps'
@@ -132,25 +81,6 @@
cmd = 'sudo sh -c "echo \\"%s\\" >/dev/console"' % message
return self.exec_command(cmd)
- def ping_host(self, host, count=None, size=None, nic=None):
- if count is None:
- count = self.ping_count
- if size is None:
- size = self.ping_size
-
- addr = netaddr.IPAddress(host)
- cmd = 'ping6' if addr.version == 6 else 'ping'
- if nic:
- cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
- cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
- 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
@@ -166,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/validation_resources.py b/tempest/common/validation_resources.py
index 88697c4..b15796f 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -15,7 +15,7 @@
from tempest import config
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
CONF = config.CONF
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 b79f502..00c69b0 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -217,14 +217,6 @@
"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 version of OpenStack now "
- "supports 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.
@@ -291,7 +283,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 +328,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 +369,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 +493,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.")
]
@@ -514,14 +504,27 @@
ImageFeaturesGroup = [
cfg.BoolOpt('api_v2',
default=True,
- help="Is the v2 image API enabled"),
+ help="Is the v2 image API enabled",
+ deprecated_for_removal=True,
+ deprecated_reason='Glance v1 APIs are deprecated and v2 APIs '
+ 'are current one. In future, Tempest will '
+ 'test v2 APIs only so this config option '
+ 'will be removed.'),
cfg.BoolOpt('api_v1',
default=True,
- help="Is the v1 image API enabled"),
+ help="Is the v1 image API enabled",
+ deprecated_for_removal=True,
+ deprecated_reason='Glance v1 APIs are deprecated and v2 APIs '
+ 'are current one. In future, Tempest will '
+ 'test v2 APIs only so this config option '
+ 'will be removed.'),
cfg.BoolOpt('deactivate_image',
default=False,
help="Is the deactivate-image feature enabled."
- " The feature has been integrated since Kilo."),
+ " The feature has been integrated since Kilo.",
+ deprecated_for_removal=True,
+ deprecated_reason="All supported versions of OpenStack now "
+ "support the 'deactivate_image' feature"),
]
network_group = cfg.OptGroup(name='network',
@@ -598,15 +601,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.")
]
network_feature_group = cfg.OptGroup(name='network-feature-enabled',
@@ -668,9 +662,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 '
@@ -680,13 +672,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."),
@@ -711,9 +697,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',
@@ -757,6 +741,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. "
@@ -796,14 +786,21 @@
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 '
'entry all which indicates every extension is enabled. '
'Empty list indicates all extensions are disabled'),
cfg.BoolOpt('api_v1',
- default=True,
- help="Is the v1 volume API enabled"),
+ default=False,
+ help="Is the v1 volume API enabled",
+ deprecated_for_removal=True,
+ deprecated_reason="The v1 volume API has been deprecated "
+ "since Juno release, and the API will be "
+ "removed."),
cfg.BoolOpt('api_v2',
default=True,
help="Is the v2 volume API enabled"),
@@ -984,9 +981,6 @@
cfg.BoolOpt('heat',
default=False,
help="Whether or not Heat is expected to be available"),
- cfg.BoolOpt('sahara',
- default=False,
- help="Whether or not Sahara is expected to be available"),
]
debug_group = cfg.OptGroup(name="debug",
@@ -1090,6 +1084,12 @@
return getattr(_CONF, attr)
def _set_attrs(self):
+ # This methods ensures that config options in Tempest as well as
+ # in Tempest plugins can be accessed via:
+ # CONF.<normalised_group_name>.<key_name>
+ # where:
+ # normalised_group_name = group_name.replace('-', '_')
+ # Attributes are set at __init__ time *only* for known option groups
self.auth = _CONF.auth
self.compute = _CONF.compute
self.compute_feature_enabled = _CONF['compute-feature-enabled']
@@ -1111,6 +1111,23 @@
self.service_available = _CONF.service_available
self.debug = _CONF.debug
logging.tempest_set_log_file('tempest.log')
+ # Setting attributes for plugins
+ # NOTE(andreaf) Plugins have no access to the TempestConfigPrivate
+ # instance at discovery time, so they have no way of setting these
+ # aliases themselves.
+ ext_plugins = plugins.TempestTestPluginManager()
+ for group, _ in ext_plugins.get_plugin_options_list():
+ if isinstance(group, cfg.OptGroup):
+ # If we have an OptGroup
+ group_name = group.name
+ group_dest = group.dest
+ else:
+ # If we have a group name as a string
+ group_name = group
+ group_dest = group.replace('-', '_')
+ # NOTE(andreaf) We can set the attribute safely here since in
+ # case of name conflict we would not have reached this point.
+ setattr(self, group_dest, _CONF[group_name])
def __init__(self, parse_conf=True, config_path=None):
"""Initialize a configuration from a conf directory and conf file."""
@@ -1188,6 +1205,12 @@
# loaded, options registered, and _config is set.
_register_tempest_service_clients()
+ # Registering service clients and pushing their configuration to
+ # the service clients register. Doing this in the config module
+ # ensures that the configuration is available by the time we
+ # discover tests from plugins.
+ plugins.TempestTestPluginManager()._register_service_clients()
+
return getattr(self._config, attr)
def set_config_path(self, path):
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/hacking/ignored_list_T110.txt b/tempest/hacking/ignored_list_T110.txt
index be875ee..0e7e894 100644
--- a/tempest/hacking/ignored_list_T110.txt
+++ b/tempest/hacking/ignored_list_T110.txt
@@ -1,4 +1 @@
./tempest/services/object_storage/object_client.py
-./tempest/services/volume/base/base_qos_client.py
-./tempest/services/volume/base/base_backups_client.py
-./tempest/services/baremetal/base.py
diff --git a/tempest/hacking/ignored_list_T111.txt b/tempest/hacking/ignored_list_T111.txt
deleted file mode 100644
index 8017e76..0000000
--- a/tempest/hacking/ignored_list_T111.txt
+++ /dev/null
@@ -1 +0,0 @@
-./tempest/services/baremetal/base.py
diff --git a/tempest/lib/api_schema/response/compute/v2_1/quotas.py b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
index 7953983..44f5bdf 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/quotas.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/quotas.py
@@ -60,6 +60,124 @@
get_quota_set['response_body']['properties']['quota_set']['required'].extend([
'id'])
+get_quota_set_details = copy.deepcopy(get_quota_set)
+get_quota_set_details['response_body']['properties']['quota_set'][
+ 'properties'] = {
+ 'id': {'type': 'string'},
+ 'instances': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'cores': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'ram': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'floating_ips': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'fixed_ips': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'metadata_items': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'key_pairs': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'security_groups': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'security_group_rules': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'server_group_members': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'server_groups': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'injected_files': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'injected_file_content_bytes': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ },
+ 'injected_file_path_bytes': {
+ 'type': 'object',
+ 'properties': {
+ 'reserved': {'type': 'integer'},
+ 'limit': {'type': 'integer'},
+ 'in_use': {'type': 'integer'}
+ }
+ }
+}
+
delete_quota = {
'status_code': [202]
}
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 4ccca6f..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',
@@ -564,3 +566,19 @@
update_attached_volume = {
'status_code': [202]
}
+
+evacuate_server = {
+ 'status_code': [200]
+}
+
+evacuate_server_with_admin_pass = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'adminPass': {'type': 'string'}
+ },
+ 'additionalProperties': False,
+ 'required': ['adminPass']
+ }
+}
diff --git a/tempest/lib/api_schema/response/compute/v2_26/servers.py b/tempest/lib/api_schema/response/compute/v2_26/servers.py
index d873402..b03bdf6 100644
--- a/tempest/lib/api_schema/response/compute/v2_26/servers.py
+++ b/tempest/lib/api_schema/response/compute/v2_26/servers.py
@@ -52,12 +52,7 @@
'response_body': {
'type': 'object',
'properties': {
- 'tags': {
- 'type': 'array',
- 'items': {
- 'type': 'string'
- }
- }
+ 'tags': tag_items,
},
'additionalProperties': False,
'required': ['tags']
diff --git a/tempest/api/volume/admin/v2/__init__.py b/tempest/lib/api_schema/response/volume/__init__.py
similarity index 100%
copy from tempest/api/volume/admin/v2/__init__.py
copy to tempest/lib/api_schema/response/volume/__init__.py
diff --git a/tempest/lib/api_schema/response/volume/versions.py b/tempest/lib/api_schema/response/volume/versions.py
new file mode 100644
index 0000000..2391a8c
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/versions.py
@@ -0,0 +1,60 @@
+# 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.
+
+
+list_versions = {
+ 'status_code': [300],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'versions': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'status': {'type': 'string'},
+ 'updated': {'type': 'string'},
+ 'id': {'type': 'string'},
+ 'links': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'href': {'type': 'string',
+ 'format': 'uri'},
+ 'rel': {'type': 'string'},
+ 'type': {'type': 'string'},
+ },
+ 'required': ['href', 'rel']
+ }
+ },
+ 'min_version': {'type': 'string'},
+ 'version': {'type': 'string'},
+ 'media-types': {
+ 'type': 'array',
+ 'properties': {
+ 'base': {'type': 'string'},
+ 'type': {'type': 'string'}
+ },
+ 'required': ['base', 'type']
+ }
+ },
+ 'required': ['status', 'updated', 'id', 'links',
+ 'min_version', 'version', 'media-types']
+ }
+ }
+ },
+ 'required': ['versions'],
+ }
+}
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/common/cred_client.py b/tempest/lib/common/cred_client.py
index ea06011..a81f53c 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -59,7 +59,8 @@
def _check_role_exists(self, role_name):
try:
roles = self._list_roles()
- role = next(r for r in roles if r['name'] == role_name)
+ lc_role_name = role_name.lower()
+ role = next(r for r in roles if r['name'].lower() == lc_role_name)
except StopIteration:
return None
return role
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 3246d6a..99ba6ab 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -732,12 +732,21 @@
if resp.status < 400:
return
- JSON_ENC = ['application/json', 'application/json; charset=utf-8']
+ # NOTE(zhipengh): There is a purposefully duplicate of content-type
+ # with the only difference is with or without spaces, as specified
+ # in RFC7231.
+ JSON_ENC = ['application/json', 'application/json; charset=utf-8',
+ 'application/json;charset=utf-8']
+
# NOTE(mtreinish): This is for compatibility with Glance and swift
# APIs. These are the return content types that Glance api v1
# (and occasionally swift) are using.
+ # NOTE(zhipengh): There is a purposefully duplicate of content-type
+ # with the only difference is with or without spaces, as specified
+ # in RFC7231.
TXT_ENC = ['text/plain', 'text/html', 'text/html; charset=utf-8',
- 'text/plain; charset=utf-8']
+ 'text/plain; charset=utf-8', 'text/html;charset=utf-8',
+ 'text/plain;charset=utf-8']
if ctype.lower() in JSON_ENC:
parse_resp = True
diff --git a/tempest/api/volume/admin/v2/__init__.py b/tempest/lib/common/utils/linux/__init__.py
similarity index 100%
rename from tempest/api/volume/admin/v2/__init__.py
rename to tempest/lib/common/utils/linux/__init__.py
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
new file mode 100644
index 0000000..64d6be2
--- /dev/null
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -0,0 +1,117 @@
+# 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 sys
+
+import netaddr
+from oslo_log import log as logging
+import six
+
+from tempest.lib.common import ssh
+from tempest.lib.common.utils import test_utils
+import tempest.lib.exceptions
+
+LOG = logging.getLogger(__name__)
+
+
+def debug_ssh(function):
+ """Decorator to generate extra debug info in case off SSH failure"""
+ def wrapper(self, *args, **kwargs):
+ try:
+ return function(self, *args, **kwargs)
+ except tempest.lib.exceptions.SSHTimeout:
+ try:
+ original_exception = sys.exc_info()
+ caller = test_utils.find_test_caller() or "not found"
+ if self.server:
+ msg = 'Caller: %s. Timeout trying to ssh to server %s'
+ LOG.debug(msg, caller, self.server)
+ if self.console_output_enabled and self.servers_client:
+ try:
+ msg = 'Console log for server %s: %s'
+ console_log = (
+ self.servers_client.get_console_output(
+ self.server['id'])['output'])
+ LOG.debug(msg, self.server['id'], console_log)
+ except Exception:
+ msg = 'Could not get console_log for server %s'
+ LOG.debug(msg, self.server['id'])
+ # re-raise the original ssh timeout exception
+ six.reraise(*original_exception)
+ finally:
+ # Delete the traceback to avoid circular references
+ _, _, trace = original_exception
+ del trace
+ return wrapper
+
+
+class RemoteClient(object):
+
+ def __init__(self, ip_address, username, password=None, pkey=None,
+ server=None, servers_client=None, ssh_timeout=300,
+ connect_timeout=60, console_output_enabled=True,
+ ssh_shell_prologue="set -eu -o pipefail; PATH=$$PATH:/sbin;",
+ ping_count=1, ping_size=56):
+ """Executes commands in a VM over ssh
+
+ :param ip_address: IP address to ssh to
+ :param username: Ssh username
+ :param password: Ssh password
+ :param pkey: Ssh public key
+ :param server: Server dict, used for debugging purposes
+ :param servers_client: Servers client, used for debugging purposes
+ :param ssh_timeout: Timeout in seconds to wait for the ssh banner
+ :param connect_timeout: Timeout in seconds to wait for TCP connection
+ :param console_output_enabled: Support serial console output?
+ :param ssh_shell_prologue: Shell fragments to use before command
+ :param ping_count: Number of ping packets
+ :param ping_size: Packet size for ping packets
+ """
+ self.server = server
+ self.servers_client = servers_client
+ self.console_output_enabled = console_output_enabled
+ self.ssh_shell_prologue = ssh_shell_prologue
+ self.ping_count = ping_count
+ self.ping_size = ping_size
+
+ self.ssh_client = ssh.Client(ip_address, username, password,
+ ssh_timeout, pkey=pkey,
+ channel_timeout=connect_timeout)
+
+ @debug_ssh
+ def exec_command(self, cmd):
+ # Shell options below add more clearness on failures,
+ # path is extended for some non-cirros guest oses (centos7)
+ cmd = self.ssh_shell_prologue + " " + cmd
+ LOG.debug("Remote command: %s", cmd)
+ return self.ssh_client.exec_command(cmd)
+
+ @debug_ssh
+ def validate_authentication(self):
+ """Validate ssh connection and authentication
+
+ This method raises an Exception when the validation fails.
+ """
+ self.ssh_client.test_connection_auth()
+
+ def ping_host(self, host, count=None, size=None, nic=None):
+ if count is None:
+ count = self.ping_count
+ if size is None:
+ size = self.ping_size
+
+ addr = netaddr.IPAddress(host)
+ cmd = 'ping6' if addr.version == 6 else 'ping'
+ if nic:
+ cmd = 'sudo {cmd} -I {nic}'.format(cmd=cmd, nic=nic)
+ cmd += ' -c{0} -w{0} -s{1} {2}'.format(count, size, host)
+ return self.exec_command(cmd)
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/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 265969a..daf4bc0 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -14,6 +14,7 @@
# under the License.
from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
from tempest.lib.api_schema.response.compute.v2_1 import quotas as schema
from tempest.lib.common import rest_client
@@ -22,15 +23,29 @@
class QuotasClient(base_compute_client.BaseComputeClient):
- def show_quota_set(self, tenant_id, user_id=None):
- """List the quota set for a tenant."""
+ def show_quota_set(self, tenant_id, user_id=None, detail=False):
+ """List the quota set for a tenant.
+ For a full list of available parameters, please refer to the official
+ API reference:
+ http://developer.openstack.org/api-ref-compute-v2.1.html/#show-a-quota
+ http://developer.openstack.org/api-ref-compute-v2.1.html/#show-the-detail-of-quota
+ """
+
+ params = {}
url = 'os-quota-sets/%s' % tenant_id
+ if detail:
+ url += '/detail'
if user_id:
- url += '?user_id=%s' % user_id
+ params.update({'user_id': user_id})
+ if params:
+ url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url)
body = json.loads(body)
- self.validate_response(schema.get_quota_set, resp, body)
+ if detail:
+ self.validate_response(schema.get_quota_set_details, resp, body)
+ else:
+ self.validate_response(schema.get_quota_set, resp, body)
return rest_client.ResponseBody(resp, body)
def show_default_quota_set(self, tenant_id):
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index b37afb3..0d355a1 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -814,3 +814,19 @@
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.delete_tag, resp, body)
return rest_client.ResponseBody(resp, body)
+
+ def evacuate_server(self, server_id, **kwargs):
+ """Evacuate the given server.
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/compute/#evacuate-server-evacuate-action
+ """
+ if self.enable_instance_password:
+ evacuate_schema = schema.evacuate_server_with_admin_pass
+ else:
+ evacuate_schema = schema.evacuate_server
+
+ return self.action(server_id, 'evacuate',
+ evacuate_schema,
+ **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..1489b50 100644
--- a/tempest/lib/services/identity/v3/__init__.py
+++ b/tempest/lib/services/identity/v3/__init__.py
@@ -20,6 +20,8 @@
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
@@ -36,4 +38,5 @@
'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
'PoliciesClient', 'ProjectsClient', 'RegionsClient',
'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
- 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
+ 'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient',
+ 'OAUTHConsumerClient']
diff --git a/tempest/lib/services/identity/v3/identity_client.py b/tempest/lib/services/identity/v3/identity_client.py
index 8177e35..755c14b 100644
--- a/tempest/lib/services/identity/v3/identity_client.py
+++ b/tempest/lib/services/identity/v3/identity_client.py
@@ -43,3 +43,10 @@
resp, body = self.delete("auth/tokens", headers=headers)
self.expected_success(204, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def list_auth_projects(self):
+ """Get available project scopes."""
+ resp, body = self.get("auth/projects")
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ 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/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 7d973e5..99a5321 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -25,7 +25,9 @@
from tempest.lib.services.image.v2.resource_types_client import \
ResourceTypesClient
from tempest.lib.services.image.v2.schemas_client import SchemasClient
+from tempest.lib.services.image.v2.versions_client import VersionsClient
__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespaceObjectsClient',
'NamespacePropertiesClient', 'NamespaceTagsClient',
- 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
+ 'NamespacesClient', 'ResourceTypesClient', 'SchemasClient',
+ 'VersionsClient']
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/image/v2/versions_client.py b/tempest/lib/services/image/v2/versions_client.py
new file mode 100644
index 0000000..1adc466
--- /dev/null
+++ b/tempest/lib/services/image/v2/versions_client.py
@@ -0,0 +1,38 @@
+# Copyright 2017 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class VersionsClient(rest_client.RestClient):
+ api_version = "v2"
+
+ def list_versions(self):
+ """List API versions"""
+ 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)
+
+ self.expected_success(300, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
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 b6de5b9..240be13 100644
--- a/tempest/lib/services/volume/v2/capabilities_client.py
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -19,13 +19,14 @@
class CapabilitiesClient(rest_client.RestClient):
+ api_version = "v2"
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 637254b..0d04f85 100644
--- a/tempest/lib/services/volume/v2/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -19,12 +19,14 @@
class SchedulerStatsClient(rest_client.RestClient):
+ api_version = "v2"
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..853948e
--- /dev/null
+++ b/tempest/lib/services/volume/v2/transfers_client.py
@@ -0,0 +1,80 @@
+# 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, **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
+ """
+ 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:
+ 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/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py
index a4600a8..72ab785 100644
--- a/tempest/lib/services/volume/v3/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -14,5 +14,6 @@
from tempest.lib.services.volume.v3.base_client import BaseClient
from tempest.lib.services.volume.v3.messages_client import MessagesClient
+from tempest.lib.services.volume.v3.versions_client import VersionsClient
-__all__ = ['MessagesClient', 'BaseClient']
+__all__ = ['MessagesClient', 'BaseClient', 'VersionsClient']
diff --git a/tempest/lib/services/volume/v3/versions_client.py b/tempest/lib/services/volume/v3/versions_client.py
new file mode 100644
index 0000000..e2941c4
--- /dev/null
+++ b/tempest/lib/services/volume/v3/versions_client.py
@@ -0,0 +1,47 @@
+# Copyright 2017 NEC Corporation. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import time
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.api_schema.response.volume import versions as schema
+from tempest.lib.common import rest_client
+from tempest.lib.services.volume.v3 import base_client
+
+
+class VersionsClient(base_client.BaseClient):
+
+ def list_versions(self):
+ """List API versions
+
+ For a full list of available parameters, please refer to the official
+ API reference:
+ https://developer.openstack.org/api-ref/block-storage/v3/#list-all-api-versions
+ """
+ version_url = self._get_base_version_url()
+
+ start = time.time()
+ resp, body = self.raw_request(version_url, 'GET')
+ end = time.time()
+ # NOTE: We need a raw_request() here instead of request() call because
+ # "list API versions" API doesn't require an authentication and we can
+ # skip it with raw_request() call.
+ 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)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index ff48abe..3418a2f 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -23,12 +23,12 @@
from tempest.common import compute
from tempest.common import image as common_image
-from tempest.common.utils import data_utils
from tempest.common.utils.linux import remote_client
from tempest.common.utils import net_utils
from tempest.common import waiters
from tempest import config
from tempest import exceptions
+from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from tempest.lib import exceptions as lib_exc
import tempest.test
@@ -79,13 +79,8 @@
cls.security_groups_client = cls.manager.security_groups_client
cls.security_group_rules_client = (
cls.manager.security_group_rules_client)
-
- if CONF.volume_feature_enabled.api_v2:
- cls.volumes_client = cls.manager.volumes_v2_client
- cls.snapshots_client = cls.manager.snapshots_v2_client
- else:
- cls.volumes_client = cls.manager.volumes_client
- cls.snapshots_client = cls.manager.snapshots_client
+ cls.volumes_client = cls.manager.volumes_v2_client
+ cls.snapshots_client = cls.manager.snapshots_v2_client
# ## Test functions library
#
@@ -235,12 +230,7 @@
volume['id'])
self.addCleanup(test_utils.call_and_ignore_notfound_exc,
self.volumes_client.delete_volume, volume['id'])
-
- # NOTE(e0ne): Cinder API v2 uses name instead of display_name
- if 'display_name' in volume:
- self.assertEqual(name, volume['display_name'])
- else:
- self.assertEqual(name, volume['name'])
+ self.assertEqual(name, volume['name'])
waiters.wait_for_volume_resource_status(self.volumes_client,
volume['id'], 'available')
# The volume retrieved on creation has a non-up-to-date status.
@@ -421,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)
@@ -888,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))
@@ -1251,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 b0b516a..8408a1e 100644
--- a/tempest/scenario/test_aggregates_basic_ops.py
+++ b/tempest/scenario/test_aggregates_basic_ops.py
@@ -14,7 +14,7 @@
# under the License.
from tempest.common import tempest_fixtures as fixtures
-from tempest.common.utils import data_utils
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -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 5bbc039..756ca4d 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -104,7 +104,6 @@
def _setup_network_and_servers(self, **kwargs):
boot_with_port = kwargs.pop('boot_with_port', False)
- self.security_group = self._create_security_group()
self.network, self.subnet, self.router = self.create_networks(**kwargs)
self.check_networks()
@@ -152,7 +151,9 @@
def _create_server(self, network, port_id=None):
keypair = self.create_keypair()
self.keypairs[keypair['name']] = keypair
- security_groups = [{'name': self.security_group['name']}]
+ security_groups = [
+ {'name': self._create_security_group()['name']}
+ ]
network = {'uuid': network['id']}
if port_id is not None:
network['port'] = port_id
@@ -285,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+):')
@@ -350,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):
@@ -406,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."""
@@ -419,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
@@ -495,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
@@ -529,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.
@@ -613,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
@@ -659,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
@@ -711,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.
@@ -791,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
@@ -832,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 d9b93fe..daf4d13 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -72,11 +72,11 @@
if dualnet - create IPv6 subnets on a different network
:return: list of created networks
"""
- self.network = self._create_network()
+ network = self._create_network()
if dualnet:
- self.network_v6 = self._create_network()
+ network_v6 = self._create_network()
- sub4 = self._create_subnet(network=self.network,
+ sub4 = self._create_subnet(network=network,
namestart='sub4',
ip_version=4)
@@ -90,7 +90,7 @@
self.subnets_v6 = []
for _ in range(n_subnets6):
- net6 = self.network_v6 if dualnet else self.network
+ net6 = network_v6 if dualnet else network
sub6 = self._create_subnet(network=net6,
namestart='sub6',
ip_version=6,
@@ -105,7 +105,7 @@
router['id'], subnet_id=sub6['id'])
self.subnets_v6.append(sub6)
- return [self.network, self.network_v6] if dualnet else [self.network]
+ return [network, network_v6] if dualnet else [network]
@staticmethod
def define_server_ips(srv):
@@ -121,8 +121,6 @@
def prepare_server(self, networks=None):
username = CONF.validation.image_ssh_user
- networks = networks or [self.network]
-
srv = self.create_server(
key_name=self.keypair['name'],
security_groups=[{'name': self.sec_grp['name']}],
@@ -134,7 +132,7 @@
username=username)
return ssh, ips, srv["id"]
- def turn_nic6_on(self, ssh, sid):
+ def turn_nic6_on(self, ssh, sid, network_id):
"""Turns the IPv6 vNIC on
Required because guest images usually set only the first vNIC on boot.
@@ -142,18 +140,21 @@
@param ssh: RemoteClient ssh instance to server
@param sid: server uuid
+ @param network_id: the network id the NIC is connected to
"""
ports = [
p["mac_address"] for p in
self.admin_manager.ports_client.list_ports(
- device_id=sid, network_id=self.network_v6['id'])['ports']
+ device_id=sid, network_id=network_id)['ports']
]
+
self.assertEqual(1, len(ports),
message=("Multiple IPv6 ports found on network %s. "
"ports: %s")
- % (self.network_v6, ports))
+ % (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,
@@ -168,8 +169,9 @@
# Turn on 2nd NIC for Cirros when dualnet
if dualnet:
- self.turn_nic6_on(sshv4_1, sid1)
- self.turn_nic6_on(sshv4_2, sid2)
+ network, network_v6 = net_list
+ self.turn_nic6_on(sshv4_1, sid1, network_v6['id'])
+ self.turn_nic6_on(sshv4_2, sid2, network_v6['id'])
# get addresses assigned to vNIC as reported by 'ip address' utility
ips_from_ip_1 = sshv4_1.exec_command("ip address")
@@ -204,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 fa12f33..2116fe8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,9 +15,9 @@
from oslo_log import log
import testtools
-from tempest.common.utils import data_utils
from tempest.common.utils import net_info
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -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 1960e9a..6005e28 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -37,21 +37,19 @@
"""
@classmethod
- def skip_checks(cls):
- super(TestServerAdvancedOps, cls).skip_checks()
- if CONF.compute.flavor_ref_alt == CONF.compute.flavor_ref:
- msg = "Skipping test - flavor_ref and flavor_ref_alt are identical"
- raise cls.skipException(msg)
-
- @classmethod
def setup_credentials(cls):
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.')
+ @testtools.skipUnless(CONF.compute.flavor_ref !=
+ CONF.compute.flavor_ref_alt
+ and CONF.compute.flavor_ref_alt != "",
+ 'The flavor_ref_alt option should not be empty and '
+ 'should not be identical with flavor_ref')
@test.services('compute', 'volume')
def test_resize_volume_backed_server_confirm(self):
# We create an instance for use in this test
@@ -70,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 aabb767..debd664 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -16,9 +16,9 @@
from oslo_log import log as logging
import testtools
-from tempest.common.utils import data_utils
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
@@ -68,10 +68,7 @@
volume['id'], 'available')
waiters.wait_for_volume_resource_status(self.snapshots_client,
snapshot['id'], 'available')
- if 'display_name' in snapshot:
- self.assertEqual(snapshot_name, snapshot['display_name'])
- else:
- self.assertEqual(snapshot_name, snapshot['name'])
+ self.assertEqual(snapshot_name, snapshot['name'])
return snapshot
def _wait_for_volume_available_on_the_system(self, ip_address,
@@ -88,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 8cab19c..3dfbf18 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -13,9 +13,9 @@
from oslo_log import log as logging
import testtools
-from tempest.common.utils import data_utils
from tempest.common import waiters
from tempest import config
+from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
from tempest.scenario import manager
from tempest import test
@@ -81,13 +81,7 @@
self.addCleanup(self.snapshots_client.delete_snapshot, snap['id'])
waiters.wait_for_volume_resource_status(self.snapshots_client,
snap['id'], 'available')
-
- # NOTE(e0ne): Cinder API v2 uses name instead of display_name
- if 'display_name' in snap:
- self.assertEqual(snap_name, snap['display_name'])
- else:
- self.assertEqual(snap_name, snap['name'])
-
+ self.assertEqual(snap_name, snap['name'])
return snap
def _delete_server(self, server):
@@ -176,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..2ebdc60 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
@@ -642,12 +614,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 abe2b73..9f75962 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -46,10 +46,42 @@
"""Add additional configuration options to tempest.
This method will be run for the plugin during the register_opts()
- function in tempest.config
+ function in tempest.config.
: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)
"""
return
@@ -71,43 +103,46 @@
in any ServiceClients object instantiated by tests.
The default implementation returns an empty list.
- :return list of dictionaries. Each element of the list represents
- the service client for an API. Each dict must define all
- parameters required for the invocation of
- `service_clients.ServiceClients.register_service_client_module`.
- :rtype: list
+ :returns: Each element of the list represents the service client for an
+ API. Each dict must define all parameters required for the invocation
+ 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 []
@@ -124,7 +159,6 @@
'tempest.test_plugins', invoke_on_load=True,
propagate_map_exceptions=True,
on_load_failure_callback=self.failure_hook)
- self._register_service_clients()
@staticmethod
def failure_hook(_, ep, err):
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 1af0d95..98bf145 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -192,7 +192,7 @@
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
- fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v2.0'}]}
+ fake_resp = {'versions': [{'id': 'v2.0'}]}
fake_resp = json.dumps(fake_resp)
mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
@@ -206,7 +206,7 @@
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
- fake_resp = {'versions': [{'id': 'v1.0'}, {'id': 'v3.0'}]}
+ fake_resp = {'versions': [{'id': 'v3.0'}]}
fake_resp = json.dumps(fake_resp)
mock_request.return_value = (None, fake_resp)
fake_os = mock.MagicMock()
@@ -231,11 +231,9 @@
with mock.patch.object(verify_tempest_config,
'print_and_or_update') as print_mock:
verify_tempest_config.verify_cinder_api_versions(fake_os, True)
- print_mock.assert_any_call('api_v1', 'volume-feature-enabled',
- False, True)
print_mock.assert_any_call('api_v3', 'volume-feature-enabled',
True, True)
- self.assertEqual(2, print_mock.call_count)
+ self.assertEqual(1, print_mock.call_count)
def test_verify_glance_version_no_v2_with_v1_1(self):
def fake_get_versions():
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
index 7199206..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):
@@ -182,7 +165,7 @@
user='user',
password='pass')))
self.log = self.useFixture(fixtures.FakeLogger(
- name='tempest.common.utils.linux.remote_client',
+ name='tempest.lib.common.utils.linux.remote_client',
level='DEBUG'))
def test_validate_debug_ssh_console(self):
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/api/volume/admin/v2/__init__.py b/tempest/tests/lib/common/utils/linux/__init__.py
similarity index 100%
copy from tempest/api/volume/admin/v2/__init__.py
copy to tempest/tests/lib/common/utils/linux/__init__.py
diff --git a/tempest/tests/lib/common/utils/linux/test_remote_client.py b/tempest/tests/lib/common/utils/linux/test_remote_client.py
new file mode 100644
index 0000000..cf312f4
--- /dev/null
+++ b/tempest/tests/lib/common/utils/linux/test_remote_client.py
@@ -0,0 +1,67 @@
+# Copyright 2017 NEC Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from tempest.lib.common import ssh
+from tempest.lib.common.utils.linux import remote_client
+from tempest.lib import exceptions as lib_exc
+from tempest.tests import base
+
+
+class FakeServersClient(object):
+
+ def get_console_output(self, server_id):
+ return {"output": "fake_output"}
+
+
+class TestRemoteClient(base.TestCase):
+
+ @mock.patch.object(ssh.Client, 'exec_command', return_value='success')
+ def test_exec_command(self, mock_ssh_exec_command):
+ client = remote_client.RemoteClient('192.168.1.10', 'username')
+ client.exec_command('ls')
+ mock_ssh_exec_command.assert_called_once_with(
+ 'set -eu -o pipefail; PATH=$$PATH:/sbin; ls')
+
+ @mock.patch.object(ssh.Client, 'test_connection_auth')
+ def test_validate_authentication(self, mock_test_connection_auth):
+ client = remote_client.RemoteClient('192.168.1.10', 'username')
+ client.validate_authentication()
+ mock_test_connection_auth.assert_called_once_with()
+
+ @mock.patch.object(remote_client.LOG, 'debug')
+ @mock.patch.object(ssh.Client, 'exec_command')
+ def test_debug_ssh_without_console(self, mock_exec_command, mock_debug):
+ mock_exec_command.side_effect = lib_exc.SSHTimeout
+ server = {'id': 'fake_id'}
+ client = remote_client.RemoteClient('192.168.1.10', 'username',
+ server=server)
+ self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
+ mock_debug.assert_called_with(
+ 'Caller: %s. Timeout trying to ssh to server %s',
+ 'TestRemoteClient:test_debug_ssh_without_console', server)
+
+ @mock.patch.object(remote_client.LOG, 'debug')
+ @mock.patch.object(ssh.Client, 'exec_command')
+ def test_debug_ssh_with_console(self, mock_exec_command, mock_debug):
+ mock_exec_command.side_effect = lib_exc.SSHTimeout
+ server = {'id': 'fake_id'}
+ client = remote_client.RemoteClient('192.168.1.10', 'username',
+ server=server,
+ servers_client=FakeServersClient())
+ self.assertRaises(lib_exc.SSHTimeout, client.exec_command, 'ls')
+ mock_debug.assert_called_with(
+ 'Console log for server %s: %s', server['id'], 'fake_output')
diff --git a/tempest/tests/lib/services/compute/test_quotas_client.py b/tempest/tests/lib/services/compute/test_quotas_client.py
index 4c49e8d..bbb8eb7 100644
--- a/tempest/tests/lib/services/compute/test_quotas_client.py
+++ b/tempest/tests/lib/services/compute/test_quotas_client.py
@@ -49,22 +49,35 @@
self.client = quotas_client.QuotasClient(
fake_auth, 'compute', 'regionOne')
- def _test_show_quota_set(self, bytes_body=False, user_id=None):
+ def _get_quota_set(self, detail):
+ if not detail:
+ return self.FAKE_QUOTA_SET
+ fake_quota_set = {"quota_set": {}}
+ for key, val in self.FAKE_QUOTA_SET['quota_set'].items():
+ fake_quota_set['quota_set'][key] = \
+ {'limit': val, 'reserved': 0, 'in_use': 0}
+ fake_quota_set['quota_set']['id'] = "8421f7be61064f50b680465c07f334af"
+ return fake_quota_set
+
+ def _test_show_quota_set(self, bytes_body=False, detail=False,
+ user_id=None):
if user_id:
self.check_service_client_function(
self.client.show_quota_set,
'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_QUOTA_SET,
+ self._get_quota_set(detail),
to_utf=bytes_body,
tenant_id=self.project_id,
+ detail=detail,
user_id=user_id)
else:
self.check_service_client_function(
self.client.show_quota_set,
'tempest.lib.common.rest_client.RestClient.get',
- self.FAKE_QUOTA_SET,
+ self._get_quota_set(detail),
to_utf=bytes_body,
- tenant_id=self.project_id)
+ tenant_id=self.project_id,
+ detail=detail)
def test_show_quota_set_with_str_body(self):
self._test_show_quota_set()
@@ -78,6 +91,9 @@
def test_show_quota_set_for_user_with_bytes_body(self):
self._test_show_quota_set(bytes_body=True, user_id=self.fake_user_id)
+ def test_show_quota_set_with_details(self):
+ self._test_show_quota_set(detail=True)
+
def _test_show_default_quota_set(self, bytes_body=False):
self.check_service_client_function(
self.client.show_default_quota_set,
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index 8d391c1..a277dfe 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -168,6 +168,10 @@
"url": "http://os.co/v2/616fb98f-46ca-475e-917e-2563e5a8cd19"
}
+ FAKE_SERVER_PASSWORD = {
+ "adminPass": "fake-password",
+ }
+
FAKE_INSTANCE_ACTION_EVENTS = {
"event": "fake-event",
"start_time": "2016-10-02T10:00:00-05:00",
@@ -322,6 +326,21 @@
name='fake-name'
)
+ def test_evacuate_server_with_str_body(self):
+ self._test_evacuate_server()
+
+ def test_evacuate_server_with_bytes_body(self):
+ self._test_evacuate_server(bytes_body=True)
+
+ def _test_evacuate_server(self, bytes_body=False):
+ kwargs = {'server_id': self.server_id,
+ 'host': 'fake-target-host'}
+ self.check_service_client_function(
+ self.client.evacuate_server,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SERVER_PASSWORD,
+ **kwargs)
+
def test_change_password_with_str_body(self):
self._test_change_password()
diff --git a/tempest/tests/lib/services/identity/v3/test_identity_client.py b/tempest/tests/lib/services/identity/v3/test_identity_client.py
index 9eaaaaf..e435fe2 100644
--- a/tempest/tests/lib/services/identity/v3/test_identity_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_identity_client.py
@@ -32,6 +32,34 @@
"description": "test_description"
}
+ FAKE_AUTH_PROJECTS = {
+ "projects": [
+ {
+ "domain_id": "1789d1",
+ "enabled": True,
+ "id": "263fd9",
+ "links": {
+ "self": "https://example.com/identity/v3/projects/263fd9"
+ },
+ "name": "Test Group"
+ },
+ {
+ "domain_id": "1789d1",
+ "enabled": True,
+ "id": "50ef01",
+ "links": {
+ "self": "https://example.com/identity/v3/projects/50ef01"
+ },
+ "name": "Build Group"
+ }
+ ],
+ "links": {
+ "self": "https://example.com/identity/v3/auth/projects",
+ "previous": None,
+ "next": None
+ }
+ }
+
def setUp(self):
super(TestIdentityClient, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -54,6 +82,13 @@
bytes_body,
resp_token="cbc36478b0bd8e67e89")
+ def _test_list_auth_projects(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_auth_projects,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ self.FAKE_AUTH_PROJECTS,
+ bytes_body)
+
def test_show_api_description_with_str_body(self):
self._test_show_api_description()
@@ -73,3 +108,9 @@
{},
resp_token="cbc36478b0bd8e67e89",
status=204)
+
+ def test_list_auth_projects_with_str_body(self):
+ self._test_list_auth_projects()
+
+ def test_list_auth_projects_with_bytes_body(self):
+ self._test_list_auth_projects(bytes_body=True)
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/image/v2/test_versions_client.py b/tempest/tests/lib/services/image/v2/test_versions_client.py
new file mode 100644
index 0000000..6234b06
--- /dev/null
+++ b/tempest/tests/lib/services/image/v2/test_versions_client.py
@@ -0,0 +1,94 @@
+# Copyright 2017 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.lib.services.image.v2 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+ FAKE_VERSIONS_INFO = {
+ "versions": [
+ {
+ "status": "CURRENT", "id": "v2.5",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.4",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.3",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.2",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.1",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "SUPPORTED", "id": "v2.0",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v2/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "DEPRECATED", "id": "v1.1",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+ ]
+ },
+ {
+ "status": "DEPRECATED", "id": "v1.0",
+ "links": [
+ {"href": "https://10.220.1.21:9292/v1/", "rel": "self"}
+ ]
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestVersionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = versions_client.VersionsClient(fake_auth,
+ 'image',
+ 'regionOne')
+
+ def _test_list_versions(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_versions,
+ 'tempest.lib.common.rest_client.RestClient.raw_request',
+ self.FAKE_VERSIONS_INFO,
+ bytes_body,
+ 300)
+
+ def test_list_versions_with_str_body(self):
+ self._test_list_versions()
+
+ def test_list_versions_with_bytes_body(self):
+ self._test_list_versions(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/v3/test_versions_client.py b/tempest/tests/lib/services/volume/v3/test_versions_client.py
new file mode 100644
index 0000000..9627b9a
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_versions_client.py
@@ -0,0 +1,91 @@
+# Copyright 2017 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.lib.services.volume.v3 import versions_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestVersionsClient(base.BaseServiceTest):
+
+ FAKE_VERSIONS_INFO = {
+ "versions": [
+ {
+ "status": "DEPRECATED", "updated": "2016-05-02T20:25:19Z",
+ "links": [
+ {"href": "http://docs.openstack.org/", "type": "text/html",
+ "rel": "describedby"},
+ {"href": "https://10.30.197.39:8776/v1/", "rel": "self"}
+ ],
+ "min_version": "",
+ "version": "",
+ "media-types": [
+ {"base": "application/json",
+ "type": "application/vnd.openstack.volume+json;version=1"}
+ ],
+ "id": "v1.0"
+ },
+ {
+ "status": "DEPRECATED", "updated": "2017-02-25T12:00:00Z",
+ "links": [
+ {"href": "http://docs.openstack.org/", "type": "text/html",
+ "rel": "describedby"},
+ {"href": "https://10.30.197.39:8776/v2/", "rel": "self"}
+ ],
+ "min_version": "",
+ "version": "",
+ "media-types": [
+ {"base": "application/json",
+ "type": "application/vnd.openstack.volume+json;version=1"}
+ ],
+ "id": "v2.0"
+ },
+ {
+ "status": "CURRENT", "updated": "2016-02-08T12:20:21Z",
+ "links": [
+ {"href": "http://docs.openstack.org/", "type": "text/html",
+ "rel": "describedby"},
+ {"href": "https://10.30.197.39:8776/v3/", "rel": "self"}
+ ],
+ "min_version": "3.0",
+ "version": "3.28",
+ "media-types": [
+ {"base": "application/json",
+ "type": "application/vnd.openstack.volume+json;version=1"}
+ ],
+ "id": "v3.0"
+ }
+ ]
+ }
+
+ def setUp(self):
+ super(TestVersionsClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = versions_client.VersionsClient(fake_auth,
+ 'volume',
+ 'regionOne')
+
+ def _test_list_versions(self, bytes_body=False):
+ self.check_service_client_function(
+ self.client.list_versions,
+ 'tempest.lib.common.rest_client.RestClient.raw_request',
+ self.FAKE_VERSIONS_INFO,
+ bytes_body,
+ 300)
+
+ def test_list_versions_with_str_body(self):
+ self._test_list_versions()
+
+ def test_list_versions_with_bytes_body(self):
+ self._test_list_versions(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/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 dfa8332..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
@@ -11,7 +11,6 @@
BRANCH_NAME=master
CLIENT_NAME=tempest
deps =
- setuptools
-r{toxinidir}/requirements.txt
[testenv]
@@ -37,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]